@capawesome/capacitor-mail-composer 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CapawesomeCapacitorMailComposer.podspec +17 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +28 -0
  4. package/README.md +208 -0
  5. package/android/build.gradle +58 -0
  6. package/android/src/main/AndroidManifest.xml +8 -0
  7. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/MailComposer.java +120 -0
  8. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/MailComposerPlugin.java +101 -0
  9. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/classes/CustomException.java +20 -0
  10. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/classes/CustomExceptions.java +9 -0
  11. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/classes/options/ComposeMailOptions.java +86 -0
  12. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/classes/results/CanComposeMailResult.java +22 -0
  13. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/classes/results/ComposeMailResult.java +23 -0
  14. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/interfaces/Callback.java +5 -0
  15. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/interfaces/NonEmptyResultCallback.java +7 -0
  16. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/interfaces/Result.java +7 -0
  17. package/android/src/main/res/.gitkeep +0 -0
  18. package/dist/docs.json +261 -0
  19. package/dist/esm/definitions.d.ts +133 -0
  20. package/dist/esm/definitions.js +19 -0
  21. package/dist/esm/definitions.js.map +1 -0
  22. package/dist/esm/index.d.ts +4 -0
  23. package/dist/esm/index.js +7 -0
  24. package/dist/esm/index.js.map +1 -0
  25. package/dist/esm/web.d.ts +8 -0
  26. package/dist/esm/web.js +35 -0
  27. package/dist/esm/web.js.map +1 -0
  28. package/dist/plugin.cjs.js +68 -0
  29. package/dist/plugin.cjs.js.map +1 -0
  30. package/dist/plugin.js +71 -0
  31. package/dist/plugin.js.map +1 -0
  32. package/ios/Plugin/Classes/Options/ComposeMailOptions.swift +22 -0
  33. package/ios/Plugin/Classes/Results/CanComposeMailResult.swift +16 -0
  34. package/ios/Plugin/Classes/Results/ComposeMailResult.swift +16 -0
  35. package/ios/Plugin/Enums/CustomError.swift +31 -0
  36. package/ios/Plugin/Info.plist +24 -0
  37. package/ios/Plugin/MailComposer.swift +88 -0
  38. package/ios/Plugin/MailComposerPlugin.swift +62 -0
  39. package/ios/Plugin/Protocols/Result.swift +5 -0
  40. package/package.json +91 -0
