@capawesome/capacitor-clipboard 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 (41) hide show
  1. package/CapawesomeCapacitorClipboard.podspec +17 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +28 -0
  4. package/README.md +220 -0
  5. package/android/build.gradle +58 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/io/capawesome/capacitorjs/plugins/clipboard/Clipboard.java +141 -0
  8. package/android/src/main/java/io/capawesome/capacitorjs/plugins/clipboard/ClipboardPlugin.java +96 -0
  9. package/android/src/main/java/io/capawesome/capacitorjs/plugins/clipboard/classes/ClipboardContentType.java +22 -0
  10. package/android/src/main/java/io/capawesome/capacitorjs/plugins/clipboard/classes/CustomException.java +20 -0
  11. package/android/src/main/java/io/capawesome/capacitorjs/plugins/clipboard/classes/CustomExceptions.java +12 -0
  12. package/android/src/main/java/io/capawesome/capacitorjs/plugins/clipboard/classes/options/WriteOptions.java +60 -0
  13. package/android/src/main/java/io/capawesome/capacitorjs/plugins/clipboard/classes/results/ReadResult.java +29 -0
  14. package/android/src/main/java/io/capawesome/capacitorjs/plugins/clipboard/interfaces/Callback.java +5 -0
  15. package/android/src/main/java/io/capawesome/capacitorjs/plugins/clipboard/interfaces/EmptyCallback.java +5 -0
  16. package/android/src/main/java/io/capawesome/capacitorjs/plugins/clipboard/interfaces/NonEmptyResultCallback.java +7 -0
  17. package/android/src/main/java/io/capawesome/capacitorjs/plugins/clipboard/interfaces/Result.java +7 -0
  18. package/android/src/main/res/.gitkeep +0 -0
  19. package/dist/docs.json +249 -0
  20. package/dist/esm/definitions.d.ts +144 -0
  21. package/dist/esm/definitions.js +57 -0
  22. package/dist/esm/definitions.js.map +1 -0
  23. package/dist/esm/index.d.ts +4 -0
  24. package/dist/esm/index.js +7 -0
  25. package/dist/esm/index.js.map +1 -0
  26. package/dist/esm/web.d.ts +16 -0
  27. package/dist/esm/web.js +109 -0
  28. package/dist/esm/web.js.map +1 -0
  29. package/dist/plugin.cjs.js +179 -0
  30. package/dist/plugin.cjs.js.map +1 -0
  31. package/dist/plugin.js +182 -0
  32. package/dist/plugin.js.map +1 -0
  33. package/ios/Plugin/Classes/Options/WriteOptions.swift +19 -0
  34. package/ios/Plugin/Classes/Results/ReadResult.swift +19 -0
  35. package/ios/Plugin/Clipboard.swift +74 -0
  36. package/ios/Plugin/ClipboardPlugin.swift +62 -0
  37. package/ios/Plugin/Enums/ClipboardContentType.swift +8 -0
  38. package/ios/Plugin/Enums/CustomError.swift +36 -0
  39. package/ios/Plugin/Info.plist +24 -0
  40. package/ios/Plugin/Protocols/Result.swift +5 -0
  41. package/package.json +94 -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 = 'CapawesomeCapacitorClipboard'
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: "CapawesomeCapacitorClipboard",
6
+ platforms: [.iOS(.v15)],
7
+ products: [
8
+ .library(
9
+ name: "CapawesomeCapacitorClipboard",
10
+ targets: ["ClipboardPlugin"])
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: "ClipboardPlugin",
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: "ClipboardPluginTests",
25
+ dependencies: ["ClipboardPlugin"],
26
+ path: "ios/PluginTests")
27
+ ]
28
+ )
package/README.md ADDED
@@ -0,0 +1,220 @@
1
+ # Capacitor Clipboard Plugin
2
+
3
+ Capacitor plugin to read from and write to the system clipboard.
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
+ - 📋 **Read & Write**: Read from and write to the system clipboard.
14
+ - 🖼️ **Images**: Copy and paste real images, not just data URLs as plain text.
15
+ - 📝 **Rich Text**: Write HTML content with a plain-text fallback.
16
+ - 🔗 **URLs**: Copy URLs as native URL clipboard items.
17
+ - 🌐 **Cross-platform**: Works on Android, iOS, and the web.
18
+ - 🔒 **App Store safe**: Uses only official platform APIs.
19
+ - 📦 **CocoaPods & SPM**: Supports CocoaPods and Swift Package Manager for iOS.
20
+ - 🔁 **Up-to-date**: Always supports the latest Capacitor version.
21
+
22
+ Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look!
23
+
24
+ ## Newsletter
25
+
26
+ 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/).
27
+
28
+ ## Compatibility
29
+
30
+ | Plugin Version | Capacitor Version | Status |
31
+ | -------------- | ----------------- | -------------- |
32
+ | 0.x.x | >=8.x.x | Active support |
33
+
34
+ ## Installation
35
+
36
+ You can use our **AI-Assisted Setup** to install the plugin.
37
+ Add the [Capawesome Skills](https://github.com/capawesome-team/skills) to your AI tool using the following command:
38
+
39
+ ```bash
40
+ npx skills add capawesome-team/skills --skill capacitor-plugins
41
+ ```
42
+
43
+ Then use the following prompt:
44
+
45
+ ```
46
+ Use the `capacitor-plugins` skill from `capawesome-team/skills` to install the `@capawesome/capacitor-clipboard` plugin in my project.
47
+ ```
48
+
49
+ If you prefer **Manual Setup**, install the plugin by running the following commands and follow the platform-specific instructions below:
50
+
51
+ ```bash
52
+ npm install @capawesome/capacitor-clipboard
53
+ npx cap sync
54
+ ```
55
+
56
+ ### Android
57
+
58
+ No additional configuration is required for this plugin.
59
+
60
+ ### iOS
61
+
62
+ No additional configuration is required for this plugin.
63
+
64
+ ## Configuration
65
+
66
+ No configuration required for this plugin.
67
+
68
+ ## Usage
69
+
70
+ ```typescript
71
+ import { Clipboard } from '@capawesome/capacitor-clipboard';
72
+
73
+ const writeText = async () => {
74
+ await Clipboard.write({ text: 'Hello World' });
75
+ };
76
+
77
+ const writeHtml = async () => {
78
+ await Clipboard.write({
79
+ html: '<b>Hello World</b>',
80
+ text: 'Hello World',
81
+ });
82
+ };
83
+
84
+ const writeImage = async () => {
85
+ await Clipboard.write({
86
+ image: 'data:image/png;base64,iVBORw0KGgo...',
87
+ });
88
+ };
89
+
90
+ const writeUrl = async () => {
91
+ await Clipboard.write({ url: 'https://capawesome.io' });
92
+ };
93
+
94
+ const read = async () => {
95
+ const { type, value } = await Clipboard.read();
96
+ console.log('Type:', type, 'Value:', value);
97
+ };
98
+ ```
99
+
100
+ ## API
101
+
102
+ <docgen-index>
103
+
104
+ * [`read()`](#read)
105
+ * [`write(...)`](#write)
106
+ * [Interfaces](#interfaces)
107
+ * [Enums](#enums)
108
+
109
+ </docgen-index>
110
+
111
+ <docgen-api>
112
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
113
+
114
+ ### read()
115
+
116
+ ```typescript
117
+ read() => Promise<ReadResult>
118
+ ```
119
+
120
+ Read the current content of the system clipboard.
121
+
122
+ On Android, reading the clipboard is only possible while the app is in the
123
+ foreground.
124
+
125
+ On iOS, reading the clipboard displays a system paste notification. This is
126
+ expected behavior and cannot be suppressed.
127
+
128
+ **Returns:** <code>Promise&lt;<a href="#readresult">ReadResult</a>&gt;</code>
129
+
130
+ **Since:** 0.1.0
131
+
132
+ --------------------
133
+
134
+
135
+ ### write(...)
136
+
137
+ ```typescript
138
+ write(options: WriteOptions) => Promise<void>
139
+ ```
140
+
141
+ Write content to the system clipboard.
142
+
143
+ Exactly one of `text`, `html`, `image` or `url` must be provided. The
144
+ `html` property may additionally be combined with `text` to provide a
145
+ plain-text fallback.
146
+
147
+ | Param | Type |
148
+ | ------------- | ----------------------------------------------------- |
149
+ | **`options`** | <code><a href="#writeoptions">WriteOptions</a></code> |
150
+
151
+ **Since:** 0.1.0
152
+
153
+ --------------------
154
+
155
+
156
+ ### Interfaces
157
+
158
+
159
+ #### ReadResult
160
+
161
+ | Prop | Type | Description | Since |
162
+ | ----------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----- |
163
+ | **`type`** | <code><a href="#clipboardcontenttype">ClipboardContentType</a></code> | The type of the content that was read from the clipboard. | 0.1.0 |
164
+ | **`value`** | <code>string</code> | The content that was read from the clipboard. Images are returned as a Base64-encoded data URL. | 0.1.0 |
165
+
166
+
167
+ #### WriteOptions
168
+
169
+ | Prop | Type | Description | Since |
170
+ | ----------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
171
+ | **`html`** | <code>string</code> | The HTML content to write to the clipboard. Combine this with `text` to provide a plain-text fallback for apps that cannot handle HTML content. | 0.1.0 |
172
+ | **`image`** | <code>string</code> | The image to write to the clipboard as a Base64-encoded data URL. | 0.1.0 |
173
+ | **`label`** | <code>string</code> | The label used to describe the clipboard content. Only available on Android. | 0.1.0 |
174
+ | **`text`** | <code>string</code> | The plain text to write to the clipboard. | 0.1.0 |
175
+ | **`url`** | <code>string</code> | The URL to write to the clipboard. | 0.1.0 |
176
+
177
+
178
+ ### Enums
179
+
180
+
181
+ #### ClipboardContentType
182
+
183
+ | Members | Value | Description | Since |
184
+ | ----------- | -------------------- | --------------------------------------------------------------- | ----- |
185
+ | **`Html`** | <code>'HTML'</code> | The content is HTML. | 0.1.0 |
186
+ | **`Image`** | <code>'IMAGE'</code> | The content is an image, returned as a Base64-encoded data URL. | 0.1.0 |
187
+ | **`Text`** | <code>'TEXT'</code> | The content is plain text. | 0.1.0 |
188
+ | **`Url`** | <code>'URL'</code> | The content is a URL. | 0.1.0 |
189
+
190
+ </docgen-api>
191
+
192
+ ## Platform Behavior
193
+
194
+ ### Android
195
+
196
+ - On Android 10 (API level 29) and later, apps can only read the clipboard while they are in the foreground and have input focus. The `read(...)` method rejects otherwise.
197
+ - On Android 12 (API level 31) and later, the system shows a toast message when an app reads the clipboard content that was written by another app. This is expected behavior and cannot be suppressed.
198
+
199
+ ### iOS
200
+
201
+ - On iOS 14 and later, the system shows a paste notification banner every time the clipboard is read. This is expected behavior and cannot be suppressed.
202
+
203
+ ## Migrating from `@capacitor/clipboard`
204
+
205
+ This plugin is a drop-in alternative to the official [`@capacitor/clipboard`](https://github.com/ionic-team/capacitor-plugins/tree/main/clipboard) plugin with real image support on Android and HTML support on all platforms. The following differences apply:
206
+
207
+ | `@capacitor/clipboard` | `@capawesome/capacitor-clipboard` |
208
+ | ------------------------------------------ | ------------------------------------------------------ |
209
+ | `write({ string: 'Hello' })` | `write({ text: 'Hello' })` |
210
+ | `write({ image: dataUrl })` (text-only on Android) | `write({ image: dataUrl })` (real image on all platforms) |
211
+ | — | `write({ html: '<b>Hello</b>', text: 'Hello' })` |
212
+ | `read() → { value, type: 'text/plain' }` | `read() → { value, type: 'TEXT' }` |
213
+
214
+ ## Changelog
215
+
216
+ See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/clipboard/CHANGELOG.md).
217
+
218
+ ## License
219
+
220
+ See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/clipboard/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.clipboard"
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,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,141 @@
1
+ package io.capawesome.capacitorjs.plugins.clipboard;
2
+
3
+ import android.content.ClipData;
4
+ import android.content.ClipboardManager;
5
+ import android.content.Context;
6
+ import android.graphics.Bitmap;
7
+ import android.graphics.BitmapFactory;
8
+ import android.net.Uri;
9
+ import android.util.Base64;
10
+ import androidx.annotation.NonNull;
11
+ import androidx.annotation.Nullable;
12
+ import androidx.core.content.FileProvider;
13
+ import io.capawesome.capacitorjs.plugins.clipboard.classes.ClipboardContentType;
14
+ import io.capawesome.capacitorjs.plugins.clipboard.classes.CustomExceptions;
15
+ import io.capawesome.capacitorjs.plugins.clipboard.classes.options.WriteOptions;
16
+ import io.capawesome.capacitorjs.plugins.clipboard.classes.results.ReadResult;
17
+ import io.capawesome.capacitorjs.plugins.clipboard.interfaces.EmptyCallback;
18
+ import io.capawesome.capacitorjs.plugins.clipboard.interfaces.NonEmptyResultCallback;
19
+ import java.io.ByteArrayOutputStream;
20
+ import java.io.File;
21
+ import java.io.FileOutputStream;
22
+ import java.io.InputStream;
23
+
24
+ public class Clipboard {
25
+
26
+ private static final String DEFAULT_LABEL = "";
27
+ private static final String IMAGE_FILE_NAME = "capawesome_capacitor_clipboard_image.png";
28
+
29
+ @NonNull
30
+ private final ClipboardPlugin plugin;
31
+
32
+ public Clipboard(@NonNull ClipboardPlugin plugin) {
33
+ this.plugin = plugin;
34
+ }
35
+
36
+ public void read(@NonNull NonEmptyResultCallback<ReadResult> callback) {
37
+ try {
38
+ ClipboardManager clipboardManager = getClipboardManager();
39
+ ClipData clipData = clipboardManager.getPrimaryClip();
40
+ if (clipData == null || clipData.getItemCount() == 0) {
41
+ callback.error(CustomExceptions.EMPTY_CLIPBOARD);
42
+ return;
43
+ }
44
+ ClipData.Item item = clipData.getItemAt(0);
45
+ ReadResult result = createReadResult(item);
46
+ if (result == null) {
47
+ callback.error(CustomExceptions.EMPTY_CLIPBOARD);
48
+ return;
49
+ }
50
+ callback.success(result);
51
+ } catch (Exception exception) {
52
+ callback.error(CustomExceptions.READ_FAILED);
53
+ }
54
+ }
55
+
56
+ public void write(@NonNull WriteOptions options, @NonNull EmptyCallback callback) {
57
+ try {
58
+ String label = options.getLabel() == null ? DEFAULT_LABEL : options.getLabel();
59
+ ClipData clipData;
60
+ if (options.getImage() != null) {
61
+ clipData = createImageClipData(label, options.getImage());
62
+ } else if (options.getHtml() != null) {
63
+ String text = options.getText() == null ? options.getHtml() : options.getText();
64
+ clipData = ClipData.newHtmlText(label, text, options.getHtml());
65
+ } else if (options.getUrl() != null) {
66
+ clipData = ClipData.newRawUri(label, Uri.parse(options.getUrl()));
67
+ } else {
68
+ clipData = ClipData.newPlainText(label, options.getText());
69
+ }
70
+ getClipboardManager().setPrimaryClip(clipData);
71
+ callback.success();
72
+ } catch (Exception exception) {
73
+ callback.error(CustomExceptions.WRITE_FAILED);
74
+ }
75
+ }
76
+
77
+ @NonNull
78
+ private ClipData createImageClipData(@NonNull String label, @NonNull String image) throws Exception {
79
+ byte[] bytes = decodeDataUrl(image);
80
+ File file = new File(getContext().getCacheDir(), IMAGE_FILE_NAME);
81
+ try (FileOutputStream outputStream = new FileOutputStream(file)) {
82
+ outputStream.write(bytes);
83
+ }
84
+ String authority = getContext().getPackageName() + ".fileprovider";
85
+ Uri uri = FileProvider.getUriForFile(getContext(), authority, file);
86
+ return ClipData.newUri(getContext().getContentResolver(), label, uri);
87
+ }
88
+
89
+ @Nullable
90
+ private ReadResult createReadResult(@NonNull ClipData.Item item) throws Exception {
91
+ Uri uri = item.getUri();
92
+ if (uri != null) {
93
+ String mimeType = getContext().getContentResolver().getType(uri);
94
+ if (mimeType != null && mimeType.startsWith("image/")) {
95
+ return new ReadResult(ClipboardContentType.IMAGE, encodeImageAsDataUrl(uri));
96
+ }
97
+ return new ReadResult(ClipboardContentType.URL, uri.toString());
98
+ }
99
+ String html = item.getHtmlText();
100
+ if (html != null) {
101
+ return new ReadResult(ClipboardContentType.HTML, html);
102
+ }
103
+ CharSequence text = item.getText();
104
+ if (text != null) {
105
+ String value = text.toString();
106
+ if (value.startsWith("http://") || value.startsWith("https://")) {
107
+ return new ReadResult(ClipboardContentType.URL, value);
108
+ }
109
+ return new ReadResult(ClipboardContentType.TEXT, value);
110
+ }
111
+ return null;
112
+ }
113
+
114
+ @NonNull
115
+ private byte[] decodeDataUrl(@NonNull String dataUrl) throws Exception {
116
+ int index = dataUrl.indexOf(',');
117
+ String base64 = index == -1 ? dataUrl : dataUrl.substring(index + 1);
118
+ return Base64.decode(base64, Base64.DEFAULT);
119
+ }
120
+
121
+ @NonNull
122
+ private String encodeImageAsDataUrl(@NonNull Uri uri) throws Exception {
123
+ try (InputStream inputStream = getContext().getContentResolver().openInputStream(uri)) {
124
+ Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
125
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
126
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
127
+ String base64 = Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP);
128
+ return "data:image/png;base64," + base64;
129
+ }
130
+ }
131
+
132
+ @NonNull
133
+ private ClipboardManager getClipboardManager() {
134
+ return (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
135
+ }
136
+
137
+ @NonNull
138
+ private Context getContext() {
139
+ return plugin.getContext();
140
+ }
141
+ }
@@ -0,0 +1,96 @@
1
+ package io.capawesome.capacitorjs.plugins.clipboard;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import androidx.annotation.Nullable;
5
+ import com.getcapacitor.Logger;
6
+ import com.getcapacitor.Plugin;
7
+ import com.getcapacitor.PluginCall;
8
+ import com.getcapacitor.PluginMethod;
9
+ import com.getcapacitor.annotation.CapacitorPlugin;
10
+ import io.capawesome.capacitorjs.plugins.clipboard.classes.CustomException;
11
+ import io.capawesome.capacitorjs.plugins.clipboard.classes.options.WriteOptions;
12
+ import io.capawesome.capacitorjs.plugins.clipboard.classes.results.ReadResult;
13
+ import io.capawesome.capacitorjs.plugins.clipboard.interfaces.EmptyCallback;
14
+ import io.capawesome.capacitorjs.plugins.clipboard.interfaces.NonEmptyResultCallback;
15
+ import io.capawesome.capacitorjs.plugins.clipboard.interfaces.Result;
16
+
17
+ @CapacitorPlugin(name = "Clipboard")
18
+ public class ClipboardPlugin extends Plugin {
19
+
20
+ public static final String ERROR_UNKNOWN_ERROR = "An unknown error has occurred.";
21
+ public static final String TAG = "ClipboardPlugin";
22
+
23
+ private Clipboard implementation;
24
+
25
+ @Override
26
+ public void load() {
27
+ super.load();
28
+ this.implementation = new Clipboard(this);
29
+ }
30
+
31
+ @PluginMethod
32
+ public void read(PluginCall call) {
33
+ try {
34
+ NonEmptyResultCallback<ReadResult> callback = new NonEmptyResultCallback<>() {
35
+ @Override
36
+ public void success(@NonNull ReadResult result) {
37
+ resolveCall(call, result);
38
+ }
39
+
40
+ @Override
41
+ public void error(Exception exception) {
42
+ rejectCall(call, exception);
43
+ }
44
+ };
45
+ implementation.read(callback);
46
+ } catch (Exception exception) {
47
+ rejectCall(call, exception);
48
+ }
49
+ }
50
+
51
+ @PluginMethod
52
+ public void write(PluginCall call) {
53
+ try {
54
+ WriteOptions options = new WriteOptions(call);
55
+ EmptyCallback callback = new EmptyCallback() {
56
+ @Override
57
+ public void success() {
58
+ resolveCall(call);
59
+ }
60
+
61
+ @Override
62
+ public void error(Exception exception) {
63
+ rejectCall(call, exception);
64
+ }
65
+ };
66
+ implementation.write(options, callback);
67
+ } catch (Exception exception) {
68
+ rejectCall(call, exception);
69
+ }
70
+ }
71
+
72
+ private void rejectCall(@NonNull PluginCall call, @NonNull Exception exception) {
73
+ String message = exception.getMessage();
74
+ if (message == null) {
75
+ message = ERROR_UNKNOWN_ERROR;
76
+ }
77
+ String code = null;
78
+ if (exception instanceof CustomException) {
79
+ code = ((CustomException) exception).getCode();
80
+ }
81
+ Logger.error(TAG, message, exception);
82
+ call.reject(message, code);
83
+ }
84
+
85
+ private void resolveCall(@NonNull PluginCall call) {
86
+ call.resolve();
87
+ }
88
+
89
+ private void resolveCall(@NonNull PluginCall call, @Nullable Result result) {
90
+ if (result == null) {
91
+ call.resolve();
92
+ } else {
93
+ call.resolve(result.toJSObject());
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,22 @@
1
+ package io.capawesome.capacitorjs.plugins.clipboard.classes;
2
+
3
+ import androidx.annotation.NonNull;
4
+
5
+ public enum ClipboardContentType {
6
+ HTML("HTML"),
7
+ IMAGE("IMAGE"),
8
+ TEXT("TEXT"),
9
+ URL("URL");
10
+
11
+ @NonNull
12
+ private final String value;
13
+
14
+ ClipboardContentType(@NonNull String value) {
15
+ this.value = value;
16
+ }
17
+
18
+ @NonNull
19
+ public String getValue() {
20
+ return value;
21
+ }
22
+ }
@@ -0,0 +1,20 @@
1
+ package io.capawesome.capacitorjs.plugins.clipboard.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,12 @@
1
+ package io.capawesome.capacitorjs.plugins.clipboard.classes;
2
+
3
+ public class CustomExceptions {
4
+
5
+ public static final CustomException CONTENT_MISSING = new CustomException(null, "One of text, html, image or url must be provided.");
6
+ public static final CustomException EMPTY_CLIPBOARD = new CustomException("EMPTY_CLIPBOARD", "The clipboard is empty.");
7
+ public static final CustomException READ_FAILED = new CustomException("READ_FAILED", "The clipboard content could not be read.");
8
+ public static final CustomException WRITE_FAILED = new CustomException(
9
+ "WRITE_FAILED",
10
+ "The content could not be written to the clipboard."
11
+ );
12
+ }