summaryrefslogtreecommitdiff
path: root/StoneIsland/plugins/cordova-plugin-x-socialsharing/src
diff options
context:
space:
mode:
Diffstat (limited to 'StoneIsland/plugins/cordova-plugin-x-socialsharing/src')
-rw-r--r--StoneIsland/plugins/cordova-plugin-x-socialsharing/src/android/nl/xservices/plugins/SocialSharing.java533
-rwxr-xr-xStoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h27
-rwxr-xr-xStoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m715
-rw-r--r--StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js114
-rw-r--r--StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs103
5 files changed, 1492 insertions, 0 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
new file mode 100644
index 00000000..f1168f89
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/android/nl/xservices/plugins/SocialSharing.java
@@ -0,0 +1,533 @@
+package nl.xservices.plugins;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.*;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.text.Html;
+import android.util.Base64;
+import android.view.Gravity;
+import android.widget.Toast;
+
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaInterface;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.PluginResult;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+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_CAN_SHARE_VIA = "canShareVia";
+ private static final String ACTION_CAN_SHARE_VIA_EMAIL = "canShareViaEmail";
+ private static final String ACTION_SHARE_VIA = "shareVia";
+ private static final String ACTION_SHARE_VIA_TWITTER_EVENT = "shareViaTwitter";
+ private static final String ACTION_SHARE_VIA_FACEBOOK_EVENT = "shareViaFacebook";
+ private static final String ACTION_SHARE_VIA_FACEBOOK_WITH_PASTEMESSAGEHINT = "shareViaFacebookWithPasteMessageHint";
+ private static final String ACTION_SHARE_VIA_WHATSAPP_EVENT = "shareViaWhatsApp";
+ private static final String ACTION_SHARE_VIA_INSTAGRAM_EVENT = "shareViaInstagram";
+ 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 CallbackContext _callbackContext;
+
+ private String pasteMessage;
+
+ private abstract class SocialSharingRunnable implements Runnable {
+ public CallbackContext callbackContext;
+ SocialSharingRunnable(CallbackContext cb) {
+ this.callbackContext = cb;
+ }
+ }
+
+ @Override
+ public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
+ this._callbackContext = callbackContext; // only used for onActivityResult
+ this.pasteMessage = null;
+ if (ACTION_AVAILABLE_EVENT.equals(action)) {
+ 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);
+ } 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);
+ } 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);
+ } 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);
+ } 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);
+ } 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);
+ } 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);
+ } else if (ACTION_CAN_SHARE_VIA_EMAIL.equals(action)) {
+ if (isEmailAvailable()) {
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
+ return true;
+ } else {
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "not available"));
+ 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);
+ } 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)) {
+ return invokeEmailIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.isNull(3) ? null : args.getJSONArray(3), args.isNull(4) ? null : args.getJSONArray(4), args.isNull(5) ? null : args.getJSONArray(5));
+ } else {
+ callbackContext.error("socialSharing." + action + " is not a supported function. Did you mean '" + ACTION_SHARE_EVENT + "'?");
+ return false;
+ }
+ }
+
+ private boolean isEmailAvailable() {
+ final Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", "someone@domain.com", null));
+ return cordova.getActivity().getPackageManager().queryIntentActivities(intent, 0).size() > 0;
+ }
+
+ private boolean invokeEmailIntent(final CallbackContext callbackContext, final String message, final String subject, final JSONArray to, final JSONArray cc, final JSONArray bcc, final JSONArray files) throws JSONException {
+
+ final SocialSharing plugin = this;
+ cordova.getThreadPool().execute(new SocialSharingRunnable(callbackContext) {
+ public void run() {
+ final Intent draft = new Intent(Intent.ACTION_SEND_MULTIPLE);
+ if (notEmpty(message)) {
+ Pattern htmlPattern = Pattern.compile(".*\\<[^>]+>.*", Pattern.DOTALL);
+ if (htmlPattern.matcher(message).matches()) {
+ draft.putExtra(android.content.Intent.EXTRA_TEXT, Html.fromHtml(message));
+ draft.setType("text/html");
+ } else {
+ draft.putExtra(android.content.Intent.EXTRA_TEXT, message);
+ draft.setType("text/plain");
+ }
+ }
+ if (notEmpty(subject)) {
+ draft.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
+ }
+ try {
+ if (to != null && to.length() > 0) {
+ draft.putExtra(android.content.Intent.EXTRA_EMAIL, toStringArray(to));
+ }
+ if (cc != null && cc.length() > 0) {
+ draft.putExtra(android.content.Intent.EXTRA_CC, toStringArray(cc));
+ }
+ if (bcc != null && bcc.length() > 0) {
+ draft.putExtra(android.content.Intent.EXTRA_BCC, toStringArray(bcc));
+ }
+ if (files.length() > 0) {
+ final String dir = getDownloadDir();
+ if (dir != null) {
+ ArrayList<Uri> fileUris = new ArrayList<Uri>();
+ for (int i = 0; i < files.length(); i++) {
+ final Uri fileUri = getFileUriAndSetType(draft, dir, files.getString(i), subject, i);
+ if (fileUri != null) {
+ fileUris.add(fileUri);
+ }
+ }
+ if (!fileUris.isEmpty()) {
+ draft.putExtra(Intent.EXTRA_STREAM, fileUris);
+ }
+ }
+ }
+ } catch (Exception e) {
+ callbackContext.error(e.getMessage());
+ }
+
+ draft.setType("application/octet-stream");
+ cordova.startActivityForResult(plugin, Intent.createChooser(draft, "Choose Email App"), ACTIVITY_CODE_SENDVIAEMAIL);
+ }
+ });
+
+ return true;
+ }
+
+ private String getDownloadDir() throws IOException {
+ // better check, otherwise it may crash the app
+ if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ // we need to use external storage since we need to share to another app
+ final String dir = webView.getContext().getExternalFilesDir(null) + "/socialsharing-downloads";
+ createOrCleanDir(dir);
+ return dir;
+ } else {
+ return null;
+ }
+ }
+
+ private boolean doSendIntent(final CallbackContext callbackContext, final String msg, final String subject, final JSONArray files, final String url, final String appPackageName, final boolean peek) {
+
+ final CordovaInterface mycordova = cordova;
+ final CordovaPlugin plugin = this;
+
+ cordova.getThreadPool().execute(new SocialSharingRunnable(callbackContext) {
+ public void run() {
+ String message = msg;
+ final boolean hasMultipleAttachments = files.length() > 1;
+ final Intent sendIntent = new Intent(hasMultipleAttachments ? Intent.ACTION_SEND_MULTIPLE : Intent.ACTION_SEND);
+ sendIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+
+ try {
+ if (files.length() > 0 && !"".equals(files.getString(0))) {
+ 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(sendIntent, dir, files.getString(i), subject, i);
+ if (fileUri != null) {
+ fileUris.add(fileUri);
+ }
+ }
+ if (!fileUris.isEmpty()) {
+ if (hasMultipleAttachments) {
+ sendIntent.putExtra(Intent.EXTRA_STREAM, fileUris);
+ } else {
+ sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
+ }
+ }
+ } else {
+ sendIntent.setType("text/plain");
+ }
+ } else {
+ sendIntent.setType("text/plain");
+ }
+ } catch (Exception e) {
+ callbackContext.error(e.getMessage());
+ }
+
+ 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)) {
+ message += " " + url;
+ } else {
+ message = url;
+ }
+ }
+ if (notEmpty(message)) {
+ sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
+ // sometimes required when the user picks share via sms
+ if (Build.VERSION.SDK_INT < 21) { // LOLLIPOP
+ sendIntent.putExtra("sms_body", message);
+ }
+ }
+
+ if (appPackageName != null) {
+ String packageName = appPackageName;
+ String passedActivityName = null;
+ if (packageName.contains("/")) {
+ String[] items = appPackageName.split("/");
+ packageName = items[0];
+ passedActivityName = items[1];
+ }
+ final ActivityInfo activity = getActivity(callbackContext, sendIntent, packageName);
+ if (activity != null) {
+ if (peek) {
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
+ } else {
+ sendIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ sendIntent.setComponent(new ComponentName(activity.applicationInfo.packageName,
+ passedActivityName != null ? passedActivityName : activity.name));
+ mycordova.startActivityForResult(plugin, sendIntent, 0);
+
+ if (pasteMessage != null) {
+ // add a little delay because target app (facebook only atm) needs to be started first
+ new Timer().schedule(new TimerTask() {
+ public void run() {
+ cordova.getActivity().runOnUiThread(new Runnable() {
+ public void run() {
+ copyHintToClipboard(msg, pasteMessage);
+ showPasteMessage(pasteMessage);
+ }
+ });
+ }
+ }, 2000);
+ }
+ }
+ }
+ } else {
+ if (peek) {
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
+ } else {
+ mycordova.startActivityForResult(plugin, Intent.createChooser(sendIntent, null), 1);
+ }
+ }
+ }
+ });
+ return true;
+ }
+
+ @SuppressLint("NewApi")
+ private void copyHintToClipboard(String msg, String label) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ return;
+ }
+ final ClipboardManager clipboard = (android.content.ClipboardManager) cordova.getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
+ final ClipData clip = android.content.ClipData.newPlainText(label, msg);
+ clipboard.setPrimaryClip(clip);
+ }
+
+ @SuppressLint("NewApi")
+ private void showPasteMessage(String label) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ return;
+ }
+ final Toast toast = Toast.makeText(webView.getContext(), label, Toast.LENGTH_LONG);
+ toast.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL, 0, 0);
+ toast.show();
+ }
+
+ 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.startsWith("http") || image.startsWith("www/")) {
+ String filename = getFileName(image);
+ localImage = "file://" + dir + "/" + filename;
+ if (image.startsWith("http")) {
+ // filename optimisation taken from https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin/pull/56
+ URLConnection connection = new URL(image).openConnection();
+ String disposition = connection.getHeaderField("Content-Disposition");
+ if (disposition != null) {
+ final Pattern dispositionPattern = Pattern.compile("filename=([^;]+)");
+ Matcher matcher = dispositionPattern.matcher(disposition);
+ if (matcher.find()) {
+ filename = matcher.group(1).replaceAll("[^a-zA-Z0-9._-]", "");
+ localImage = "file://" + dir + "/" + filename;
+ }
+ }
+ saveFile(getBytes(connection.getInputStream()), dir, filename);
+ } else {
+ saveFile(getBytes(webView.getContext().getAssets().open(image)), dir, filename);
+ }
+ } else if (image.startsWith("data:")) {
+ // safeguard for https://code.google.com/p/android/issues/detail?id=7901#c43
+ if (!image.contains(";base64,")) {
+ sendIntent.setType("text/plain");
+ return null;
+ }
+ // image looks like this: data:image/png;base64,R0lGODlhDAA...
+ final String encodedImg = image.substring(image.indexOf(";base64,") + 8);
+ // correct the intent type if anything else was passed, like a pdf: data:application/pdf;base64,..
+ if (!image.contains("data:image/")) {
+ sendIntent.setType(image.substring(image.indexOf("data:") + 5, image.indexOf(";base64")));
+ }
+ // the filename needs a valid extension, so it renders correctly in target apps
+ final String imgExtension = image.substring(image.indexOf("/") + 1, image.indexOf(";base64"));
+ String fileName;
+ // if a subject was passed, use it as the filename
+ // filenames must be unique when passing in multiple files [#158]
+ if (notEmpty(subject)) {
+ fileName = sanitizeFilename(subject) + (nthFile == 0 ? "" : "_" + nthFile) + "." + imgExtension;
+ } else {
+ fileName = "file" + (nthFile == 0 ? "" : "_" + nthFile) + "." + imgExtension;
+ }
+ saveFile(Base64.decode(encodedImg, Base64.DEFAULT), dir, fileName);
+ localImage = "file://" + dir + "/" + fileName;
+ } else if (image.startsWith("df:")) {
+ // safeguard for https://code.google.com/p/android/issues/detail?id=7901#c43
+ if (!image.contains(";base64,")) {
+ sendIntent.setType("text/plain");
+ return null;
+ }
+ // format looks like this : df:filename.txt;data:image/png;base64,R0lGODlhDAA...
+ final String fileName = image.substring(image.indexOf("df:") + 3, image.indexOf(";data:"));
+ final String fileType = image.substring(image.indexOf(";data:") + 6, image.indexOf(";base64,"));
+ final String encodedImg = image.substring(image.indexOf(";base64,") + 8);
+ sendIntent.setType(fileType);
+ saveFile(Base64.decode(encodedImg, Base64.DEFAULT), dir, sanitizeFilename(fileName));
+ localImage = "file://" + dir + "/" + fileName;
+ } else if (!image.startsWith("file://")) {
+ throw new IllegalArgumentException("URL_NOT_SUPPORTED");
+ }
+ return Uri.parse(localImage);
+ }
+
+ 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
+// final String subject = options.optString("subject");
+// final String image = options.optString("image");
+ final String subject = null; //options.optString("subject");
+ final String image = null; // options.optString("image");
+ final String phonenumbers = getPhoneNumbersWithManufacturerSpecificSeparators(p_phonenumbers);
+ final SocialSharing plugin = this;
+ cordova.getThreadPool().execute(new SocialSharingRunnable(callbackContext) {
+ public void run() {
+ Intent intent;
+
+ if (Build.VERSION.SDK_INT >= 19) { // Build.VERSION_CODES.KITKAT) {
+ // passing in no phonenumbers for kitkat may result in an error,
+ // but it may also work for some devices, so documentation will need to cover this case
+ intent = new Intent(Intent.ACTION_SENDTO);
+ intent.setData(Uri.parse("smsto:" + (notEmpty(phonenumbers) ? phonenumbers : "")));
+ } else {
+ intent = new Intent(Intent.ACTION_VIEW);
+ intent.setType("vnd.android-dir/mms-sms");
+ if (phonenumbers != null) {
+ intent.putExtra("address", phonenumbers);
+ }
+ }
+ intent.putExtra("sms_body", message);
+ intent.putExtra("sms_subject", subject);
+
+ try {
+ if (image != null && !"".equals(image)) {
+ final Uri fileUri = getFileUriAndSetType(intent, getDownloadDir(), image, subject, 0);
+ if (fileUri != null) {
+ intent.putExtra(Intent.EXTRA_STREAM, fileUri);
+ }
+ }
+ cordova.startActivityForResult(plugin, intent, 0);
+ } catch (Exception e) {
+ callbackContext.error(e.getMessage());
+ }
+ }
+ });
+ return true;
+ }
+
+ private static String getPhoneNumbersWithManufacturerSpecificSeparators(String phonenumbers) {
+ if (notEmpty(phonenumbers)) {
+ char separator;
+ if (android.os.Build.MANUFACTURER.equalsIgnoreCase("samsung")) {
+ separator = ',';
+ } else {
+ separator = ';';
+ }
+ return phonenumbers.replace(';', separator).replace(',', separator);
+ }
+ return null;
+ }
+
+ private ActivityInfo getActivity(final CallbackContext callbackContext, final Intent shareIntent, final String appPackageName) {
+ final PackageManager pm = webView.getContext().getPackageManager();
+ List<ResolveInfo> activityList = pm.queryIntentActivities(shareIntent, 0);
+ for (final ResolveInfo app : activityList) {
+ if ((app.activityInfo.packageName).contains(appPackageName)) {
+ return app.activityInfo;
+ }
+ }
+ // no matching app found
+ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, getShareActivities(activityList)));
+ return null;
+ }
+
+ private JSONArray getShareActivities(List<ResolveInfo> activityList) {
+ List<String> packages = new ArrayList<String>();
+ for (final ResolveInfo app : activityList) {
+ packages.add(app.activityInfo.packageName);
+ }
+ return new JSONArray(packages);
+ }
+
+ @Override
+ 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));
+ }
+ }
+ }
+
+ private void createOrCleanDir(final String downloadDir) throws IOException {
+ final File dir = new File(downloadDir);
+ if (!dir.exists()) {
+ if (!dir.mkdirs()) {
+ throw new IOException("CREATE_DIRS_FAILED");
+ }
+ } else {
+ cleanupOldFiles(dir);
+ }
+ }
+
+ private static String getFileName(String url) {
+ final String pattern = ".*/([^?#]+)?";
+ Pattern r = Pattern.compile(pattern);
+ Matcher m = r.matcher(url);
+ if (m.find()) {
+ return m.group(1);
+ } else {
+ return null;
+ }
+ }
+
+ private byte[] getBytes(InputStream is) throws IOException {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ int nRead;
+ byte[] data = new byte[16384];
+ while ((nRead = is.read(data, 0, data.length)) != -1) {
+ buffer.write(data, 0, nRead);
+ }
+ buffer.flush();
+ return buffer.toByteArray();
+ }
+
+ private void saveFile(byte[] bytes, String dirName, String fileName) throws IOException {
+ final File dir = new File(dirName);
+ final FileOutputStream fos = new FileOutputStream(new File(dir, fileName));
+ fos.write(bytes);
+ fos.flush();
+ fos.close();
+ }
+
+ /**
+ * As file.deleteOnExit does not work on Android, we need to delete files manually.
+ * Deleting them in onActivityResult is not a good idea, because for example a base64 encoded file
+ * will not be available for upload to Facebook (it's deleted before it's uploaded).
+ * So the best approach is deleting old files when saving (sharing) a new one.
+ */
+ private void cleanupOldFiles(File dir) {
+ for (File f : dir.listFiles()) {
+ //noinspection ResultOfMethodCallIgnored
+ f.delete();
+ }
+ }
+
+ private static boolean notEmpty(String what) {
+ return what != null &&
+ !"".equals(what) &&
+ !"null".equalsIgnoreCase(what);
+ }
+
+ private static String[] toStringArray(JSONArray jsonArray) throws JSONException {
+ String[] result = new String[jsonArray.length()];
+ for (int i = 0; i < jsonArray.length(); i++) {
+ result[i] = jsonArray.getString(i);
+ }
+ return result;
+ }
+
+ public static String sanitizeFilename(String name) {
+ return name.replaceAll("[:\\\\/*?|<> ]", "_");
+ }
+}
diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h
new file mode 100755
index 00000000..b51474d3
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h
@@ -0,0 +1,27 @@
+#import <Cordova/CDV.h>
+#import <MessageUI/MFMailComposeViewController.h>
+
+@interface SocialSharing : CDVPlugin <UIPopoverControllerDelegate, MFMailComposeViewControllerDelegate, UIDocumentInteractionControllerDelegate>
+
+@property (nonatomic, strong) MFMailComposeViewController *globalMailComposer;
+@property (retain) UIDocumentInteractionController * documentInteractionController;
+@property (retain) NSString * tempStoredFile;
+@property (retain) CDVInvokedUrlCommand * command;
+
+- (void)available:(CDVInvokedUrlCommand*)command;
+- (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command;
+- (void)share:(CDVInvokedUrlCommand*)command;
+- (void)canShareVia:(CDVInvokedUrlCommand*)command;
+- (void)canShareViaEmail:(CDVInvokedUrlCommand*)command;
+- (void)shareVia:(CDVInvokedUrlCommand*)command;
+- (void)shareViaTwitter:(CDVInvokedUrlCommand*)command;
+- (void)shareViaFacebook:(CDVInvokedUrlCommand*)command;
+- (void)shareViaFacebookWithPasteMessageHint:(CDVInvokedUrlCommand*)command;
+- (void)shareViaWhatsApp:(CDVInvokedUrlCommand*)command;
+- (void)shareViaSMS:(CDVInvokedUrlCommand*)command;
+- (void)shareViaEmail:(CDVInvokedUrlCommand*)command;
+- (void)shareViaInstagram:(CDVInvokedUrlCommand*)command;
+
+- (void)saveToPhotoAlbum:(CDVInvokedUrlCommand*)command;
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m
new file mode 100755
index 00000000..cd0913a4
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m
@@ -0,0 +1,715 @@
+#import "SocialSharing.h"
+#import <Cordova/CDV.h>
+#import <Social/Social.h>
+#import <Foundation/NSException.h>
+#import <MessageUI/MFMessageComposeViewController.h>
+#import <MessageUI/MFMailComposeViewController.h>
+#import <MobileCoreServices/MobileCoreServices.h>
+
+@implementation SocialSharing {
+ UIPopoverController *_popover;
+ NSString *_popupCoordinates;
+}
+
+- (void)pluginInitialize {
+ if ([self isEmailAvailable]) {
+ [self cycleTheGlobalMailComposer];
+ }
+}
+
+- (void)available:(CDVInvokedUrlCommand*)command {
+ BOOL avail = NO;
+ if (NSClassFromString(@"UIActivityViewController")) {
+ avail = YES;
+ }
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:avail];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (NSString*)getIPadPopupCoordinates {
+ if (_popupCoordinates != nil) {
+ return _popupCoordinates;
+ }
+ if ([self.webView respondsToSelector:@selector(stringByEvaluatingJavaScriptFromString:)]) {
+ return [(UIWebView*)self.webView stringByEvaluatingJavaScriptFromString:@"window.plugins.socialsharing.iPadPopupCoordinates();"];
+ } else {
+ // prolly a wkwebview, ignoring for now
+ return nil;
+ }
+}
+
+- (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command {
+ _popupCoordinates = [command.arguments objectAtIndex:0];
+}
+
+- (CGRect)getPopupRectFromIPadPopupCoordinates:(NSArray*)comps {
+ CGRect rect = CGRectZero;
+ if ([comps count] == 4) {
+ rect = CGRectMake([[comps objectAtIndex:0] integerValue], [[comps objectAtIndex:1] integerValue], [[comps objectAtIndex:2] integerValue], [[comps objectAtIndex:3] integerValue]);
+ }
+ return rect;
+}
+
+- (void)share:(CDVInvokedUrlCommand*)command {
+ [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];
+
+ NSMutableArray *activityItems = [[NSMutableArray alloc] init];
+ [activityItems addObject:message];
+
+ NSMutableArray *files = [[NSMutableArray alloc] init];
+ if (filenames != (id)[NSNull null] && filenames.count > 0) {
+ for (NSString* filename in filenames) {
+ NSObject *file = [self getImage:filename];
+ if (file == nil) {
+ file = [self getFile:filename];
+ }
+ if (file != nil) {
+ [files addObject:file];
+ }
+ }
+ [activityItems addObjectsFromArray:files];
+ }
+
+ if (urlString != (id)[NSNull null]) {
+ [activityItems addObject:[NSURL URLWithString:urlString]];
+ }
+
+ 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]) {
+ [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];
+ }];
+
+ 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) {
+ NSString* iPadCoords = [self getIPadPopupCoordinates];
+ if (iPadCoords != nil && ![iPadCoords isEqual:@"-1,-1,-1,-1"]) {
+ NSArray *comps = [iPadCoords componentsSeparatedByString:@","];
+ CGRect rect = [self getPopupRectFromIPadPopupCoordinates:comps];
+ if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) {
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported
+ activityVC.popoverPresentationController.sourceView = self.webView;
+ activityVC.popoverPresentationController.sourceRect = rect;
+#endif
+ } else {
+ _popover = [[UIPopoverController alloc] initWithContentViewController:activityVC];
+ _popover.delegate = self;
+ [_popover presentPopoverFromRect:rect inView:self.webView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
+ }
+ } else if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) {
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported
+ activityVC.popoverPresentationController.sourceView = self.webView;
+ // position the popup at the bottom, just like iOS < 8 did (and iPhone still does on iOS 8)
+ NSArray *comps = [NSArray arrayWithObjects:
+ [NSNumber numberWithInt:(self.viewController.view.frame.size.width/2)-200],
+ [NSNumber numberWithInt:self.viewController.view.frame.size.height],
+ [NSNumber numberWithInt:400],
+ [NSNumber numberWithInt:400],
+ nil];
+ CGRect rect = [self getPopupRectFromIPadPopupCoordinates:comps];
+ activityVC.popoverPresentationController.sourceRect = rect;
+#endif
+ }
+ }
+ [[self getTopMostViewController] presentViewController:activityVC animated:YES completion:nil];
+ });
+ }];
+}
+
+- (void)shareViaTwitter:(CDVInvokedUrlCommand*)command {
+ [self shareViaInternal:command type:SLServiceTypeTwitter];
+}
+
+- (void)shareViaFacebook:(CDVInvokedUrlCommand*)command {
+ [self shareViaInternal:command type:SLServiceTypeFacebook];
+}
+
+- (void)shareViaFacebookWithPasteMessageHint:(CDVInvokedUrlCommand*)command {
+ // If Fb app is installed a message is not prefilled.
+ // When shared through the default iOS widget (iOS Settings > Facebook) the message is prefilled already.
+ NSString *message = [command.arguments objectAtIndex:0];
+ if (message != (id)[NSNull null]) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1000 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
+ BOOL fbAppInstalled = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]]; // requires whitelisting on iOS9
+ if (fbAppInstalled) {
+ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
+ [pasteboard setValue:message forPasteboardType:@"public.text"];
+ NSString *hint = [command.arguments objectAtIndex:4];
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:hint delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
+ [alert show];
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2800 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
+ [alert dismissWithClickedButtonIndex:-1 animated:YES];
+ });
+ }
+ });
+ }
+ [self shareViaInternal:command type:SLServiceTypeFacebook];
+}
+
+- (void)shareVia:(CDVInvokedUrlCommand*)command {
+ [self shareViaInternal:command type:[command.arguments objectAtIndex:4]];
+}
+
+- (void)canShareVia:(CDVInvokedUrlCommand*)command {
+ NSString *via = [command.arguments objectAtIndex:4];
+ CDVPluginResult * pluginResult;
+ if ([@"sms" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaSMS]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else if ([@"email" caseInsensitiveCompare:via] == NSOrderedSame && [self isEmailAvailable]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else if ([@"whatsapp" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaWhatsApp]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else if ([@"instagram" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaInstagram]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else if ([self isAvailableForSharing:command type:via]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ }
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void)canShareViaEmail:(CDVInvokedUrlCommand*)command {
+ if ([self isEmailAvailable]) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ } else {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }
+}
+
+- (bool)isEmailAvailable {
+ Class messageClass = (NSClassFromString(@"MFMailComposeViewController"));
+ return messageClass != nil && [messageClass canSendMail];
+}
+
+- (bool)isAvailableForSharing:(CDVInvokedUrlCommand*)command
+ type:(NSString *) type {
+ // isAvailableForServiceType returns true if you pass it a type that is not
+ // in the defined constants, this is probably a bug on apples part
+ if(!([type isEqualToString:SLServiceTypeFacebook]
+ || [type isEqualToString:SLServiceTypeTwitter]
+ || [type isEqualToString:SLServiceTypeTencentWeibo]
+ || [type isEqualToString:SLServiceTypeSinaWeibo])) {
+ return false;
+ }
+ // wrapped in try-catch, because isAvailableForServiceType may crash if an invalid type is passed
+ @try {
+ return [SLComposeViewController isAvailableForServiceType:type];
+ }
+ @catch (NSException* exception) {
+ return false;
+ }
+}
+
+- (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 setCompletionHandler:^(SLComposeViewControllerResult result) {
+ if (SLComposeViewControllerResultCancelled == result) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"cancelled"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ } else if ([self isAvailableForSharing:command type:type]) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:SLComposeViewControllerResultDone == result];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ } else {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }
+ // required for iOS6 (issues #162 and #167)
+ [self.viewController dismissViewControllerAnimated:YES completion:nil];
+ }];
+ [[self getTopMostViewController] presentViewController:composeViewController animated:YES completion:nil];
+}
+
+- (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."
+ delegate:nil
+ cancelButtonTitle:@"OK"
+ otherButtonTitles:nil];
+ [alert show];
+ return;
+ }
+
+ 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];
+
+ if ([basename hasPrefix:@"data:"]) {
+ mimeType = (NSString*)[[[basename substringFromIndex:5] componentsSeparatedByString: @";"] objectAtIndex:0];
+ fileName = @"attachment.";
+ fileName = [fileName stringByAppendingString:(NSString*)[[mimeType componentsSeparatedByString: @"/"] lastObject]];
+ NSString *base64content = (NSString*)[[basename componentsSeparatedByString: @","] lastObject];
+ data = [SocialSharing dataFromBase64String:base64content];
+ } else {
+ fileName = [basename pathComponents].lastObject;
+ mimeType = [self getMimeTypeFromFileExtension:[basename pathExtension]];
+ }
+ [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];
+ }
+}
+
+- (UIViewController*) getTopMostViewController {
+ UIViewController *presentingViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController;
+ while (presentingViewController.presentedViewController != nil) {
+ presentingViewController = presentingViewController.presentedViewController;
+ }
+ return presentingViewController;
+}
+
+- (NSString*) getBasenameFromAttachmentPath:(NSString*)path {
+ if ([path hasPrefix:@"base64:"]) {
+ NSString* pathWithoutPrefix = [path stringByReplacingOccurrencesOfString:@"base64:" withString:@""];
+ return [pathWithoutPrefix substringToIndex:[pathWithoutPrefix rangeOfString:@"//"].location];
+ }
+ return path;
+}
+
+- (NSString*) getMimeTypeFromFileExtension:(NSString*)extension {
+ if (!extension) {
+ return nil;
+ }
+ // Get the UTI from the file's extension
+ CFStringRef ext = (CFStringRef)CFBridgingRetain(extension);
+ CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL);
+ // Converting UTI to a mime type
+ NSString *result = (NSString*)CFBridgingRelease(UTTypeCopyPreferredTagWithClass(type, kUTTagClassMIMEType));
+ CFRelease(ext);
+ CFRelease(type);
+ return result;
+}
+
+/**
+ * Delegate will be called after the mail composer did finish an action
+ * to dismiss the view.
+ */
+- (void) mailComposeController:(MFMailComposeViewController*)controller
+ didFinishWithResult:(MFMailComposeResult)result
+ error:(NSError*)error {
+ bool ok = result == MFMailComposeResultSent;
+ [self.globalMailComposer dismissViewControllerAnimated:YES completion:^{[self cycleTheGlobalMailComposer];}];
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId];
+}
+
+-(void)cycleTheGlobalMailComposer {
+ // we are cycling the damned GlobalMailComposer: http://stackoverflow.com/questions/25604552/i-have-real-misunderstanding-with-mfmailcomposeviewcontroller-in-swift-ios8-in/25604976#25604976
+ self.globalMailComposer = nil;
+ self.globalMailComposer = [[MFMailComposeViewController alloc] init];
+}
+
+- (bool)canShareViaSMS {
+ Class messageClass = (NSClassFromString(@"MFMessageComposeViewController"));
+ return messageClass != nil && [messageClass canSendText];
+}
+
+- (void)shareViaSMS:(CDVInvokedUrlCommand*)command {
+ if ([self canShareViaSMS]) {
+ NSDictionary* options = [command.arguments objectAtIndex:0];
+ NSString *phonenumbers = [command.arguments objectAtIndex:1];
+ 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]) {
+ picker.body = message;
+ }
+ if (subject != (id)[NSNull null]) {
+ [picker setSubject:subject];
+ }
+ if (image != nil && image != (id)[NSNull null]) {
+ BOOL canSendAttachments = [[MFMessageComposeViewController class] respondsToSelector:@selector(canSendAttachments)];
+ if (canSendAttachments) {
+ NSURL *file = [self getFile:image];
+ if (file != nil) {
+ [picker addAttachmentURL:file withAlternateFilename:nil];
+ }
+ }
+ }
+
+ if (phonenumbers != (id)[NSNull null]) {
+ [picker setRecipients:[phonenumbers componentsSeparatedByString:@","]];
+ }
+ // remember the command, because we need it in the didFinishWithResult method
+ _command = command;
+ [self.commandDelegate runInBackground:^{
+ picker.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
+ [[self getTopMostViewController] presentViewController:picker animated:YES completion:nil];
+ }];
+ } else {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }
+}
+
+// Dismisses the SMS composition interface when users taps Cancel or Send
+- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result {
+ bool ok = result == MessageComposeResultSent;
+ [[self getTopMostViewController] dismissViewControllerAnimated:YES completion:nil];
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId];
+}
+
+- (bool)canShareViaInstagram {
+ return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"instagram://app"]]; // requires whitelisting on iOS9
+}
+
+- (bool)canShareViaWhatsApp {
+ return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"whatsapp://app"]]; // requires whitelisting on iOS9
+}
+
+// this is only an internal test method for now, can be used to open a share sheet with 'Open in xx' links for tumblr, drive, dropbox, ..
+- (void)openImage:(NSString *)imageName {
+ UIImage* image =[self getImage:imageName];
+ if (image != nil) {
+ NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/myTempImage.jpg"];
+ [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES];
+ _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]];
+ _documentInteractionController.UTI = @""; // TODO find the scheme for google drive and create a shareViaGoogleDrive function
+ [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES];
+ }
+}
+
+- (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.
+ // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('instagram'..)
+ if (!IsAtLeastiOSVersion(@"9.0")) {
+ if (![self canShareViaInstagram]) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ return;
+ }
+ }
+
+ NSString *message = [command.arguments objectAtIndex:0];
+ // subject is not supported by the SLComposeViewController
+ NSArray *filenames = [command.arguments objectAtIndex:2];
+
+ // only use the first image (for now.. maybe we can share in a loop?)
+ UIImage* image = nil;
+ for (NSString* filename in filenames) {
+ image = [self getImage:filename];
+ break;
+ }
+
+// NSData *imageObj = [NSData dataFromBase64String:objectAtIndex0];
+ NSString *tmpDir = NSTemporaryDirectory();
+ NSString *path = [tmpDir stringByAppendingPathComponent:@"instagram.igo"];
+ [UIImageJPEGRepresentation(image, 1.0) writeToFile:path atomically:YES];
+
+ _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:path]];
+ _documentInteractionController.delegate = self;
+ _documentInteractionController.UTI = @"com.instagram.exclusivegram";
+
+ if (message != (id)[NSNull null]) {
+ // no longer working, so ..
+ _documentInteractionController.annotation = @{@"InstagramCaption" : message};
+
+ // .. we put the message on the clipboard (you app can prompt the user to paste it)
+ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
+ [pasteboard setValue:message forPasteboardType:@"public.text"];
+ }
+
+ // remember the command for the delegate method
+ _command = command;
+ [_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.
+ // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('whatsapp'..)
+ if (!IsAtLeastiOSVersion(@"9.0")) {
+ if (![self canShareViaWhatsApp]) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ return;
+ }
+ }
+
+ 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];
+
+ // only use the first image (for now.. maybe we can share in a loop?)
+ UIImage* image = nil;
+ for (NSString* filename in filenames) {
+ image = [self getImage:filename];
+ break;
+ }
+
+ // with WhatsApp, we can share an image OR text+url.. image wins if set
+ if (image != nil) {
+ NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/whatsAppTmp.wai"];
+ [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES];
+ _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]];
+ _documentInteractionController.UTI = @"net.whatsapp.image";
+ _documentInteractionController.delegate = self;
+ _command = command;
+ [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES];
+ } else {
+ // append an url to a message, if both are passed
+ NSString * shareString = @"";
+ if (message != (id)[NSNull null]) {
+ shareString = message;
+ }
+ if (urlString != (id)[NSNull null]) {
+ if ([shareString isEqual: @""]) {
+ shareString = urlString;
+ } else {
+ shareString = [NSString stringWithFormat:@"%@ %@", shareString, urlString];
+ }
+ }
+ 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];
+
+ NSURL *whatsappURL = [NSURL URLWithString:encodedShareStringForWhatsApp];
+ [[UIApplication sharedApplication] openURL: whatsappURL];
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }
+}
+
+- (void)saveToPhotoAlbum:(CDVInvokedUrlCommand*)command {
+ self.command = command;
+ NSArray *filenames = [command.arguments objectAtIndex:0];
+ [self.commandDelegate runInBackground:^{
+ bool shared = false;
+ for (NSString* filename in filenames) {
+ UIImage* image = [self getImage:filename];
+ if (image != nil) {
+ shared = true;
+ UIImageWriteToSavedPhotosAlbum(image, self, @selector(thisImage:wasSavedToPhotoAlbumWithError:contextInfo:), nil);
+ }
+ }
+ if (!shared) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no valid image was passed"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId];
+ }
+ }];
+}
+
+// called from saveToPhotoAlbum, note that we only send feedback for the first image that's being saved (not keeping the callback)
+// but since the UIImageWriteToSavedPhotosAlbum function is only called with valid images that should not be a problem
+- (void)thisImage:(UIImage *)image wasSavedToPhotoAlbumWithError:(NSError *)error contextInfo:(void*)ctxInfo {
+ if (error) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.localizedDescription];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId];
+ } else {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId];
+ }
+}
+
+-(UIImage*)getImage: (NSString *)imageName {
+ UIImage *image = nil;
+ if (imageName != (id)[NSNull null]) {
+ if ([imageName hasPrefix:@"http"]) {
+ image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageName]]];
+ } else if ([imageName hasPrefix:@"www/"]) {
+ image = [UIImage imageNamed:imageName];
+ } else if ([imageName hasPrefix:@"file://"]) {
+ image = [UIImage imageWithData:[NSData dataWithContentsOfFile:[[NSURL URLWithString:imageName] path]]];
+ } else if ([imageName hasPrefix:@"data:"]) {
+ // using a base64 encoded string
+ NSURL *imageURL = [NSURL URLWithString:imageName];
+ NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
+ image = [UIImage imageWithData:imageData];
+ } else if ([imageName hasPrefix:@"assets-library://"]) {
+ // use assets-library
+ NSURL *imageURL = [NSURL URLWithString:imageName];
+ NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
+ image = [UIImage imageWithData:imageData];
+ } else {
+ // assume anywhere else, on the local filesystem
+ image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imageName]];
+ }
+ }
+ return image;
+}
+
+-(NSURL*)getFile: (NSString *)fileName {
+ NSURL *file = nil;
+ if (fileName != (id)[NSNull null]) {
+ 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]];
+ } else if ([fileName hasPrefix:@"www/"]) {
+ NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
+ NSString *fullPath = [NSString stringWithFormat:@"%@/%@", bundlePath, fileName];
+ file = [NSURL fileURLWithPath:fullPath];
+ } else if ([fileName hasPrefix:@"file://"]) {
+ // stripping the first 6 chars, because the path should start with / instead of file://
+ file = [NSURL fileURLWithPath:[fileName substringFromIndex:6]];
+ } else if ([fileName hasPrefix:@"data:"]) {
+ // using a base64 encoded string
+ // extract some info from the 'fileName', which is for example: data:text/calendar;base64,<encoded stuff here>
+ NSString *fileType = (NSString*)[[[fileName substringFromIndex:5] componentsSeparatedByString: @";"] objectAtIndex:0];
+ fileType = (NSString*)[[fileType componentsSeparatedByString: @"/"] lastObject];
+ NSString *base64content = (NSString*)[[fileName componentsSeparatedByString: @","] lastObject];
+ NSData *fileData = [SocialSharing dataFromBase64String:base64content];
+ file = [NSURL fileURLWithPath:[self storeInFile:[NSString stringWithFormat:@"%@.%@", @"file", fileType] fileData:fileData]];
+ } else {
+ // assume anywhere else, on the local filesystem
+ file = [NSURL fileURLWithPath:fileName];
+ }
+ }
+ return file;
+}
+
+-(NSString*) storeInFile: (NSString*) fileName
+ fileData: (NSData*) fileData {
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *documentsDirectory = [paths objectAtIndex:0];
+ NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
+ [fileData writeToFile:filePath atomically:YES];
+ _tempStoredFile = filePath;
+ return filePath;
+}
+
+- (void) cleanupStoredFiles {
+ if (_tempStoredFile != nil) {
+ NSError *error;
+ [[NSFileManager defaultManager]removeItemAtPath:_tempStoredFile error:&error];
+ }
+}
+
++ (NSData*) dataFromBase64String:(NSString*)aString {
+ return [[NSData alloc] initWithBase64EncodedString:aString options:0];
+}
+
+#pragma mark - UIPopoverControllerDelegate methods
+
+- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView **)view {
+ NSArray *comps = [[self getIPadPopupCoordinates] componentsSeparatedByString:@","];
+ CGRect newRect = [self getPopupRectFromIPadPopupCoordinates:comps];
+ rect->origin = newRect.origin;
+}
+
+- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
+ _popover = nil;
+}
+
+#pragma mark - UIDocumentInteractionControllerDelegate methods
+
+- (void) documentInteractionController: (UIDocumentInteractionController *) controller willBeginSendingToApplication: (NSString *) application {
+ // note that the application actually contains the app bundle id which was picked (for whatsapp and instagram only)
+ NSLog(@"SocialSharing app selected: %@", application);
+}
+
+- (void) documentInteractionControllerDidDismissOpenInMenu: (UIDocumentInteractionController *) controller {
+ if (self.command != nil) {
+ CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:result callbackId: self.command.callbackId];
+ }
+}
+
+@end
diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js
new file mode 100644
index 00000000..99e0af15
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js
@@ -0,0 +1,114 @@
+var cordova = require('cordova');
+
+module.exports = {
+ share: function (win, fail, args) {
+ //Text Message
+ var message = args[0];
+ //Title
+ var subject = args[1];
+ //File(s) Path
+ var fileOrFileArray = args[2];
+ //Web link
+ var url = args[3];
+
+ var doShare = function (e) {
+ e.request.data.properties.title = subject?subject: "Sharing";
+ if (message) e.request.data.setText(message);
+ if (url) e.request.data.setWebLink(new Windows.Foundation.Uri(url));
+ if (fileOrFileArray.length > 0) {
+ var deferral = e.request.getDeferral();
+ var storageItems = [];
+ var filesCount = fileOrFileArray.length;
+ 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 dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();
+
+ dataTransferManager.addEventListener("datarequested", doShare);
+
+ try {
+ Windows.ApplicationModel.DataTransfer.DataTransferManager.showShareUI();
+ win(true);
+ } catch (err) {
+ fail(err);
+ }
+ },
+
+ canShareViaEmail: function (win, fail, args) {
+ win(true);
+ },
+
+ shareViaEmail: function (win, fail, args) {
+ //Text Message
+ var message = args[0];
+ //Title
+ var subject = args[1];
+ //File(s) Path
+ var fileOrFileArray = args[5];
+
+ var doShare = function (e) {
+ e.request.data.properties.title = subject ? subject : "Sharing";
+ if (message) {
+ var htmlFormat = Windows.ApplicationModel.DataTransfer.HtmlFormatHelper.createHtmlFormat(message);
+ e.request.data.setHtmlFormat(htmlFormat);
+ }
+
+ if (fileOrFileArray.length > 0) {
+ var deferral = e.request.getDeferral();
+ var storageItems = [];
+ var filesCount = fileOrFileArray.length;
+ for (var i = 0; i < fileOrFileArray.length; i++) {
+ Windows.Storage.StorageFile.getFileFromPathAsync(fileOrFileArray[i]).done(
+ function (index, file) {
+ var path = fileOrFileArray[index];
+ var streamRef = Windows.Storage.Streams.RandomAccessStreamReference.createFromFile(file);
+ e.request.data.resourceMap[path] = streamRef;
+ storageItems.push(file);
+ if (!--filesCount) {
+ e.request.data.setStorageItems(storageItems);
+ deferral.complete();
+ }
+ }.bind(this, i),
+ function () {
+ if (!--filesCount) {
+ e.request.data.setStorageItems(storageItems);
+ deferral.complete();
+ }
+ }
+ );
+ }
+ }
+ }
+
+ var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();
+
+ dataTransferManager.addEventListener("datarequested", doShare);
+
+ try {
+ Windows.ApplicationModel.DataTransfer.DataTransferManager.showShareUI();
+ win(true);
+ } catch (err) {
+ fail(err);
+ }
+ }
+};
+
+require("cordova/exec/proxy").add("SocialSharing", module.exports); \ No newline at end of file
diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs
new file mode 100644
index 00000000..1a165127
--- /dev/null
+++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs
@@ -0,0 +1,103 @@
+using Microsoft.Phone.Tasks;
+
+using WPCordovaClassLib.Cordova;
+using WPCordovaClassLib.Cordova.Commands;
+using WPCordovaClassLib.Cordova.JSON;
+
+using Newtonsoft.Json;
+
+namespace Cordova.Extension.Commands
+{
+ public class SocialSharing : BaseCommand
+ {
+
+ public void available(string jsonArgs)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+ }
+
+ public void share(string jsonArgs)
+ {
+
+ var options = JsonHelper.Deserialize<string[]>(jsonArgs);
+
+ var message = options[0];
+ var title = options[1];
+ var files = JsonHelper.Deserialize<string[]>(options[2]);
+ var link = options[3];
+
+ if (!"null".Equals(link))
+ {
+ ShareLinkTask shareLinkTask = new ShareLinkTask();
+ shareLinkTask.Title = title;
+ shareLinkTask.LinkUri = new System.Uri(link, System.UriKind.Absolute);
+ shareLinkTask.Message = message;
+ shareLinkTask.Show();
+ }
+ else if (files != null && files.Length > 0)
+ {
+ ShareLinkTask shareLinkTask = new ShareLinkTask();
+ shareLinkTask.Title = title;
+ shareLinkTask.LinkUri = new System.Uri(files[0], System.UriKind.Absolute);
+ shareLinkTask.Message = message;
+ shareLinkTask.Show();
+ }
+ else
+ {
+ var shareStatusTask = new ShareStatusTask { Status = message };
+ shareStatusTask.Show();
+ }
+ // unfortunately, there is no way to tell if something was shared, so just invoke the successCallback
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+ }
+
+ public void canShareViaEmail(string jsonArgs)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
+ }
+
+ // HTML and attachments are currently not supported on WP8
+ public void shareViaEmail(string jsonArgs)
+ {
+ var options = JsonHelper.Deserialize<string[]>(jsonArgs);
+ EmailComposeTask draft = new EmailComposeTask();
+ draft.Body = options[0];
+ draft.Subject = options[1];
+ if (!"null".Equals(options[2]))
+ {
+ draft.To = string.Join(",", options[2]);
+ }
+ if (!"null".Equals(options[3]))
+ {
+ draft.Cc = string.Join(",", options[3]);
+ }
+ if (!"null".Equals(options[4]))
+ {
+ draft.Bcc = string.Join(",", options[4]);
+ }
+ draft.Show();
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK, true));
+ }
+
+ public void shareViaSMS(string jsonArgs)
+ {
+ var options = JsonHelper.Deserialize<string[]>(jsonArgs);
+
+ SmsComposeTask smsComposeTask = new SmsComposeTask();
+
+ smsComposeTask.To = options[1];
+ SMSMessageClass m = JsonConvert.DeserializeObject<SMSMessageClass>(options[0]);
+ smsComposeTask.Body = m.message;
+
+ smsComposeTask.Show();
+
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK, true));
+ }
+ }
+
+ public class SMSMessageClass
+ {
+ public string message { get; set; }
+ }
+
+} \ No newline at end of file