diff options
Diffstat (limited to 'StoneIsland/platforms/android/src/nl/xservices/plugins/SocialSharing.java')
| -rw-r--r--[-rwxr-xr-x] | StoneIsland/platforms/android/src/nl/xservices/plugins/SocialSharing.java | 280 |
1 files changed, 261 insertions, 19 deletions
diff --git a/StoneIsland/platforms/android/src/nl/xservices/plugins/SocialSharing.java b/StoneIsland/platforms/android/src/nl/xservices/plugins/SocialSharing.java index f1168f89..8de31da8 100755..100644 --- a/StoneIsland/platforms/android/src/nl/xservices/plugins/SocialSharing.java +++ b/StoneIsland/platforms/android/src/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"; } } |
