Skip to content

Commit

Permalink
Add support for sharing text (incomplete)
Browse files Browse the repository at this point in the history
  • Loading branch information
kasemmohamed committed Jun 18, 2019
1 parent 5040425 commit 864b835
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 216 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ class ReceiveSharingIntentPlugin(val registrar: Registrar) :
latestIntentData = value
changeReceiverImage?.onReceive(context, intent)
}
intent.action == Intent.ACTION_VIEW -> {
(intent.type == null || intent.type?.startsWith("text") == true)
&& (intent.action == Intent.ACTION_SEND
|| intent.action == Intent.ACTION_VIEW) -> {
val value = intent.dataString
if (initial) initialLink = value
latestLink = value
Expand Down Expand Up @@ -131,7 +133,9 @@ class ReceiveSharingIntentPlugin(val registrar: Registrar) :
intent?.type?.startsWith("image") == true
&& (intent.action == Intent.ACTION_SEND
|| intent.action == Intent.ACTION_SEND_MULTIPLE) -> getImageUris(intent)
intent?.action == Intent.ACTION_VIEW -> intent.dataString
(intent?.type == null || intent.type?.startsWith("text") == true)
&& (intent?.action == Intent.ACTION_SEND
|| intent?.action == Intent.ACTION_VIEW) -> intent.dataString
else -> null
}

Expand Down
17 changes: 17 additions & 0 deletions example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>

<!--TODO: Change the url host name you want to support-->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="example.com"
android:pathPrefix="/invite"/>
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
Expand Down
6 changes: 6 additions & 0 deletions example/ios/Runner/Runner.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:getpool.io</string>
</array>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.kasem.sharing</string>
Expand Down
4 changes: 4 additions & 0 deletions example/ios/Sharing Extension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<string>1</string>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>100</integer>
</dict>
Expand Down
138 changes: 91 additions & 47 deletions example/ios/Sharing Extension/ShareViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import Photos
class ShareViewController: SLComposeServiceViewController {

let sharedKey = "ImageSharePhotoKey"
var imagesData: [String] = []
var sharedData: [String] = []
let imageContentType = kUTTypeImage as String
let textContentType = kUTTypePropertyList as String

override func isContentValid() -> Bool {
return true
Expand All @@ -24,65 +26,102 @@ class ShareViewController: SLComposeServiceViewController {
// 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 {
let contentType = kUTTypeImage as String

if let contents = content.attachments {
for (index, attachment) in (contents as! [NSItemProvider]).enumerated() {
if attachment.hasItemConformingToTypeIdentifier(contentType) {
attachment.loadItem(forTypeIdentifier: contentType, options: nil) { [weak self] data, error in

if error == nil, let url = data as? URL, let this = self {

for component in url.path.components(separatedBy: "/") where component.contains("IMG_") {

// photo: /var/mobile/Media/DCIM/101APPLE/IMG_1320.PNG
// edited photo: /var/mobile/Media/PhotoData/Mutations/DCIM/101APPLE/IMG_1309/Adjustments/FullSizeRender.jpg

// cut file's suffix if have, get file name like IMG_1309.
let fileName = component.components(separatedBy: ".").first!
if let asset = this.imageAssetDictionary[fileName] {
this.imagesData.append( asset.localIdentifier)
}
break
}

// If this is the last item, save imagesData in userDefaults and redirect to host app
if index == (content.attachments?.count)! - 1 {
// TODO: IMPROTANT: This should be your host app bundle identiefier
let hostAppBundleIdentiefier = "com.kasem.sharing"
let userDefaults = UserDefaults(suiteName: "group.\(hostAppBundleIdentiefier)")
userDefaults?.set(this.imagesData, forKey: this.sharedKey)
userDefaults?.synchronize()
this.redirectToHostApp()
this.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}

} else {
print("GETTING ERROR")
let alert = UIAlertController(title: "Error", message: "Error loading image", preferredStyle: .alert)

let action = UIAlertAction(title: "Error", style: .cancel) { _ in
self?.dismiss(animated: true, completion: nil)
}

alert.addAction(action)
self?.present(alert, animated: true, completion: nil)
self?.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
}

if attachment.hasItemConformingToTypeIdentifier(imageContentType) {
handleImages(content: content, attachment: attachment, index: index)
} else if attachment.hasItemConformingToTypeIdentifier(textContentType) {
handleText(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 redirectToHostApp() {
let url = URL(string: "SharePhotos://dataUrl=\(sharedKey)")
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.sharedData.append( item)

// If this is the last item, save imagesData in userDefaults and redirect to host app
if index == (content.attachments?.count)! - 1 {
// TODO: IMPROTANT: This should be your host app bundle identiefier
let hostAppBundleIdentiefier = "com.kasem.sharing"
let userDefaults = UserDefaults(suiteName: "group.\(hostAppBundleIdentiefier)")
userDefaults?.set(this.sharedData, forKey: this.sharedKey)
userDefaults?.synchronize()
this.redirectToHostApp(type: .text)
this.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}

} 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 {

for component in url.path.components(separatedBy: "/") where component.contains("IMG_") {

// photo: /var/mobile/Media/DCIM/101APPLE/IMG_1320.PNG
// edited photo: /var/mobile/Media/PhotoData/Mutations/DCIM/101APPLE/IMG_1309/Adjustments/FullSizeRender.jpg

// cut file's suffix if have, get file name like IMG_1309.
let fileName = component.components(separatedBy: ".").first!
if let asset = this.imageAssetDictionary[fileName] {
this.sharedData.append( asset.localIdentifier)
}
break
}

// If this is the last item, save imagesData in userDefaults and redirect to host app
if index == (content.attachments?.count)! - 1 {
// TODO: IMPROTANT: This should be your host app bundle identiefier
let hostAppBundleIdentiefier = "com.kasem.sharing"
let userDefaults = UserDefaults(suiteName: "group.\(hostAppBundleIdentiefier)")
userDefaults?.set(this.sharedData, forKey: this.sharedKey)
userDefaults?.synchronize()
this.redirectToHostApp(type: .image)
this.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}

} else {
self?.dismissWithError()
}
}
}

private func dismissWithError(){
print("GETTING ERROR")
let alert = UIAlertController(title: "Error", message: "Error loading image", 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: "SharePhotos://dataUrl=\(sharedKey)#\(type)")
var responder = self as UIResponder?
let selectorOpenURL = sel_registerName("openURL:")

Expand All @@ -94,6 +133,11 @@ class ShareViewController: SLComposeServiceViewController {
}
}

enum RedirectType {
case image
case text
}

/// Key is the matched asset's original file name without suffix. E.g. IMG_193
private lazy var imageAssetDictionary: [String : PHAsset] = {

Expand Down
40 changes: 33 additions & 7 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,44 @@ class MyApp extends StatefulWidget {

class _MyAppState extends State<MyApp> {
StreamSubscription _intentDataStreamSubscription;
List<Uri> _sharedFiles;
List<String> _sharedFiles;
String _sharedText;

@override
void initState() {
super.initState();

// For sharing images coming from outside the app while the app is in the memory
_intentDataStreamSubscription =
ReceiveSharingIntent.getIntentDataStreamAsUri().listen(
(List<Uri> uris) {
ReceiveSharingIntent.getIntentDataStream().listen((List<String> value) {
setState(() {
_sharedFiles = uris;
_sharedFiles = value;
});
}, onError: (err) {
print("Latest Intent Data error: $err");
});

// For sharing images coming from outside the app while the app is closed
ReceiveSharingIntent.getInitialIntentDataAsUri().then((List<Uri> uris) {
ReceiveSharingIntent.getInitialIntentData().then((List<String> value) {
setState(() {
_sharedFiles = uris;
_sharedFiles = value;
});
});

// For sharing or opening urls/text coming from outside the app while the app is in the memory
_intentDataStreamSubscription =
ReceiveSharingIntent.getLinkStream().listen((String value) {
setState(() {
_sharedText = value;
});
}, onError: (err) {
print("Latest Intent Data error: $err");
});

// For sharing or opening urls/text coming from outside the app while the app is closed
ReceiveSharingIntent.getInitialLink().then((String value) {
setState(() {
_sharedText = value;
});
});
}
Expand All @@ -45,13 +62,22 @@ class _MyAppState extends State<MyApp> {

@override
Widget build(BuildContext context) {
const textStyleBold = const TextStyle(fontWeight: FontWeight.bold);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Text('Number of shared files: ${_sharedFiles?.length ?? 0}'),
child: Column(
children: <Widget>[
Text("Shared files:", style: textStyleBold),
Text(_sharedFiles?.join(",") ?? ""),
SizedBox(height: 100),
Text("Shared urls/text:", style: textStyleBold),
Text(_sharedText ?? "")
],
),
),
),
);
Expand Down
Loading

0 comments on commit 864b835

Please sign in to comment.