@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.
- package/CapawesomeCapacitorMailComposer.podspec +17 -0
- package/LICENSE +21 -0
- package/Package.swift +28 -0
- package/README.md +208 -0
- package/android/build.gradle +58 -0
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/MailComposer.java +120 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/MailComposerPlugin.java +101 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/classes/CustomException.java +20 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/classes/CustomExceptions.java +9 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/classes/options/ComposeMailOptions.java +86 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/classes/results/CanComposeMailResult.java +22 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/classes/results/ComposeMailResult.java +23 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/interfaces/Callback.java +5 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/interfaces/NonEmptyResultCallback.java +7 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/interfaces/Result.java +7 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +261 -0
- package/dist/esm/definitions.d.ts +133 -0
- package/dist/esm/definitions.js +19 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +8 -0
- package/dist/esm/web.js +35 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +68 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +71 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/Classes/Options/ComposeMailOptions.swift +22 -0
- package/ios/Plugin/Classes/Results/CanComposeMailResult.swift +16 -0
- package/ios/Plugin/Classes/Results/ComposeMailResult.swift +16 -0
- package/ios/Plugin/Enums/CustomError.swift +31 -0
- package/ios/Plugin/Info.plist +24 -0
- package/ios/Plugin/MailComposer.swift +88 -0
- package/ios/Plugin/MailComposerPlugin.swift +62 -0
- package/ios/Plugin/Protocols/Result.swift +5 -0
- 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<<a href="#cancomposemailresult">CanComposeMailResult</a>></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<<a href="#composemailresult">ComposeMailResult</a>></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
|
+
}
|
package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/MailComposer.java
ADDED
|
@@ -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
|
+
}
|
package/android/src/main/java/io/capawesome/capacitorjs/plugins/mailcomposer/MailComposerPlugin.java
ADDED
|
@@ -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
|
+
}
|