This repository has been archived by the owner on Aug 4, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
168 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,15 +8,16 @@ | |
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.apache.http.util.ByteArrayBuffer; | ||
import org.json.JSONArray; | ||
import org.json.JSONException; | ||
import org.json.JSONObject; | ||
|
@@ -42,10 +43,13 @@ public class SocialSharing extends CordovaPlugin { | |
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_SEND = 1; | ||
private static final int ACTIVITY_CODE_SENDVIAEMAIL = 2; | ||
private static final int ACTIVITY_CODE_SENDVIAWHATSAPP = 3; | ||
|
||
private CallbackContext _callbackContext; | ||
|
||
|
@@ -75,7 +79,16 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo | |
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); | ||
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", 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)) { | ||
|
@@ -100,7 +113,7 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo | |
|
||
private boolean isEmailAvailable() { | ||
final Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", "[email protected]", null)); | ||
return cordova.getActivity().getPackageManager().queryIntentActivities(intent, 0).size() > 1; | ||
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 { | ||
|
@@ -133,16 +146,18 @@ public void run() { | |
draft.putExtra(android.content.Intent.EXTRA_BCC, toStringArray(bcc)); | ||
} | ||
if (files.length() > 0) { | ||
ArrayList<Uri> fileUris = new ArrayList<Uri>(); | ||
final String dir = getDownloadDir(); | ||
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 (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); | ||
} | ||
} | ||
if (!fileUris.isEmpty()) { | ||
draft.putExtra(Intent.EXTRA_STREAM, fileUris); | ||
} | ||
} | ||
} catch (Exception e) { | ||
|
@@ -158,11 +173,15 @@ public void run() { | |
} | ||
|
||
private String getDownloadDir() throws IOException { | ||
final String dir = webView.getContext().getExternalFilesDir(null) + "/socialsharing-downloads"; // external | ||
|
||
// final String dir = webView.getContext().getCacheDir() + "/socialsharing-downloads"; // internal (no external permission needed) | ||
createOrCleanDir(dir); | ||
return dir; | ||
// 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) { | ||
|
@@ -177,34 +196,39 @@ public void run() { | |
final Intent sendIntent = new Intent(hasMultipleAttachments ? Intent.ACTION_SEND_MULTIPLE : Intent.ACTION_SEND); | ||
sendIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); | ||
|
||
if (files.length() > 0) { | ||
ArrayList<Uri> fileUris = new ArrayList<Uri>(); | ||
try { | ||
try { | ||
if (files.length() > 0 && !"".equals(files.getString(0))) { | ||
final String dir = getDownloadDir(); | ||
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 (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); | ||
if (!fileUris.isEmpty()) { | ||
if (hasMultipleAttachments) { | ||
sendIntent.putExtra(Intent.EXTRA_STREAM, fileUris); | ||
} else { | ||
sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri); | ||
} | ||
} | ||
} else { | ||
sendIntent.setType("text/plain"); | ||
} | ||
} catch (Exception e) { | ||
callbackContext.error(e.getMessage()); | ||
} 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)) { | ||
|
@@ -215,7 +239,10 @@ public void run() { | |
} | ||
if (notEmpty(message)) { | ||
sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, message); | ||
sendIntent.putExtra("sms_body", message); // sometimes required when the user picks share via sms | ||
// sometimes required when the user picks share via sms | ||
if (Build.VERSION.SDK_INT < 21) { // LOLLIPOP | ||
sendIntent.putExtra("sms_body", message); | ||
} | ||
} | ||
|
||
if (appPackageName != null) { | ||
|
@@ -242,7 +269,8 @@ public void run() { | |
public void run() { | ||
cordova.getActivity().runOnUiThread(new Runnable() { | ||
public void run() { | ||
showPasteMessage(msg, pasteMessage); | ||
copyHintToClipboard(msg, pasteMessage); | ||
showPasteMessage(pasteMessage); | ||
} | ||
}); | ||
} | ||
|
@@ -254,7 +282,7 @@ public void run() { | |
if (peek) { | ||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK)); | ||
} else { | ||
mycordova.startActivityForResult(plugin, Intent.createChooser(sendIntent, null), 1); | ||
mycordova.startActivityForResult(plugin, Intent.createChooser(sendIntent, null), ACTIVITY_CODE_SEND); | ||
} | ||
} | ||
} | ||
|
@@ -263,16 +291,20 @@ public void run() { | |
} | ||
|
||
@SuppressLint("NewApi") | ||
private void showPasteMessage(String msg, String label) { | ||
private void copyHintToClipboard(String msg, String label) { | ||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { | ||
return; | ||
} | ||
// copy to clipboard | ||
final ClipboardManager clipboard = (android.content.ClipboardManager) cordova.getActivity().getSystemService(Context.CLIPBOARD_SERVICE); | ||
final ClipData clip = android.content.ClipData.newPlainText(label, msg); | ||
clipboard.setPrimaryClip(clip); | ||
} | ||
|
||
// show a toast | ||
@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(); | ||
|
@@ -294,6 +326,10 @@ private Uri getFileUriAndSetType(Intent sendIntent, String dir, String image, St | |
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; | ||
} | ||
} | ||
|
@@ -325,12 +361,80 @@ private Uri getFileUriAndSetType(Intent sendIntent, String dir, String image, St | |
} | ||
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 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() { | ||
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 { | ||
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 | ||
|
@@ -411,11 +515,13 @@ private JSONArray getShareActivities(List<ResolveInfo> activityList) { | |
|
||
@Override | ||
public void onActivityResult(int requestCode, int resultCode, Intent intent) { | ||
if (ACTIVITY_CODE_SENDVIAEMAIL == requestCode) { | ||
super.onActivityResult(requestCode, resultCode, intent); | ||
_callbackContext.success(); | ||
} else { | ||
_callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, resultCode == Activity.RESULT_OK)); | ||
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)); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -430,23 +536,29 @@ private void createOrCleanDir(final String downloadDir) throws IOException { | |
} | ||
} | ||
|
||
private String getFileName(String url) { | ||
final int lastIndexOfSlash = url.lastIndexOf('/'); | ||
if (lastIndexOfSlash == -1) { | ||
return url; | ||
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 url.substring(lastIndexOfSlash + 1); | ||
return "file"; | ||
} | ||
} | ||
|
||
private byte[] getBytes(InputStream is) throws IOException { | ||
BufferedInputStream bis = new BufferedInputStream(is); | ||
ByteArrayBuffer baf = new ByteArrayBuffer(5000); | ||
int current; | ||
while ((current = bis.read()) != -1) { | ||
baf.append((byte) current); | ||
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); | ||
} | ||
return baf.toByteArray(); | ||
buffer.flush(); | ||
return buffer.toByteArray(); | ||
} | ||
|
||
private void saveFile(byte[] bytes, String dirName, String fileName) throws IOException { | ||
|