@@ -0,0 +1,17 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'CapawesomeCapacitorMailComposer'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.license = package['license']
10
+ s.homepage = package['repository']['url']
11
+ s.author = package['author']
12
+ s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
+ s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.ios.deployment_target = '15.0'
15
+ s.dependency 'Capacitor'
16
+ s.swift_version = '5.1'
17
+ end
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Robin Genz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/Package.swift ADDED
@@ -0,0 +1,28 @@
1
+ // swift-tools-version: 5.9
2
+ import PackageDescription
3
+
4
+ let package = Package(
5
+ name: "CapawesomeCapacitorMailComposer",
6
+ platforms: [.iOS(.v15)],
7
+ products: [
8
+ .library(
9
+ name: "CapawesomeCapacitorMailComposer",
10
+ targets: ["MailComposerPlugin"])
11
+ ],
12
+ dependencies: [
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
14
+ ],
15
+ targets: [
16
+ .target(
17
+ name: "MailComposerPlugin",
18
+ dependencies: [
19
+ .product(name: "Capacitor", package: "capacitor-swift-pm"),
20
+ .product(name: "Cordova", package: "capacitor-swift-pm")
21
+ ],
22
+ path: "ios/Plugin"),
23
+ .testTarget(
24
+ name: "MailComposerPluginTests",
25
+ dependencies: ["MailComposerPlugin"],
26
+ path: "ios/PluginTests")
27
+ ]
28
+ )
package/README.md ADDED
@@ -0,0 +1,208 @@
1
+ # @capawesome/capacitor-mail-composer
2
+
3
+ Capacitor plugin to open the native email composer.
4
+
5
+ <div class="capawesome-z29o10a">
6
+ <a href="https://cloud.capawesome.io/" target="_blank">
7
+ <img alt="Deliver Live Updates to your Capacitor app with Capawesome Cloud" src="https://cloud.capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
8
+ </a>
9
+ </div>
10
+
11
+ ## Features
12
+
13
+ - ✉️ **Compose**: Open the native email composer prefilled with recipients, subject and body.
14
+ - 📎 **Attachments**: Attach one or more files to the email.
15
+ - ✅ **Availability**: Check whether the device is able to compose and send emails.
16
+ - 🌐 **Cross-platform**: Supports Android, iOS and the web (via `mailto:`).
17
+ - 📦 **CocoaPods & SPM**: Supports CocoaPods and Swift Package Manager for iOS.
18
+ - 🔁 **Up-to-date**: Always supports the latest Capacitor version.
19
+
20
+ Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look!
21
+
22
+ ## Newsletter
23
+
24
+ Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/).
25
+
26
+ ## Compatibility
27
+
28
+ | Plugin Version | Capacitor Version | Status |
29
+ | -------------- | ----------------- | -------------- |
30
+ | 0.x.x | >=8.x.x | Active support |
31
+
32
+ ## Installation
33
+
34
+ You can use our **AI-Assisted Setup** to install the plugin.
35
+ Add the [Capawesome Skills](https://github.com/capawesome-team/skills) to your AI tool using the following command:
36
+
37
+ ```bash
38
+ npx skills add capawesome-team/skills --skill capacitor-plugins
39
+ ```
40
+
41
+ Then use the following prompt:
42
+
43
+ ```
44
+ Use the `capacitor-plugins` skill from `capawesome-team/skills` to install the `@capawesome/capacitor-mail-composer` plugin in my project.
45
+ ```
46
+
47
+ If you prefer **Manual Setup**, install the plugin by running the following commands and follow the platform-specific instructions below:
48
+
49
+ ```bash
50
+ npm install @capawesome/capacitor-mail-composer
51
+ npx cap sync
52
+ ```
53
+
54
+ ### Android
55
+
56
+ #### Attachments
57
+
58
+ To attach files, the plugin uses the [`FileProvider`](https://developer.android.com/reference/androidx/core/content/FileProvider) that Capacitor already registers for your app (authority `${applicationId}.fileprovider`). Make sure that the directories your attachments are stored in are covered by the `res/xml/file_paths.xml` resource of your app. Otherwise, the `composeMail(...)` method will reject with an error.
59
+
60
+ ### Web
61
+
62
+ On the web, the plugin builds a `mailto:` URL and opens it. This has the following limitations:
63
+
64
+ - Attachments are **not** supported and cause the `composeMail(...)` method to reject.
65
+ - HTML bodies are **not** supported. The body is always sent as plain text.
66
+ - The overall URL length is limited, so long bodies may be truncated.
67
+
68
+ ## Configuration
69
+
70
+ No configuration required for this plugin.
71
+
72
+ ## Usage
73
+
74
+ ```typescript
75
+ import { MailComposer } from '@capawesome/capacitor-mail-composer';
76
+
77
+ const canComposeMail = async () => {
78
+ const { canCompose } = await MailComposer.canComposeMail();
79
+ return canCompose;
80
+ };
81
+
82
+ const composeMail = async () => {
83
+ const { status } = await MailComposer.composeMail({
84
+ to: ['jane@example.com'],
85
+ cc: ['john@example.com'],
86
+ subject: 'Hello World',
87
+ body: 'This is the body of the email.',
88
+ });
89
+ return status;
90
+ };
91
+ ```
92
+
93
+ ## API
94
+
95
+ <docgen-index>
96
+
97
+ * [`canComposeMail()`](#cancomposemail)
98
+ * [`composeMail(...)`](#composemail)
99
+ * [Interfaces](#interfaces)
100
+ * [Type Aliases](#type-aliases)
101
+
102
+ </docgen-index>
103
+
104
+ <docgen-api>
105
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
106
+
107
+ ### canComposeMail()
108
+
109
+ ```typescript
110
+ canComposeMail() => Promise<CanComposeMailResult>
111
+ ```
112
+
113
+ Check whether the device is able to compose and send emails.
114
+
115
+ On iOS, this returns `true` only if a mail account is configured. On
116
+ Android, this returns `true` if a mail app is installed. On the web, this
117
+ always returns `true` (best effort, not verifiable).
118
+
119
+ **Returns:** <code>Promise&lt;<a href="#cancomposemailresult">CanComposeMailResult</a>&gt;</code>
120
+
121
+ **Since:** 0.1.0
122
+
123
+ --------------------
124
+
125
+
126
+ ### composeMail(...)
127
+
128
+ ```typescript
129
+ composeMail(options: ComposeMailOptions) => Promise<ComposeMailResult>
130
+ ```
131
+
132
+ Open the native email composer prefilled with the provided data.
133
+
134
+ The user reviews the email and decides whether to send it. The plugin
135
+ never sends the email itself.
136
+
137
+ | Param | Type |
138
+ | ------------- | ----------------------------------------------------------------- |
139
+ | **`options`** | <code><a href="#composemailoptions">ComposeMailOptions</a></code> |
140
+
141
+ **Returns:** <code>Promise&lt;<a href="#composemailresult">ComposeMailResult</a>&gt;</code>
142
+
143
+ **Since:** 0.1.0
144
+
145
+ --------------------
146
+
147
+
148
+ ### Interfaces
149
+
150
+
151
+ #### CanComposeMailResult
152
+
153
+ | Prop | Type | Description | Since |
154
+ | ---------------- | -------------------- | ------------------------------------------------------ | ----- |
155
+ | **`canCompose`** | <code>boolean</code> | Whether the device is able to compose and send emails. | 0.1.0 |
156
+
157
+
158
+ #### ComposeMailResult
159
+
160
+ | Prop | Type | Description | Since |
161
+ | ------------ | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
162
+ | **`status`** | <code><a href="#mailcomposestatus">MailComposeStatus</a></code> | The status of the email compose operation. **Note**: On Android, the status is always `unknown` because the mail intent does not return a reliable result. | 0.1.0 |
163
+
164
+
165
+ #### ComposeMailOptions
166
+
167
+ | Prop | Type | Description | Default | Since |
168
+ | ----------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
169
+ | **`attachments`** | <code>string[]</code> | The absolute file paths or `file://` URIs of the files to attach. Attachments are not supported on the web. | | 0.1.0 |
170
+ | **`bcc`** | <code>string[]</code> | The email addresses of the blind carbon copy (BCC) recipients. | | 0.1.0 |
171
+ | **`body`** | <code>string</code> | The body of the email. | | 0.1.0 |
172
+ | **`cc`** | <code>string[]</code> | The email addresses of the carbon copy (CC) recipients. | | 0.1.0 |
173
+ | **`isHtml`** | <code>boolean</code> | Whether the body should be interpreted as HTML. **Note**: On Android, HTML is best-effort as many mail apps ignore it. On the web, HTML is not supported and the body is always sent as plain text. | <code>false</code> | 0.1.0 |
174
+ | **`subject`** | <code>string</code> | The subject of the email. | | 0.1.0 |
175
+ | **`to`** | <code>string[]</code> | The email addresses of the primary recipients. | | 0.1.0 |
176
+
177
+
178
+ ### Type Aliases
179
+
180
+
181
+ #### MailComposeStatus
182
+
183
+ The status of an email compose operation.
184
+
185
+ - `sent`: The email was sent.
186
+ - `saved`: The email was saved as a draft.
187
+ - `canceled`: The user canceled the operation.
188
+ - `unknown`: The status is unknown.
189
+
190
+ <code>'sent' | 'saved' | 'canceled' | 'unknown'</code>
191
+
192
+ </docgen-api>
193
+
194
+ ## Platform Support
195
+
196
+ Keep the following platform-specific behavior in mind:
197
+
198
+ - **Status**: On Android, the `status` returned by `composeMail(...)` is always `unknown` because the mail intent does not return a reliable result. On iOS, the real status is returned via the delegate. On the web, the status is always `unknown`.
199
+ - **HTML body**: On Android, the `isHtml` option is best-effort because many mail apps ignore HTML content. On the web, HTML is not supported at all.
200
+ - **No mail account**: On iOS, `composeMail(...)` rejects as unavailable if no mail account is configured. Use `canComposeMail(...)` to check upfront.
201
+
202
+ ## Changelog
203
+
204
+ See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/mail-composer/CHANGELOG.md).
205
+
206
+ ## License
207
+
208
+ See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/mail-composer/LICENSE).
@@ -0,0 +1,58 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
+ }
7
+
8
+ buildscript {
9
+ repositories {
10
+ google()
11
+ mavenCentral()
12
+ }
13
+ dependencies {
14
+ classpath 'com.android.tools.build:gradle:8.13.0'
15
+ }
16
+ }
17
+
18
+ apply plugin: 'com.android.library'
19
+
20
+ android {
21
+ namespace = "io.capawesome.capacitorjs.plugins.mailcomposer"
22
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
23
+ defaultConfig {
24
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
25
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
26
+ versionCode 1
27
+ versionName "1.0"
28
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
29
+ }
30
+ buildTypes {
31
+ release {
32
+ minifyEnabled false
33
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
34
+ }
35
+ }
36
+ lintOptions {
37
+ abortOnError = false
38
+ }
39
+ compileOptions {
40
+ sourceCompatibility JavaVersion.VERSION_21
41
+ targetCompatibility JavaVersion.VERSION_21
42
+ }
43
+ }
44
+
45
+ repositories {
46
+ google()
47
+ mavenCentral()
48
+ }
49
+
50
+
51
+ dependencies {
52
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
53
+ implementation project(':capacitor-android')
54
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
55
+ testImplementation "junit:junit:$junitVersion"
56
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
57
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
58
+ }
@@ -0,0 +1,8 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <queries>
3
+ <intent>
4
+ <action android:name="android.intent.action.SENDTO" />
5
+ <data android:scheme="mailto" />
6
+ </intent>
7
+ </queries>
8
+ </manifest>
@@ -0,0 +1,120 @@
1
+ package io.capawesome.capacitorjs.plugins.mailcomposer;
2
+
3
+ import android.content.Context;
4
+ import android.content.Intent;
5
+ import android.net.Uri;
6
+ import android.text.Html;
7
+ import androidx.annotation.NonNull;
8
+ import androidx.core.content.FileProvider;
9
+ import io.capawesome.capacitorjs.plugins.mailcomposer.classes.CustomExceptions;
10
+ import io.capawesome.capacitorjs.plugins.mailcomposer.classes.options.ComposeMailOptions;
11
+ import io.capawesome.capacitorjs.plugins.mailcomposer.classes.results.CanComposeMailResult;
12
+ import io.capawesome.capacitorjs.plugins.mailcomposer.interfaces.NonEmptyResultCallback;
13
+ import java.io.File;
14
+ import java.util.ArrayList;
15
+ import java.util.List;
16
+
17
+ public class MailComposer {
18
+
19
+ @NonNull
20
+ private final MailComposerPlugin plugin;
21
+
22
+ public MailComposer(@NonNull MailComposerPlugin plugin) {
23
+ this.plugin = plugin;
24
+ }
25
+
26
+ public void canComposeMail(@NonNull NonEmptyResultCallback<CanComposeMailResult> callback) {
27
+ boolean canCompose = canResolveIntent(createMailtoIntent());
28
+ callback.success(new CanComposeMailResult(canCompose));
29
+ }
30
+
31
+ public boolean canResolveIntent(@NonNull Intent intent) {
32
+ return intent.resolveActivity(getContext().getPackageManager()) != null;
33
+ }
34
+
35
+ @NonNull
36
+ public Intent createComposeIntent(@NonNull ComposeMailOptions options) throws Exception {
37
+ List<Uri> attachmentUris = createAttachmentUris(options.getAttachments());
38
+ Intent intent;
39
+ if (attachmentUris.isEmpty()) {
40
+ intent = createMailtoIntent();
41
+ } else if (attachmentUris.size() == 1) {
42
+ intent = new Intent(Intent.ACTION_SEND);
43
+ intent.setType("message/rfc822");
44
+ intent.putExtra(Intent.EXTRA_STREAM, attachmentUris.get(0));
45
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
46
+ } else {
47
+ intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
48
+ intent.setType("message/rfc822");
49
+ intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, new ArrayList<>(attachmentUris));
50
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
51
+ }
52
+ applyRecipients(intent, options);
53
+ applyContent(intent, options);
54
+ return intent;
55
+ }
56
+
57
+ private void applyContent(@NonNull Intent intent, @NonNull ComposeMailOptions options) {
58
+ if (options.getSubject() != null) {
59
+ intent.putExtra(Intent.EXTRA_SUBJECT, options.getSubject());
60
+ }
61
+ String body = options.getBody();
62
+ if (body != null) {
63
+ if (options.isHtml()) {
64
+ intent.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(body, Html.FROM_HTML_MODE_LEGACY).toString());
65
+ intent.putExtra(Intent.EXTRA_HTML_TEXT, body);
66
+ } else {
67
+ intent.putExtra(Intent.EXTRA_TEXT, body);
68
+ }
69
+ }
70
+ }
71
+
72
+ private void applyRecipients(@NonNull Intent intent, @NonNull ComposeMailOptions options) {
73
+ if (!options.getTo().isEmpty()) {
74
+ intent.putExtra(Intent.EXTRA_EMAIL, options.getTo().toArray(new String[0]));
75
+ }
76
+ if (!options.getCc().isEmpty()) {
77
+ intent.putExtra(Intent.EXTRA_CC, options.getCc().toArray(new String[0]));
78
+ }
79
+ if (!options.getBcc().isEmpty()) {
80
+ intent.putExtra(Intent.EXTRA_BCC, options.getBcc().toArray(new String[0]));
81
+ }
82
+ }
83
+
84
+ @NonNull
85
+ private List<Uri> createAttachmentUris(@NonNull List<String> attachments) throws Exception {
86
+ List<Uri> uris = new ArrayList<>();
87
+ for (String attachment : attachments) {
88
+ File file = createFile(attachment);
89
+ if (!file.exists()) {
90
+ throw CustomExceptions.ATTACHMENT_NOT_FOUND;
91
+ }
92
+ try {
93
+ String authority = getContext().getPackageName() + ".fileprovider";
94
+ uris.add(FileProvider.getUriForFile(getContext(), authority, file));
95
+ } catch (IllegalArgumentException exception) {
96
+ throw CustomExceptions.ATTACHMENT_NOT_FOUND;
97
+ }
98
+ }
99
+ return uris;
100
+ }
101
+
102
+ @NonNull
103
+ private File createFile(@NonNull String path) {
104
+ if (path.startsWith("file://")) {
105
+ String filePath = Uri.parse(path).getPath();
106
+ return new File(filePath == null ? path : filePath);
107
+ }
108
+ return new File(path);
109
+ }
110
+
111
+ @NonNull
112
+ private Intent createMailtoIntent() {
113
+ return new Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:"));
114
+ }
115
+
116
+ @NonNull
117
+ private Context getContext() {
118
+ return plugin.getContext();
119
+ }
120
+ }
@@ -0,0 +1,101 @@
1
+ package io.capawesome.capacitorjs.plugins.mailcomposer;
2
+
3
+ import android.content.Intent;
4
+ import androidx.activity.result.ActivityResult;
5
+ import androidx.annotation.NonNull;
6
+ import androidx.annotation.Nullable;
7
+ import com.getcapacitor.Logger;
8
+ import com.getcapacitor.Plugin;
9
+ import com.getcapacitor.PluginCall;
10
+ import com.getcapacitor.PluginMethod;
11
+ import com.getcapacitor.annotation.ActivityCallback;
12
+ import com.getcapacitor.annotation.CapacitorPlugin;
13
+ import io.capawesome.capacitorjs.plugins.mailcomposer.classes.CustomException;
14
+ import io.capawesome.capacitorjs.plugins.mailcomposer.classes.options.ComposeMailOptions;
15
+ import io.capawesome.capacitorjs.plugins.mailcomposer.classes.results.CanComposeMailResult;
16
+ import io.capawesome.capacitorjs.plugins.mailcomposer.classes.results.ComposeMailResult;
17
+ import io.capawesome.capacitorjs.plugins.mailcomposer.interfaces.NonEmptyResultCallback;
18
+ import io.capawesome.capacitorjs.plugins.mailcomposer.interfaces.Result;
19
+
20
+ @CapacitorPlugin(name = "MailComposer")
21
+ public class MailComposerPlugin extends Plugin {
22
+
23
+ public static final String ERROR_UNKNOWN_ERROR = "An unknown error has occurred.";
24
+ public static final String TAG = "MailComposerPlugin";
25
+
26
+ private MailComposer implementation;
27
+
28
+ @Override
29
+ public void load() {
30
+ super.load();
31
+ this.implementation = new MailComposer(this);
32
+ }
33
+
34
+ @PluginMethod
35
+ public void canComposeMail(PluginCall call) {
36
+ try {
37
+ NonEmptyResultCallback<CanComposeMailResult> callback = new NonEmptyResultCallback<>() {
38
+ @Override
39
+ public void success(@NonNull CanComposeMailResult result) {
40
+ resolveCall(call, result);
41
+ }
42
+
43
+ @Override
44
+ public void error(Exception exception) {
45
+ rejectCall(call, exception);
46
+ }
47
+ };
48
+ implementation.canComposeMail(callback);
49
+ } catch (Exception exception) {
50
+ rejectCall(call, exception);
51
+ }
52
+ }
53
+
54
+ @PluginMethod
55
+ public void composeMail(PluginCall call) {
56
+ try {
57
+ ComposeMailOptions options = new ComposeMailOptions(call);
58
+ Intent intent = implementation.createComposeIntent(options);
59
+ if (implementation.canResolveIntent(intent)) {
60
+ startActivityForResult(call, intent, "handleComposeMailResult");
61
+ } else {
62
+ rejectCallAsUnavailable(call);
63
+ }
64
+ } catch (Exception exception) {
65
+ rejectCall(call, exception);
66
+ }
67
+ }
68
+
69
+ @ActivityCallback
70
+ private void handleComposeMailResult(PluginCall call, ActivityResult result) {
71
+ if (call == null) {
72
+ return;
73
+ }
74
+ resolveCall(call, new ComposeMailResult("unknown"));
75
+ }
76
+
77
+ private void rejectCall(@NonNull PluginCall call, @NonNull Exception exception) {
78
+ String message = exception.getMessage();
79
+ if (message == null) {
80
+ message = ERROR_UNKNOWN_ERROR;
81
+ }
82
+ String code = null;
83
+ if (exception instanceof CustomException) {
84
+ code = ((CustomException) exception).getCode();
85
+ }
86
+ Logger.error(TAG, message, exception);
87
+ call.reject(message, code);
88
+ }
89
+
90
+ private void rejectCallAsUnavailable(@NonNull PluginCall call) {
91
+ call.unavailable("This method is not available on this platform.");
92
+ }
93
+
94
+ private void resolveCall(@NonNull PluginCall call, @Nullable Result result) {
95
+ if (result == null) {
96
+ call.resolve();
97
+ } else {
98
+ call.resolve(result.toJSObject());
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,20 @@
1
+ package io.capawesome.capacitorjs.plugins.mailcomposer.classes;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import androidx.annotation.Nullable;
5
+
6
+ public class CustomException extends Exception {
7
+
8
+ @Nullable
9
+ private final String code;
10
+
11
+ public CustomException(@Nullable String code, @NonNull String message) {
12
+ super(message);
13
+ this.code = code;
14
+ }
15
+
16
+ @Nullable
17
+ public String getCode() {
18
+ return code;
19
+ }
20
+ }
@@ -0,0 +1,9 @@
1
+ package io.capawesome.capacitorjs.plugins.mailcomposer.classes;
2
+
3
+ public class CustomExceptions {
4
+
5
+ public static final CustomException ATTACHMENT_NOT_FOUND = new CustomException(
6
+ "ATTACHMENT_NOT_FOUND",
7
+ "An attachment could not be found at the provided path."
8
+ );
9
+ }
@@ -0,0 +1,86 @@
1
+ package io.capawesome.capacitorjs.plugins.mailcomposer.classes.options;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import androidx.annotation.Nullable;
5
+ import com.getcapacitor.JSArray;
6
+ import com.getcapacitor.PluginCall;
7
+ import java.util.ArrayList;
8
+ import java.util.List;
9
+ import org.json.JSONException;
10
+
11
+ public class ComposeMailOptions {
12
+
13
+ @NonNull
14
+ private final List<String> attachments;
15
+
16
+ @NonNull
17
+ private final List<String> bcc;
18
+
19
+ @Nullable
20
+ private final String body;
21
+
22
+ @NonNull
23
+ private final List<String> cc;
24
+
25
+ private final boolean isHtml;
26
+
27
+ @Nullable
28
+ private final String subject;
29
+
30
+ @NonNull
31
+ private final List<String> to;
32
+
33
+ public ComposeMailOptions(@NonNull PluginCall call) throws JSONException {
34
+ this.to = getStringListFromCall(call, "to");
35
+ this.cc = getStringListFromCall(call, "cc");
36
+ this.bcc = getStringListFromCall(call, "bcc");
37
+ this.subject = call.getString("subject");
38
+ this.body = call.getString("body");
39
+ this.isHtml = call.getBoolean("isHtml", false);
40
+ this.attachments = getStringListFromCall(call, "attachments");
41
+ }
42
+
43
+ @NonNull
44
+ public List<String> getAttachments() {
45
+ return attachments;
46
+ }
47
+
48
+ @NonNull
49
+ public List<String> getBcc() {
50
+ return bcc;
51
+ }
52
+
53
+ @Nullable
54
+ public String getBody() {
55
+ return body;
56
+ }
57
+
58
+ @NonNull
59
+ public List<String> getCc() {
60
+ return cc;
61
+ }
62
+
63
+ @Nullable
64
+ public String getSubject() {
65
+ return subject;
66
+ }
67
+
68
+ @NonNull
69
+ public List<String> getTo() {
70
+ return to;
71
+ }
72
+
73
+ public boolean isHtml() {
74
+ return isHtml;
75
+ }
76
+
77
+ @NonNull
78
+ private static List<String> getStringListFromCall(@NonNull PluginCall call, @NonNull String key) throws JSONException {
79
+ List<String> values = new ArrayList<>();
80
+ JSArray array = call.getArray(key);
81
+ if (array != null) {
82
+ values.addAll(array.toList());
83
+ }
84
+ return values;
85
+ }
86
+ }