diff options
Diffstat (limited to 'StoneIsland/plugins/cordova-plugin-x-socialsharing')
20 files changed, 599 insertions, 121 deletions
diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/MIT b/StoneIsland/plugins/cordova-plugin-x-socialsharing/MIT new file mode 100644 index 00000000..f932f6d9 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2016 Eddy Verbruggen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.
\ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/README.md b/StoneIsland/plugins/cordova-plugin-x-socialsharing/README.md index 21709982..52e5515c 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/README.md +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/README.md @@ -24,6 +24,7 @@ Every now and then kind folks ask me how they can give me all their money. So if 4. [iOS and Android](#4a-usage-on-ios-and-android) 4. [Windows Phone](#4b-usage-on-windows-phone) 4. [Share-popover on iPad](#4c-share-popover-on-ipad) + 4. [Whitelisting on iOS 9](#4d-whitelisting-on-ios-9) 5. [Credits](#5-credits) 6. [License](#6-license) @@ -154,6 +155,33 @@ However, what exactly gets shared, depends on the application the user chooses t - Facebook iOS: message, image (other filetypes are not supported), link. Beware that since a Fb update in April 2015 sharing a prefilled message is no longer possible when the Fb app is installed (like Android), see #344. Alternative: use `shareViaFacebookWithPasteMessageHint`. ### Using the share sheet +Since version 5.1.0 (for iOS and Android) it's recommended to use `shareWithOptions` as it's the most feature rich way to share stuff cross-platform. + +It will also tell you if sharing to an app completed and which app that was (if that app plays nice, that is). + +```js +// this is the complete list of currently supported params you can pass to the plugin (all optional) +var options = { + message: 'share this', // not supported on some apps (Facebook, Instagram) + subject: 'the subject', // fi. for email + files: ['', ''], // an array of filenames either locally or remotely + url: 'https://www.website.com/foo/#bar?a=b', + chooserTitle: 'Pick an app' // Android only, you can override the default share sheet title +} + +var onSuccess = function(result) { + console.log("Share completed? " + result.completed); // On Android apps mostly return false even while it's true + console.log("Shared to app: " + result.app); // On Android result.app is currently empty. On iOS it's empty when sharing is cancelled (result.completed=false) +} + +var onError = function(msg) { + console.log("Sharing failed with message: " + msg); +} + +window.plugins.socialsharing.shareWithOptions(options, onSuccess, onError); +``` + +#### You can still use the older `share` method as well Here are some examples you can copy-paste to test the various combinations: ```html <button onclick="window.plugins.socialsharing.share('Message only')">message only</button> @@ -226,6 +254,18 @@ iOS Quirks: <button onclick="window.plugins.socialsharing.shareViaWhatsApp('Message via WhatsApp', null /* img */, null /* url */, function() {console.log('share ok')}, function(errormsg){alert(errormsg)})">msg via WhatsApp (with errcallback)</button> ``` +##### Experimental feature: sharing directly to someone +Available in 5.0.8 and up - please let me know if this works for your device! Open an issue if not.. + +```html +<button onclick="window.plugins.socialsharing.shareViaWhatsAppToReceiver(receiver, 'Message via WhatsApp', null /* img */, null /* url */, function() {console.log('share ok')})">msg via WhatsApp for Addressbook ID 101</button> +``` +For `receiver` on iOS pass in the Addressbook ID (or 'abid'). You can find those abid's by using the [Cordova Contacts Plugin](https://github.com/apache/cordova-plugin-contacts). +The result in the success callback of the `find` function is a JSON array of contact objects, use the 'id' you find in those objects. +Don't pass in an image on iOS because that can't be sent to someone directly unfortunately. Message and URL are fine though. + +On Android pass in the phone number of the person you want to send a message to (untested at the moment). + ####SMS Note that on Android, SMS via Hangouts may not behave correctly ```html @@ -247,7 +287,7 @@ window.plugins.socialsharing.shareViaEmail( ['cc@person1.com'], // CC: must be null or an array null, // BCC: must be null or an array ['https://www.google.nl/images/srpr/logo4w.png','www/localimage.png'], // FILES: can be null, a string, or an array - onSuccess, // called when sharing worked, but also when the user cancelled sharing via email (I've found no way to detect the difference) + onSuccess, // called when sharing worked, but also when the user cancelled sharing via email. On iOS, the callbacks' boolean result parameter is true when sharing worked, false if cancelled. On Android, this parameter is always true so it can't be used). See section "Notes about the successCallback" below. onError // called when sh*t hits the fan ); ``` @@ -441,31 +481,51 @@ window.plugins.socialsharing.setIPadPopupCoordinates(targetBounds); window.plugins.socialsharing.share('Hello from iOS :)') ``` -## 5. Credits ## +## 4d. Whitelisting on iOS 9 -This plugin was enhanced for Plugman / PhoneGap Build by [Eddy Verbruggen](http://www.x-services.nl). -The Android and Windows Phone code was entirely created by the author. -The first iteration of the iOS code was inspired by [Cameron Lerch](https://github.com/bfcam/phonegap-ios-social-plugin). +On iOS 9 you have to make sure to whitelist the applications you want to use for sharing. Without whitelisting "query schemes", you may get the error callback invoked when calling the `canShareVia` function (and possibly the `shareVia`). You can verify this is a permissions issue by observing the output in XCode for something like: + +> -canOpenURL: failed for URL: "whatsapp://app" - error: "This app is not allowed to query for scheme whatsapp" +You have a few options to prevent this by whitelisting the application you want to share via: -## 6. License +### Directly editing the .plist file +This is a stright forward approach - you just manually edit the .plist file - either from within XCode or using a text editor. You can see example entries above (e.g. xyz). While this is simple to do, the changes may be lost when rebuilding the project or tweaking the platform (e.g. upgrading) and is less recomended. -[The MIT License (MIT)](http://www.opensource.org/licenses/mit-license.html) +### Use query schema plugin +There is a plugin designed specifically to address query schema whitelisting. You can find the plugin and how to use it [here](https://www.npmjs.com/package/cordova-plugin-queries-schemes). In general, after installation, you can change plugin.xml file under the plugin subfolder within the plugins directory of your project to add the required schemas. Here again though, you have to edit an additional file and should take care not to overwrite it when making changes to your project. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +### Use Custom Config plugin +The Custom Config plugin ([here](https://github.com/dpa99c/cordova-custom-config)) allows you to add configuration to your platforms "native" configuration files (e.g. .plist or AndroidManifest.xml) through the project's main config.xml file. -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +To address query schema issue, after installaing the plugin you can edit the iOS platform section of your config.xml (in the project main folder) to include the required entries: + +```xml +<?xml version='1.0' encoding='utf-8'?> +<widget id="your.app.id" + version="0.9.1" + xmlns="http://www.w3.org/ns/widgets" + xmlns:cdv="http://cordova.apache.org/ns/1.0"> + + <!-- a bunch of elements like name, description etc --> + + <platform name="ios"> + + <!-- add this entry --> + <config-file platform="ios" target="*-Info.plist" parent="LSApplicationQueriesSchemes"> + <array> + <string>whatsapp</string> + <!-- add more query scheme strings --> + </array> + </config-file> + </platform> +</widget> +``` -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +The advantage with this method is that editing is done in the config.xml file which will most often be in your source control anyway and hence, changes to it will be reserved. + +## 5. Credits ## + +This plugin was enhanced for Plugman / PhoneGap Build by [Eddy Verbruggen](http://www.x-services.nl). +The Android and Windows Phone code was entirely created by the author. +The first iteration of the iOS code was inspired by [Cameron Lerch](https://github.com/bfcam/phonegap-ios-social-plugin). diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/package.json b/StoneIsland/plugins/cordova-plugin-x-socialsharing/package.json index 66bc9646..62e6a1c0 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/package.json +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/package.json @@ -1,6 +1,6 @@ { "name": "cordova-plugin-x-socialsharing", - "version": "5.0.7", + "version": "5.1.3", "description": "Share text, images (and other files), or a link via the native sharing widget of your device. Android is fully supported, as well as iOS 6 and up. WP8 has somewhat limited support.", "cordova": { "id": "cordova-plugin-x-socialsharing", @@ -29,7 +29,7 @@ "ecosystem:cordova", "cordova-ios", "cordova-android", - "cordova-wp8" + "cordova-windows" ], "engines": [ { diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/plugin.xml b/StoneIsland/plugins/cordova-plugin-x-socialsharing/plugin.xml index ba7183cb..0a367427 100755 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/plugin.xml +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/plugin.xml @@ -2,7 +2,7 @@ <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-x-socialsharing" - version="5.0.7"> + version="5.1.3"> <name>SocialSharing</name> @@ -39,6 +39,8 @@ </feature> </config-file> + <header-file src="src/ios/NSString+URLEncoding.h"/> + <source-file src="src/ios/NSString+URLEncoding.m"/> <header-file src="src/ios/SocialSharing.h"/> <source-file src="src/ios/SocialSharing.m"/> @@ -71,7 +73,7 @@ </config-file> <source-file src="src/wp8/SocialSharing.cs" /> - <framework src="src/wp8/SocialSharingSDK/Newtonsoft.Json.dll" custom="true" /> + <framework src="src/wp8/Newtonsoft.Json.dll" custom="true"/> </platform> <!-- windows --> @@ -79,7 +81,6 @@ <js-module src="src/windows/SocialSharingProxy.js" name="SocialSharingProxy"> <merges target="" /> </js-module> - <source-file src="" /> </platform> </plugin> diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-android-share.png b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-android-share.png Binary files differindex f3ab135f..f3ab135f 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-android-share.png +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-android-share.png diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios6-share.png b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios6-share.png Binary files differindex 63b0b4c5..63b0b4c5 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios6-share.png +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios6-share.png diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-ipad-share.png b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-ipad-share.png Binary files differindex 640c94f8..640c94f8 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-ipad-share.png +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-ipad-share.png diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-share.png b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-share.png Binary files differindex 7545d10d..7545d10d 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-share.png +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-share.png diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-wp8-share.jpg b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-wp8-share.jpg Binary files differindex 0ced2eb5..0ced2eb5 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-wp8-share.jpg +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-wp8-share.jpg diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshots-ios7-shareconfig.png b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshots-ios7-shareconfig.png Binary files differindex 3afe5fef..3afe5fef 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshots-ios7-shareconfig.png +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshots-ios7-shareconfig.png 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 +} diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/plugin.xml b/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/plugin.xml index 1ccb2217..1ccb2217 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/plugin.xml +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/plugin.xml diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/test.js b/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/test.js index 630899db..ad1a8229 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/test.js +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/test.js @@ -1,4 +1,4 @@ -/** +/** * Jasmine Based test suites * * Several of SocialSharing APIs cannot be automatically tested, because diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/www/SocialSharing.js b/StoneIsland/plugins/cordova-plugin-x-socialsharing/www/SocialSharing.js index 6ccd567b..3fc9bb92 100755..100644 --- a/StoneIsland/plugins/cordova-plugin-x-socialsharing/www/SocialSharing.js +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/www/SocialSharing.js @@ -1,6 +1,4 @@ -var cordova = require('cordova'); - -function SocialSharing() { +function SocialSharing() { } // Override this method (after deviceready) to set the location where you want the iPad popup arrow to appear. @@ -25,6 +23,11 @@ SocialSharing.prototype.available = function (callback) { }, null, "SocialSharing", "available", []); }; +// this is the recommended way to share as it is the most feature-rich with respect to what you pass in and get back +SocialSharing.prototype.shareWithOptions = function (options, successCallback, errorCallback) { + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "shareWithOptions"), "SocialSharing", "shareWithOptions", [options]); +}; + SocialSharing.prototype.share = function (message, subject, fileOrFileArray, url, successCallback, errorCallback) { cordova.exec(successCallback, this._getErrorCallback(errorCallback, "share"), "SocialSharing", "share", [message, subject, this._asArray(fileOrFileArray), url]); }; @@ -49,7 +52,11 @@ SocialSharing.prototype.shareViaFacebookWithPasteMessageHint = function (message }; SocialSharing.prototype.shareViaWhatsApp = function (message, fileOrFileArray, url, successCallback, errorCallback) { - cordova.exec(successCallback, this._getErrorCallback(errorCallback, "shareViaWhatsApp"), "SocialSharing", "shareViaWhatsApp", [message, null, this._asArray(fileOrFileArray), url]); + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "shareViaWhatsApp"), "SocialSharing", "shareViaWhatsApp", [message, null, this._asArray(fileOrFileArray), url, null]); +}; + +SocialSharing.prototype.shareViaWhatsAppToReceiver = function (receiver, message, fileOrFileArray, url, successCallback, errorCallback) { + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "shareViaWhatsAppToReceiver"), "SocialSharing", "shareViaWhatsApp", [message, null, this._asArray(fileOrFileArray), url, receiver]); }; SocialSharing.prototype.shareViaSMS = function (options, phonenumbers, successCallback, errorCallback) { @@ -112,4 +119,4 @@ SocialSharing.install = function () { return window.plugins.socialsharing; }; -cordova.addConstructor(SocialSharing.install); +cordova.addConstructor(SocialSharing.install);
\ No newline at end of file |
