From a6bc4939c2176e227f656dde720d1fb79afdba14 Mon Sep 17 00:00:00 2001
From: Corentin <2089620+shajz@users.noreply.github.com>
Date: Fri, 30 Apr 2021 17:55:22 +0200
Subject: [PATCH] ADD [Android/iOS] Allow multiple mime types & multiple files
share (#2)
Co-authored-by: lucca-dev <>
---
hooks/androidIntentFilters.js | 76 ++++
hooks/iosAddTarget.js | 11 +-
hooks/iosCopyShareExtension.js | 6 +-
hooks/iosRemoveTarget.js | 6 +-
hooks/npmInstall.js | 30 +-
hooks/utils.js | 10 +
package.json | 2 +
plugin.xml | 32 +-
src/ios/OpenWithPlugin.m | 380 ++++++++--------
.../ShareExtension/ShareExtension-Info.plist | 78 ++--
src/ios/ShareExtension/ShareViewController.h | 1 -
src/ios/ShareExtension/ShareViewController.m | 406 +++++++++---------
www/openwith.js | 265 ++++++------
13 files changed, 698 insertions(+), 605 deletions(-)
create mode 100644 hooks/androidIntentFilters.js
create mode 100644 hooks/utils.js
diff --git a/hooks/androidIntentFilters.js b/hooks/androidIntentFilters.js
new file mode 100644
index 0000000..bfae795
--- /dev/null
+++ b/hooks/androidIntentFilters.js
@@ -0,0 +1,76 @@
+const fs = require("fs");
+const path = require("path");
+const et = require("elementtree");
+
+// Parses a given file into an elementtree object
+function parseElementtreeSync(filename) {
+ var contents = fs.readFileSync(filename, "utf-8");
+ if (contents) {
+ //Windows is the BOM. Skip the Byte Order Mark.
+ contents = contents.substring(contents.indexOf("<"));
+ }
+ return new et.ElementTree(et.XML(contents));
+}
+
+function updateMimeTypes(manifest, mimeTypes) {
+ const tempManifest = parseElementtreeSync(manifest);
+ const root = tempManifest.getroot();
+
+ const parent = "application/activity";
+
+ mimeTypes.forEach((mimeType) => {
+ const parentEl = root.find(parent);
+
+ const intentFilter = new et.Element("intent-filter");
+ intentFilter.append(
+ new et.Element("data", { "android:mimeType": mimeType })
+ );
+ intentFilter.append(
+ new et.Element("action", {
+ "android:name": "android.intent.action.SEND",
+ })
+ );
+ intentFilter.append(
+ new et.Element("action", {
+ "android:name": "android.intent.action.SEND_MULTIPLE",
+ })
+ );
+ intentFilter.append(
+ new et.Element("category", {
+ "android:name": "android.intent.category.DEFAULT",
+ })
+ );
+ parentEl.append(intentFilter);
+ });
+
+ fs.writeFileSync(manifest, tempManifest.write({ indent: 4 }), "utf-8");
+}
+
+module.exports = function (context) {
+ // Prevent double execution
+ if (
+ context.hook == "after_prepare" &&
+ !RegExp("\\s+prepare").test(context.cmdLine)
+ ) {
+ return "";
+ }
+
+ const packageJson = require(path.join(
+ context.opts.projectRoot,
+ "package.json"
+ ));
+
+ const manifestPath = path.join(
+ context.opts.projectRoot,
+ "platforms",
+ "android",
+ "app",
+ "src",
+ "main",
+ "AndroidManifest.xml"
+ );
+ const pluginProperties = packageJson.cordova.plugins["cc.fovea.cordova.openwith"];
+
+ const mimeTypes = pluginProperties["ANDROID_MIME_TYPES"].split(",");
+ updateMimeTypes(manifestPath, mimeTypes);
+};
diff --git a/hooks/iosAddTarget.js b/hooks/iosAddTarget.js
index 68cc801..12b81ce 100755
--- a/hooks/iosAddTarget.js
+++ b/hooks/iosAddTarget.js
@@ -29,7 +29,7 @@
// THE SOFTWARE.
//
-const PLUGIN_ID = 'cc.fovea.cordova.openwith';
+const { PLUGIN_ID, redError } = require("./utils");
const BUNDLE_SUFFIX = '.shareextension';
var fs = require('fs');
@@ -37,10 +37,6 @@ var path = require('path');
var packageJson;
var bundleIdentifier;
-function redError(message) {
- return new Error('"' + PLUGIN_ID + '" \x1b[1m\x1b[31m' + message + '\x1b[0m');
-}
-
function replacePreferencesInFile(filePath, preferences) {
var content = fs.readFileSync(filePath, 'utf8');
for (var i = 0; i < preferences.length; i++) {
@@ -138,7 +134,7 @@ function getCordovaParameter(configXml, variableName) {
function getBundleId(context, configXml) {
var elementTree = require('elementtree');
var etree = elementTree.parse(configXml);
- return etree.getroot().get('id');
+ return etree.getroot().get('ios-CFBundleIdentifier');
}
function parsePbxProject(context, pbxProjectPath) {
@@ -206,9 +202,6 @@ function getPreferences(context, configXml, projectName) {
}, {
key: '__URL_SCHEME__',
value: getCordovaParameter(configXml, 'IOS_URL_SCHEME')
- }, {
- key: '__UNIFORM_TYPE_IDENTIFIER__',
- value: getCordovaParameter(configXml, 'IOS_UNIFORM_TYPE_IDENTIFIER')
}];
}
diff --git a/hooks/iosCopyShareExtension.js b/hooks/iosCopyShareExtension.js
index 4fe160a..155c804 100644
--- a/hooks/iosCopyShareExtension.js
+++ b/hooks/iosCopyShareExtension.js
@@ -29,13 +29,9 @@
// THE SOFTWARE.
//
+const { PLUGIN_ID, redError } = require("./utils");
var fs = require('fs');
var path = require('path');
-const PLUGIN_ID = "cc.fovea.cordova.openwith";
-
-function redError(message) {
- return new Error('"' + PLUGIN_ID + '" \x1b[1m\x1b[31m' + message + '\x1b[0m');
-}
function getPreferenceValue (config, name) {
var value = config.match(new RegExp('name="' + name + '" value="(.*?)"', "i"));
diff --git a/hooks/iosRemoveTarget.js b/hooks/iosRemoveTarget.js
index 12d1cf3..2ddce4e 100644
--- a/hooks/iosRemoveTarget.js
+++ b/hooks/iosRemoveTarget.js
@@ -29,16 +29,12 @@
// THE SOFTWARE.
//
-const PLUGIN_ID = "cc.fovea.cordova.openwith";
+const { PLUGIN_ID, redError } = require("./utils");
const BUNDLE_SUFFIX = ".shareextension";
var fs = require('fs');
var path = require('path');
-function redError(message) {
- return new Error('"' + PLUGIN_ID + '" \x1b[1m\x1b[31m' + message + '\x1b[0m');
-}
-
// Determine the full path to the app's xcode project file.
function findXCodeproject(context, callback) {
fs.readdir(iosFolder(context), function(err, data) {
diff --git a/hooks/npmInstall.js b/hooks/npmInstall.js
index f712ece..a319b85 100644
--- a/hooks/npmInstall.js
+++ b/hooks/npmInstall.js
@@ -18,22 +18,24 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
-const PLUGIN_ID = "cc.fovea.cordova.openwith";
+const { PLUGIN_ID } = require("./utils");
module.exports = function (context) {
- var child_process = require('child_process');
- var deferral = require('q').defer();
+ var child_process = require("child_process");
+ var deferral = require("q").defer();
- console.log('Installing "' + PLUGIN_ID + '" dependencies');
- child_process.exec('npm install --production', {cwd:__dirname}, function (error) {
- if (error !== null) {
- console.log('exec error: ' + error);
- deferral.reject('npm installation failed');
- }
- deferral.resolve();
- });
+ console.log('Installing "' + PLUGIN_ID + '" dependencies');
+ child_process.exec(
+ "npm install --production",
+ { cwd: __dirname },
+ function (error) {
+ if (error !== null) {
+ console.log("exec error: " + error);
+ deferral.reject("npm installation failed");
+ }
+ deferral.resolve();
+ }
+ );
- return deferral.promise;
+ return deferral.promise;
};
-
-// vim: ts=4:sw=4:et
diff --git a/hooks/utils.js b/hooks/utils.js
new file mode 100644
index 0000000..4c92baa
--- /dev/null
+++ b/hooks/utils.js
@@ -0,0 +1,10 @@
+const PLUGIN_ID = "cc.fovea.cordova.openwith";
+
+function redError(message) {
+ return new Error(`"${PLUGIN_ID}" \x1b[1m\x1b[31m${message}\x1b[0m`);
+}
+
+module.exports = {
+ PLUGIN_ID,
+ redError,
+};
diff --git a/package.json b/package.json
index 8d38604..104e9e2 100644
--- a/package.json
+++ b/package.json
@@ -72,10 +72,12 @@
"src/ios/ShareExtension/ShareViewController.m",
"www/openwith.js",
"www/test-openwith.js",
+ "hooks/androidIntentFilters.js",
"hooks/iosAddTarget.js",
"hooks/iosRemoveTarget.js",
"hooks/iosCopyShareExtension.js",
"hooks/npmInstall.js",
+ "hooks/utils.js",
"install-pmd",
"plugin.xml",
"LICENSE",
diff --git a/plugin.xml b/plugin.xml
index b6f0a45..2a1d1bf 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -24,26 +24,22 @@ SOFTWARE.
-->
+ xmlns:android="http://schemas.android.com/apk/res/android" id="cc.fovea.cordova.openwith" version="2.0.0">
OpenWith
- Cordova "Open With" plugin for iOS and Android
+ Cordova "Open With" plugin for iOS and Android
https://github.com/j3k0/cordova-plugin-openwith.git
https://github.com/j3k0/cordova-plugin-openwith/issues
- MIT
+ MIT
cordova,phonegap,openwith,ios,android
-
-
-
+
@@ -106,24 +102,11 @@ SOFTWARE.
-
-
-
-
-
-
- $ANDROID_EXTRA_ACTIONS
-
-
-
-
-
-
-
-
+
+
@@ -132,9 +115,10 @@ SOFTWARE.
+
-
diff --git a/src/ios/OpenWithPlugin.m b/src/ios/OpenWithPlugin.m
index 289e167..8e164e2 100644
--- a/src/ios/OpenWithPlugin.m
+++ b/src/ios/OpenWithPlugin.m
@@ -11,38 +11,38 @@ - (NSString*)convertToBase64;
@implementation NSData (Base64)
- (NSString*)convertToBase64 {
- const uint8_t* input = (const uint8_t*)[self bytes];
- NSInteger length = [self length];
-
- static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
-
- NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
- uint8_t* output = (uint8_t*)data.mutableBytes;
-
- NSInteger i;
- for (i=0; i < length; i += 3) {
- NSInteger value = 0;
- NSInteger j;
- for (j = i; j < (i + 3); j++) {
- value <<= 8;
-
- if (j < length) {
- value |= (0xFF & input[j]);
- }
- }
-
- NSInteger theIndex = (i / 3) * 4;
- output[theIndex + 0] = table[(value >> 18) & 0x3F];
- output[theIndex + 1] = table[(value >> 12) & 0x3F];
- output[theIndex + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
- output[theIndex + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
- }
-
- NSString *ret = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
+ const uint8_t* input = (const uint8_t*)[self bytes];
+ NSInteger length = [self length];
+
+ static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+ NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
+ uint8_t* output = (uint8_t*)data.mutableBytes;
+
+ NSInteger i;
+ for (i=0; i < length; i += 3) {
+ NSInteger value = 0;
+ NSInteger j;
+ for (j = i; j < (i + 3); j++) {
+ value <<= 8;
+
+ if (j < length) {
+ value |= (0xFF & input[j]);
+ }
+ }
+
+ NSInteger theIndex = (i / 3) * 4;
+ output[theIndex + 0] = table[(value >> 18) & 0x3F];
+ output[theIndex + 1] = table[(value >> 12) & 0x3F];
+ output[theIndex + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
+ output[theIndex + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
+ }
+
+ NSString *ret = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
#if ARC_DISABLED
- [ret autorelease];
+ [ret autorelease];
#endif
- return ret;
+ return ret;
}
@end
@@ -66,11 +66,11 @@ - (NSString*)convertToBase64 {
*/
@interface OpenWithPlugin : CDVPlugin {
- NSString* _loggerCallback;
- NSString* _handlerCallback;
- NSUserDefaults *_userDefaults;
- int _verbosityLevel;
- NSString *_backURL;
+ NSString* _loggerCallback;
+ NSString* _handlerCallback;
+ NSUserDefaults *_userDefaults;
+ int _verbosityLevel;
+ NSString *_backURL;
}
@property (nonatomic,retain) NSString* loggerCallback;
@@ -105,24 +105,24 @@ @implementation OpenWithPlugin
//
+ (void) load {
[[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(didFinishLaunching:)
- name:UIApplicationDidFinishLaunchingNotification
- object:nil];
+ selector:@selector(didFinishLaunching:)
+ name:UIApplicationDidFinishLaunchingNotification
+ object:nil];
}
+ (void) didFinishLaunching:(NSNotification*)notification {
- launchOptions = notification.userInfo;
+ launchOptions = notification.userInfo;
}
- (void) log:(int)level message:(NSString*)message {
- if (level >= self.verbosityLevel) {
- NSLog(@"[OpenWithPlugin.m]%@", message);
- if (self.loggerCallback != nil) {
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message];
- pluginResult.keepCallback = [NSNumber numberWithBool:YES];
- [self.commandDelegate sendPluginResult:pluginResult callbackId:self.loggerCallback];
- }
- }
+ if (level >= self.verbosityLevel) {
+ NSLog(@"[OpenWithPlugin.m]%@", message);
+ if (self.loggerCallback != nil) {
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message];
+ pluginResult.keepCallback = [NSNumber numberWithBool:YES];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.loggerCallback];
+ }
+ }
}
- (void) debug:(NSString*)message { [self log:VERBOSITY_DEBUG message:message]; }
- (void) info:(NSString*)message { [self log:VERBOSITY_INFO message:message]; }
@@ -130,176 +130,188 @@ - (void) warn:(NSString*)message { [self log:VERBOSITY_WARN message:message]; }
- (void) error:(NSString*)message { [self log:VERBOSITY_ERROR message:message]; }
- (void) pluginInitialize {
- // You can listen to more app notifications, see:
- // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4
-
- // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler
-
- // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil];
- // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
- // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
-
- // Added in 2.5.0
- // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:self.webView];
- //Added in 4.3.0
- // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillAppear:) name:CDVViewWillAppearNotification object:nil];
- // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidAppear:) name:CDVViewDidAppearNotification object:nil];
- // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillDisappear:) name:CDVViewWillDisappearNotification object:nil];
- // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidDisappear:) name:CDVViewDidDisappearNotification object:nil];
- // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillLayoutSubviews:) name:CDVViewWillLayoutSubviewsNotification object:nil];
- // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidLayoutSubviews:) name:CDVViewDidLayoutSubviewsNotification object:nil];
- // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillTransitionToSize:) name:CDVViewWillTransitionToSizeNotification object:nil];
- [self onReset];
- [self info:@"[pluginInitialize] OK"];
+ // You can listen to more app notifications, see:
+ // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4
+
+ // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler
+
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil];
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
+
+ // Added in 2.5.0
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:self.webView];
+ //Added in 4.3.0
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillAppear:) name:CDVViewWillAppearNotification object:nil];
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidAppear:) name:CDVViewDidAppearNotification object:nil];
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillDisappear:) name:CDVViewWillDisappearNotification object:nil];
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidDisappear:) name:CDVViewDidDisappearNotification object:nil];
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillLayoutSubviews:) name:CDVViewWillLayoutSubviewsNotification object:nil];
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidLayoutSubviews:) name:CDVViewDidLayoutSubviewsNotification object:nil];
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewWillTransitionToSize:) name:CDVViewWillTransitionToSizeNotification object:nil];
+ [self onReset];
+ [self info:@"[pluginInitialize] OK"];
}
- (void) onReset {
- [self info:@"[onReset]"];
- self.userDefaults = [[NSUserDefaults alloc] initWithSuiteName:SHAREEXT_GROUP_IDENTIFIER];
- self.verbosityLevel = VERBOSITY_INFO;
- self.loggerCallback = nil;
- self.handlerCallback = nil;
+ [self info:@"[onReset]"];
+ self.userDefaults = [[NSUserDefaults alloc] initWithSuiteName:SHAREEXT_GROUP_IDENTIFIER];
+ self.verbosityLevel = VERBOSITY_INFO;
+ self.loggerCallback = nil;
+ self.handlerCallback = nil;
}
- (void) onResume {
- [self debug:@"[onResume]"];
- [self checkForFileToShare];
+ [self debug:@"[onResume]"];
+ [self checkForFileToShare];
}
- (void) setVerbosity:(CDVInvokedUrlCommand*)command {
- NSNumber *value = [command argumentAtIndex:0
- withDefault:[NSNumber numberWithInt: VERBOSITY_INFO]
- andClass:[NSNumber class]];
- self.verbosityLevel = value.integerValue;
- [self.userDefaults setInteger:self.verbosityLevel forKey:@"verbosityLevel"];
- [self.userDefaults synchronize];
- [self debug:[NSString stringWithFormat:@"[setVerbosity] %d", self.verbosityLevel]];
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
- [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ NSNumber *value = [command argumentAtIndex:0
+ withDefault:[NSNumber numberWithInt: VERBOSITY_INFO]
+ andClass:[NSNumber class]];
+ self.verbosityLevel = value.integerValue;
+ [self.userDefaults setInteger:self.verbosityLevel forKey:@"verbosityLevel"];
+ [self.userDefaults synchronize];
+ [self debug:[NSString stringWithFormat:@"[setVerbosity] %d", self.verbosityLevel]];
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void) setLogger:(CDVInvokedUrlCommand*)command {
- self.loggerCallback = command.callbackId;
- [self debug:[NSString stringWithFormat:@"[setLogger] %@", self.loggerCallback]];
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT];
- pluginResult.keepCallback = [NSNumber numberWithBool:YES];
- [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ self.loggerCallback = command.callbackId;
+ [self debug:[NSString stringWithFormat:@"[setLogger] %@", self.loggerCallback]];
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT];
+ pluginResult.keepCallback = [NSNumber numberWithBool:YES];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void) setHandler:(CDVInvokedUrlCommand*)command {
- self.handlerCallback = command.callbackId;
- [self debug:[NSString stringWithFormat:@"[setHandler] %@", self.handlerCallback]];
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT];
- pluginResult.keepCallback = [NSNumber numberWithBool:YES];
- [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ self.handlerCallback = command.callbackId;
+ [self debug:[NSString stringWithFormat:@"[setHandler] %@", self.handlerCallback]];
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT];
+ pluginResult.keepCallback = [NSNumber numberWithBool:YES];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (NSString *)mimeTypeFromUti: (NSString*)uti {
- if (uti == nil) {
- return nil;
- }
- CFStringRef cret = UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)uti, kUTTagClassMIMEType);
- NSString *ret = (__bridge_transfer NSString *)cret;
- return ret == nil ? uti : ret;
+ if (uti == nil) {
+ return nil;
+ }
+ CFStringRef cret = UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)uti, kUTTagClassMIMEType);
+ NSString *ret = (__bridge_transfer NSString *)cret;
+ return ret == nil ? uti : ret;
+}
+
+- (NSDictionary*) mapToCordova: (NSDictionary*)object {
+ // Extract sharing data, make sure that it is valid
+ if (![object isKindOfClass:[NSDictionary class]]) {
+ [self debug:@"[checkForFileToShare] Data object is invalid"];
+ return nil;
+ }
+ NSDictionary *dict = (NSDictionary*)object;
+ NSData *data = dict[@"data"];
+ NSString *text = dict[@"text"];
+ NSString *name = dict[@"name"];
+ NSString *url = dict[@"url"];
+ NSString *path = dict[@"path"];
+ self.backURL = dict[@"backURL"];
+ NSString *type = [self mimeTypeFromUti:dict[@"uti"]];
+ if (![data isKindOfClass:NSData.class] || ![text isKindOfClass:NSString.class]) {
+ [self debug:@"[checkForFileToShare] Data content is invalid"];
+ return nil;
+ }
+ NSArray *utis = dict[@"utis"];
+ if (utis == nil) {
+ utis = @[];
+ }
+
+ // TODO: add the backURL to the shared intent, put it aside in the plugin
+ // TODO: implement cordova.openwith.exit(intent), will check if backURL is set
+
+ // Send to javascript
+ [self debug:[NSString stringWithFormat:
+ @"[checkForFileToShare] Sharing text \"%@\" and a %d bytes file",
+ text, data.length]];
+
+ NSString *uri = [NSString stringWithFormat: @"shareextension://index=0,name=%@,type=%@",
+ name, type];
+ return @{
+ @"text" : text,
+ @"base64": [data convertToBase64],
+ @"type": type,
+ @"utis": utis,
+ @"uri": uri,
+ @"name": name,
+ @"url": url,
+ @"path": path
+ };
}
- (void) checkForFileToShare {
- [self debug:@"[checkForFileToShare]"];
- if (self.handlerCallback == nil) {
- [self debug:@"[checkForFileToShare] javascript not ready yet."];
- return;
- }
-
- [self.userDefaults synchronize];
- NSObject *object = [self.userDefaults objectForKey:@"items"];
- if (object == nil) {
- [self debug:@"[checkForFileToShare] Nothing to share"];
- return;
- }
-
- // Clean-up the object, assume it's been handled from now, prevent double processing
- [self.userDefaults removeObjectForKey:@"items"];
-
- // Extract sharing data, make sure that it is valid
- if (![object isKindOfClass:[NSDictionary class]]) {
- [self debug:@"[checkForFileToShare] Data object is invalid"];
- return;
- }
- NSDictionary *dict = (NSDictionary*)object;
- NSData *data = dict[@"data"];
- NSString *text = dict[@"text"];
- NSString *name = dict[@"name"];
- NSString *url = dict[@"url"];
- NSString *path = dict[@"path"];
- self.backURL = dict[@"backURL"];
- NSString *type = [self mimeTypeFromUti:dict[@"uti"]];
- if (![data isKindOfClass:NSData.class] || ![text isKindOfClass:NSString.class]) {
- [self debug:@"[checkForFileToShare] Data content is invalid"];
- return;
- }
- NSArray *utis = dict[@"utis"];
- if (utis == nil) {
- utis = @[];
- }
-
- // TODO: add the backURL to the shared intent, put it aside in the plugin
- // TODO: implement cordova.openwith.exit(intent), will check if backURL is set
-
- // Send to javascript
- [self debug:[NSString stringWithFormat:
- @"[checkForFileToShare] Sharing text \"%@\" and a %d bytes file",
- text, data.length]];
-
- NSString *uri = [NSString stringWithFormat: @"shareextension://index=0,name=%@,type=%@",
- name, type];
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:@{
- @"action": @"SEND",
- @"exit": @YES,
- @"items": @[@{
- @"text" : text,
- @"base64": [data convertToBase64],
- @"type": type,
- @"utis": utis,
- @"uri": uri,
- @"name": name,
- @"url": url,
- @"path": path
- }]
- }];
- pluginResult.keepCallback = [NSNumber numberWithBool:YES];
- [self.commandDelegate sendPluginResult:pluginResult callbackId:self.handlerCallback];
+ [self debug:@"[checkForFileToShare]"];
+ if (self.handlerCallback == nil) {
+ [self debug:@"[checkForFileToShare] javascript not ready yet."];
+ return;
+ }
+
+ [self.userDefaults synchronize];
+ NSArray *object = [self.userDefaults objectForKey:@"items"];
+ if (object == nil) {
+ [self debug:@"[checkForFileToShare] Nothing to share"];
+ return;
+ }
+
+ NSMutableArray* items = [[NSMutableArray alloc] init];
+ for (NSDictionary* item in object) {
+ NSDictionary* cordovaItem = [self mapToCordova:item];
+ if (cordovaItem != nil) {
+ [items addObject:cordovaItem];
+ }
+ }
+
+ // Clean-up the object, assume it's been handled from now, prevent double processing
+ [self.userDefaults removeObjectForKey:@"items"];
+
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:@{
+ @"action": @"SEND",
+ @"exit": @NO,
+ @"items": items
+ }];
+ pluginResult.keepCallback = [NSNumber numberWithBool:YES];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.handlerCallback];
}
// Initialize the plugin
- (void) init:(CDVInvokedUrlCommand*)command {
- [self debug:@"[init]"];
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
- [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
- [self checkForFileToShare];
+ [self debug:@"[init]"];
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ [self checkForFileToShare];
}
// Load data from URL
- (void) load:(CDVInvokedUrlCommand*)command {
- [self debug:@"[load]"];
- // Base64 data already loaded, so this shouldn't happen
- // the function is defined just to prevent crashes from unexpected client behaviours.
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Load, it shouldn't have been!"];
- [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ [self debug:@"[load]"];
+ // Base64 data already loaded, so this shouldn't happen
+ // the function is defined just to prevent crashes from unexpected client behaviours.
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Load, it shouldn't have been!"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
// Exit after sharing
- (void) exit:(CDVInvokedUrlCommand*)command {
- [self debug:[NSString stringWithFormat:@"[exit] %@", self.backURL]];
- if (self.backURL != nil) {
- UIApplication *app = [UIApplication sharedApplication];
- NSURL *url = [NSURL URLWithString:self.backURL];
- if ([app canOpenURL:url]) {
- [app openURL:url];
- }
- }
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
- [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ [self debug:[NSString stringWithFormat:@"[exit] %@", self.backURL]];
+ if (self.backURL != nil) {
+ UIApplication *app = [UIApplication sharedApplication];
+ NSURL *url = [NSURL URLWithString:self.backURL];
+ if ([app canOpenURL:url]) {
+ [app openURL:url];
+ }
+ }
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
@end
diff --git a/src/ios/ShareExtension/ShareExtension-Info.plist b/src/ios/ShareExtension/ShareExtension-Info.plist
index 2a1e0ea..0b795e3 100644
--- a/src/ios/ShareExtension/ShareExtension-Info.plist
+++ b/src/ios/ShareExtension/ShareExtension-Info.plist
@@ -1,45 +1,49 @@
-
- CFBundleDevelopmentRegion
- en
- CFBundleDisplayName
- __DISPLAY_NAME__
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- __BUNDLE_IDENTIFIER__
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- $(PRODUCT_NAME)
- CFBundlePackageType
- XPC!
- CFBundleShortVersionString
- __BUNDLE_SHORT_VERSION_STRING__
- CFBundleVersion
- __BUNDLE_VERSION__
- NSExtension
- NSExtensionAttributes
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ __DISPLAY_NAME__
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ __BUNDLE_IDENTIFIER__
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ XPC!
+ CFBundleShortVersionString
+ __BUNDLE_SHORT_VERSION_STRING__
+ CFBundleVersion
+ __BUNDLE_VERSION__
+ NSExtension
- NSExtensionActivationRule
- SUBQUERY (
- extensionItems,
- $extensionItem,
- SUBQUERY (
- $extensionItem.attachments,
- $attachment,
- ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "__UNIFORM_TYPE_IDENTIFIER__"
- ).@count == $extensionItem.attachments.@count
- ).@count = 1
-
+ NSExtensionAttributes
+
+ NSExtensionActivationDictionaryVersion
+ 2
+ NSExtensionActivationRule
+
+ NSExtensionActivationSupportsAttachmentsWithMaxCount
+ 10
+ NSExtensionActivationSupportsAttachmentsWithMinCount
+ 1
+ NSExtensionActivationSupportsFileWithMaxCount
+ 10
+ NSExtensionActivationSupportsImageWithMaxCount
+ 10
+
+ NSExtensionActivationUsesStrictMatching
+ 1
+
+ NSExtensionMainStoryboard
+ MainInterface
+ NSExtensionPointIdentifier
+ com.apple.share-services
- NSExtensionMainStoryboard
- MainInterface
- NSExtensionPointIdentifier
- com.apple.share-services
-
diff --git a/src/ios/ShareExtension/ShareViewController.h b/src/ios/ShareExtension/ShareViewController.h
index f57416e..2051dad 100644
--- a/src/ios/ShareExtension/ShareViewController.h
+++ b/src/ios/ShareExtension/ShareViewController.h
@@ -27,4 +27,3 @@
#define SHAREEXT_GROUP_IDENTIFIER @"__GROUP_IDENTIFIER__"
#define SHAREEXT_URL_SCHEME @"__URL_SCHEME__"
-#define SHAREEXT_UNIFORM_TYPE_IDENTIFIER @"__UNIFORM_TYPE_IDENTIFIER__"
diff --git a/src/ios/ShareExtension/ShareViewController.m b/src/ios/ShareExtension/ShareViewController.m
index ff21bf8..f6e059d 100644
--- a/src/ios/ShareExtension/ShareViewController.m
+++ b/src/ios/ShareExtension/ShareViewController.m
@@ -32,9 +32,9 @@
#import "ShareViewController.h"
@interface ShareViewController : SLComposeServiceViewController {
- int _verbosityLevel;
- NSUserDefaults *_userDefaults;
- NSString *_backURL;
+ int _verbosityLevel;
+ NSUserDefaults *_userDefaults;
+ NSString *_backURL;
}
@property (nonatomic) int verbosityLevel;
@property (nonatomic,retain) NSUserDefaults *userDefaults;
@@ -57,9 +57,9 @@ @implementation ShareViewController
@synthesize backURL = _backURL;
- (void) log:(int)level message:(NSString*)message {
- if (level >= self.verbosityLevel) {
- NSLog(@"[ShareViewController.m]%@", message);
- }
+ if (level >= self.verbosityLevel) {
+ NSLog(@"[ShareViewController.m]%@", message);
+ }
}
- (void) debug:(NSString*)message { [self log:VERBOSITY_DEBUG message:message]; }
- (void) info:(NSString*)message { [self log:VERBOSITY_INFO message:message]; }
@@ -67,221 +67,233 @@ - (void) warn:(NSString*)message { [self log:VERBOSITY_WARN message:message]; }
- (void) error:(NSString*)message { [self log:VERBOSITY_ERROR message:message]; }
- (void) setup {
- self.userDefaults = [[NSUserDefaults alloc] initWithSuiteName:SHAREEXT_GROUP_IDENTIFIER];
- self.verbosityLevel = [self.userDefaults integerForKey:@"verbosityLevel"];
- [self debug:@"[setup]"];
+ self.userDefaults = [[NSUserDefaults alloc] initWithSuiteName:SHAREEXT_GROUP_IDENTIFIER];
+ self.verbosityLevel = [self.userDefaults integerForKey:@"verbosityLevel"];
+ [self debug:@"[setup]"];
}
- (BOOL) isContentValid {
- return YES;
+ return YES;
}
- (void) openURL:(nonnull NSURL *)url {
- SEL selector = NSSelectorFromString(@"openURL:options:completionHandler:");
-
- UIResponder* responder = self;
- while ((responder = [responder nextResponder]) != nil) {
- NSLog(@"responder = %@", responder);
- if([responder respondsToSelector:selector] == true) {
- NSMethodSignature *methodSignature = [responder methodSignatureForSelector:selector];
- NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
-
- // Arguments
- void (^completion)(BOOL success) = ^void(BOOL success) {
- NSLog(@"Completions block: %i", success);
- };
- if (@available(iOS 13.0, *)) {
- UISceneOpenExternalURLOptions * options = [[UISceneOpenExternalURLOptions alloc] init];
- options.universalLinksOnly = false;
-
- [invocation setTarget: responder];
- [invocation setSelector: selector];
- [invocation setArgument: &url atIndex: 2];
- [invocation setArgument: &options atIndex:3];
- [invocation setArgument: &completion atIndex: 4];
- [invocation invoke];
- break;
- } else {
- NSDictionary *options = [NSDictionary dictionary];
-
- [invocation setTarget: responder];
- [invocation setSelector: selector];
- [invocation setArgument: &url atIndex: 2];
- [invocation setArgument: &options atIndex:3];
- [invocation setArgument: &completion atIndex: 4];
- [invocation invoke];
- break;
- }
- }
- }
+ SEL selector = NSSelectorFromString(@"openURL:options:completionHandler:");
+
+ UIResponder* responder = self;
+ while ((responder = [responder nextResponder]) != nil) {
+ NSLog(@"responder = %@", responder);
+ if([responder respondsToSelector:selector] == true) {
+ NSMethodSignature *methodSignature = [responder methodSignatureForSelector:selector];
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
+
+ // Arguments
+ void (^completion)(BOOL success) = ^void(BOOL success) {
+ NSLog(@"Completions block: %i", success);
+ };
+ if (@available(iOS 13.0, *)) {
+ UISceneOpenExternalURLOptions * options = [[UISceneOpenExternalURLOptions alloc] init];
+ options.universalLinksOnly = false;
+
+ [invocation setTarget: responder];
+ [invocation setSelector: selector];
+ [invocation setArgument: &url atIndex: 2];
+ [invocation setArgument: &options atIndex:3];
+ [invocation setArgument: &completion atIndex: 4];
+ [invocation invoke];
+ break;
+ } else {
+ NSDictionary *options = [NSDictionary dictionary];
+
+ [invocation setTarget: responder];
+ [invocation setSelector: selector];
+ [invocation setArgument: &url atIndex: 2];
+ [invocation setArgument: &options atIndex:3];
+ [invocation setArgument: &completion atIndex: 4];
+ [invocation invoke];
+ break;
+ }
+ }
+ }
}
-- (void)viewWillAppear:(BOOL)animated {
- if( [(NSString*)self.contentText length] == 0 ) {
- [super viewWillAppear:animated];
- }
- else {
- [self.view endEditing:YES];
- [self didSelectPost];
- }
+- (void) viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+ self.view.hidden = YES;
+ [self didSelectPost];
+}
+
+- (void) sendItemForIdentifier:(NSString*) utiToLoad itemProvider:(NSItemProvider*) itemProvider items:(NSMutableArray*) items totalCount:(NSUInteger) totalCount {
+ [self debug:[NSString stringWithFormat:@"item provider = %@", itemProvider]];
+
+ if ([itemProvider.registeredTypeIdentifiers count] > 0) {
+ utiToLoad = itemProvider.registeredTypeIdentifiers[0];
+ }
+
+ [itemProvider loadItemForTypeIdentifier:utiToLoad options:nil completionHandler: ^(id item, NSError *error) {
+
+ NSString *webURL = @"";
+ NSString *fileName = @"";
+ NSString *filePath = @"";
+ NSData *data = [[NSData alloc] init];
+ if([(NSObject*)item isKindOfClass:[NSURL class]]) {
+ if ([[(NSURL*)item scheme] isEqualToString:@"http"] || [[(NSURL*)item scheme] isEqualToString:@"https"]) {
+ webURL = [(NSURL*)item absoluteString];
+ }
+ else {
+ data = [NSData dataWithContentsOfURL:(NSURL*)item];
+ fileName = [(NSURL*)item lastPathComponent];
+ filePath = [(NSURL*)item absoluteString];
+ }
+ }
+ else if([(NSObject*)item isKindOfClass:[NSData class]]) {
+ data = [NSData dataWithData:(NSData *)item];
+ }
+ if([(NSObject*)item isKindOfClass:[UIImage class]]) {
+ data = UIImagePNGRepresentation((UIImage*)item);
+ }
+
+ NSString *suggestedName = fileName;
+ if ([itemProvider respondsToSelector:NSSelectorFromString(@"getSuggestedName")]) {
+ suggestedName = [itemProvider valueForKey:@"suggestedName"];
+ }
+
+ NSString *uti = @"";
+ NSArray *utis = [NSArray new];
+ if ([itemProvider.registeredTypeIdentifiers count] > 0) {
+ uti = itemProvider.registeredTypeIdentifiers[0];
+ utis = itemProvider.registeredTypeIdentifiers;
+ }
+ else {
+ uti = utiToLoad;
+ }
+ NSDictionary *dict = @{
+ @"text": @"",
+ @"backURL": self.backURL,
+ @"data" : data,
+ @"uti": uti,
+ @"utis": utis,
+ @"name": suggestedName,
+ @"url": webURL,
+ @"path": filePath
+ };
+ [items addObject:dict];
+
+ if (totalCount == [items count]) {
+ [self sendItems:items];
+ }
+ }];
}
- (void) didSelectPost {
- [self setup];
- [self debug:@"[didSelectPost]"];
-
- // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
- for (NSItemProvider* itemProvider in ((NSExtensionItem*)self.extensionContext.inputItems[0]).attachments) {
-
- if ([itemProvider hasItemConformingToTypeIdentifier:SHAREEXT_UNIFORM_TYPE_IDENTIFIER]) {
- [self debug:[NSString stringWithFormat:@"item provider = %@", itemProvider]];
-
- NSString *utiToLoad = SHAREEXT_UNIFORM_TYPE_IDENTIFIER;
- if ([itemProvider.registeredTypeIdentifiers count] > 0) {
- utiToLoad = itemProvider.registeredTypeIdentifiers[0];
- }
-
- [itemProvider loadItemForTypeIdentifier:utiToLoad options:nil completionHandler: ^(id item, NSError *error) {
-
- NSString *contentText = self.contentText;
- NSString *webURL = @"";
- NSString *fileName = @"";
- NSString *filePath = @"";
- NSData *data = [[NSData alloc] init];
- if([(NSObject*)item isKindOfClass:[NSURL class]]) {
- if ([[(NSURL*)item scheme] isEqualToString:@"http"] || [[(NSURL*)item scheme] isEqualToString:@"https"]) {
- webURL = [(NSURL*)item absoluteString];
- }
- else {
- data = [NSData dataWithContentsOfURL:(NSURL*)item];
- fileName = [(NSURL*)item lastPathComponent];
- filePath = [(NSURL*)item absoluteString];
- }
- }
- else if([(NSObject*)item isKindOfClass:[NSData class]]) {
- data = [NSData dataWithData:(NSData *)item];
- }
- else if([(NSObject*)item isKindOfClass:[NSString class]]) {
- contentText = (NSString*)item;
- }
- if([(NSObject*)item isKindOfClass:[UIImage class]]) {
- data = UIImagePNGRepresentation((UIImage*)item);
- }
-
- NSString *suggestedName = fileName;
- if ([itemProvider respondsToSelector:NSSelectorFromString(@"getSuggestedName")]) {
- suggestedName = [itemProvider valueForKey:@"suggestedName"];
- }
-
- NSString *uti = @"";
- NSArray *utis = [NSArray new];
- if ([itemProvider.registeredTypeIdentifiers count] > 0) {
- uti = itemProvider.registeredTypeIdentifiers[0];
- utis = itemProvider.registeredTypeIdentifiers;
- }
- else {
- uti = SHAREEXT_UNIFORM_TYPE_IDENTIFIER;
- }
- NSDictionary *dict = @{
- @"text": contentText,
- @"backURL": self.backURL,
- @"data" : data,
- @"uti": uti,
- @"utis": utis,
- @"name": suggestedName,
- @"url": webURL,
- @"path": filePath
- };
- [self.userDefaults setObject:dict forKey:@"items"];
- [self.userDefaults synchronize];
-
- // Emit a URL that opens the cordova app
- NSString *url = [NSString stringWithFormat:@"%@://items", SHAREEXT_URL_SCHEME];
-
- // Not allowed:
- // [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
-
- // Crashes:
- // [self.extensionContext openURL:[NSURL URLWithString:url] completionHandler:nil];
-
- // From https://stackoverflow.com/a/25750229/2343390
- // Reported not to work since iOS 8.3
- // NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
- // [self.webView loadRequest:request];
-
- [self openURL:[NSURL URLWithString:url]];
-
- // Inform the host that we're done, so it un-blocks its UI.
- [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
- }];
-
- return;
- }
- }
-
- // Inform the host that we're done, so it un-blocks its UI.
- [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
+ [self setup];
+ [self debug:@"[didSelectPost]"];
+
+ // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
+ NSMutableArray* items = [[NSMutableArray alloc] init];
+ NSArray* attachments = ((NSExtensionItem*)self.extensionContext.inputItems[0]).attachments;
+ NSMutableArray* itemProviders = [[NSMutableArray alloc] init];
+
+ // filter specific types
+ for (NSItemProvider* itemProvider in attachments) {
+ if ([itemProvider hasItemConformingToTypeIdentifier:@"public.image"] || [itemProvider hasItemConformingToTypeIdentifier:@"com.adobe.pdf"]) {
+ [itemProviders addObject:itemProvider];
+ }
+ }
+
+ for (NSItemProvider* itemProvider in itemProviders) {
+ if ([itemProvider hasItemConformingToTypeIdentifier:@"public.image"]) {
+ [self sendItemForIdentifier:@"public.image" itemProvider:itemProvider items:items totalCount:[itemProviders count]];
+ } else if ([itemProvider hasItemConformingToTypeIdentifier:@"com.adobe.pdf"]) {
+ [self sendItemForIdentifier:@"com.adobe.pdf" itemProvider:itemProvider items:items totalCount:[itemProviders count]];
+ }
+ }
+}
+
+- (void) sendItems:(NSArray*) items {
+ [self.userDefaults setObject:items forKey:@"items"];
+ [self.userDefaults synchronize];
+
+ // Emit a URL that opens the cordova app
+ NSString *url = [NSString stringWithFormat:@"%@://items", SHAREEXT_URL_SCHEME];
+
+ // Not allowed:
+ // [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
+
+ // Crashes:
+ // [self.extensionContext openURL:[NSURL URLWithString:url] completionHandler:nil];
+
+ // From https://stackoverflow.com/a/25750229/2343390
+ // Reported not to work since iOS 8.3
+ // NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
+ // [self.webView loadRequest:request];
+
+ [self openURL:[NSURL URLWithString:url]];
+
+ // Inform the host that we're done, so it un-blocks its UI.
+ [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
}
- (NSArray*) configurationItems {
- // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
- return @[];
+ // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
+ return @[];
}
- (NSString*) backURLFromBundleID: (NSString*)bundleId {
- if (bundleId == nil) return nil;
- // App Store - com.apple.AppStore
- if ([bundleId isEqualToString:@"com.apple.AppStore"]) return @"itms-apps://";
- // Calculator - com.apple.calculator
- // Calendar - com.apple.mobilecal
- // Camera - com.apple.camera
- // Clock - com.apple.mobiletimer
- // Compass - com.apple.compass
- // Contacts - com.apple.MobileAddressBook
- // FaceTime - com.apple.facetime
- // Find Friends - com.apple.mobileme.fmf1
- // Find iPhone - com.apple.mobileme.fmip1
- // Game Center - com.apple.gamecenter
- // Health - com.apple.Health
- // iBooks - com.apple.iBooks
- // iTunes Store - com.apple.MobileStore
- // Mail - com.apple.mobilemail - message://
- if ([bundleId isEqualToString:@"com.apple.mobilemail"]) return @"message://";
- // Maps - com.apple.Maps - maps://
- if ([bundleId isEqualToString:@"com.apple.Maps"]) return @"maps://";
- // Messages - com.apple.MobileSMS
- // Music - com.apple.Music
- // News - com.apple.news - applenews://
- if ([bundleId isEqualToString:@"com.apple.news"]) return @"applenews://";
- // Notes - com.apple.mobilenotes - mobilenotes://
- if ([bundleId isEqualToString:@"com.apple.mobilenotes"]) return @"mobilenotes://";
- // Phone - com.apple.mobilephone
- // Photos - com.apple.mobileslideshow
- if ([bundleId isEqualToString:@"com.apple.mobileslideshow"]) return @"photos-redirect://";
- // Podcasts - com.apple.podcasts
- // Reminders - com.apple.reminders - x-apple-reminder://
- if ([bundleId isEqualToString:@"com.apple.reminders"]) return @"x-apple-reminder://";
- // Safari - com.apple.mobilesafari
- // Settings - com.apple.Preferences
- // Stocks - com.apple.stocks
- // Tips - com.apple.tips
- // Videos - com.apple.videos - videos://
- if ([bundleId isEqualToString:@"com.apple.videos"]) return @"videos://";
- // Voice Memos - com.apple.VoiceMemos - voicememos://
- if ([bundleId isEqualToString:@"com.apple.VoiceMemos"]) return @"voicememos://";
- // Wallet - com.apple.Passbook
- // Watch - com.apple.Bridge
- // Weather - com.apple.weather
- return @"";
+ if (bundleId == nil) return nil;
+ // App Store - com.apple.AppStore
+ if ([bundleId isEqualToString:@"com.apple.AppStore"]) return @"itms-apps://";
+ // Calculator - com.apple.calculator
+ // Calendar - com.apple.mobilecal
+ // Camera - com.apple.camera
+ // Clock - com.apple.mobiletimer
+ // Compass - com.apple.compass
+ // Contacts - com.apple.MobileAddressBook
+ // FaceTime - com.apple.facetime
+ // Find Friends - com.apple.mobileme.fmf1
+ // Find iPhone - com.apple.mobileme.fmip1
+ // Game Center - com.apple.gamecenter
+ // Health - com.apple.Health
+ // iBooks - com.apple.iBooks
+ // iTunes Store - com.apple.MobileStore
+ // Mail - com.apple.mobilemail - message://
+ if ([bundleId isEqualToString:@"com.apple.mobilemail"]) return @"message://";
+ // Maps - com.apple.Maps - maps://
+ if ([bundleId isEqualToString:@"com.apple.Maps"]) return @"maps://";
+ // Messages - com.apple.MobileSMS
+ // Music - com.apple.Music
+ // News - com.apple.news - applenews://
+ if ([bundleId isEqualToString:@"com.apple.news"]) return @"applenews://";
+ // Notes - com.apple.mobilenotes - mobilenotes://
+ if ([bundleId isEqualToString:@"com.apple.mobilenotes"]) return @"mobilenotes://";
+ // Phone - com.apple.mobilephone
+ // Photos - com.apple.mobileslideshow
+ if ([bundleId isEqualToString:@"com.apple.mobileslideshow"]) return @"photos-redirect://";
+ // Podcasts - com.apple.podcasts
+ // Reminders - com.apple.reminders - x-apple-reminder://
+ if ([bundleId isEqualToString:@"com.apple.reminders"]) return @"x-apple-reminder://";
+ // Safari - com.apple.mobilesafari
+ // Settings - com.apple.Preferences
+ // Stocks - com.apple.stocks
+ // Tips - com.apple.tips
+ // Videos - com.apple.videos - videos://
+ if ([bundleId isEqualToString:@"com.apple.videos"]) return @"videos://";
+ // Voice Memos - com.apple.VoiceMemos - voicememos://
+ if ([bundleId isEqualToString:@"com.apple.VoiceMemos"]) return @"voicememos://";
+ // Wallet - com.apple.Passbook
+ // Watch - com.apple.Bridge
+ // Weather - com.apple.weather
+ return @"";
}
// This is called at the point where the Post dialog is about to be shown.
// We use it to store the _hostBundleID
- (void) willMoveToParentViewController: (UIViewController*)parent {
- NSString *hostBundleID = [parent valueForKey:(@"_hostBundleID")];
- self.backURL = [self backURLFromBundleID:hostBundleID];
+ NSString *hostBundleID = [parent valueForKey:(@"_hostBundleID")];
+ self.backURL = [self backURLFromBundleID:hostBundleID];
}
@end
+
+
diff --git a/www/openwith.js b/www/openwith.js
index a1c7cd1..db26ea3 100644
--- a/www/openwith.js
+++ b/www/openwith.js
@@ -1,230 +1,237 @@
-function initOpenwithPlugin (root) {
- 'use strict'
+function initOpenwithPlugin(root) {
+ "use strict";
// imports
// var cordova = require('cordova')
- var PLUGIN_NAME = 'OpenWithPlugin'
+ let PLUGIN_NAME = "OpenWithPlugin";
// the returned object
- var openwith = {}
+ let openwith = {};
//
// exported constants
//
// logging levels
- var DEBUG = openwith.DEBUG = 0
- var INFO = openwith.INFO = 10
- var WARN = openwith.WARN = 20
- var ERROR = openwith.ERROR = 30
+ let DEBUG = (openwith.DEBUG = 0);
+ let INFO = (openwith.INFO = 10);
+ let WARN = (openwith.WARN = 20);
+ let ERROR = (openwith.ERROR = 30);
// actions
- openwith.SEND = 'SEND'
- openwith.VIEW = 'VIEW'
+ openwith.SEND = "SEND";
+ openwith.VIEW = "VIEW";
//
// state variables
//
// default verbosity level is to show errors only
- var verbosity
+ let verbosity;
// list of registered handlers
- var handlers
+ let handlers;
// list of intents sent to this app
//
// it's never cleaned up, so that newly registered handlers (especially those registered a bit too late)
// will still receive the list of intents.
- var intents
+ let intents;
// the logger function (defaults to console.log)
- var logger
+ let logger;
// the cordova object (defaults to global one)
- var cordova
+ let cordova;
// has init() been called or not already
- var initCalled
+ let initCalled;
// make sure a number is displayed with 2 digits
- var twoDigits = function (n) {
- return n < 10
- ? '0' + n
- : '' + n
- }
+ let twoDigits = function (n) {
+ return n < 10 ? `0${n}` : `${n}`;
+ };
// format a date for display
- var formatDate = function (now) {
- var date = now ? new Date(now) : new Date()
- var d = [date.getMonth() + 1, date.getDate()].map(twoDigits)
- var t = [date.getHours(), date.getMinutes(), date.getSeconds()].map(twoDigits)
- return d.join('-') + ' ' + t.join(':')
- }
+ let formatDate = function (now) {
+ let date = now ? new Date(now) : new Date();
+ let d = [date.getMonth() + 1, date.getDate()].map(twoDigits);
+ let t = [date.getHours(), date.getMinutes(), date.getSeconds()].map(
+ twoDigits
+ );
+ return `${d.join("-")} ${t.join(":")}`;
+ };
// format verbosity level for display
- var formatVerbosity = function (level) {
- if (level <= DEBUG) return 'D'
- if (level <= INFO) return 'I'
- if (level <= WARN) return 'W'
- return 'E'
- }
+ let formatVerbosity = function (level) {
+ if (level <= DEBUG) return "D";
+ if (level <= INFO) return "I";
+ if (level <= WARN) return "W";
+ return "E";
+ };
// display a log in the console only if the level is higher than current verbosity
- var log = function (level, message) {
+ let log = function (level, message) {
if (level >= verbosity) {
- logger(formatDate() + ' ' + formatVerbosity(level) + ' openwith: ' + message)
+ logger(`${formatDate()} ${formatVerbosity(level)} openwith: ${message}`);
}
- }
+ };
// reset the state to default
openwith.reset = function () {
- log(DEBUG, 'reset')
- verbosity = openwith.INFO
- handlers = []
- intents = []
- logger = console.log
- cordova = root.cordova
- initCalled = false
- }
+ log(DEBUG, "reset");
+ verbosity = openwith.INFO;
+ handlers = [];
+ intents = [];
+ logger = console.log;
+ cordova = root.cordova;
+ initCalled = false;
+ };
// perform the initial reset
- openwith.reset()
+ openwith.reset();
// change the logger function
openwith.setLogger = function (value) {
- logger = value
- }
+ logger = value;
+ };
// change the cordova object (mostly for testing)
openwith.setCordova = function (value) {
- cordova = value
- }
+ cordova = value;
+ };
// change the verbosity level
openwith.setVerbosity = function (value) {
- log(DEBUG, 'setVerbosity()')
- if (value !== DEBUG && value !== INFO && value !== WARN && value !== ERROR) {
- throw new Error('invalid verbosity level')
+ log(DEBUG, "setVerbosity()");
+ if (
+ value !== DEBUG &&
+ value !== INFO &&
+ value !== WARN &&
+ value !== ERROR
+ ) {
+ throw new Error("invalid verbosity level");
}
- verbosity = value
- cordova.exec(null, null, PLUGIN_NAME, 'setVerbosity', [value])
- }
+ verbosity = value;
+ cordova.exec(null, null, PLUGIN_NAME, "setVerbosity", [value]);
+ };
// retrieve the verbosity level
openwith.getVerbosity = function () {
- log(DEBUG, 'getVerbosity()')
- return verbosity
- }
+ log(DEBUG, "getVerbosity()");
+ return verbosity;
+ };
// a simple function to test that the plugin is correctly installed
openwith.about = function () {
- log(DEBUG, 'about()')
- return 'cordova-plugin-openwith, (c) 2017 fovea.cc'
- }
+ log(DEBUG, "about()");
+ return "cordova-plugin-openwith, (c) 2017 fovea.cc";
+ };
- var findHandler = function (callback) {
- for (var i = 0; i < handlers.length; ++i) {
+ let findHandler = function (callback) {
+ for (let i = 0; i < handlers.length; ++i) {
if (handlers[i] === callback) {
- return i
+ return i;
}
}
- return -1
- }
+ return -1;
+ };
// registers a intent handler
openwith.addHandler = function (callback) {
- log(DEBUG, 'addHandler()')
- if (typeof callback !== 'function') {
- throw new Error('invalid handler function')
+ log(DEBUG, "addHandler()");
+ if (typeof callback !== "function") {
+ throw new Error("invalid handler function");
}
if (findHandler(callback) >= 0) {
- throw new Error('handler already defined')
+ throw new Error("handler already defined");
}
- handlers.push(callback)
- intents.forEach(function handleIntent (intent) {
- callback(intent)
- })
- }
+ handlers.push(callback);
+ intents.forEach((intent) => {
+ callback(intent);
+ });
+ };
openwith.numHandlers = function () {
- log(DEBUG, 'numHandler()')
- return handlers.length
- }
+ log(DEBUG, "numHandler()");
+ return handlers.length;
+ };
openwith.load = function (dataDescriptor, successCallback, errorCallback) {
- var loadSuccess = function (base64) {
- dataDescriptor.base64 = base64
+ let loadSuccess = function (base64) {
+ dataDescriptor.base64 = base64;
if (successCallback) {
- successCallback(base64, dataDescriptor)
+ successCallback(base64, dataDescriptor);
}
- }
- var loadError = function (err) {
+ };
+ let loadError = function (err) {
if (errorCallback) {
- errorCallback(err, dataDescriptor)
+ errorCallback(err, dataDescriptor);
}
- }
+ };
if (dataDescriptor.base64) {
- loadSuccess(dataDescriptor.base64)
+ loadSuccess(dataDescriptor.base64);
} else {
- cordova.exec(loadSuccess, loadError, PLUGIN_NAME, 'load', [dataDescriptor])
+ cordova.exec(loadSuccess, loadError, PLUGIN_NAME, "load", [
+ dataDescriptor,
+ ]);
}
- }
+ };
- openwith.exit = function () {
- log(DEBUG, 'exit()')
- cordova.exec(null, null, PLUGIN_NAME, 'exit', [])
- }
+ openwith.exit = () => {
+ log(DEBUG, "exit()");
+ cordova.exec(null, null, PLUGIN_NAME, "exit", []);
+ };
- var onNewIntent = function (intent) {
- log(DEBUG, 'onNewIntent(' + intent.action + ')')
+ let onNewIntent = function (intent) {
+ log(DEBUG, `onNewIntent(${intent.action})`);
// process the new intent
- handlers.forEach(function (handler) {
- handler(intent)
- })
- intents.push(intent)
- }
+ handlers.forEach((handler) => {
+ handler(intent);
+ });
+ intents.push(intent);
+ };
// Initialize the native side at startup
openwith.init = function (successCallback, errorCallback) {
- log(DEBUG, 'init()')
+ log(DEBUG, "init()");
if (initCalled) {
- throw new Error('init should only be called once')
+ throw new Error("init should only be called once");
}
- initCalled = true
+ initCalled = true;
// callbacks have to be functions
- if (successCallback && typeof successCallback !== 'function') {
- throw new Error('invalid success callback')
+ if (successCallback && typeof successCallback !== "function") {
+ throw new Error("invalid success callback");
}
- if (errorCallback && typeof errorCallback !== 'function') {
- throw new Error('invalid error callback')
- }
-
- var initSuccess = function () {
- log(DEBUG, 'initSuccess()')
- if (successCallback) successCallback()
+ if (errorCallback && typeof errorCallback !== "function") {
+ throw new Error("invalid error callback");
}
- var initError = function () {
- log(DEBUG, 'initError()')
- if (errorCallback) errorCallback()
- }
- var nativeLogger = function (data) {
- var split = data.split(':')
- log(+split[0], '[native] ' + split.slice(1).join(':'))
- }
-
- cordova.exec(nativeLogger, null, PLUGIN_NAME, 'setLogger', [])
- cordova.exec(onNewIntent, null, PLUGIN_NAME, 'setHandler', [])
- cordova.exec(initSuccess, initError, PLUGIN_NAME, 'init', [])
- }
- return openwith
+ let initSuccess = function () {
+ log(DEBUG, "initSuccess()");
+ if (successCallback) successCallback();
+ };
+ let initError = function () {
+ log(DEBUG, "initError()");
+ if (errorCallback) errorCallback();
+ };
+ let nativeLogger = function (data) {
+ let split = data.split(":");
+ log(+split[0], `[native] ${split.slice(1).join(":")}`);
+ };
+
+ cordova.exec(nativeLogger, null, PLUGIN_NAME, "setLogger", []);
+ cordova.exec(onNewIntent, null, PLUGIN_NAME, "setHandler", []);
+ cordova.exec(initSuccess, initError, PLUGIN_NAME, "init", []);
+ };
+
+ return openwith;
}
// Export the plugin object
-var openwith = initOpenwithPlugin(this)
-module.exports = openwith
-this.plugins = this.plugins || {}
-this.plugins.openwith = openwith
+let openwith = initOpenwithPlugin(this);
+module.exports = openwith;
+this.plugins = this.plugins || {};
+this.plugins.openwith = openwith;