@intuiface/capacitor-plugin-screenshot 1.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.
@@ -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 = 'IntuifaceCapacitorPluginScreenshot'
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 = '13.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) 2024 Intuiface
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/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # capacitor-plugin-screenshot
2
+
3
+ Capacitor plugin to take screenshot for iOS and Android devices.
4
+
5
+ The main goal of this plugin is to be able to take a screenshot of the application exactly as you see it. Even if there are playing videos, an iframe with some content, a cross-origin CSS...etc.
6
+
7
+ This plugin uses the `takeSnapshot` method of the `WKWebView` for iOS : https://developer.apple.com/documentation/webkit/wkwebview/2873260-takesnapshot.
8
+
9
+ And for Android, the `MediaProjection` API : https://developer.android.com/reference/android/media/projection/MediaProjection.
10
+
11
+ For the web, `MediaDevices` is use to cast the browser tab : https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install @intuiface/capacitor-plugin-screenshot
17
+ npx cap sync
18
+ ```
19
+
20
+ ## API
21
+
22
+ <docgen-index>
23
+
24
+ * [`getScreenshot(...)`](#getscreenshot)
25
+ * [Interfaces](#interfaces)
26
+
27
+ </docgen-index>
28
+
29
+ <docgen-api>
30
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
31
+
32
+ ### getScreenshot(...)
33
+
34
+ ```typescript
35
+ getScreenshot(options: ScreenshotOptions) => Promise<ScreenshotValue | null>
36
+ ```
37
+
38
+ Function to take a screenshot
39
+
40
+ | Param | Type | Description |
41
+ | ------------- | --------------------------------------------------------------- | ------------------------------------------------- |
42
+ | **`options`** | <code><a href="#screenshotoptions">ScreenshotOptions</a></code> | : options with quality desired for the screenshot |
43
+
44
+ **Returns:** <code>Promise&lt;<a href="#screenshotvalue">ScreenshotValue</a> | null&gt;</code>
45
+
46
+ --------------------
47
+
48
+
49
+ ### Interfaces
50
+
51
+
52
+ #### ScreenshotValue
53
+
54
+ | Prop | Type | Description |
55
+ | ------------ | ------------------- | ------------------------------------------------- |
56
+ | **`base64`** | <code>string</code> | The base64 string of the screenshot. Can be null |
57
+ | **`URI`** | <code>string</code> | The file uri where the image is saved Can be null |
58
+
59
+
60
+ #### ScreenshotOptions
61
+
62
+ | Prop | Type | Description |
63
+ | ------------- | ------------------- | ------------------------------------------- |
64
+ | **`quality`** | <code>number</code> | The quality of the screenshot between 0-100 |
65
+ | **`name`** | <code>string</code> | The name of the file to save |
66
+
67
+ </docgen-api>
68
+
69
+ ---
70
+
71
+ ## iOS
72
+
73
+ iOS version 11+ is supported.
74
+
75
+ Nothing more to do, it should work by calling the `getScreenshot` function.
76
+
77
+
78
+ ## Android
79
+
80
+ Android Version 6+ is supported.
81
+
82
+ To be able to take screenshot on Android, you have to declare a foreground service in the `AndroidManifest.xml` in the application tag :
83
+
84
+ ```xml
85
+ <service android:enabled="true" android:foregroundServiceType="mediaProjection" android:name="com.intuiface.plugins.screenshot.ScreenCaptureService" />
86
+ ```
87
+
88
+ The foreground service will ask you to cast your screen and this is mandatory to take screenshot with the `MediaProjection` API.
89
+
90
+
91
+ ## License
92
+
93
+ The scripts and documentation in this project are released under the [MIT License](./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.6.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.5'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.5.1'
6
+ }
7
+
8
+ buildscript {
9
+ repositories {
10
+ google()
11
+ mavenCentral()
12
+ }
13
+ dependencies {
14
+ classpath 'com.android.tools.build:gradle:8.0.0'
15
+ }
16
+ }
17
+
18
+ apply plugin: 'com.android.library'
19
+
20
+ android {
21
+ namespace "com.intuiface.plugins.screenshot"
22
+ compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 33
23
+ defaultConfig {
24
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
25
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 33
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_17
41
+ targetCompatibility JavaVersion.VERSION_17
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,11 @@
1
+ package com.intuiface.plugins.screenshot;
2
+
3
+ import android.util.Log;
4
+
5
+ public class CapacitorScreenshot {
6
+
7
+ public String echo(String value) {
8
+ Log.i("Echo", value);
9
+ return value;
10
+ }
11
+ }
@@ -0,0 +1,204 @@
1
+ package com.intuiface.plugins.screenshot;
2
+
3
+ import android.annotation.SuppressLint;
4
+ import android.app.Activity;
5
+ import android.content.Intent;
6
+ import android.graphics.Bitmap;
7
+ import android.graphics.PixelFormat;
8
+ import android.hardware.display.DisplayManager;
9
+ import android.hardware.display.VirtualDisplay;
10
+ import android.media.Image;
11
+ import android.media.ImageReader;
12
+ import android.media.projection.MediaProjection;
13
+ import android.media.projection.MediaProjectionManager;
14
+ import android.os.Build;
15
+ import android.os.Handler;
16
+ import android.os.HandlerThread;
17
+ import android.util.Base64;
18
+ import android.util.DisplayMetrics;
19
+ import androidx.activity.result.ActivityResult;
20
+ import androidx.activity.result.ActivityResultCallback;
21
+ import androidx.activity.result.ActivityResultLauncher;
22
+ import androidx.activity.result.contract.ActivityResultContracts;
23
+ import com.getcapacitor.JSObject;
24
+ import com.getcapacitor.Plugin;
25
+ import com.getcapacitor.PluginCall;
26
+ import com.getcapacitor.PluginMethod;
27
+ import com.getcapacitor.annotation.CapacitorPlugin;
28
+ import java.io.ByteArrayOutputStream;
29
+ import java.io.File;
30
+ import java.io.FileNotFoundException;
31
+ import java.io.FileOutputStream;
32
+ import java.io.IOException;
33
+ import java.nio.ByteBuffer;
34
+
35
+ @CapacitorPlugin(name = "CapacitorScreenshot", requestCodes = { 1 })
36
+ public class CapacitorScreenshotPlugin extends Plugin {
37
+
38
+ private MediaProjectionManager mediaProjectionManager;
39
+ private MediaProjection mediaProjection;
40
+
41
+ private ActivityResultLauncher<Intent> mediaProjectionActivityLauncher;
42
+
43
+ private PluginCall savedCall;
44
+
45
+ @Override
46
+ public void load() {
47
+ mediaProjectionActivityLauncher =
48
+ getActivity()
49
+ .registerForActivityResult(
50
+ new ActivityResultContracts.StartActivityForResult(),
51
+ new ActivityResultCallback<ActivityResult>() {
52
+ @Override
53
+ public void onActivityResult(ActivityResult result) {
54
+ if (result.getResultCode() == Activity.RESULT_OK) {
55
+ assert result.getData() != null;
56
+ mediaProjection = mediaProjectionManager.getMediaProjection(result.getResultCode(), result.getData());
57
+ startScreenshotCapture(mediaProjection);
58
+ }
59
+ }
60
+ }
61
+ );
62
+ }
63
+
64
+ @PluginMethod
65
+ public void getScreenshot(PluginCall call) {
66
+ if (mediaProjection != null) {
67
+ savedCall = call;
68
+ startScreenshotCapture(mediaProjection);
69
+ } else {
70
+ ScreenCaptureManager screenCaptureManager = new ScreenCaptureManager(getContext());
71
+ screenCaptureManager.startForeground();
72
+
73
+ getBridge()
74
+ .getActivity()
75
+ .runOnUiThread(
76
+ new Runnable() {
77
+ @Override
78
+ public void run() {
79
+ Activity activity = getBridge().getActivity();
80
+ if (activity != null) {
81
+ mediaProjectionManager =
82
+ (MediaProjectionManager) activity.getSystemService(Activity.MEDIA_PROJECTION_SERVICE);
83
+
84
+ savedCall = call;
85
+ Intent projectionIntent = mediaProjectionManager.createScreenCaptureIntent();
86
+ mediaProjectionActivityLauncher.launch(projectionIntent);
87
+ } else {
88
+ call.reject("Activity is null");
89
+ }
90
+ }
91
+ }
92
+ );
93
+ }
94
+ }
95
+
96
+ private void startScreenshotCapture(MediaProjection mediaProjection) {
97
+ HandlerThread handlerThread = new HandlerThread("ScreenshotThread");
98
+ handlerThread.start();
99
+
100
+ final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
101
+ int screenWidth = metrics.widthPixels;
102
+ int screenHeight = metrics.heightPixels;
103
+
104
+ Handler handler = new Handler(handlerThread.getLooper());
105
+
106
+ int screenDensity = metrics.densityDpi;
107
+
108
+ // Create an ImageReader to capture the screen content
109
+ @SuppressLint("WrongConstant")
110
+ ImageReader imageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, 1);
111
+ // Create a VirtualDisplay using the mediaProjection and imageReader
112
+ final VirtualDisplay virtualDisplay = mediaProjection.createVirtualDisplay(
113
+ "ScreenCapture",
114
+ screenWidth,
115
+ screenHeight,
116
+ screenDensity,
117
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
118
+ imageReader.getSurface(),
119
+ null,
120
+ handler
121
+ );
122
+
123
+ // Handle the captured images from the ImageReader
124
+ imageReader.setOnImageAvailableListener(
125
+ reader -> {
126
+ Image image = imageReader.acquireLatestImage();
127
+ if (image != null) {
128
+ // Process the captured image
129
+ processScreenshot(image);
130
+ // Release the image resources
131
+ image.close();
132
+
133
+ virtualDisplay.release();
134
+ }
135
+ },
136
+ handler
137
+ );
138
+ }
139
+
140
+ private void processScreenshot(Image image) {
141
+ if (savedCall != null) {
142
+ // Convert the Image to a Bitmap
143
+ Bitmap bitmap = convertImageToBitmap(image);
144
+
145
+ // Save or send the bitmap to your server
146
+ int quality = savedCall.getInt("quality", 100);
147
+
148
+ JSObject ret = new JSObject();
149
+ String base64Image = convertBitmapToBase64(bitmap, quality);
150
+
151
+ // save the bitmap as file
152
+ String filename = savedCall.getString("name", "screenshot");
153
+ File file = new File(getContext().getFilesDir(), filename + ".png");
154
+ try (FileOutputStream out = new FileOutputStream(file)) {
155
+ bitmap.compress(Bitmap.CompressFormat.PNG, quality, out);
156
+ out.flush();
157
+ } catch (IOException e) {
158
+ e.printStackTrace();
159
+ }
160
+
161
+ ret.put("base64", "data:image/png;base64," + base64Image);
162
+ ret.put("URI", file.toURI());
163
+ savedCall.resolve(ret);
164
+
165
+ // Release any resources
166
+ bitmap.recycle();
167
+ }
168
+ }
169
+
170
+ private String convertBitmapToBase64(Bitmap bitmap, int quality) {
171
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
172
+ bitmap.compress(Bitmap.CompressFormat.PNG, quality, byteArrayOutputStream);
173
+ byte[] byteArray = byteArrayOutputStream.toByteArray();
174
+ return Base64.encodeToString(byteArray, Base64.DEFAULT);
175
+ }
176
+
177
+ private Bitmap convertImageToBitmap(Image image) {
178
+ if (image == null) {
179
+ return null;
180
+ }
181
+
182
+ Image.Plane[] planes = image.getPlanes();
183
+ ByteBuffer buffer = planes[0].getBuffer();
184
+
185
+ int width = image.getWidth();
186
+ int height = image.getHeight();
187
+ int pixelStride = planes[0].getPixelStride();
188
+ int rowStride = planes[0].getRowStride();
189
+ int rowPadding = rowStride - pixelStride * width;
190
+
191
+ Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
192
+ bitmap.copyPixelsFromBuffer(buffer);
193
+
194
+ // generate the final bitmap at the right size from the bitmap created
195
+ bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
196
+
197
+ // scale but keep ratio
198
+ int scaledWidth = Math.min(width, 1920);
199
+ int scaledHeight = height * scaledWidth / width;
200
+ bitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, false);
201
+
202
+ return bitmap;
203
+ }
204
+ }
@@ -0,0 +1,69 @@
1
+ package com.intuiface.plugins.screenshot;
2
+
3
+ import android.content.ComponentName;
4
+ import android.content.Context;
5
+ import android.content.Intent;
6
+ import android.content.ServiceConnection;
7
+ import android.os.IBinder;
8
+
9
+ public class ScreenCaptureManager {
10
+
11
+ private ScreenCaptureService mService;
12
+ private Context mContext;
13
+ private State currentState = State.UNBIND_SERVICE;
14
+
15
+ /** Defines callbacks for service binding, passed to bindService() */
16
+ private final ServiceConnection connection = new ServiceConnection() {
17
+ @Override
18
+ public void onServiceConnected(ComponentName className, IBinder service) {
19
+ // We've bound to ScreenCaptureService, cast the IBinder and get ScreenCaptureService instance
20
+ ScreenCaptureService.LocalBinder binder = (ScreenCaptureService.LocalBinder) service;
21
+ mService = binder.getService();
22
+ if (currentState == State.START_FOREGROUND) {
23
+ mService.startForeground();
24
+ } else {
25
+ currentState = State.BIND_SERVICE;
26
+ }
27
+ }
28
+
29
+ @Override
30
+ public void onServiceDisconnected(ComponentName arg0) {}
31
+ };
32
+
33
+ /**
34
+ * An enum describing the possible states of a ScreenCaptureManager.
35
+ */
36
+ public enum State {
37
+ BIND_SERVICE,
38
+ START_FOREGROUND,
39
+ END_FOREGROUND,
40
+ UNBIND_SERVICE
41
+ }
42
+
43
+ public ScreenCaptureManager(Context context) {
44
+ mContext = context;
45
+ bindService();
46
+ }
47
+
48
+ private void bindService() {
49
+ Intent intent = new Intent(mContext, ScreenCaptureService.class);
50
+ mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
51
+ }
52
+
53
+ public void startForeground() {
54
+ if (mService != null) {
55
+ mService.startForeground();
56
+ }
57
+ currentState = State.START_FOREGROUND;
58
+ }
59
+
60
+ public void endForeground() {
61
+ mService.endForeground();
62
+ currentState = State.END_FOREGROUND;
63
+ }
64
+
65
+ public void unbindService() {
66
+ mContext.unbindService(connection);
67
+ currentState = State.UNBIND_SERVICE;
68
+ }
69
+ }
@@ -0,0 +1,83 @@
1
+ package com.intuiface.plugins.screenshot;
2
+
3
+ import android.app.Notification;
4
+ import android.app.NotificationChannel;
5
+ import android.app.NotificationManager;
6
+ import android.app.Service;
7
+ import android.content.Context;
8
+ import android.content.Intent;
9
+ import android.os.Binder;
10
+ import android.os.Build;
11
+ import android.os.IBinder;
12
+
13
+ /**
14
+ * Foreground service to ask screen capture permission.
15
+ */
16
+ public class ScreenCaptureService extends Service {
17
+
18
+ private static final String CHANNEL_ID = "default";
19
+ private static final String CHANNEL_NAME = "ScreenCapture";
20
+
21
+ // Binder given to clients
22
+ private final IBinder binder = new LocalBinder();
23
+
24
+ /**
25
+ * Class used for the client Binder. We know this service always
26
+ * runs in the same process as its clients, we don't need to deal with IPC.
27
+ */
28
+ public class LocalBinder extends Binder {
29
+
30
+ public ScreenCaptureService getService() {
31
+ // Return this instance of ScreenCaptureService so clients can call public methods
32
+ return ScreenCaptureService.this;
33
+ }
34
+ }
35
+
36
+ @Override
37
+ public void onCreate() {
38
+ super.onCreate();
39
+ }
40
+
41
+ @Override
42
+ public int onStartCommand(Intent intent, int flags, int startId) {
43
+ return START_NOT_STICKY;
44
+ }
45
+
46
+ public void startForeground() {
47
+ NotificationChannel chan = null;
48
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
49
+ chan = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE);
50
+ }
51
+ NotificationManager manager = null;
52
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
53
+ manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
54
+ }
55
+ assert manager != null;
56
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
57
+ manager.createNotificationChannel(chan);
58
+ }
59
+
60
+ final int notificationId = (int) System.currentTimeMillis();
61
+ Notification.Builder notificationBuilder = null;
62
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
63
+ notificationBuilder = new Notification.Builder(this, CHANNEL_ID);
64
+ } else {
65
+ notificationBuilder = new Notification.Builder(this);
66
+ }
67
+ Notification notification = notificationBuilder
68
+ .setOngoing(true)
69
+ .setContentTitle("ScreenCaptureService is running in the foreground")
70
+ .setCategory(Notification.CATEGORY_SERVICE)
71
+ .build();
72
+ startForeground(notificationId, notification);
73
+ }
74
+
75
+ public void endForeground() {
76
+ stopForeground(true);
77
+ }
78
+
79
+ @Override
80
+ public IBinder onBind(Intent intent) {
81
+ return binder;
82
+ }
83
+ }
File without changes
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <drawable name="icon" />
4
+ </resources>
package/dist/docs.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "api": {
3
+ "name": "CapacitorScreenshotPlugin",
4
+ "slug": "capacitorscreenshotplugin",
5
+ "docs": "",
6
+ "tags": [],
7
+ "methods": [
8
+ {
9
+ "name": "getScreenshot",
10
+ "signature": "(options: ScreenshotOptions) => Promise<ScreenshotValue | null>",
11
+ "parameters": [
12
+ {
13
+ "name": "options",
14
+ "docs": ": options with quality desired for the screenshot",
15
+ "type": "ScreenshotOptions"
16
+ }
17
+ ],
18
+ "returns": "Promise<ScreenshotValue | null>",
19
+ "tags": [
20
+ {
21
+ "name": "param",
22
+ "text": "options : options with quality desired for the screenshot"
23
+ },
24
+ {
25
+ "name": "returns",
26
+ "text": "Promise with the base64 string of the image if available, null instead"
27
+ }
28
+ ],
29
+ "docs": "Function to take a screenshot",
30
+ "complexTypes": [
31
+ "ScreenshotValue",
32
+ "ScreenshotOptions"
33
+ ],
34
+ "slug": "getscreenshot"
35
+ }
36
+ ],
37
+ "properties": []
38
+ },
39
+ "interfaces": [
40
+ {
41
+ "name": "ScreenshotValue",
42
+ "slug": "screenshotvalue",
43
+ "docs": "",
44
+ "tags": [],
45
+ "methods": [],
46
+ "properties": [
47
+ {
48
+ "name": "base64",
49
+ "tags": [],
50
+ "docs": "The base64 string of the screenshot.\nCan be null",
51
+ "complexTypes": [],
52
+ "type": "string | undefined"
53
+ },
54
+ {
55
+ "name": "URI",
56
+ "tags": [],
57
+ "docs": "The file uri where the image is saved\nCan be null",
58
+ "complexTypes": [],
59
+ "type": "string | undefined"
60
+ }
61
+ ]
62
+ },
63
+ {
64
+ "name": "ScreenshotOptions",
65
+ "slug": "screenshotoptions",
66
+ "docs": "",
67
+ "tags": [],
68
+ "methods": [],
69
+ "properties": [
70
+ {
71
+ "name": "quality",
72
+ "tags": [],
73
+ "docs": "The quality of the screenshot between 0-100",
74
+ "complexTypes": [],
75
+ "type": "number"
76
+ },
77
+ {
78
+ "name": "name",
79
+ "tags": [],
80
+ "docs": "The name of the file to save",
81
+ "complexTypes": [],
82
+ "type": "string | undefined"
83
+ }
84
+ ]
85
+ }
86
+ ],
87
+ "enums": [],
88
+ "typeAliases": [],
89
+ "pluginConfigs": []
90
+ }
@@ -0,0 +1,30 @@
1
+ export interface CapacitorScreenshotPlugin {
2
+ /**
3
+ * Function to take a screenshot
4
+ * @param {ScreenshotOptions} options : options with quality desired for the screenshot
5
+ * @returns {Promise<ScreenshotValue | null>} Promise with the base64 string of the image if available, null instead
6
+ */
7
+ getScreenshot(options: ScreenshotOptions): Promise<ScreenshotValue | null>;
8
+ }
9
+ export interface ScreenshotOptions {
10
+ /**
11
+ * The quality of the screenshot between 0-100
12
+ */
13
+ quality: number;
14
+ /**
15
+ * The name of the file to save
16
+ */
17
+ name?: string;
18
+ }
19
+ export interface ScreenshotValue {
20
+ /**
21
+ * The base64 string of the screenshot.
22
+ * Can be null
23
+ */
24
+ base64?: string;
25
+ /**
26
+ * The file uri where the image is saved
27
+ * Can be null
28
+ */
29
+ URI?: string;
30
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface CapacitorScreenshotPlugin {\n /**\n * Function to take a screenshot\n * @param {ScreenshotOptions} options : options with quality desired for the screenshot\n * @returns {Promise<ScreenshotValue | null>} Promise with the base64 string of the image if available, null instead\n */\n getScreenshot(options: ScreenshotOptions): Promise<ScreenshotValue | null>;\n}\n\nexport interface ScreenshotOptions {\n /**\n * The quality of the screenshot between 0-100\n */\n quality: number;\n /**\n * The name of the file to save\n */\n name?: string;\n}\n\nexport interface ScreenshotValue {\n /**\n * The base64 string of the screenshot.\n * Can be null\n */\n base64?: string;\n /**\n * The file uri where the image is saved\n * Can be null\n */\n URI?: string;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { CapacitorScreenshotPlugin } from './definitions';
2
+ declare const CapacitorScreenshot: CapacitorScreenshotPlugin;
3
+ export * from './definitions';
4
+ export { CapacitorScreenshot };
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ const CapacitorScreenshot = registerPlugin('CapacitorScreenshot', {
3
+ web: () => import('./web').then(m => new m.CapacitorScreenshotWeb()),
4
+ });
5
+ export * from './definitions';
6
+ export { CapacitorScreenshot };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,mBAAmB,GAAG,cAAc,CACxC,qBAAqB,EACrB;IACE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,sBAAsB,EAAE,CAAC;CACrE,CACF,CAAC;AAEF,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,mBAAmB,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { CapacitorScreenshotPlugin } from './definitions';\n\nconst CapacitorScreenshot = registerPlugin<CapacitorScreenshotPlugin>(\n 'CapacitorScreenshot',\n {\n web: () => import('./web').then(m => new m.CapacitorScreenshotWeb()),\n },\n);\n\nexport * from './definitions';\nexport { CapacitorScreenshot };\n"]}
@@ -0,0 +1,8 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { CapacitorScreenshotPlugin, ScreenshotOptions, ScreenshotValue } from './definitions';
3
+ export declare class CapacitorScreenshotWeb extends WebPlugin implements CapacitorScreenshotPlugin {
4
+ private captureStream;
5
+ private videoCapture;
6
+ private captureCanvas;
7
+ getScreenshot(options: ScreenshotOptions): Promise<ScreenshotValue | null>;
8
+ }
@@ -0,0 +1,61 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ export class CapacitorScreenshotWeb extends WebPlugin {
3
+ async getScreenshot(options) {
4
+ try {
5
+ if (!this.captureStream) {
6
+ // display a message ?
7
+ // get the media device
8
+ const mediaDevices = navigator.mediaDevices;
9
+ const width = screen.width * (window.devicePixelRatio || 1);
10
+ const height = screen.height * (window.devicePixelRatio || 1);
11
+ // start sharing screen (ask for it)
12
+ this.captureStream = await mediaDevices.getDisplayMedia({
13
+ preferCurrentTab: true,
14
+ audio: false,
15
+ video: {
16
+ width,
17
+ height,
18
+ },
19
+ });
20
+ // create a video tag
21
+ this.videoCapture = document.createElement('video');
22
+ // create a canvas
23
+ this.captureCanvas = document.createElement('canvas');
24
+ }
25
+ return new Promise(resolve => {
26
+ const callbackLoadedMetadata = () => {
27
+ // unbind from loaded metadata
28
+ this.videoCapture.removeEventListener('loadedmetadata', callbackLoadedMetadata);
29
+ // set the canvas size with the video size
30
+ this.captureCanvas.width = this.videoCapture.videoWidth;
31
+ this.captureCanvas.height = this.videoCapture.videoHeight;
32
+ // draw the video into canvas
33
+ this.captureCanvas
34
+ .getContext('2d')
35
+ .drawImage(this.videoCapture, 0, 0);
36
+ let quality = 1.0;
37
+ if (options.quality) {
38
+ quality = options.quality / 100;
39
+ }
40
+ // get the image of the canvas
41
+ const frame = this.captureCanvas.toDataURL('image/jpeg', quality);
42
+ // pause the video
43
+ this.videoCapture.pause();
44
+ // return the image
45
+ resolve({ base64: frame });
46
+ };
47
+ // bind on loaded metadata to draw the video when the video is ready
48
+ this.videoCapture.onloadedmetadata = callbackLoadedMetadata;
49
+ // set the src of the video
50
+ this.videoCapture.srcObject = this.captureStream;
51
+ // play the video
52
+ this.videoCapture.play();
53
+ });
54
+ }
55
+ catch (err) {
56
+ console.error(`Error: ${err}`);
57
+ return null;
58
+ }
59
+ }
60
+ }
61
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAQ5C,MAAM,OAAO,sBACX,SAAQ,SAAS;IAOjB,KAAK,CAAC,aAAa,CACjB,OAA0B;QAE1B,IAAI;YACF,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;gBACvB,sBAAsB;gBAEtB,uBAAuB;gBACvB,MAAM,YAAY,GAAG,SAAS,CAAC,YAAmB,CAAC;gBACnD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC;gBAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC;gBAE9D,oCAAoC;gBACpC,IAAI,CAAC,aAAa,GAAG,MAAM,YAAY,CAAC,eAAe,CAAC;oBACtD,gBAAgB,EAAE,IAAI;oBACtB,KAAK,EAAE,KAAK;oBACZ,KAAK,EAAE;wBACL,KAAK;wBACL,MAAM;qBACP;iBACF,CAAC,CAAC;gBACH,qBAAqB;gBACrB,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;gBACpD,kBAAkB;gBAClB,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;aACvD;YAED,OAAO,IAAI,OAAO,CAAM,OAAO,CAAC,EAAE;gBAChC,MAAM,sBAAsB,GAAG,GAAG,EAAE;oBAClC,8BAA8B;oBAC9B,IAAI,CAAC,YAAY,CAAC,mBAAmB,CACnC,gBAAgB,EAChB,sBAAsB,CACvB,CAAC;oBACF,0CAA0C;oBAC1C,IAAI,CAAC,aAAa,CAAC,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC;oBACxD,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC;oBAC1D,6BAA6B;oBAC7B,IAAI,CAAC,aAAa;yBACf,UAAU,CAAC,IAAI,CAAC;yBAChB,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;oBACtC,IAAI,OAAO,GAAG,GAAG,CAAC;oBAClB,IAAI,OAAO,CAAC,OAAO,EAAE;wBACnB,OAAO,GAAG,OAAO,CAAC,OAAO,GAAG,GAAG,CAAC;qBACjC;oBACD,8BAA8B;oBAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;oBAClE,kBAAkB;oBAClB,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;oBAE1B,mBAAmB;oBACnB,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC7B,CAAC,CAAC;gBAEF,oEAAoE;gBACpE,IAAI,CAAC,YAAY,CAAC,gBAAgB,GAAG,sBAAsB,CAAC;gBAC5D,2BAA2B;gBAC3B,IAAI,CAAC,YAAY,CAAC,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC;gBACjD,iBAAiB;gBACjB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;YAC3B,CAAC,CAAC,CAAC;SACJ;QAAC,OAAO,GAAG,EAAE;YACZ,OAAO,CAAC,KAAK,CAAC,UAAU,GAAa,EAAE,CAAC,CAAC;YACzC,OAAO,IAAI,CAAC;SACb;IACH,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type {\n CapacitorScreenshotPlugin,\n ScreenshotOptions,\n ScreenshotValue,\n} from './definitions';\n\nexport class CapacitorScreenshotWeb\n extends WebPlugin\n implements CapacitorScreenshotPlugin\n{\n private captureStream: any;\n private videoCapture: any;\n private captureCanvas: any;\n\n async getScreenshot(\n options: ScreenshotOptions,\n ): Promise<ScreenshotValue | null> {\n try {\n if (!this.captureStream) {\n // display a message ?\n\n // get the media device\n const mediaDevices = navigator.mediaDevices as any;\n const width = screen.width * (window.devicePixelRatio || 1);\n const height = screen.height * (window.devicePixelRatio || 1);\n\n // start sharing screen (ask for it)\n this.captureStream = await mediaDevices.getDisplayMedia({\n preferCurrentTab: true,\n audio: false,\n video: {\n width,\n height,\n },\n });\n // create a video tag\n this.videoCapture = document.createElement('video');\n // create a canvas\n this.captureCanvas = document.createElement('canvas');\n }\n\n return new Promise<any>(resolve => {\n const callbackLoadedMetadata = () => {\n // unbind from loaded metadata\n this.videoCapture.removeEventListener(\n 'loadedmetadata',\n callbackLoadedMetadata,\n );\n // set the canvas size with the video size\n this.captureCanvas.width = this.videoCapture.videoWidth;\n this.captureCanvas.height = this.videoCapture.videoHeight;\n // draw the video into canvas\n this.captureCanvas\n .getContext('2d')\n .drawImage(this.videoCapture, 0, 0);\n let quality = 1.0;\n if (options.quality) {\n quality = options.quality / 100;\n }\n // get the image of the canvas\n const frame = this.captureCanvas.toDataURL('image/jpeg', quality);\n // pause the video\n this.videoCapture.pause();\n\n // return the image\n resolve({ base64: frame });\n };\n\n // bind on loaded metadata to draw the video when the video is ready\n this.videoCapture.onloadedmetadata = callbackLoadedMetadata;\n // set the src of the video\n this.videoCapture.srcObject = this.captureStream;\n // play the video\n this.videoCapture.play();\n });\n } catch (err) {\n console.error(`Error: ${err as string}`);\n return null;\n }\n }\n}\n"]}
@@ -0,0 +1,77 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var core = require('@capacitor/core');
6
+
7
+ const CapacitorScreenshot = core.registerPlugin('CapacitorScreenshot', {
8
+ web: () => Promise.resolve().then(function () { return web; }).then(m => new m.CapacitorScreenshotWeb()),
9
+ });
10
+
11
+ class CapacitorScreenshotWeb extends core.WebPlugin {
12
+ async getScreenshot(options) {
13
+ try {
14
+ if (!this.captureStream) {
15
+ // display a message ?
16
+ // get the media device
17
+ const mediaDevices = navigator.mediaDevices;
18
+ const width = screen.width * (window.devicePixelRatio || 1);
19
+ const height = screen.height * (window.devicePixelRatio || 1);
20
+ // start sharing screen (ask for it)
21
+ this.captureStream = await mediaDevices.getDisplayMedia({
22
+ preferCurrentTab: true,
23
+ audio: false,
24
+ video: {
25
+ width,
26
+ height,
27
+ },
28
+ });
29
+ // create a video tag
30
+ this.videoCapture = document.createElement('video');
31
+ // create a canvas
32
+ this.captureCanvas = document.createElement('canvas');
33
+ }
34
+ return new Promise(resolve => {
35
+ const callbackLoadedMetadata = () => {
36
+ // unbind from loaded metadata
37
+ this.videoCapture.removeEventListener('loadedmetadata', callbackLoadedMetadata);
38
+ // set the canvas size with the video size
39
+ this.captureCanvas.width = this.videoCapture.videoWidth;
40
+ this.captureCanvas.height = this.videoCapture.videoHeight;
41
+ // draw the video into canvas
42
+ this.captureCanvas
43
+ .getContext('2d')
44
+ .drawImage(this.videoCapture, 0, 0);
45
+ let quality = 1.0;
46
+ if (options.quality) {
47
+ quality = options.quality / 100;
48
+ }
49
+ // get the image of the canvas
50
+ const frame = this.captureCanvas.toDataURL('image/jpeg', quality);
51
+ // pause the video
52
+ this.videoCapture.pause();
53
+ // return the image
54
+ resolve({ base64: frame });
55
+ };
56
+ // bind on loaded metadata to draw the video when the video is ready
57
+ this.videoCapture.onloadedmetadata = callbackLoadedMetadata;
58
+ // set the src of the video
59
+ this.videoCapture.srcObject = this.captureStream;
60
+ // play the video
61
+ this.videoCapture.play();
62
+ });
63
+ }
64
+ catch (err) {
65
+ console.error(`Error: ${err}`);
66
+ return null;
67
+ }
68
+ }
69
+ }
70
+
71
+ var web = /*#__PURE__*/Object.freeze({
72
+ __proto__: null,
73
+ CapacitorScreenshotWeb: CapacitorScreenshotWeb
74
+ });
75
+
76
+ exports.CapacitorScreenshot = CapacitorScreenshot;
77
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst CapacitorScreenshot = registerPlugin('CapacitorScreenshot', {\n web: () => import('./web').then(m => new m.CapacitorScreenshotWeb()),\n});\nexport * from './definitions';\nexport { CapacitorScreenshot };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class CapacitorScreenshotWeb extends WebPlugin {\n async getScreenshot(options) {\n try {\n if (!this.captureStream) {\n // display a message ?\n // get the media device\n const mediaDevices = navigator.mediaDevices;\n const width = screen.width * (window.devicePixelRatio || 1);\n const height = screen.height * (window.devicePixelRatio || 1);\n // start sharing screen (ask for it)\n this.captureStream = await mediaDevices.getDisplayMedia({\n preferCurrentTab: true,\n audio: false,\n video: {\n width,\n height,\n },\n });\n // create a video tag\n this.videoCapture = document.createElement('video');\n // create a canvas\n this.captureCanvas = document.createElement('canvas');\n }\n return new Promise(resolve => {\n const callbackLoadedMetadata = () => {\n // unbind from loaded metadata\n this.videoCapture.removeEventListener('loadedmetadata', callbackLoadedMetadata);\n // set the canvas size with the video size\n this.captureCanvas.width = this.videoCapture.videoWidth;\n this.captureCanvas.height = this.videoCapture.videoHeight;\n // draw the video into canvas\n this.captureCanvas\n .getContext('2d')\n .drawImage(this.videoCapture, 0, 0);\n let quality = 1.0;\n if (options.quality) {\n quality = options.quality / 100;\n }\n // get the image of the canvas\n const frame = this.captureCanvas.toDataURL('image/jpeg', quality);\n // pause the video\n this.videoCapture.pause();\n // return the image\n resolve({ base64: frame });\n };\n // bind on loaded metadata to draw the video when the video is ready\n this.videoCapture.onloadedmetadata = callbackLoadedMetadata;\n // set the src of the video\n this.videoCapture.srcObject = this.captureStream;\n // play the video\n this.videoCapture.play();\n });\n }\n catch (err) {\n console.error(`Error: ${err}`);\n return null;\n }\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
package/dist/plugin.js ADDED
@@ -0,0 +1,80 @@
1
+ var capacitorCapacitorScreenshot = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ const CapacitorScreenshot = core.registerPlugin('CapacitorScreenshot', {
5
+ web: () => Promise.resolve().then(function () { return web; }).then(m => new m.CapacitorScreenshotWeb()),
6
+ });
7
+
8
+ class CapacitorScreenshotWeb extends core.WebPlugin {
9
+ async getScreenshot(options) {
10
+ try {
11
+ if (!this.captureStream) {
12
+ // display a message ?
13
+ // get the media device
14
+ const mediaDevices = navigator.mediaDevices;
15
+ const width = screen.width * (window.devicePixelRatio || 1);
16
+ const height = screen.height * (window.devicePixelRatio || 1);
17
+ // start sharing screen (ask for it)
18
+ this.captureStream = await mediaDevices.getDisplayMedia({
19
+ preferCurrentTab: true,
20
+ audio: false,
21
+ video: {
22
+ width,
23
+ height,
24
+ },
25
+ });
26
+ // create a video tag
27
+ this.videoCapture = document.createElement('video');
28
+ // create a canvas
29
+ this.captureCanvas = document.createElement('canvas');
30
+ }
31
+ return new Promise(resolve => {
32
+ const callbackLoadedMetadata = () => {
33
+ // unbind from loaded metadata
34
+ this.videoCapture.removeEventListener('loadedmetadata', callbackLoadedMetadata);
35
+ // set the canvas size with the video size
36
+ this.captureCanvas.width = this.videoCapture.videoWidth;
37
+ this.captureCanvas.height = this.videoCapture.videoHeight;
38
+ // draw the video into canvas
39
+ this.captureCanvas
40
+ .getContext('2d')
41
+ .drawImage(this.videoCapture, 0, 0);
42
+ let quality = 1.0;
43
+ if (options.quality) {
44
+ quality = options.quality / 100;
45
+ }
46
+ // get the image of the canvas
47
+ const frame = this.captureCanvas.toDataURL('image/jpeg', quality);
48
+ // pause the video
49
+ this.videoCapture.pause();
50
+ // return the image
51
+ resolve({ base64: frame });
52
+ };
53
+ // bind on loaded metadata to draw the video when the video is ready
54
+ this.videoCapture.onloadedmetadata = callbackLoadedMetadata;
55
+ // set the src of the video
56
+ this.videoCapture.srcObject = this.captureStream;
57
+ // play the video
58
+ this.videoCapture.play();
59
+ });
60
+ }
61
+ catch (err) {
62
+ console.error(`Error: ${err}`);
63
+ return null;
64
+ }
65
+ }
66
+ }
67
+
68
+ var web = /*#__PURE__*/Object.freeze({
69
+ __proto__: null,
70
+ CapacitorScreenshotWeb: CapacitorScreenshotWeb
71
+ });
72
+
73
+ exports.CapacitorScreenshot = CapacitorScreenshot;
74
+
75
+ Object.defineProperty(exports, '__esModule', { value: true });
76
+
77
+ return exports;
78
+
79
+ })({}, capacitorExports);
80
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst CapacitorScreenshot = registerPlugin('CapacitorScreenshot', {\n web: () => import('./web').then(m => new m.CapacitorScreenshotWeb()),\n});\nexport * from './definitions';\nexport { CapacitorScreenshot };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class CapacitorScreenshotWeb extends WebPlugin {\n async getScreenshot(options) {\n try {\n if (!this.captureStream) {\n // display a message ?\n // get the media device\n const mediaDevices = navigator.mediaDevices;\n const width = screen.width * (window.devicePixelRatio || 1);\n const height = screen.height * (window.devicePixelRatio || 1);\n // start sharing screen (ask for it)\n this.captureStream = await mediaDevices.getDisplayMedia({\n preferCurrentTab: true,\n audio: false,\n video: {\n width,\n height,\n },\n });\n // create a video tag\n this.videoCapture = document.createElement('video');\n // create a canvas\n this.captureCanvas = document.createElement('canvas');\n }\n return new Promise(resolve => {\n const callbackLoadedMetadata = () => {\n // unbind from loaded metadata\n this.videoCapture.removeEventListener('loadedmetadata', callbackLoadedMetadata);\n // set the canvas size with the video size\n this.captureCanvas.width = this.videoCapture.videoWidth;\n this.captureCanvas.height = this.videoCapture.videoHeight;\n // draw the video into canvas\n this.captureCanvas\n .getContext('2d')\n .drawImage(this.videoCapture, 0, 0);\n let quality = 1.0;\n if (options.quality) {\n quality = options.quality / 100;\n }\n // get the image of the canvas\n const frame = this.captureCanvas.toDataURL('image/jpeg', quality);\n // pause the video\n this.videoCapture.pause();\n // return the image\n resolve({ base64: frame });\n };\n // bind on loaded metadata to draw the video when the video is ready\n this.videoCapture.onloadedmetadata = callbackLoadedMetadata;\n // set the src of the video\n this.videoCapture.srcObject = this.captureStream;\n // play the video\n this.videoCapture.play();\n });\n }\n catch (err) {\n console.error(`Error: ${err}`);\n return null;\n }\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,mBAAmB,GAAGA,mBAAc,CAAC,qBAAqB,EAAE;IAClE,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,sBAAsB,EAAE,CAAC;IACxE,CAAC;;ICFM,MAAM,sBAAsB,SAASC,cAAS,CAAC;IACtD,IAAI,MAAM,aAAa,CAAC,OAAO,EAAE;IACjC,QAAQ,IAAI;IACZ,YAAY,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;IACrC;IACA;IACA,gBAAgB,MAAM,YAAY,GAAG,SAAS,CAAC,YAAY,CAAC;IAC5D,gBAAgB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC;IAC5E,gBAAgB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC;IAC9E;IACA,gBAAgB,IAAI,CAAC,aAAa,GAAG,MAAM,YAAY,CAAC,eAAe,CAAC;IACxE,oBAAoB,gBAAgB,EAAE,IAAI;IAC1C,oBAAoB,KAAK,EAAE,KAAK;IAChC,oBAAoB,KAAK,EAAE;IAC3B,wBAAwB,KAAK;IAC7B,wBAAwB,MAAM;IAC9B,qBAAqB;IACrB,iBAAiB,CAAC,CAAC;IACnB;IACA,gBAAgB,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACpE;IACA,gBAAgB,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACtE,aAAa;IACb,YAAY,OAAO,IAAI,OAAO,CAAC,OAAO,IAAI;IAC1C,gBAAgB,MAAM,sBAAsB,GAAG,MAAM;IACrD;IACA,oBAAoB,IAAI,CAAC,YAAY,CAAC,mBAAmB,CAAC,gBAAgB,EAAE,sBAAsB,CAAC,CAAC;IACpG;IACA,oBAAoB,IAAI,CAAC,aAAa,CAAC,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC;IAC5E,oBAAoB,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC;IAC9E;IACA,oBAAoB,IAAI,CAAC,aAAa;IACtC,yBAAyB,UAAU,CAAC,IAAI,CAAC;IACzC,yBAAyB,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5D,oBAAoB,IAAI,OAAO,GAAG,GAAG,CAAC;IACtC,oBAAoB,IAAI,OAAO,CAAC,OAAO,EAAE;IACzC,wBAAwB,OAAO,GAAG,OAAO,CAAC,OAAO,GAAG,GAAG,CAAC;IACxD,qBAAqB;IACrB;IACA,oBAAoB,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACtF;IACA,oBAAoB,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;IAC9C;IACA,oBAAoB,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/C,iBAAiB,CAAC;IAClB;IACA,gBAAgB,IAAI,CAAC,YAAY,CAAC,gBAAgB,GAAG,sBAAsB,CAAC;IAC5E;IACA,gBAAgB,IAAI,CAAC,YAAY,CAAC,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC;IACjE;IACA,gBAAgB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;IACzC,aAAa,CAAC,CAAC;IACf,SAAS;IACT,QAAQ,OAAO,GAAG,EAAE;IACpB,YAAY,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAC3C,YAAY,OAAO,IAAI,CAAC;IACxB,SAAS;IACT,KAAK;IACL;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,8 @@
1
+ import Foundation
2
+
3
+ @objc public class CapacitorScreenshot: NSObject {
4
+ @objc public func echo(_ value: String) -> String {
5
+ print(value)
6
+ return value
7
+ }
8
+ }
@@ -0,0 +1,10 @@
1
+ #import <UIKit/UIKit.h>
2
+
3
+ //! Project version number for Plugin.
4
+ FOUNDATION_EXPORT double PluginVersionNumber;
5
+
6
+ //! Project version string for Plugin.
7
+ FOUNDATION_EXPORT const unsigned char PluginVersionString[];
8
+
9
+ // In this header, you should import all the public headers of your framework using statements like #import <Plugin/PublicHeader.h>
10
+
@@ -0,0 +1,8 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import <Capacitor/Capacitor.h>
3
+
4
+ // Define the plugin using the CAP_PLUGIN Macro, and
5
+ // each method the plugin supports using the CAP_PLUGIN_METHOD macro.
6
+ CAP_PLUGIN(CapacitorScreenshotPlugin, "CapacitorScreenshot",
7
+ CAP_PLUGIN_METHOD(getScreenshot, CAPPluginReturnPromise);
8
+ )
@@ -0,0 +1,49 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ /**
5
+ * Please read the Capacitor iOS Plugin Development Guide
6
+ * here: https://capacitorjs.com/docs/plugins/ios
7
+ */
8
+ @objc(CapacitorScreenshotPlugin)
9
+ public class CapacitorScreenshotPlugin: CAPPlugin {
10
+ private let implementation = CapacitorScreenshot()
11
+
12
+ @objc func getScreenshot(_ call: CAPPluginCall) {
13
+
14
+ let quality = ((call.getDouble("quality") ?? 100) / 100)
15
+ let filename = (call.getString("name") ?? "screenshot")
16
+ DispatchQueue.main.async {
17
+ let config = WKSnapshotConfiguration()
18
+ config.rect = self.webView!.frame
19
+ config.afterScreenUpdates = false
20
+ self.webView?.takeSnapshot(with: config, completionHandler: {
21
+ (image: UIImage?, error: Error?) in
22
+ if error != nil {
23
+ return
24
+ }
25
+ guard let imageData = image!.jpegData(compressionQuality: quality) else {return}
26
+ let base64Result = imageData.base64EncodedString()
27
+ let file = "data:image/png;base64," + base64Result
28
+
29
+ var fileURI: URL = URL.init(fileURLWithPath: "")
30
+ do {
31
+
32
+ if #available(iOS 16.0, *) {
33
+ fileURI = URL(fileURLWithPath: filename+".png", relativeTo: .documentsDirectory)
34
+ } else {
35
+ // Fallback on earlier versions
36
+ fileURI = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathExtension(filename+".png")
37
+ }
38
+ try imageData.write(to: fileURI)
39
+ } catch {
40
+ // nothing to do
41
+ }
42
+ call.resolve([
43
+ "URI": fileURI.absoluteString,
44
+ "base64": file
45
+ ])
46
+ })
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDevelopmentRegion</key>
6
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
7
+ <key>CFBundleExecutable</key>
8
+ <string>$(EXECUTABLE_NAME)</string>
9
+ <key>CFBundleIdentifier</key>
10
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11
+ <key>CFBundleInfoDictionaryVersion</key>
12
+ <string>6.0</string>
13
+ <key>CFBundleName</key>
14
+ <string>$(PRODUCT_NAME)</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>FMWK</string>
17
+ <key>CFBundleShortVersionString</key>
18
+ <string>1.0</string>
19
+ <key>CFBundleVersion</key>
20
+ <string>$(CURRENT_PROJECT_VERSION)</string>
21
+ <key>NSPrincipalClass</key>
22
+ <string></string>
23
+ </dict>
24
+ </plist>
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@intuiface/capacitor-plugin-screenshot",
3
+ "version": "1.0.1",
4
+ "description": "Capacitor plugin to take screenshot for iOS and Android devices",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Plugin/",
14
+ "IntuifaceCapacitorPluginScreenshot.podspec"
15
+ ],
16
+ "author": "intuiface",
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/intuiface/capacitor-plugin-screenshot.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/intuiface/capacitor-plugin-screenshot/issues"
24
+ },
25
+ "keywords": [
26
+ "capacitor",
27
+ "plugin",
28
+ "native"
29
+ ],
30
+ "scripts": {
31
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
32
+ "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..",
33
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
34
+ "verify:web": "npm run build",
35
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
36
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
37
+ "eslint": "eslint . --ext ts",
38
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
39
+ "swiftlint": "node-swiftlint",
40
+ "docgen": "docgen --api CapacitorScreenshotPlugin --output-readme README.md --output-json dist/docs.json",
41
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js",
42
+ "clean": "rimraf ./dist",
43
+ "watch": "tsc --watch",
44
+ "prepublishOnly": "npm run build",
45
+ "prepare": "npx shx cp -u ./.githooks/* ./.git/hooks/"
46
+ },
47
+ "devDependencies": {
48
+ "@capacitor/android": "^5.0.0",
49
+ "@capacitor/core": "^5.0.0",
50
+ "@capacitor/docgen": "^0.0.18",
51
+ "@capacitor/ios": "^5.0.0",
52
+ "@ionic/eslint-config": "^0.3.0",
53
+ "@ionic/prettier-config": "^1.0.1",
54
+ "@ionic/swiftlint-config": "^1.1.2",
55
+ "eslint": "^7.11.0",
56
+ "prettier": "~2.3.0",
57
+ "prettier-plugin-java": "~1.0.2",
58
+ "rimraf": "^3.0.2",
59
+ "rollup": "^2.32.0",
60
+ "swiftlint": "^1.0.1",
61
+ "typescript": "~4.1.5"
62
+ },
63
+ "peerDependencies": {
64
+ "@capacitor/core": "^5.0.0"
65
+ },
66
+ "prettier": "@ionic/prettier-config",
67
+ "swiftlint": "@ionic/swiftlint-config",
68
+ "eslintConfig": {
69
+ "extends": "@ionic/eslint-config/recommended"
70
+ },
71
+ "capacitor": {
72
+ "ios": {
73
+ "src": "ios"
74
+ },
75
+ "android": {
76
+ "src": "android"
77
+ }
78
+ }
79
+ }