summaryrefslogtreecommitdiff
path: root/plugins/de.appplant.cordova.plugin.email-composer/src
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/de.appplant.cordova.plugin.email-composer/src')
-rwxr-xr-xplugins/de.appplant.cordova.plugin.email-composer/src/android/EmailComposer.java574
-rw-r--r--plugins/de.appplant.cordova.plugin.email-composer/src/ios/APPEmailComposer.h33
-rw-r--r--plugins/de.appplant.cordova.plugin.email-composer/src/ios/APPEmailComposer.m490
-rw-r--r--plugins/de.appplant.cordova.plugin.email-composer/src/windows/EmailComposerProxy.js273
-rw-r--r--plugins/de.appplant.cordova.plugin.email-composer/src/wp8/EmailComposer.cs133
-rw-r--r--plugins/de.appplant.cordova.plugin.email-composer/src/wp8/Options.cs76
6 files changed, 1579 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();
+ }
+}
diff --git a/plugins/de.appplant.cordova.plugin.email-composer/src/ios/APPEmailComposer.h b/plugins/de.appplant.cordova.plugin.email-composer/src/ios/APPEmailComposer.h
new file mode 100644
index 00000000..c7ea09b8
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.email-composer/src/ios/APPEmailComposer.h
@@ -0,0 +1,33 @@
+/*
+ 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.
+ */
+
+#import <Foundation/Foundation.h>
+#import <MessageUI/MFMailComposeViewController.h>
+#import <Cordova/CDVPlugin.h>
+
+@interface APPEmailComposer : CDVPlugin <MFMailComposeViewControllerDelegate>
+
+// Shows the email composer view with pre-filled data
+- (void) open:(CDVInvokedUrlCommand*)command;
+// Checks if the mail composer is able to send mails
+- (void) isAvailable:(CDVInvokedUrlCommand*)command;
+
+@end
diff --git a/plugins/de.appplant.cordova.plugin.email-composer/src/ios/APPEmailComposer.m b/plugins/de.appplant.cordova.plugin.email-composer/src/ios/APPEmailComposer.m
new file mode 100644
index 00000000..52bb66d7
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.email-composer/src/ios/APPEmailComposer.m
@@ -0,0 +1,490 @@
+/*
+ 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.
+ */
+
+#import "APPEmailComposer.h"
+#import "Cordova/NSData+Base64.h"
+#import "Cordova/CDVAvailability.h"
+#import <MobileCoreServices/MobileCoreServices.h>
+
+#include "TargetConditionals.h"
+
+@interface APPEmailComposer ()
+
+@property (nonatomic, retain) CDVInvokedUrlCommand* command;
+
+@end
+
+@implementation APPEmailComposer
+
+#pragma mark -
+#pragma mark Plugin interface methods
+
+/**
+ * Checks if the mail composer is able to send mails.
+ *
+ * @param callbackId
+ * The ID of the JS function to be called with the result
+ */
+- (void) isAvailable:(CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^{
+ bool canSendMail = [MFMailComposeViewController canSendMail];
+ CDVPluginResult* result;
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
+ messageAsBool:canSendMail];
+
+ [self.commandDelegate sendPluginResult:result
+ callbackId:command.callbackId];
+ }];
+}
+
+/**
+ * Shows the email composer view with pre-filled data.
+ *
+ * @param properties
+ * The email properties like subject, body, attachments
+ */
+- (void) open:(CDVInvokedUrlCommand*)command
+{
+ _command = command;
+
+ if (TARGET_IPHONE_SIMULATOR && IsAtLeastiOSVersion(@"8.0")) {
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Email-Composer Plug-in"
+ message:@"Plug-in cannot run on the iOS8 Simulator.\nPlease downgrade or use a physical device."
+ delegate:nil
+ cancelButtonTitle:@"OK"
+ otherButtonTitles:nil];
+ [alert show];
+ [self execCallback];
+ return;
+ }
+
+ [self.commandDelegate runInBackground:^{
+ NSArray* args = command.arguments;
+ NSDictionary* properties = [args objectAtIndex:0];
+ MFMailComposeViewController* draft;
+
+ draft = [self getDraftWithProperties:properties];
+
+ if (!draft) {
+ [self execCallback];
+ return;
+ }
+
+ [self openDraft:draft];
+ }];
+}
+
+#pragma mark -
+#pragma mark MFMailComposeViewControllerDelegate methods
+
+/**
+ * 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
+{
+ [controller dismissViewControllerAnimated:YES completion:nil];
+
+ [self execCallback];
+}
+
+#pragma mark -
+#pragma mark Plugin core methods
+
+/**
+ * Instantiates an email composer view.
+ *
+ * @param properties
+ * The email properties like subject, body, attachments
+ *
+ * @return
+ * The configured email composer view
+ */
+- (MFMailComposeViewController*) getDraftWithProperties:(NSDictionary*)properties
+{
+ // Falls das Gerät kein Email Interface unterstützt
+ if (![MFMailComposeViewController canSendMail]) {
+ return NULL;
+ }
+
+ BOOL isHTML = [[properties objectForKey:@"isHtml"] boolValue];
+
+ MFMailComposeViewController* draft;
+
+ draft = [[MFMailComposeViewController alloc] init];
+
+ // Subject
+ [self setSubject:[properties objectForKey:@"subject"] ofDraft:draft];
+ // Body (as HTML)
+ [self setBody:[properties objectForKey:@"body"] ofDraft:draft isHTML:isHTML];
+ // Recipients
+ [self setToRecipients:[properties objectForKey:@"to"] ofDraft:draft];
+ // CC Recipients
+ [self setCcRecipients:[properties objectForKey:@"cc"] ofDraft:draft];
+ // BCC Recipients
+ [self setBccRecipients:[properties objectForKey:@"bcc"] ofDraft:draft];
+ // Attachments
+ [self setAttachments:[properties objectForKey:@"attachments"] ofDraft:draft];
+
+ draft.mailComposeDelegate = self;
+
+ return draft;
+}
+
+/**
+ * Displays the email draft.
+ *
+ * @param draft
+ * The email composer view
+ */
+- (void) openDraft:(MFMailComposeViewController*)draft
+{
+ [self.viewController presentViewController:draft
+ animated:YES
+ completion:NULL];
+}
+
+/**
+ * Sets the subject of the email draft.
+ *
+ * @param subject
+ * The subject of the email
+ * @param draft
+ * The email composer view
+ */
+- (void) setSubject:(NSString*)subject
+ ofDraft:(MFMailComposeViewController*)draft
+{
+ [draft setSubject:subject];
+}
+
+/**
+ * Sets the body of the email draft.
+ *
+ * @param body
+ * The body of the email
+ * @param isHTML
+ * Indicates if the body is an HTML encoded string
+ * @param draft
+ * The email composer view
+ */
+- (void) setBody:(NSString*)body ofDraft:(MFMailComposeViewController*)draft
+ isHTML:(BOOL)isHTML
+{
+ [draft setMessageBody:body isHTML:isHTML];
+}
+
+/**
+ * Sets the recipients of the email draft.
+ *
+ * @param recipients
+ * The recipients of the email
+ * @param draft
+ * The email composer view
+ */
+- (void) setToRecipients:(NSArray*)recipients
+ ofDraft:(MFMailComposeViewController*)draft
+{
+ [draft setToRecipients:recipients];
+}
+
+/**
+ * Sets the CC recipients of the email draft.
+ *
+ * @param ccRecipients
+ * The CC recipients of the email
+ * @param draft
+ * The email composer view
+ */
+- (void) setCcRecipients:(NSArray*)ccRecipients
+ ofDraft:(MFMailComposeViewController*)draft
+{
+ [draft setCcRecipients:ccRecipients];
+}
+
+/**
+ * Sets the BCC recipients of the email draft.
+ *
+ * @param bccRecipients
+ * The BCC recipients of the email
+ * @param draft
+ * The email composer view
+ */
+- (void) setBccRecipients:(NSArray*)bccRecipients
+ ofDraft:(MFMailComposeViewController*)draft
+{
+ [draft setBccRecipients:bccRecipients];
+}
+
+/**
+ * Sets the attachments of the email draft.
+ *
+ * @param attachments
+ * The attachments of the email
+ * @param draft
+ * The email composer view
+ */
+- (void) setAttachments:(NSArray*)attatchments
+ ofDraft:(MFMailComposeViewController*)draft
+{
+ if (attatchments)
+ {
+ for (NSString* path in attatchments)
+ {
+ NSData* data = [self getDataForAttachmentPath:path];
+
+ NSString* basename = [self getBasenameFromAttachmentPath:path];
+ NSString* pathExt = [basename pathExtension];
+ NSString* fileName = [basename pathComponents].lastObject;
+ NSString* mimeType = [self getMimeTypeFromFileExtension:pathExt];
+
+ // Couldn't find mimeType, must be some type of binary data
+ if (mimeType == nil) mimeType = @"application/octet-stream";
+
+ [draft addAttachmentData:data mimeType:mimeType fileName:fileName];
+ }
+ }
+}
+
+/**
+ * Returns the data for a given (relative) attachment path.
+ *
+ * @param path
+ * An absolute/relative path or the base64 data
+ *
+ * @return
+ * The data for the attachment
+ */
+- (NSData*) getDataForAttachmentPath:(NSString*)path
+{
+ if ([path hasPrefix:@"file:///"])
+ {
+ return [self dataForAbsolutePath:path];
+ }
+ else if ([path hasPrefix:@"res:"])
+ {
+ return [self dataForResource:path];
+ }
+ else if ([path hasPrefix:@"file://"])
+ {
+ return [self dataForAsset:path];
+ }
+ else if ([path hasPrefix:@"base64:"])
+ {
+ return [self dataFromBase64:path];
+ }
+
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+
+ if (![fileManager fileExistsAtPath:path]){
+ NSLog(@"File not found: %@", path);
+ }
+
+ return [fileManager contentsAtPath:path];
+}
+
+/**
+ * Retrieves the data for an absolute attachment path.
+ *
+ * @param path
+ * An absolute file path
+ *
+ * @return
+ * The data for the attachment
+ */
+- (NSData*) dataForAbsolutePath:(NSString*)path
+{
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+ NSString* absPath;
+
+ absPath = [path stringByReplacingOccurrencesOfString:@"file://"
+ withString:@""];
+
+ if (![fileManager fileExistsAtPath:absPath]){
+ NSLog(@"File not found: %@", absPath);
+ }
+
+ NSData* data = [fileManager contentsAtPath:absPath];
+
+ return data;
+}
+
+/**
+ * Retrieves the data for a resource path.
+ *
+ * @param path
+ * A relative file path
+ *
+ * @return
+ * The data for the attachment
+ */
+- (NSData*) dataForResource:(NSString*)path
+{
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+ NSString* absPath;
+
+ NSBundle* mainBundle = [NSBundle mainBundle];
+ NSString* bundlePath = [[mainBundle bundlePath]
+ stringByAppendingString:@"/"];
+
+ absPath = [path pathComponents].lastObject;
+
+ absPath = [bundlePath stringByAppendingString:absPath];
+
+ if (![fileManager fileExistsAtPath:absPath]){
+ NSLog(@"File not found: %@", absPath);
+ }
+
+ NSData* data = [fileManager contentsAtPath:absPath];
+
+ return data;
+}
+
+/**
+ * Retrieves the data for a asset path.
+ *
+ * @param path
+ * A relative www file path
+ *
+ * @return
+ * The data for the attachment
+ */
+- (NSData*) dataForAsset:(NSString*)path
+{
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+ NSString* absPath;
+
+ NSBundle* mainBundle = [NSBundle mainBundle];
+ NSString* bundlePath = [[mainBundle bundlePath]
+ stringByAppendingString:@"/"];
+
+ absPath = [path stringByReplacingOccurrencesOfString:@"file:/"
+ withString:@"www"];
+
+ absPath = [bundlePath stringByAppendingString:absPath];
+
+ if (![fileManager fileExistsAtPath:absPath]){
+ NSLog(@"File not found: %@", absPath);
+ }
+
+ NSData* data = [fileManager contentsAtPath:absPath];
+
+ return data;
+}
+
+/**
+ * Retrieves the data for a base64 encoded string.
+ *
+ * @param base64String
+ * Base64 encoded string
+ *
+ * @return
+ * The data for the attachment
+ */
+- (NSData*) dataFromBase64:(NSString*)base64String
+{
+ NSUInteger length = [base64String length];
+ NSRegularExpression *regex;
+ NSString *dataString;
+
+ regex = [NSRegularExpression regularExpressionWithPattern:@"^base64:[^/]+.."
+ options:NSRegularExpressionCaseInsensitive
+ error:Nil];
+
+ dataString = [regex stringByReplacingMatchesInString:base64String
+ options:0
+ range:NSMakeRange(0, length)
+ withTemplate:@""];
+
+ NSData* data = [NSData dataFromBase64String:dataString];
+
+ return data;
+}
+
+#pragma mark -
+#pragma mark Plugin helper methods
+
+/**
+ * Retrieves the mime type from the file extension.
+ *
+ * @param extension
+ * The file's extension
+ *
+ * @return
+ * The coresponding MIME type
+ */
+- (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
+ return (NSString*)CFBridgingRelease(UTTypeCopyPreferredTagWithClass(type, kUTTagClassMIMEType));
+}
+
+/**
+ * Retrieves the attachments basename.
+ *
+ * @param path
+ * The file path or bas64 data of the attachment
+ *
+ * @return
+ * The attachments basename
+ */
+- (NSString*) getBasenameFromAttachmentPath:(NSString*)path
+{
+ if ([path hasPrefix:@"base64:"])
+ {
+ NSString* pathWithoutPrefix;
+
+ pathWithoutPrefix = [path stringByReplacingOccurrencesOfString:@"base64:"
+ withString:@""];
+
+ return [pathWithoutPrefix substringToIndex:
+ [pathWithoutPrefix rangeOfString:@"//"].location];
+ }
+
+ return path;
+
+}
+
+/**
+ * Invokes the callback without any parameter.
+ */
+- (void) execCallback
+{
+ CDVPluginResult *result = [CDVPluginResult
+ resultWithStatus:CDVCommandStatus_OK];
+
+ [self.commandDelegate sendPluginResult:result
+ callbackId:_command.callbackId];
+}
+
+@end
diff --git a/plugins/de.appplant.cordova.plugin.email-composer/src/windows/EmailComposerProxy.js b/plugins/de.appplant.cordova.plugin.email-composer/src/windows/EmailComposerProxy.js
new file mode 100644
index 00000000..382eb79a
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.email-composer/src/windows/EmailComposerProxy.js
@@ -0,0 +1,273 @@
+/*
+ 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.
+*/
+
+/**
+ * Verifies if sending emails is supported on the device.
+ *
+ * @param {Function} success
+ * Success callback function
+ * @param {Function} error
+ * Error callback function
+ * @param {Array} args
+ * Interface arguments
+ */
+exports.isAvailable = function (success, error, args) {
+ success(true);
+};
+
+/**
+ * Displays the email composer pre-filled with data.
+ *
+ * @param {Function} success
+ * Success callback function
+ * @param {Function} error
+ * Error callback function
+ * @param {Array} args
+ * Interface arguments
+ */
+exports.open = function (success, error, args) {
+ var props = args[0],
+ email = exports.getDraftWithProperties(props);
+
+ Windows.ApplicationModel.Email.EmailManager
+ .showComposeNewEmailAsync(email)
+ .done(success());
+};
+
+/**
+ * The Email with the containing properties.
+ *
+ * @param {Object} props
+ * The email properties like subject or body
+ * @return {Windows.ApplicationModel.Email.EmailMessage}
+ * The resulting email draft
+ */
+exports.getDraftWithProperties = function (props) {
+ var mail = new Windows.ApplicationModel.Email.EmailMessage();
+
+ // subject
+ exports.setSubject(props.subject, mail);
+ // body
+ exports.setBody(props.body, props.isHtml, mail);
+ // To recipients
+ exports.setRecipients(props.to, mail);
+ // CC recipients
+ exports.setCcRecipients(props.cc, mail);
+ // BCC recipients
+ exports.setBccRecipients(props.bcc, mail);
+ // attachments
+ exports.setAttachments(props.attachments, mail);
+
+ return mail;
+};
+
+/**
+ * Setter for the subject.
+ *
+ * @param {String} subject
+ * The subject
+ * @param {Windows.ApplicationModel.Email.EmailMessage} draft
+ * The draft
+ */
+exports.setSubject = function (subject, draft) {
+ draft.subject = subject;
+};
+
+/**
+ * Setter for the body.
+ *
+ * @param {String} body
+ * The body
+ * @param isHTML
+ * Indicates the encoding
+ * (HTML or plain text)
+ * @param {Windows.ApplicationModel.Email.EmailMessage} draft
+ * The draft
+ */
+exports.setBody = function (body, isHTML, draft) {
+ draft.body = body;
+};
+
+/**
+ * Setter for the recipients.
+ *
+ * @param {String[]} recipients
+ * List of mail addresses
+ * @param {Windows.ApplicationModel.Email.EmailMessage} draft
+ * The draft
+ */
+exports.setRecipients = function (recipients, draft) {
+ recipients.forEach(function (address) {
+ draft.to.push(
+ new Windows.ApplicationModel.Email.EmailRecipient(address));
+ });
+};
+
+/**
+ * Setter for the cc recipients.
+ *
+ * @param {String[]} recipients
+ * List of mail addresses
+ * @param {Windows.ApplicationModel.Email.EmailMessage} draft
+ * The draft
+ */
+exports.setCcRecipients = function (recipients, draft) {
+ recipients.forEach(function (address) {
+ draft.cc.push(
+ new Windows.ApplicationModel.Email.EmailRecipient(address));
+ });
+};
+
+/**
+ * Setter for the bcc recipients.
+ *
+ * @param {String[]} recipients
+ * List of mail addresses
+ * @param {Windows.ApplicationModel.Email.EmailMessage} draft
+ * The draft
+ */
+exports.setBccRecipients = function (recipients, draft) {
+ recipients.forEach(function (address) {
+ draft.bcc.push(
+ new Windows.ApplicationModel.Email.EmailRecipient(address));
+ });
+};
+
+/**
+ * Setter for the attachments.
+ *
+ * @param {String[]} attachments
+ * List of URIs
+ * @param {Windows.ApplicationModel.Email.EmailMessage} draft
+ * The draft
+ */
+exports.setAttachments = function (attachments, draft) {
+ attachments.forEach(function (path) {
+ var uri = exports.getUriForPath(path),
+ name = uri.path.split('/').reverse()[0],
+ stream = Windows.Storage.Streams.RandomAccessStreamReference
+ .createFromUri(uri);
+
+ draft.attachments.push(
+ new Windows.ApplicationModel.Email.
+ EmailAttachment(name, stream)
+ );
+ });
+};
+
+/**
+ * The URI for an attachment path.
+ *
+ * @param {String} path
+ * The given path to the attachment
+ *
+ * @return
+ * The URI pointing to the given path
+ */
+exports.getUriForPath = function (path) {
+ if (path.match(/^res:/)) {
+ return exports.getUriForResourcePath(path);
+ } else if (path.match(/^file:\/{3}/)) {
+ return exports.getUriForAbsolutePath(path);
+ } else if (path.match(/^file:/)) {
+ return exports.getUriForAssetPath(path);
+ } else if (path.match(/^base64:/)) {
+ return exports.getUriForBase64Content(path);
+ }
+
+ return new Windows.Foundation.Uri(path);
+};
+
+/**
+ * The URI for a file.
+ *
+ * @param {String} path
+ * The given absolute path
+ *
+ * @return
+ * The URI pointing to the given path
+ */
+exports.getUriForAbsolutePath = function (path) {
+ return new Windows.Foundation.Uri(path);
+};
+
+/**
+ * The URI for an asset.
+ *
+ * @param {String} path
+ * The given asset path
+ *
+ * @return
+ * The URI pointing to the given path
+ */
+exports.getUriForAssetPath = function (path) {
+ var host = document.location.host,
+ protocol = document.location.protocol,
+ resPath = path.replace('file:/', '/www'),
+ rawUri = protocol + '//' + host + resPath;
+
+ return new Windows.Foundation.Uri(rawUri);
+};
+
+/**
+ * The URI for a resource.
+ *
+ * @param {String} path
+ * The given relative path
+ *
+ * @return
+ * The URI pointing to the given path
+ */
+exports.getUriForResourcePath = function (path) {
+ var host = document.location.host,
+ protocol = document.location.protocol,
+ resPath = path.replace('res:/', '/images'),
+ rawUri = protocol + '//' + host + resPath;
+
+ return new Windows.Foundation.Uri(rawUri);
+};
+
+/**
+ * The URI for a base64 encoded content.
+ *
+ * @param {String} content
+ * The given base64 encoded content
+ *
+ * @return
+ * The URI including the given content
+ */
+exports.getUriForBase64Content = function (content) {
+ var match = content.match(/^base64:([^\/]+)\/\/(.*)/),
+ base64 = match[2],
+ name = match[1],
+ buffer = Windows.Security.Cryptography.CryptographicBuffer.decodeFromBase64String(base64),
+ rwplus = Windows.Storage.CreationCollisionOption.openIfExists,
+ folder = Windows.Storage.ApplicationData.current.temporaryFolder,
+ uri = new Windows.Foundation.Uri('ms-appdata:///temp/' + name);
+
+ folder.createFileAsync(name, rwplus).done(function (file) {
+ Windows.Storage.FileIO.writeBufferAsync(file, buffer);
+ });
+
+ return uri;
+};
+
+require('cordova/exec/proxy').add('EmailComposer', exports);
diff --git a/plugins/de.appplant.cordova.plugin.email-composer/src/wp8/EmailComposer.cs b/plugins/de.appplant.cordova.plugin.email-composer/src/wp8/EmailComposer.cs
new file mode 100644
index 00000000..db0d613e
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.email-composer/src/wp8/EmailComposer.cs
@@ -0,0 +1,133 @@
+/*
+ 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.
+*/
+
+using De.APPPlant.Cordova.Plugin.EmailComposer;
+using Microsoft.Phone.Tasks;
+using System;
+using System.Linq;
+using WPCordovaClassLib.Cordova;
+using WPCordovaClassLib.Cordova.Commands;
+using WPCordovaClassLib.Cordova.JSON;
+
+namespace Cordova.Extension.Commands
+{
+ /// <summary>
+ /// Implementes access to email composer task
+ /// http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh394003(v=vs.105).aspx
+ /// </summary>
+ public class EmailComposer : BaseCommand
+ {
+ /// <summary>
+ /// Überprüft, ob Emails versendet werden können.
+ /// </summary>
+ public void isAvailable(string jsonArgs)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK, true));
+ }
+
+ /// <summary>
+ /// Öffnet den Email-Kontroller mit vorausgefüllten Daten.
+ /// </summary>
+ public void open(string jsonArgs)
+ {
+ string[] args = JsonHelper.Deserialize<string[]>(jsonArgs);
+ Options options = JsonHelper.Deserialize<Options>(args[0]);
+ EmailComposeTask draft = GetDraftWithProperties(options);
+
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK, true));
+
+ OpenDraft(draft);
+ }
+
+ /// </summary>
+ /// Erstellt den Email-Composer und fügt die übergebenen Eigenschaften ein.
+ /// </summary>
+ private EmailComposeTask GetDraftWithProperties(Options options)
+ {
+ EmailComposeTask draft = new EmailComposeTask();
+
+ SetSubject(options.Subject, draft);
+ SetBody(options.Body, options.IsHtml, draft);
+ SetTo(options.To, draft);
+ SetCc(options.Cc, draft);
+ SetBcc(options.Bcc, draft);
+ SetAttachments(options.Attachments, draft);
+
+ return draft;
+ }
+
+ /// </summary>
+ /// Zeigt den ViewController zum Versenden/Bearbeiten der Mail an.
+ /// </summary>
+ private void OpenDraft(EmailComposeTask draft)
+ {
+ draft.Show();
+ }
+
+ /// </summary>
+ /// Setzt den Subject der Mail.
+ /// </summary>
+ private void SetSubject(string subject, EmailComposeTask draft)
+ {
+ draft.Subject = subject;
+ }
+
+ /// </summary>
+ /// Setzt den Body der Mail.
+ /// </summary>
+ private void SetBody(string body, Boolean isHTML, EmailComposeTask draft)
+ {
+ draft.Body = body;
+ }
+
+ /// </summary>
+ /// Setzt die Empfänger der Mail.
+ /// </summary>
+ private void SetTo(string[] recipients, EmailComposeTask draft)
+ {
+ draft.To = string.Join(",", recipients);
+ }
+
+ /// </summary>
+ /// Setzt die CC-Empfänger der Mail.
+ /// </summary>
+ private void SetCc(string[] recipients, EmailComposeTask draft)
+ {
+ draft.Cc = string.Join(",", recipients);
+ }
+
+ /// </summary>
+ /// Setzt die BCC-Empfänger der Mail.
+ /// </summary>
+ private void SetBcc(string[] recipients, EmailComposeTask draft)
+ {
+ draft.Bcc = string.Join(",", recipients);
+ }
+
+ /// </summary>
+ /// Fügt die Anhände zur Mail hinzu.
+ /// </summary>
+ private void SetAttachments(string[] attachments, EmailComposeTask draft)
+ {
+ // Not supported on WP8.0 and WP8.1 Silverlight
+ }
+ }
+}
diff --git a/plugins/de.appplant.cordova.plugin.email-composer/src/wp8/Options.cs b/plugins/de.appplant.cordova.plugin.email-composer/src/wp8/Options.cs
new file mode 100644
index 00000000..b7c7206e
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.email-composer/src/wp8/Options.cs
@@ -0,0 +1,76 @@
+/*
+ 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.
+*/
+
+using System;
+using System.Linq;
+using System.Runtime.Serialization;
+
+namespace De.APPPlant.Cordova.Plugin.EmailComposer
+{
+ /// <summary>
+ /// Represents email composer task options
+ /// </summary>
+ [DataContract]
+ class Options
+ {
+ /// <summary>
+ /// Represents the subject of the email
+ /// </summary>
+ [DataMember(IsRequired = false, Name = "subject")]
+ public string Subject { get; set; }
+
+ /// <summary>
+ /// Represents the email body (could be HTML code, in this case set isHtml to true)
+ /// </summary>
+ [DataMember(IsRequired = false, Name = "body")]
+ public string Body { get; set; }
+
+ /// <summary>
+ /// Indicats if the body is HTML or plain text
+ /// </summary>
+ [DataMember(IsRequired = false, Name = "isHtml")]
+ public bool IsHtml { get; set; }
+
+ /// <summary>
+ /// Contains all the email addresses for TO field
+ /// </summary>
+ [DataMember(IsRequired = false, Name = "to")]
+ public string[] To { get; set; }
+
+ /// <summary>
+ /// Contains all the email addresses for CC field
+ /// </summary>
+ [DataMember(IsRequired = false, Name = "cc")]
+ public string[] Cc { get; set; }
+
+ /// <summary>
+ /// Contains all the email addresses for BCC field
+ /// </summary>
+ [DataMember(IsRequired = false, Name = "bcc")]
+ public string[] Bcc { get; set; }
+
+ /// <summary>
+ /// Contains all full paths to the files you want to attach
+ /// </summary>
+ [DataMember(IsRequired = false, Name = "attachments")]
+ public string[] Attachments { get; set; }
+ }
+}