diff options
Diffstat (limited to 'StoneIsland/plugins/cordova-plugin-x-socialsharing/src')
| -rw-r--r--[-rwxr-xr-x] | StoneIsland/plugins/cordova-plugin-x-socialsharing/src/android/nl/xservices/plugins/SocialSharing.java | 280 | ||||
| -rw-r--r-- | StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/NSString+URLEncoding.h | 5 | ||||
| -rw-r--r-- | StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/NSString+URLEncoding.m | 30 | ||||
| -rw-r--r--[-rwxr-xr-x] | StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h | 3 | ||||
| -rw-r--r--[-rwxr-xr-x] | StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m | 157 | ||||
| -rw-r--r--[-rwxr-xr-x] | StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js | 86 | ||||
| -rw-r--r--[-rwxr-xr-x] | StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs | 4 |
7 files changed, 477 insertions, 88 deletions
diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/android/nl/xservices/plugins/SocialSharing.java b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/android/nl/xservices/plugins/SocialSharing.java index f1168f89..8de31da8 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/android/nl/xservices/plugins/SocialSharing.java +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/android/nl/xservices/plugins/SocialSharing.java @@ -26,7 +26,9 @@ import java.io.*; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.regex.Matcher; @@ -36,6 +38,7 @@ public class SocialSharing extends CordovaPlugin { private static final String ACTION_AVAILABLE_EVENT = "available"; private static final String ACTION_SHARE_EVENT = "share"; + private static final String ACTION_SHARE_WITH_OPTIONS_EVENT = "shareWithOptions"; private static final String ACTION_CAN_SHARE_VIA = "canShareVia"; private static final String ACTION_CAN_SHARE_VIA_EMAIL = "canShareViaEmail"; private static final String ACTION_SHARE_VIA = "shareVia"; @@ -47,7 +50,10 @@ public class SocialSharing extends CordovaPlugin { private static final String ACTION_SHARE_VIA_SMS_EVENT = "shareViaSMS"; private static final String ACTION_SHARE_VIA_EMAIL_EVENT = "shareViaEmail"; - private static final int ACTIVITY_CODE_SENDVIAEMAIL = 2; + private static final int ACTIVITY_CODE_SEND__BOOLRESULT = 1; + private static final int ACTIVITY_CODE_SEND__OBJECT = 2; + private static final int ACTIVITY_CODE_SENDVIAEMAIL = 3; + private static final int ACTIVITY_CODE_SENDVIAWHATSAPP = 4; private CallbackContext _callbackContext; @@ -55,6 +61,7 @@ public class SocialSharing extends CordovaPlugin { private abstract class SocialSharingRunnable implements Runnable { public CallbackContext callbackContext; + SocialSharingRunnable(CallbackContext cb) { this.callbackContext = cb; } @@ -68,23 +75,29 @@ public class SocialSharing extends CordovaPlugin { callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK)); return true; } else if (ACTION_SHARE_EVENT.equals(action)) { - return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), null, false); + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), null, null, false, true); + } else if (ACTION_SHARE_WITH_OPTIONS_EVENT.equals(action)) { + return shareWithOptions(callbackContext, args.getJSONObject(0)); } else if (ACTION_SHARE_VIA_TWITTER_EVENT.equals(action)) { - return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "twitter", false); + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "twitter", null, false, true); } else if (ACTION_SHARE_VIA_FACEBOOK_EVENT.equals(action)) { - return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "com.facebook.katana", false); + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "com.facebook.katana", null, false, true); } else if (ACTION_SHARE_VIA_FACEBOOK_WITH_PASTEMESSAGEHINT.equals(action)) { this.pasteMessage = args.getString(4); - return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "com.facebook.katana", false); + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "com.facebook.katana", null, false, true); } else if (ACTION_SHARE_VIA_WHATSAPP_EVENT.equals(action)) { - return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "whatsapp", false); + if (notEmpty(args.getString(4))) { + return shareViaWhatsAppDirectly(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), args.getString(4)); + } else { + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "whatsapp", null, false, true); + } } else if (ACTION_SHARE_VIA_INSTAGRAM_EVENT.equals(action)) { if (notEmpty(args.getString(0))) { copyHintToClipboard(args.getString(0), "Instagram paste message"); } - return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "instagram", false); + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "instagram", null, false, true); } else if (ACTION_CAN_SHARE_VIA.equals(action)) { - return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), args.getString(4), true); + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), args.getString(4), null, true, true); } else if (ACTION_CAN_SHARE_VIA_EMAIL.equals(action)) { if (isEmailAvailable()) { callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK)); @@ -94,7 +107,7 @@ public class SocialSharing extends CordovaPlugin { return false; } } else if (ACTION_SHARE_VIA.equals(action)) { - return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), args.getString(4), false); + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), args.getString(4), null, false, true); } else if (ACTION_SHARE_VIA_SMS_EVENT.equals(action)) { return invokeSMSIntent(callbackContext, args.getJSONObject(0), args.getString(1)); } else if (ACTION_SHARE_VIA_EMAIL_EVENT.equals(action)) { @@ -158,8 +171,17 @@ public class SocialSharing extends CordovaPlugin { callbackContext.error(e.getMessage()); } + // this was added to start the intent in a new window as suggested in #300 to prevent crashes upon return + draft.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + draft.setType("application/octet-stream"); - cordova.startActivityForResult(plugin, Intent.createChooser(draft, "Choose Email App"), ACTIVITY_CODE_SENDVIAEMAIL); + + // as an experiment for #300 we're explicitly running it on the ui thread here + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + cordova.startActivityForResult(plugin, Intent.createChooser(draft, "Choose Email App"), ACTIVITY_CODE_SENDVIAEMAIL); + } + }); } }); @@ -178,7 +200,30 @@ public class SocialSharing extends CordovaPlugin { } } - private boolean doSendIntent(final CallbackContext callbackContext, final String msg, final String subject, final JSONArray files, final String url, final String appPackageName, final boolean peek) { + private boolean shareWithOptions(CallbackContext callbackContext, JSONObject jsonObject) { + return doSendIntent( + callbackContext, + jsonObject.optString("message", null), + jsonObject.optString("subject", null), + jsonObject.optJSONArray("files") == null ? new JSONArray() : jsonObject.optJSONArray("files"), + jsonObject.optString("url", null), + null, + jsonObject.optString("chooserTitle", null), + false, + false + ); + } + + private boolean doSendIntent( + final CallbackContext callbackContext, + final String msg, + final String subject, + final JSONArray files, + final String url, + final String appPackageName, + final String chooserTitle, + final boolean peek, + final boolean boolResult) { final CordovaInterface mycordova = cordova; final CordovaPlugin plugin = this; @@ -222,6 +267,7 @@ public class SocialSharing extends CordovaPlugin { if (notEmpty(subject)) { sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject); } + // add the URL to the message, as there seems to be no separate field if (notEmpty(url)) { if (notEmpty(message)) { @@ -238,6 +284,9 @@ public class SocialSharing extends CordovaPlugin { } } + // this was added to start the intent in a new window as suggested in #300 to prevent crashes upon return + sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (appPackageName != null) { String packageName = appPackageName; String passedActivityName = null; @@ -254,7 +303,13 @@ public class SocialSharing extends CordovaPlugin { sendIntent.addCategory(Intent.CATEGORY_LAUNCHER); sendIntent.setComponent(new ComponentName(activity.applicationInfo.packageName, passedActivityName != null ? passedActivityName : activity.name)); - mycordova.startActivityForResult(plugin, sendIntent, 0); + + // as an experiment for #300 we're explicitly running it on the ui thread here + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + mycordova.startActivityForResult(plugin, sendIntent, 0); + } + }); if (pasteMessage != null) { // add a little delay because target app (facebook only atm) needs to be started first @@ -275,7 +330,13 @@ public class SocialSharing extends CordovaPlugin { if (peek) { callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK)); } else { - mycordova.startActivityForResult(plugin, Intent.createChooser(sendIntent, null), 1); + // experimenting a bit + // as an experiment for #300 we're explicitly running it on the ui thread here + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + mycordova.startActivityForResult(plugin, Intent.createChooser(sendIntent, chooserTitle), boolResult ? ACTIVITY_CODE_SEND__BOOLRESULT : ACTIVITY_CODE_SEND__OBJECT); + } + }); } } } @@ -306,7 +367,12 @@ public class SocialSharing extends CordovaPlugin { private Uri getFileUriAndSetType(Intent sendIntent, String dir, String image, String subject, int nthFile) throws IOException { // we're assuming an image, but this can be any filetype you like String localImage = image; - sendIntent.setType("image/*"); + if (image.endsWith("mp4") || image.endsWith("mov") || image.endsWith("3gp")){ + sendIntent.setType("video/*"); + } else { + sendIntent.setType("image/*"); + } + if (image.startsWith("http") || image.startsWith("www/")) { String filename = getFileName(image); localImage = "file://" + dir + "/" + filename; @@ -319,6 +385,10 @@ public class SocialSharing extends CordovaPlugin { Matcher matcher = dispositionPattern.matcher(disposition); if (matcher.find()) { filename = matcher.group(1).replaceAll("[^a-zA-Z0-9._-]", ""); + if (filename.length() == 0) { + // in this case we can't determine a filetype so some targets (gmail) may not render it correctly + filename = "file"; + } localImage = "file://" + dir + "/" + filename; } } @@ -365,10 +435,160 @@ public class SocialSharing extends CordovaPlugin { localImage = "file://" + dir + "/" + fileName; } else if (!image.startsWith("file://")) { throw new IllegalArgumentException("URL_NOT_SUPPORTED"); + } else { + //get file MIME type + String type = getMIMEType(image); + //set intent data and Type + sendIntent.setType(type); } return Uri.parse(localImage); } + private String getMIMEType(String fileName) { + String type = "*/*"; + int dotIndex = fileName.lastIndexOf("."); + if (dotIndex == -1) { + return type; + } + final String end = fileName.substring(dotIndex+1, fileName.length()).toLowerCase(); + String fromMap = MIME_Map.get(end); + return fromMap == null ? type : fromMap; + } + + private static final Map<String, String> MIME_Map = new HashMap<String, String>(); + static { + MIME_Map.put("3gp", "video/3gpp"); + MIME_Map.put("apk", "application/vnd.android.package-archive"); + MIME_Map.put("asf", "video/x-ms-asf"); + MIME_Map.put("avi", "video/x-msvideo"); + MIME_Map.put("bin", "application/octet-stream"); + MIME_Map.put("bmp", "image/bmp"); + MIME_Map.put("c", "text/plain"); + MIME_Map.put("class", "application/octet-stream"); + MIME_Map.put("conf", "text/plain"); + MIME_Map.put("cpp", "text/plain"); + MIME_Map.put("doc", "application/msword"); + MIME_Map.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + MIME_Map.put("xls", "application/vnd.ms-excel"); + MIME_Map.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + MIME_Map.put("exe", "application/octet-stream"); + MIME_Map.put("gif", "image/gif"); + MIME_Map.put("gtar", "application/x-gtar"); + MIME_Map.put("gz", "application/x-gzip"); + MIME_Map.put("h", "text/plain"); + MIME_Map.put("htm", "text/html"); + MIME_Map.put("html", "text/html"); + MIME_Map.put("jar", "application/java-archive"); + MIME_Map.put("java", "text/plain"); + MIME_Map.put("jpeg", "image/jpeg"); + MIME_Map.put("jpg", "image/*"); + MIME_Map.put("js", "application/x-javascript"); + MIME_Map.put("log", "text/plain"); + MIME_Map.put("m3u", "audio/x-mpegurl"); + MIME_Map.put("m4a", "audio/mp4a-latm"); + MIME_Map.put("m4b", "audio/mp4a-latm"); + MIME_Map.put("m4p", "audio/mp4a-latm"); + MIME_Map.put("m4u", "video/vnd.mpegurl"); + MIME_Map.put("m4v", "video/x-m4v"); + MIME_Map.put("mov", "video/quicktime"); + MIME_Map.put("mp2", "audio/x-mpeg"); + MIME_Map.put("mp3", "audio/x-mpeg"); + MIME_Map.put("mp4", "video/mp4"); + MIME_Map.put("mpc", "application/vnd.mpohun.certificate"); + MIME_Map.put("mpe", "video/mpeg"); + MIME_Map.put("mpeg", "video/mpeg"); + MIME_Map.put("mpg", "video/mpeg"); + MIME_Map.put("mpg4", "video/mp4"); + MIME_Map.put("mpga", "audio/mpeg"); + MIME_Map.put("msg", "application/vnd.ms-outlook"); + MIME_Map.put("ogg", "audio/ogg"); + MIME_Map.put("pdf", "application/pdf"); + MIME_Map.put("png", "image/png"); + MIME_Map.put("pps", "application/vnd.ms-powerpoint"); + MIME_Map.put("ppt", "application/vnd.ms-powerpoint"); + MIME_Map.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"); + MIME_Map.put("prop", "text/plain"); + MIME_Map.put("rc", "text/plain"); + MIME_Map.put("rmvb", "audio/x-pn-realaudio"); + MIME_Map.put("rtf", "application/rtf"); + MIME_Map.put("sh", "text/plain"); + MIME_Map.put("tar", "application/x-tar"); + MIME_Map.put("tgz", "application/x-compressed"); + MIME_Map.put("txt", "text/plain"); + MIME_Map.put("wav", "audio/x-wav"); + MIME_Map.put("wma", "audio/x-ms-wma"); + MIME_Map.put("wmv", "audio/x-ms-wmv"); + MIME_Map.put("wps", "application/vnd.ms-works"); + MIME_Map.put("xml", "text/plain"); + MIME_Map.put("z", "application/x-compress"); + MIME_Map.put("zip", "application/x-zip-compressed"); + MIME_Map.put("", "*/*"); + } + + private boolean shareViaWhatsAppDirectly(final CallbackContext callbackContext, String message, final String subject, final JSONArray files, final String url, final String number) { + // add the URL to the message, as there seems to be no separate field + if (notEmpty(url)) { + if (notEmpty(message)) { + message += " " + url; + } else { + message = url; + } + } + final String shareMessage = message; + final SocialSharing plugin = this; + cordova.getThreadPool().execute(new SocialSharingRunnable(callbackContext) { + public void run() { + final Intent intent = new Intent(Intent.ACTION_SENDTO); + intent.setData(Uri.parse("smsto:" + number)); + + intent.putExtra("sms_body", shareMessage); + intent.putExtra("sms_subject", subject); + intent.setPackage("com.whatsapp"); + + try { + if (files.length() > 0 && !"".equals(files.getString(0))) { + final boolean hasMultipleAttachments = files.length() > 1; + final String dir = getDownloadDir(); + if (dir != null) { + ArrayList<Uri> fileUris = new ArrayList<Uri>(); + Uri fileUri = null; + for (int i = 0; i < files.length(); i++) { + fileUri = getFileUriAndSetType(intent, dir, files.getString(i), subject, i); + if (fileUri != null) { + fileUris.add(fileUri); + } + } + if (!fileUris.isEmpty()) { + if (hasMultipleAttachments) { + intent.putExtra(Intent.EXTRA_STREAM, fileUris); + } else { + intent.putExtra(Intent.EXTRA_STREAM, fileUri); + } + } + } + } + } catch (Exception e) { + callbackContext.error(e.getMessage()); + } + try { + // this was added to start the intent in a new window as suggested in #300 to prevent crashes upon return + // update: didn't help (doesn't seem to hurt either though) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // as an experiment for #300 we're explicitly running it on the ui thread here + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + cordova.startActivityForResult(plugin, intent, ACTIVITY_CODE_SENDVIAWHATSAPP); + } + }); + } catch (Exception e) { + callbackContext.error(e.getMessage()); + } + } + }); + return true; + } + private boolean invokeSMSIntent(final CallbackContext callbackContext, JSONObject options, String p_phonenumbers) { final String message = options.optString("message"); // TODO test this on a real SMS enabled device before releasing it @@ -404,6 +624,9 @@ public class SocialSharing extends CordovaPlugin { intent.putExtra(Intent.EXTRA_STREAM, fileUri); } } + // this was added to start the intent in a new window as suggested in #300 to prevent crashes upon return + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + cordova.startActivityForResult(plugin, intent, 0); } catch (Exception e) { callbackContext.error(e.getMessage()); @@ -451,10 +674,26 @@ public class SocialSharing extends CordovaPlugin { public void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if (_callbackContext != null) { - if (ACTIVITY_CODE_SENDVIAEMAIL == requestCode) { - _callbackContext.success(); - } else { - _callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, resultCode == Activity.RESULT_OK)); + switch (requestCode) { + case ACTIVITY_CODE_SEND__BOOLRESULT: + _callbackContext.sendPluginResult(new PluginResult( + PluginResult.Status.OK, + resultCode == Activity.RESULT_OK)); + break; + case ACTIVITY_CODE_SEND__OBJECT: + JSONObject json = new JSONObject(); + try { + json.put("completed", resultCode == Activity.RESULT_OK); + json.put("app", ""); // we need a completely different approach if we want to support this on Android. Idea: https://clickclickclack.wordpress.com/2012/01/03/intercepting-androids-action_send-intents/ + _callbackContext.sendPluginResult(new PluginResult( + PluginResult.Status.OK, + json)); + } catch (JSONException e) { + _callbackContext.error(e.getMessage()); + } + break; + default: + _callbackContext.success(); } } } @@ -471,13 +710,16 @@ public class SocialSharing extends CordovaPlugin { } private static String getFileName(String url) { + if (url.endsWith("/")) { + url = url.substring(0, url.length()-1); + } final String pattern = ".*/([^?#]+)?"; Pattern r = Pattern.compile(pattern); Matcher m = r.matcher(url); if (m.find()) { return m.group(1); } else { - return null; + return "file"; } } diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/NSString+URLEncoding.h b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/NSString+URLEncoding.h new file mode 100644 index 00000000..d7da331d --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/NSString+URLEncoding.h @@ -0,0 +1,5 @@ +#import <Foundation/Foundation.h> + +@interface NSString (URLEncoding) +@property (readonly) NSString *URLEncodedString; +@end diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/NSString+URLEncoding.m b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/NSString+URLEncoding.m new file mode 100644 index 00000000..b737626c --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/NSString+URLEncoding.m @@ -0,0 +1,30 @@ +#import "NSString+URLEncoding.h" + +@implementation NSString (URLEncoding) +- (NSString*)URLEncodedString +{ + NSString* result = (NSString *)CFBridgingRelease( + CFURLCreateStringByAddingPercentEscapes( + kCFAllocatorDefault, + (CFStringRef)self, + CFSTR("#%"), // don't escape these + NULL, // allow escaping these + kCFStringEncodingUTF8 + ) + ); + + // we may have a URL with more than one '#' now - which iOS doesn't allow, so escape all but the first one + NSArray *parts = [result componentsSeparatedByString:@"#"]; + NSString *finalResult = parts[0]; + for (int i=1; i<parts.count; i++) { + NSString *part = [parts objectAtIndex:i]; + if (i==1) { + finalResult = [finalResult stringByAppendingString:@"#"]; + } else { + finalResult = [finalResult stringByAppendingString:@"%23"]; + } + finalResult = [finalResult stringByAppendingString:part]; + } + return finalResult; +} +@end
\ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h index b51474d3..0c731450 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h @@ -4,13 +4,14 @@ @interface SocialSharing : CDVPlugin <UIPopoverControllerDelegate, MFMailComposeViewControllerDelegate, UIDocumentInteractionControllerDelegate> @property (nonatomic, strong) MFMailComposeViewController *globalMailComposer; -@property (retain) UIDocumentInteractionController * documentInteractionController; +@property (nonatomic, strong) UIDocumentInteractionController * documentInteractionController; @property (retain) NSString * tempStoredFile; @property (retain) CDVInvokedUrlCommand * command; - (void)available:(CDVInvokedUrlCommand*)command; - (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command; - (void)share:(CDVInvokedUrlCommand*)command; +- (void)shareWithOptions:(CDVInvokedUrlCommand*)command; - (void)canShareVia:(CDVInvokedUrlCommand*)command; - (void)canShareViaEmail:(CDVInvokedUrlCommand*)command; - (void)shareVia:(CDVInvokedUrlCommand*)command; diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m index cd0913a4..014925bd 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m @@ -1,4 +1,5 @@ #import "SocialSharing.h" +#import "NSString+URLEncoding.h" #import <Cordova/CDV.h> #import <Social/Social.h> #import <Foundation/NSException.h> @@ -6,6 +7,11 @@ #import <MessageUI/MFMailComposeViewController.h> #import <MobileCoreServices/MobileCoreServices.h> +static NSString *const kShareOptionMessage = @"message"; +static NSString *const kShareOptionSubject = @"subject"; +static NSString *const kShareOptionFiles = @"files"; +static NSString *const kShareOptionUrl = @"url"; + @implementation SocialSharing { UIPopoverController *_popover; NSString *_popupCoordinates; @@ -51,23 +57,46 @@ } - (void)share:(CDVInvokedUrlCommand*)command { + [self shareInternal:command + withOptions:@{ + kShareOptionMessage: [command.arguments objectAtIndex:0], + kShareOptionSubject: [command.arguments objectAtIndex:1], + kShareOptionFiles: [command.arguments objectAtIndex:2], + kShareOptionUrl: [command.arguments objectAtIndex:3] + } + isBooleanResponse:YES +]; +} + +- (void)shareWithOptions:(CDVInvokedUrlCommand*)command { + NSDictionary* options = [command.arguments objectAtIndex:0]; + [self shareInternal:command + withOptions:options + isBooleanResponse:NO + ]; +} + +- (void)shareInternal:(CDVInvokedUrlCommand*)command withOptions:(NSDictionary*)options isBooleanResponse:(BOOL)boolResponse { [self.commandDelegate runInBackground:^{ //avoid main thread block especially if sharing big files from url if (!NSClassFromString(@"UIActivityViewController")) { CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; return; } - - NSString *message = [command.arguments objectAtIndex:0]; - NSString *subject = [command.arguments objectAtIndex:1]; - NSArray *filenames = [command.arguments objectAtIndex:2]; - NSString *urlString = [command.arguments objectAtIndex:3]; - + + NSString *message = options[kShareOptionMessage]; + NSString *subject = options[kShareOptionSubject]; + NSArray *filenames = options[kShareOptionFiles]; + NSString *urlString = options[kShareOptionUrl]; + NSMutableArray *activityItems = [[NSMutableArray alloc] init]; + + if (message != (id)[NSNull null] && message != nil) { [activityItems addObject:message]; - - NSMutableArray *files = [[NSMutableArray alloc] init]; - if (filenames != (id)[NSNull null] && filenames.count > 0) { + } + + if (filenames != (id)[NSNull null] && filenames != nil && filenames.count > 0) { + NSMutableArray *files = [[NSMutableArray alloc] init]; for (NSString* filename in filenames) { NSObject *file = [self getImage:filename]; if (file == nil) { @@ -79,31 +108,47 @@ } [activityItems addObjectsFromArray:files]; } - - if (urlString != (id)[NSNull null]) { - [activityItems addObject:[NSURL URLWithString:urlString]]; + + if (urlString != (id)[NSNull null] && urlString != nil) { + [activityItems addObject:[NSURL URLWithString:[urlString URLEncodedString]]]; } - + UIActivity *activity = [[UIActivity alloc] init]; NSArray *applicationActivities = [[NSArray alloc] initWithObjects:activity, nil]; UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities]; - if (subject != (id)[NSNull null]) { + if (subject != (id)[NSNull null] && subject != nil) { [activityVC setValue:subject forKey:@"subject"]; } - - // TODO deprecated in iOS 8.0, change this some day - [activityVC setCompletionHandler:^(NSString *activityType, BOOL completed) { - [self cleanupStoredFiles]; - NSLog(@"SocialSharing app selected: %@", activityType); - CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:completed]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - }]; - + + if ([activityVC respondsToSelector:(@selector(setCompletionWithItemsHandler:))]) { + [activityVC setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray * returnedItems, NSError * activityError) { + [self cleanupStoredFiles]; + if (boolResponse) { + [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:completed] + callbackId:command.callbackId]; + } else { + NSDictionary * result = @{@"completed":@(completed), @"app":activityType == nil ? @"" : activityType}; + [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result] + callbackId:command.callbackId]; + } + }]; + } else { + // let's suppress this warning otherwise folks will start opening issues while it's not relevant +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + [activityVC setCompletionHandler:^(NSString *activityType, BOOL completed) { + [self cleanupStoredFiles]; + NSDictionary * result = @{@"completed":@(completed), @"app":activityType}; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +#pragma GCC diagnostic warning "-Wdeprecated-declarations" + } + NSArray * socialSharingExcludeActivities = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SocialSharingExcludeActivities"]; if (socialSharingExcludeActivities!=nil && [socialSharingExcludeActivities count] > 0) { activityVC.excludedActivityTypes = socialSharingExcludeActivities; } - + dispatch_async(dispatch_get_main_queue(), ^(void){ // iPad on iOS >= 8 needs a different approach if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { @@ -230,27 +275,27 @@ - (void)shareViaInternal:(CDVInvokedUrlCommand*)command type:(NSString *) type { - + NSString *message = [command.arguments objectAtIndex:0]; // subject is not supported by the SLComposeViewController NSArray *filenames = [command.arguments objectAtIndex:2]; NSString *urlString = [command.arguments objectAtIndex:3]; - + // boldly invoke the target app, because the phone will display a nice message asking to configure the app SLComposeViewController *composeViewController = [SLComposeViewController composeViewControllerForServiceType:type]; if (message != (id)[NSNull null]) { [composeViewController setInitialText:message]; } - + for (NSString* filename in filenames) { UIImage* image = [self getImage:filename]; if (image != nil) { [composeViewController addImage:image]; } } - + if (urlString != (id)[NSNull null]) { - [composeViewController addURL:[NSURL URLWithString:urlString]]; + [composeViewController addURL:[NSURL URLWithString:[urlString URLEncodedString]]]; } [composeViewController setCompletionHandler:^(SLComposeViewControllerResult result) { @@ -272,7 +317,7 @@ - (void)shareViaEmail:(CDVInvokedUrlCommand*)command { if ([self isEmailAvailable]) { - + if (TARGET_IPHONE_SIMULATOR && IsAtLeastiOSVersion(@"8.0")) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"SocialSharing plugin" message:@"Sharing via email is not supported on the iOS 8 simulator." @@ -282,38 +327,40 @@ [alert show]; return; } - + + [self cycleTheGlobalMailComposer]; + self.globalMailComposer.mailComposeDelegate = self; - + if ([command.arguments objectAtIndex:0] != (id)[NSNull null]) { NSString *message = [command.arguments objectAtIndex:0]; BOOL isHTML = [message rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch].location != NSNotFound; [self.globalMailComposer setMessageBody:message isHTML:isHTML]; } - + if ([command.arguments objectAtIndex:1] != (id)[NSNull null]) { [self.globalMailComposer setSubject: [command.arguments objectAtIndex:1]]; } - + if ([command.arguments objectAtIndex:2] != (id)[NSNull null]) { [self.globalMailComposer setToRecipients:[command.arguments objectAtIndex:2]]; } - + if ([command.arguments objectAtIndex:3] != (id)[NSNull null]) { [self.globalMailComposer setCcRecipients:[command.arguments objectAtIndex:3]]; } - + if ([command.arguments objectAtIndex:4] != (id)[NSNull null]) { [self.globalMailComposer setBccRecipients:[command.arguments objectAtIndex:4]]; } - + if ([command.arguments objectAtIndex:5] != (id)[NSNull null]) { NSArray* attachments = [command.arguments objectAtIndex:5]; NSFileManager* fileManager = [NSFileManager defaultManager]; for (NSString* path in attachments) { NSURL *file = [self getFile:path]; NSData* data = [fileManager contentsAtPath:file.path]; - + NSString* fileName; NSString* mimeType; NSString* basename = [self getBasenameFromAttachmentPath:path]; @@ -331,14 +378,14 @@ [self.globalMailComposer addAttachmentData:data mimeType:mimeType fileName:fileName]; } } - + // remember the command, because we need it in the didFinishWithResult method _command = command; [self.commandDelegate runInBackground:^{ [[self getTopMostViewController] presentViewController:self.globalMailComposer animated:YES completion:nil]; }]; - + } else { CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; @@ -358,7 +405,7 @@ NSString* pathWithoutPrefix = [path stringByReplacingOccurrencesOfString:@"base64:" withString:@""]; return [pathWithoutPrefix substringToIndex:[pathWithoutPrefix rangeOfString:@"//"].location]; } - return path; + return [path componentsSeparatedByString: @"?"][0]; } - (NSString*) getMimeTypeFromFileExtension:(NSString*)extension { @@ -406,7 +453,7 @@ NSString *message = [options objectForKey:@"message"]; NSString *subject = [options objectForKey:@"subject"]; NSString *image = [options objectForKey:@"image"]; - + MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init]; picker.messageComposeDelegate = (id) self; if (message != (id)[NSNull null]) { @@ -424,7 +471,7 @@ } } } - + if (phonenumbers != (id)[NSNull null]) { [picker setRecipients:[phonenumbers componentsSeparatedByString:@","]]; } @@ -469,7 +516,7 @@ } - (void)shareViaInstagram:(CDVInvokedUrlCommand*)command { - + // on iOS9 canShareVia('instagram'..) will only work if instagram:// is whitelisted. // If it's not, this method will ask permission to the user on iOS9 for opening the app, // which is of course better than Instagram sharing not working at all because you forgot to whitelist it. @@ -492,7 +539,7 @@ image = [self getImage:filename]; break; } - + // NSData *imageObj = [NSData dataFromBase64String:objectAtIndex0]; NSString *tmpDir = NSTemporaryDirectory(); NSString *path = [tmpDir stringByAppendingPathComponent:@"instagram.igo"]; @@ -513,11 +560,15 @@ // remember the command for the delegate method _command = command; - [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.webView animated:YES]; + + // test for #513 + dispatch_async(dispatch_get_main_queue(), ^(void){ + [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.webView animated:YES]; + }); } - (void)shareViaWhatsApp:(CDVInvokedUrlCommand*)command { - + // on iOS9 canShareVia('whatsapp'..) will only work if whatsapp:// is whitelisted. // If it's not, this method will ask permission to the user on iOS9 for opening the app, // which is of course better than WhatsApp sharing not working at all because you forgot to whitelist it. @@ -534,6 +585,7 @@ // subject is not supported by the SLComposeViewController NSArray *filenames = [command.arguments objectAtIndex:2]; NSString *urlString = [command.arguments objectAtIndex:3]; + NSString *abid = [command.arguments objectAtIndex:4]; // only use the first image (for now.. maybe we can share in a loop?) UIImage* image = nil; @@ -561,14 +613,18 @@ if ([shareString isEqual: @""]) { shareString = urlString; } else { - shareString = [NSString stringWithFormat:@"%@ %@", shareString, urlString]; + shareString = [NSString stringWithFormat:@"%@ %@", shareString, [urlString URLEncodedString]]; } } NSString * encodedShareString = [shareString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; // also encode the '=' character encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"=" withString:@"%3D"]; encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"&" withString:@"%26"]; - NSString * encodedShareStringForWhatsApp = [NSString stringWithFormat:@"whatsapp://send?text=%@", encodedShareString]; + NSString * abidString = @""; + if (abid != (id)[NSNull null]) { + abidString = [NSString stringWithFormat:@"abid=%@&", abid]; + } + NSString * encodedShareStringForWhatsApp = [NSString stringWithFormat:@"whatsapp://send?%@text=%@", abidString, encodedShareString]; NSURL *whatsappURL = [NSURL URLWithString:encodedShareStringForWhatsApp]; [[UIApplication sharedApplication] openURL: whatsappURL]; @@ -641,7 +697,8 @@ if ([fileName hasPrefix:@"http"]) { NSURL *url = [NSURL URLWithString:fileName]; NSData *fileData = [NSData dataWithContentsOfURL:url]; - file = [NSURL fileURLWithPath:[self storeInFile:(NSString*)[[fileName componentsSeparatedByString: @"/"] lastObject] fileData:fileData]]; + NSString *name = (NSString*)[[fileName componentsSeparatedByString: @"/"] lastObject]; + file = [NSURL fileURLWithPath:[self storeInFile:[name componentsSeparatedByString: @"?"][0] fileData:fileData]]; } else if ([fileName hasPrefix:@"www/"]) { NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; NSString *fullPath = [NSString stringWithFormat:@"%@/%@", bundlePath, fileName]; diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js index 99e0af15..ff257d52 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js @@ -10,6 +10,36 @@ module.exports = { var fileOrFileArray = args[2]; //Web link var url = args[3]; + + var folder = Windows.Storage.ApplicationData.current.temporaryFolder; + + var getExtension = function (strBase64) { + return strBase64.substring(strBase64.indexOf("/") + 1, strBase64.indexOf(";base64")); + }; + + var replaceAll = function (str, find, replace) { + return str.replace(new RegExp(find, 'g'), replace); + }; + + var sanitizeFilename = function (name) { + return replaceAll(name, "[:\\\\/*?|<> ]", "_"); + }; + + var getFileName = function (position, fileExtension) { + var fileName = (subject ? sanitizeFilename(subject) : "file") + (position == 0 ? "" : "_" + position) + "." + fileExtension; + return fileName; + }; + + var createTemporalFile = function (fileName, buffer) { + + var filePath = ""; + return folder.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.replaceExisting).then(function (file) { + filePath = file.path; + return Windows.Storage.FileIO.writeBufferAsync(file, buffer); + }).then(function(){ + return Windows.Storage.StorageFile.getFileFromPathAsync(filePath); + }); + }; var doShare = function (e) { e.request.data.properties.title = subject?subject: "Sharing"; @@ -19,25 +49,49 @@ module.exports = { var deferral = e.request.getDeferral(); var storageItems = []; var filesCount = fileOrFileArray.length; + + var completeFile = function () { + if (!--filesCount) { + storageItems.length && e.request.data.setStorageItems(storageItems); + deferral.complete(); + } + }; + for (var i = 0; i < fileOrFileArray.length; i++) { - Windows.Storage.StorageFile.getFileFromPathAsync(fileOrFileArray[i]).done( - function (file) { - storageItems.push(file); - if (!--filesCount) { - e.request.data.setStorageItems(storageItems); - deferral.complete(); - } - }, - function() { - if (!--filesCount) { - e.request.data.setStorageItems(storageItems); - deferral.complete(); + + var file = fileOrFileArray[i]; + if (file.indexOf("data:") >= 0) { + var fileName = getFileName(i, getExtension(file)); + var buffer = Windows.Security.Cryptography.CryptographicBuffer.decodeFromBase64String(file.split(',')[1]); + if (buffer) { + createTemporalFile(fileName, buffer).done( + function (file) { + storageItems.push(file); + completeFile(); + }, + function () { + completeFile(); + } + ); + } + else { + completeFile(); + } + } + else { + Windows.Storage.StorageFile.getFileFromPathAsync(file).done( + function (file) { + storageItems.push(file); + completeFile(); + }, + function () { + completeFile(); } - } - ); + ); + } } } - } + }; var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView(); @@ -96,7 +150,7 @@ module.exports = { ); } } - } + }; var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView(); diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs index 1a165127..9d63b2f4 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs @@ -26,7 +26,7 @@ namespace Cordova.Extension.Commands var files = JsonHelper.Deserialize<string[]>(options[2]); var link = options[3]; - if (!"null".Equals(link)) + if (link != null && !"null".Equals(link)) { ShareLinkTask shareLinkTask = new ShareLinkTask(); shareLinkTask.Title = title; @@ -100,4 +100,4 @@ namespace Cordova.Extension.Commands public string message { get; set; } } -}
\ No newline at end of file +} |
