summaryrefslogtreecommitdiff
path: root/plugins/de.appplant.cordova.plugin.email-composer/src/android/EmailComposer.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/de.appplant.cordova.plugin.email-composer/src/android/EmailComposer.java')
-rwxr-xr-xplugins/de.appplant.cordova.plugin.email-composer/src/android/EmailComposer.java574
1 files changed, 574 insertions, 0 deletions
diff --git a/plugins/de.appplant.cordova.plugin.email-composer/src/android/EmailComposer.java b/plugins/de.appplant.cordova.plugin.email-composer/src/android/EmailComposer.java
new file mode 100755
index 00000000..ddda3104
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.email-composer/src/android/EmailComposer.java
@@ -0,0 +1,574 @@
+/*
+ Copyright 2013-2015 appPlant UG
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+package de.appplant.cordova.emailcomposer;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Build;
+import android.text.Html;
+import android.util.Base64;
+import android.util.Log;
+
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.PluginResult;
+
+@SuppressWarnings("Convert2Diamond")
+public class EmailComposer extends CordovaPlugin {
+
+ //
+ static private final String STORAGE_FOLDER = "/email_composer";
+
+ // The callback context used when calling back into JavaScript
+ private CallbackContext command;
+
+ /**
+ * Executes the request.
+ *
+ * This method is called from the WebView thread.
+ * To do a non-trivial amount of work, use:
+ * cordova.getThreadPool().execute(runnable);
+ *
+ * To run on the UI thread, use:
+ * cordova.getActivity().runOnUiThread(runnable);
+ *
+ * @param action The action to execute.
+ * @param args The exec() arguments in JSON form.
+ * @param callback The callback context used when calling
+ * back into JavaScript.
+ * @return Whether the action was valid.
+ */
+ @Override
+ public boolean execute (String action, JSONArray args,
+ CallbackContext callback) throws JSONException {
+
+ this.command = callback;
+
+ if ("open".equals(action)) {
+ open(args);
+
+ return true;
+ }
+
+ if ("isAvailable".equals(action)) {
+ isAvailable();
+
+ return true;
+ }
+
+ // Returning false results in a "MethodNotFound" error.
+ return false;
+ }
+
+ /**
+ * Tells if the device has the capability to send emails.
+ */
+ private void isAvailable () {
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ Boolean available = isEmailAccountConfigured();
+ PluginResult result = new PluginResult(PluginResult.Status.OK, available);
+
+ command.sendPluginResult(result);
+ }
+ });
+ }
+
+ /**
+ * Sends an intent to the email app.
+ *
+ * @param args
+ * The email properties like subject or body
+ *
+ * @throws JSONException
+ */
+ private void open (JSONArray args) throws JSONException {
+ JSONObject properties = args.getJSONObject(0);
+ Intent draft = getDraftWithProperties(properties);
+
+ final Intent chooser = Intent.createChooser(draft, "Open with");
+ final EmailComposer plugin = this;
+
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ cordova.startActivityForResult(
+ plugin, chooser, 0);
+ }
+ });
+ }
+
+ /**
+ * The intent with the containing email properties.
+ *
+ * @param params
+ * The email properties like subject or body
+ * @return
+ * The resulting intent
+ *
+ * @throws JSONException
+ */
+ private Intent getDraftWithProperties (JSONObject params) throws JSONException {
+ Intent mail = new Intent(Intent.ACTION_SEND_MULTIPLE);
+ String app = params.optString("app", null);
+
+ if (params.has("subject"))
+ setSubject(params.getString("subject"), mail);
+ if (params.has("body"))
+ setBody(params.getString("body"), params.optBoolean("isHtml"), mail);
+ if (params.has("to"))
+ setRecipients(params.getJSONArray("to"), mail);
+ if (params.has("cc"))
+ setCcRecipients(params.getJSONArray("cc"), mail);
+ if (params.has("bcc"))
+ setBccRecipients(params.getJSONArray("bcc"), mail);
+ if (params.has("attachments"))
+ setAttachments(params.getJSONArray("attachments"), mail);
+
+ if (app != null && isAppInstalled(app)) {
+ mail.setPackage(app);
+ }
+
+ mail.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ return mail;
+ }
+
+ /**
+ * Setter for the subject.
+ *
+ * @param subject
+ * The subject
+ * @param draft
+ * The intent
+ */
+ private void setSubject (String subject, Intent draft) {
+ draft.putExtra(Intent.EXTRA_SUBJECT, subject);
+ }
+
+ /**
+ * Setter for the body.
+ *
+ * @param body
+ * The body
+ * @param isHTML
+ * Indicates the encoding
+ * (HTML or plain text)
+ * @param draft
+ * The intent
+ */
+ private void setBody (String body, Boolean isHTML, Intent draft) {
+ if (isHTML) {
+ draft.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(body));
+ draft.setType("text/html");
+
+ if (Build.VERSION.SDK_INT > 15) {
+ draft.putExtra(Intent.EXTRA_HTML_TEXT, body);
+ }
+ } else {
+ draft.putExtra(Intent.EXTRA_TEXT, body);
+ draft.setType("text/plain");
+ }
+ }
+
+ /**
+ * Setter for the recipients.
+ *
+ * @param recipients
+ * List of email addresses
+ * @param draft
+ * The intent
+ *
+ * @throws JSONException
+ */
+ private void setRecipients (JSONArray recipients, Intent draft) throws JSONException {
+ String[] receivers = new String[recipients.length()];
+
+ for (int i = 0; i < recipients.length(); i++) {
+ receivers[i] = recipients.getString(i);
+ }
+
+ draft.putExtra(Intent.EXTRA_EMAIL, receivers);
+ }
+
+ /**
+ * Setter for the cc recipients.
+ *
+ * @param recipients
+ * List of email addresses
+ * @param draft
+ * The intent
+ *
+ * @throws JSONException
+ */
+ private void setCcRecipients (JSONArray recipients, Intent draft) throws JSONException {
+ String[] receivers = new String[recipients.length()];
+
+ for (int i = 0; i < recipients.length(); i++) {
+ receivers[i] = recipients.getString(i);
+ }
+
+ draft.putExtra(Intent.EXTRA_CC, receivers);
+ }
+
+ /**
+ * Setter for the bcc recipients.
+ *
+ * @param recipients
+ * List of email addresses
+ * @param draft
+ * The intent
+ *
+ * @throws JSONException
+ */
+ private void setBccRecipients (JSONArray recipients, Intent draft) throws JSONException {
+ String[] receivers = new String[recipients.length()];
+
+ for (int i = 0; i < recipients.length(); i++) {
+ receivers[i] = recipients.getString(i);
+ }
+
+ draft.putExtra(Intent.EXTRA_BCC, receivers);
+ }
+
+ /**
+ * Setter for the attachments.
+ *
+ * @param attachments
+ * List of URIs
+ * @param draft
+ * The intent
+ *
+ * @throws JSONException
+ */
+ private void setAttachments (JSONArray attachments, Intent draft) throws JSONException {
+ ArrayList<Uri> uris = new ArrayList<Uri>();
+
+ for (int i = 0; i < attachments.length(); i++) {
+ Uri uri = getUriForPath(attachments.getString(i));
+
+ uris.add(uri);
+ }
+
+ draft.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
+ }
+
+ /**
+ * The URI for an attachment path.
+ *
+ * @param path
+ * The given path to the attachment
+ *
+ * @return
+ * The URI pointing to the given path
+ */
+ private Uri getUriForPath (String path) {
+ if (path.startsWith("res:")) {
+ return getUriForResourcePath(path);
+ } else if (path.startsWith("file:///")) {
+ return getUriForAbsolutePath(path);
+ } else if (path.startsWith("file://")) {
+ return getUriForAssetPath(path);
+ } else if (path.startsWith("base64:")) {
+ return getUriForBase64Content(path);
+ }
+
+ return Uri.parse(path);
+ }
+
+ /**
+ * The URI for a file.
+ *
+ * @param path
+ * The given absolute path
+ *
+ * @return
+ * The URI pointing to the given path
+ */
+ private Uri getUriForAbsolutePath (String path) {
+ String absPath = path.replaceFirst("file://", "");
+ File file = new File(absPath);
+
+ if (!file.exists()) {
+ Log.e("EmailComposer", "File not found: " + file.getAbsolutePath());
+ }
+
+ return Uri.fromFile(file);
+ }
+
+ /**
+ * The URI for an asset.
+ *
+ * @param path
+ * The given asset path
+ *
+ * @return
+ * The URI pointing to the given path
+ */
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ private Uri getUriForAssetPath (String path) {
+ String resPath = path.replaceFirst("file:/", "www");
+ String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
+ File dir = cordova.getActivity().getExternalCacheDir();
+
+ if (dir == null) {
+ Log.e("EmailComposer", "Missing external cache dir");
+ return Uri.EMPTY;
+ }
+
+ String storage = dir.toString() + STORAGE_FOLDER;
+ File file = new File(storage, fileName);
+
+ new File(storage).mkdir();
+
+ try {
+ AssetManager assets = cordova.getActivity().getAssets();
+
+ FileOutputStream outStream = new FileOutputStream(file);
+ InputStream inputStream = assets.open(resPath);
+
+ copyFile(inputStream, outStream);
+ outStream.flush();
+ outStream.close();
+ } catch (Exception e) {
+ Log.e("EmailComposer", "File not found: assets/" + resPath);
+ e.printStackTrace();
+ }
+
+ return Uri.fromFile(file);
+ }
+
+ /**
+ * The URI for a resource.
+ *
+ * @param path
+ * The given relative path
+ *
+ * @return
+ * The URI pointing to the given path
+ */
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ private Uri getUriForResourcePath (String path) {
+ String resPath = path.replaceFirst("res://", "");
+ String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
+ String resName = fileName.substring(0, fileName.lastIndexOf('.'));
+ String extension = resPath.substring(resPath.lastIndexOf('.'));
+ File dir = cordova.getActivity().getExternalCacheDir();
+
+ if (dir == null) {
+ Log.e("EmailComposer", "Missing external cache dir");
+ return Uri.EMPTY;
+ }
+
+ String storage = dir.toString() + STORAGE_FOLDER;
+ int resId = getResId(resPath);
+ File file = new File(storage, resName + extension);
+
+ if (resId == 0) {
+ Log.e("EmailComposer", "File not found: " + resPath);
+ }
+
+ new File(storage).mkdir();
+
+ try {
+ Resources res = cordova.getActivity().getResources();
+ FileOutputStream outStream = new FileOutputStream(file);
+ InputStream inputStream = res.openRawResource(resId);
+
+ copyFile(inputStream, outStream);
+ outStream.flush();
+ outStream.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return Uri.fromFile(file);
+ }
+
+ /**
+ * The URI for a base64 encoded content.
+ *
+ * @param content
+ * The given base64 encoded content
+ *
+ * @return
+ * The URI including the given content
+ */
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ private Uri getUriForBase64Content (String content) {
+ String resName = content.substring(content.indexOf(":") + 1, content.indexOf("//"));
+ String resData = content.substring(content.indexOf("//") + 2);
+ File dir = cordova.getActivity().getExternalCacheDir();
+ byte[] bytes;
+
+ try {
+ bytes = Base64.decode(resData, 0);
+ } catch (Exception ignored) {
+ Log.e("EmailComposer", "Invalid Base64 string");
+ return Uri.EMPTY;
+ }
+
+ if (dir == null) {
+ Log.e("EmailComposer", "Missing external cache dir");
+ return Uri.EMPTY;
+ }
+
+ String storage = dir.toString() + STORAGE_FOLDER;
+ File file = new File(storage, resName);
+
+ new File(storage).mkdir();
+
+ try {
+ FileOutputStream outStream = new FileOutputStream(file);
+
+ outStream.write(bytes);
+ outStream.flush();
+ outStream.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return Uri.fromFile(file);
+ }
+
+ /**
+ * Writes an InputStream to an OutputStream
+ *
+ * @param in
+ * The input stream
+ * @param out
+ * The output stream
+ */
+ private void copyFile (InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[1024];
+ int read;
+
+ while((read = in.read(buffer)) != -1){
+ out.write(buffer, 0, read);
+ }
+ }
+
+ /**
+ * @return
+ * The resource ID for the given resource.
+ */
+ private int getResId (String resPath) {
+ Resources res = cordova.getActivity().getResources();
+ int resId;
+
+ String pkgName = getPackageName();
+ String dirName = "drawable";
+ String fileName = resPath;
+
+ if (resPath.contains("/")) {
+ dirName = resPath.substring(0, resPath.lastIndexOf('/'));
+ fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
+ }
+
+ String resName = fileName.substring(0, fileName.lastIndexOf('.'));
+
+ resId = res.getIdentifier(resName, dirName, pkgName);
+
+ if (resId == 0) {
+ resId = res.getIdentifier(resName, "drawable", pkgName);
+ }
+
+ return resId;
+ }
+
+ /**
+ * If email apps are available.
+ *
+ * @return
+ * true if available, otherwise false
+ */
+ private Boolean isEmailAccountConfigured () {
+ Uri uri = Uri.fromParts("mailto","max@mustermann.com", null);
+ Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
+ PackageManager pm = cordova.getActivity().getPackageManager();
+ int mailApps = pm.queryIntentActivities(intent, 0).size();
+ Boolean available;
+
+ available = mailApps > 0;
+
+ return available;
+ }
+
+ /**
+ * Ask the package manager if the app is installed on the device.
+ *
+ * @param id
+ * The app id
+ *
+ * @return
+ * true if yes otherwise false
+ */
+ private boolean isAppInstalled(String id) {
+ PackageManager pm = cordova.getActivity().getPackageManager();
+
+ try {
+ pm.getPackageInfo(id, 0);
+ return true;
+ } catch(PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ /**
+ * The name for the package.
+ *
+ * @return
+ * The package name
+ */
+ private String getPackageName () {
+ return cordova.getActivity().getPackageName();
+ }
+
+ /**
+ * Called when an activity you launched exits, giving you the reqCode you
+ * started it with, the resCode it returned, and any additional data from it.
+ *
+ * @param reqCode The request code originally supplied to startActivityForResult(),
+ * allowing you to identify who this result came from.
+ * @param resCode The integer result code returned by the child activity
+ * through its setResult().
+ * @param intent An Intent, which can return result data to the caller
+ * (various data can be attached to Intent "extras").
+ */
+ @Override
+ public void onActivityResult(int reqCode, int resCode, Intent intent) {
+ command.success();
+ }
+}