capacitor-plugin-camera-forked 2.0.8
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/CapacitorPluginCameraForked.podspec +17 -0
- package/LICENSE +21 -0
- package/README.md +513 -0
- package/android/build.gradle +66 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/tonyxlh/capacitor/camera/BitmapUtils.java +210 -0
- package/android/src/main/java/com/tonyxlh/capacitor/camera/CameraPreviewPlugin.java +834 -0
- package/android/src/main/java/com/tonyxlh/capacitor/camera/FrameMetadata.java +70 -0
- package/android/src/main/java/com/tonyxlh/capacitor/camera/ScanRegion.java +16 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +495 -0
- package/dist/esm/definitions.d.ts +123 -0
- package/dist/esm/definitions.js +10 -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 +85 -0
- package/dist/esm/web.js +427 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +456 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +457 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/CameraPreview.swift +8 -0
- package/ios/Plugin/CameraPreviewPlugin.h +10 -0
- package/ios/Plugin/CameraPreviewPlugin.m +29 -0
- package/ios/Plugin/CameraPreviewPlugin.swift +670 -0
- package/ios/Plugin/Info.plist +24 -0
- package/ios/Plugin/PreviewView.swift +22 -0
- package/ios/Plugin/ScanRegion.swift +17 -0
- package/package.json +89 -0
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
package com.tonyxlh.capacitor.camera;
|
|
2
|
+
|
|
3
|
+
import android.Manifest;
|
|
4
|
+
import android.annotation.SuppressLint;
|
|
5
|
+
import android.content.ContentValues;
|
|
6
|
+
import android.content.pm.PackageManager;
|
|
7
|
+
import android.content.res.Configuration;
|
|
8
|
+
import android.content.res.Resources;
|
|
9
|
+
import android.graphics.Bitmap;
|
|
10
|
+
import android.graphics.Color;
|
|
11
|
+
import android.graphics.drawable.Drawable;
|
|
12
|
+
import android.net.Uri;
|
|
13
|
+
import android.os.Build;
|
|
14
|
+
import android.provider.MediaStore;
|
|
15
|
+
import android.util.Base64;
|
|
16
|
+
import android.util.Log;
|
|
17
|
+
import android.util.Size;
|
|
18
|
+
import android.view.View;
|
|
19
|
+
import android.view.ViewGroup;
|
|
20
|
+
import android.widget.FrameLayout;
|
|
21
|
+
|
|
22
|
+
import androidx.annotation.NonNull;
|
|
23
|
+
import androidx.annotation.RequiresApi;
|
|
24
|
+
import androidx.camera.core.AspectRatio;
|
|
25
|
+
import androidx.camera.core.Camera;
|
|
26
|
+
import androidx.camera.core.CameraSelector;
|
|
27
|
+
import androidx.camera.core.CameraState;
|
|
28
|
+
import androidx.camera.core.FocusMeteringAction;
|
|
29
|
+
import androidx.camera.core.ImageAnalysis;
|
|
30
|
+
import androidx.camera.core.ImageCapture;
|
|
31
|
+
import androidx.camera.core.ImageCaptureException;
|
|
32
|
+
import androidx.camera.core.ImageProxy;
|
|
33
|
+
import androidx.camera.core.MeteringPoint;
|
|
34
|
+
import androidx.camera.core.MeteringPointFactory;
|
|
35
|
+
import androidx.camera.core.Preview;
|
|
36
|
+
import androidx.camera.core.SurfaceOrientedMeteringPointFactory;
|
|
37
|
+
import androidx.camera.core.UseCaseGroup;
|
|
38
|
+
import androidx.camera.lifecycle.ProcessCameraProvider;
|
|
39
|
+
import androidx.camera.video.PendingRecording;
|
|
40
|
+
import androidx.camera.video.Quality;
|
|
41
|
+
import androidx.camera.video.QualitySelector;
|
|
42
|
+
import androidx.camera.video.Recorder;
|
|
43
|
+
import androidx.camera.video.Recording;
|
|
44
|
+
import androidx.camera.video.VideoCapture;
|
|
45
|
+
import androidx.camera.video.MediaStoreOutputOptions;
|
|
46
|
+
import androidx.camera.video.VideoRecordEvent;
|
|
47
|
+
import androidx.camera.view.PreviewView;
|
|
48
|
+
import androidx.core.app.ActivityCompat;
|
|
49
|
+
import androidx.core.util.Consumer;
|
|
50
|
+
import androidx.lifecycle.LifecycleOwner;
|
|
51
|
+
|
|
52
|
+
import com.getcapacitor.JSArray;
|
|
53
|
+
import com.getcapacitor.JSObject;
|
|
54
|
+
import com.getcapacitor.PermissionState;
|
|
55
|
+
import com.getcapacitor.Plugin;
|
|
56
|
+
import com.getcapacitor.PluginCall;
|
|
57
|
+
import com.getcapacitor.PluginMethod;
|
|
58
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
59
|
+
import com.getcapacitor.annotation.Permission;
|
|
60
|
+
import com.getcapacitor.annotation.PermissionCallback;
|
|
61
|
+
import com.google.common.util.concurrent.ListenableFuture;
|
|
62
|
+
|
|
63
|
+
import org.json.JSONException;
|
|
64
|
+
|
|
65
|
+
import java.io.ByteArrayOutputStream;
|
|
66
|
+
import java.io.File;
|
|
67
|
+
import java.io.FileInputStream;
|
|
68
|
+
import java.io.FileNotFoundException;
|
|
69
|
+
import java.io.IOException;
|
|
70
|
+
import java.io.InputStream;
|
|
71
|
+
import java.util.Date;
|
|
72
|
+
import java.util.concurrent.ExecutionException;
|
|
73
|
+
import java.util.concurrent.ExecutorService;
|
|
74
|
+
import java.util.concurrent.Executors;
|
|
75
|
+
import java.util.concurrent.TimeUnit;
|
|
76
|
+
|
|
77
|
+
@CapacitorPlugin(
|
|
78
|
+
name = "CameraPreview",
|
|
79
|
+
permissions = {
|
|
80
|
+
@Permission(strings = {Manifest.permission.CAMERA}, alias = CameraPreviewPlugin.CAMERA),
|
|
81
|
+
@Permission(strings = {Manifest.permission.RECORD_AUDIO}, alias = CameraPreviewPlugin.MICROPHONE),
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
public class CameraPreviewPlugin extends Plugin {
|
|
85
|
+
// Permission alias constants
|
|
86
|
+
static final String CAMERA = "camera";
|
|
87
|
+
static final String MICROPHONE = "microphone";
|
|
88
|
+
private String callbackID;
|
|
89
|
+
private PreviewView previewView;
|
|
90
|
+
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
|
|
91
|
+
private ExecutorService exec;
|
|
92
|
+
private Camera camera;
|
|
93
|
+
private CameraSelector cameraSelector;
|
|
94
|
+
private ProcessCameraProvider cameraProvider;
|
|
95
|
+
private Preview preview;
|
|
96
|
+
private ImageCapture imageCapture;
|
|
97
|
+
private UseCaseGroup useCaseGroup;
|
|
98
|
+
private ImageAnalysis imageAnalysis;
|
|
99
|
+
private Recorder recorder;
|
|
100
|
+
private Recording currentRecording;
|
|
101
|
+
private PluginCall stopRecordingCall;
|
|
102
|
+
private PluginCall takeSnapshotCall;
|
|
103
|
+
private PluginCall saveFrameCall;
|
|
104
|
+
private int desiredWidth = 1280;
|
|
105
|
+
private int desiredHeight = 720;
|
|
106
|
+
private CameraState previousCameraStatus;
|
|
107
|
+
private ScanRegion scanRegion;
|
|
108
|
+
|
|
109
|
+
static public Bitmap frameTaken;
|
|
110
|
+
|
|
111
|
+
@PluginMethod
|
|
112
|
+
public void initialize(PluginCall call) {
|
|
113
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
114
|
+
@RequiresApi(api = Build.VERSION_CODES.P)
|
|
115
|
+
public void run() {
|
|
116
|
+
previewView = new PreviewView(getContext());
|
|
117
|
+
FrameLayout.LayoutParams cameraPreviewParams = new FrameLayout.LayoutParams(
|
|
118
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
119
|
+
FrameLayout.LayoutParams.MATCH_PARENT
|
|
120
|
+
);
|
|
121
|
+
((ViewGroup) bridge.getWebView().getParent()).addView(previewView, cameraPreviewParams);
|
|
122
|
+
bridge.getWebView().bringToFront();
|
|
123
|
+
|
|
124
|
+
exec = Executors.newSingleThreadExecutor();
|
|
125
|
+
cameraProviderFuture = ProcessCameraProvider.getInstance(getContext());
|
|
126
|
+
cameraProviderFuture.addListener(() -> {
|
|
127
|
+
try {
|
|
128
|
+
cameraProvider = cameraProviderFuture.get();
|
|
129
|
+
cameraSelector = new CameraSelector.Builder()
|
|
130
|
+
.requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
|
|
131
|
+
setupUseCases(false);
|
|
132
|
+
call.resolve();
|
|
133
|
+
} catch (ExecutionException | InterruptedException e) {
|
|
134
|
+
e.printStackTrace();
|
|
135
|
+
call.reject(e.getMessage());
|
|
136
|
+
}
|
|
137
|
+
}, getContext().getMainExecutor());
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private void setupUseCases(boolean enableVideo) {
|
|
145
|
+
//set up the resolution for the preview and image analysis.
|
|
146
|
+
int orientation = getContext().getResources().getConfiguration().orientation;
|
|
147
|
+
Size resolution;
|
|
148
|
+
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
|
149
|
+
resolution = new Size(desiredHeight, desiredWidth);
|
|
150
|
+
} else {
|
|
151
|
+
resolution = new Size(desiredWidth, desiredHeight);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
Preview.Builder previewBuilder = new Preview.Builder();
|
|
155
|
+
previewBuilder.setTargetResolution(resolution);
|
|
156
|
+
preview = previewBuilder.build();
|
|
157
|
+
preview.setSurfaceProvider(previewView.getSurfaceProvider());
|
|
158
|
+
|
|
159
|
+
ImageAnalysis.Builder imageAnalysisBuilder = new ImageAnalysis.Builder();
|
|
160
|
+
|
|
161
|
+
imageAnalysisBuilder.setTargetResolution(resolution)
|
|
162
|
+
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST);
|
|
163
|
+
|
|
164
|
+
imageAnalysis = imageAnalysisBuilder.build();
|
|
165
|
+
|
|
166
|
+
imageAnalysis.setAnalyzer(exec, new ImageAnalysis.Analyzer() {
|
|
167
|
+
@Override
|
|
168
|
+
public void analyze(@NonNull ImageProxy image) {
|
|
169
|
+
if (takeSnapshotCall != null || saveFrameCall != null) {
|
|
170
|
+
@SuppressLint("UnsafeOptInUsageError")
|
|
171
|
+
Bitmap bitmap = BitmapUtils.getBitmap(image);
|
|
172
|
+
if (scanRegion != null) {
|
|
173
|
+
int left, top, width, height;
|
|
174
|
+
if (scanRegion.measuredByPercentage == 0) {
|
|
175
|
+
left = scanRegion.left;
|
|
176
|
+
top = scanRegion.top;
|
|
177
|
+
width = scanRegion.right - scanRegion.left;
|
|
178
|
+
height = scanRegion.bottom - scanRegion.top;
|
|
179
|
+
} else {
|
|
180
|
+
left = (int) ((double) scanRegion.left / 100 * bitmap.getWidth());
|
|
181
|
+
top = (int) ((double) scanRegion.top / 100 * bitmap.getHeight());
|
|
182
|
+
width = (int) ((double) scanRegion.right / 100 * bitmap.getWidth() - left);
|
|
183
|
+
height = (int) ((double) scanRegion.bottom / 100 * bitmap.getHeight() - top);
|
|
184
|
+
}
|
|
185
|
+
bitmap = Bitmap.createBitmap(bitmap, left, top, width, height, null, false);
|
|
186
|
+
}
|
|
187
|
+
if (takeSnapshotCall != null) {
|
|
188
|
+
int desiredQuality = 85;
|
|
189
|
+
if (takeSnapshotCall.hasOption("quality")) {
|
|
190
|
+
desiredQuality = takeSnapshotCall.getInt("quality");
|
|
191
|
+
}
|
|
192
|
+
String base64 = bitmap2Base64(bitmap, desiredQuality);
|
|
193
|
+
JSObject result = new JSObject();
|
|
194
|
+
result.put("base64", base64);
|
|
195
|
+
takeSnapshotCall.resolve(result);
|
|
196
|
+
takeSnapshotCall = null;
|
|
197
|
+
}
|
|
198
|
+
if (saveFrameCall != null) {
|
|
199
|
+
frameTaken = bitmap;
|
|
200
|
+
JSObject result = new JSObject();
|
|
201
|
+
result.put("success", true);
|
|
202
|
+
saveFrameCall.resolve(result);
|
|
203
|
+
saveFrameCall = null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
image.close();
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
imageCapture =
|
|
211
|
+
new ImageCapture.Builder()
|
|
212
|
+
.setTargetAspectRatio(AspectRatio.RATIO_16_9)
|
|
213
|
+
.build();
|
|
214
|
+
|
|
215
|
+
if (enableVideo) {
|
|
216
|
+
Quality quality = Quality.HD;
|
|
217
|
+
QualitySelector qualitySelector = QualitySelector.from(quality);
|
|
218
|
+
Recorder.Builder recorderBuilder = new Recorder.Builder();
|
|
219
|
+
recorderBuilder.setQualitySelector(qualitySelector);
|
|
220
|
+
recorder = recorderBuilder.build();
|
|
221
|
+
VideoCapture videoCapture = VideoCapture.withOutput(recorder);
|
|
222
|
+
useCaseGroup = new UseCaseGroup.Builder()
|
|
223
|
+
.addUseCase(preview)
|
|
224
|
+
.addUseCase(imageAnalysis)
|
|
225
|
+
.addUseCase(videoCapture)
|
|
226
|
+
.build();
|
|
227
|
+
}else{
|
|
228
|
+
useCaseGroup = new UseCaseGroup.Builder()
|
|
229
|
+
.addUseCase(preview)
|
|
230
|
+
.addUseCase(imageAnalysis)
|
|
231
|
+
.addUseCase(imageCapture)
|
|
232
|
+
.build();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
@PluginMethod
|
|
237
|
+
public void startCamera(PluginCall call) {
|
|
238
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
239
|
+
public void run() {
|
|
240
|
+
try {
|
|
241
|
+
camera = cameraProvider.bindToLifecycle((LifecycleOwner) getContext(), cameraSelector, useCaseGroup);
|
|
242
|
+
previewView.setVisibility(View.VISIBLE);
|
|
243
|
+
makeWebViewTransparent();
|
|
244
|
+
triggerOnPlayed();
|
|
245
|
+
call.resolve();
|
|
246
|
+
} catch (Exception e) {
|
|
247
|
+
e.printStackTrace();
|
|
248
|
+
call.reject(e.getMessage());
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
@PluginMethod
|
|
255
|
+
public void stopCamera(PluginCall call) {
|
|
256
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
257
|
+
public void run() {
|
|
258
|
+
try {
|
|
259
|
+
restoreWebViewBackground();
|
|
260
|
+
previewView.setVisibility(View.INVISIBLE);
|
|
261
|
+
cameraProvider.unbindAll();
|
|
262
|
+
call.resolve();
|
|
263
|
+
} catch (Exception e) {
|
|
264
|
+
call.reject(e.getMessage());
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private void makeWebViewTransparent() {
|
|
271
|
+
bridge.getWebView().setTag(bridge.getWebView().getBackground());
|
|
272
|
+
bridge.getWebView().setBackgroundColor(Color.TRANSPARENT);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private void restoreWebViewBackground() {
|
|
276
|
+
bridge.getWebView().setBackground((Drawable) bridge.getWebView().getTag());
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
@PluginMethod
|
|
280
|
+
public void toggleTorch(PluginCall call) {
|
|
281
|
+
try {
|
|
282
|
+
if (call.getBoolean("on", true)) {
|
|
283
|
+
camera.getCameraControl().enableTorch(true);
|
|
284
|
+
} else {
|
|
285
|
+
camera.getCameraControl().enableTorch(false);
|
|
286
|
+
}
|
|
287
|
+
call.resolve();
|
|
288
|
+
} catch (Exception e) {
|
|
289
|
+
call.reject(e.getMessage());
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
@PluginMethod
|
|
294
|
+
public void setScanRegion(PluginCall call) {
|
|
295
|
+
JSObject region = call.getObject("region");
|
|
296
|
+
try {
|
|
297
|
+
scanRegion = new ScanRegion(region.getInt("top"),
|
|
298
|
+
region.getInt("bottom"),
|
|
299
|
+
region.getInt("left"),
|
|
300
|
+
region.getInt("right"),
|
|
301
|
+
region.getInt("measuredByPercentage"));
|
|
302
|
+
} catch (JSONException e) {
|
|
303
|
+
call.reject(e.getMessage());
|
|
304
|
+
}
|
|
305
|
+
call.resolve();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
@PluginMethod
|
|
309
|
+
public void setZoom(PluginCall call) {
|
|
310
|
+
if (call.hasOption("factor")) {
|
|
311
|
+
Float factor = call.getFloat("factor");
|
|
312
|
+
try {
|
|
313
|
+
camera.getCameraControl().setZoomRatio(factor);
|
|
314
|
+
} catch (Exception e) {
|
|
315
|
+
e.printStackTrace();
|
|
316
|
+
call.reject(e.getMessage());
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
call.resolve();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
@PluginMethod
|
|
323
|
+
public void setFocus(PluginCall call) {
|
|
324
|
+
if (call.hasOption("x") && call.hasOption("y")) {
|
|
325
|
+
Float x = call.getFloat("x");
|
|
326
|
+
Float y = call.getFloat("y");
|
|
327
|
+
try {
|
|
328
|
+
MeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(previewView.getWidth(), previewView.getHeight());
|
|
329
|
+
MeteringPoint point = factory.createPoint(x, y);
|
|
330
|
+
FocusMeteringAction.Builder builder = new FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF);
|
|
331
|
+
// auto calling cancelFocusAndMetering in 5 seconds
|
|
332
|
+
builder.setAutoCancelDuration(5, TimeUnit.SECONDS);
|
|
333
|
+
FocusMeteringAction action = builder.build();
|
|
334
|
+
camera.getCameraControl().startFocusAndMetering(action);
|
|
335
|
+
} catch (Exception e) {
|
|
336
|
+
e.printStackTrace();
|
|
337
|
+
call.reject(e.getMessage());
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
call.resolve();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
@PluginMethod
|
|
344
|
+
public void selectCamera(PluginCall call) {
|
|
345
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
346
|
+
@RequiresApi(api = Build.VERSION_CODES.P)
|
|
347
|
+
public void run() {
|
|
348
|
+
if (call.hasOption("cameraID")) {
|
|
349
|
+
try {
|
|
350
|
+
String cameraID = call.getString("cameraID");
|
|
351
|
+
if (cameraID.equals("Front-Facing Camera")) {
|
|
352
|
+
cameraSelector = new CameraSelector.Builder()
|
|
353
|
+
.requireLensFacing(CameraSelector.LENS_FACING_FRONT).build();
|
|
354
|
+
} else {
|
|
355
|
+
cameraSelector = new CameraSelector.Builder()
|
|
356
|
+
.requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
|
|
357
|
+
}
|
|
358
|
+
if (camera != null) {
|
|
359
|
+
if (camera.getCameraInfo().getCameraState().getValue().getType() == CameraState.Type.OPEN) {
|
|
360
|
+
cameraProvider.unbindAll();
|
|
361
|
+
setupUseCases(false);
|
|
362
|
+
camera = cameraProvider.bindToLifecycle((LifecycleOwner) getContext(), cameraSelector, useCaseGroup);
|
|
363
|
+
triggerOnPlayed();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
} catch (Exception e) {
|
|
367
|
+
e.printStackTrace();
|
|
368
|
+
call.reject(e.getMessage());
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
JSObject result = new JSObject();
|
|
373
|
+
result.put("success", true);
|
|
374
|
+
call.resolve(result);
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
@PluginMethod
|
|
380
|
+
public void setLayout(PluginCall call){
|
|
381
|
+
if (previewView != null) {
|
|
382
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
383
|
+
public void run() {
|
|
384
|
+
if (call.hasOption("width") && call.hasOption("height") && call.hasOption("left") && call.hasOption("top")) {
|
|
385
|
+
try{
|
|
386
|
+
double width = getLayoutValue(call.getString("width"),true);
|
|
387
|
+
double height = getLayoutValue(call.getString("height"),false);
|
|
388
|
+
double left = getLayoutValue(call.getString("left"),true);
|
|
389
|
+
double top = getLayoutValue(call.getString("top"),false);
|
|
390
|
+
previewView.setX((int) left);
|
|
391
|
+
previewView.setY((int) top);
|
|
392
|
+
ViewGroup.LayoutParams cameraPreviewParams = previewView.getLayoutParams();
|
|
393
|
+
cameraPreviewParams.width = (int) width;
|
|
394
|
+
cameraPreviewParams.height = (int) height;
|
|
395
|
+
previewView.setLayoutParams(cameraPreviewParams);
|
|
396
|
+
}catch(Exception e) {
|
|
397
|
+
Log.d("Camera",e.getMessage());
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
call.resolve();
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
}else{
|
|
404
|
+
call.reject("Camera not initialized");
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
private double getLayoutValue(String value,boolean isWidth) {
|
|
408
|
+
if (value.indexOf("%") != -1) {
|
|
409
|
+
double percent = Double.parseDouble(value.substring(0,value.length()-1))/100;
|
|
410
|
+
if (isWidth) {
|
|
411
|
+
return percent * Resources.getSystem().getDisplayMetrics().widthPixels;
|
|
412
|
+
}else{
|
|
413
|
+
return percent * Resources.getSystem().getDisplayMetrics().heightPixels;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (value.indexOf("px") != -1) {
|
|
417
|
+
return Double.parseDouble(value.substring(0,value.length()-2));
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
return Double.parseDouble(value);
|
|
421
|
+
}catch(Exception e) {
|
|
422
|
+
if (isWidth) {
|
|
423
|
+
return Resources.getSystem().getDisplayMetrics().widthPixels;
|
|
424
|
+
}else{
|
|
425
|
+
return Resources.getSystem().getDisplayMetrics().heightPixels;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
private void triggerOnPlayed() {
|
|
430
|
+
try {
|
|
431
|
+
JSObject onPlayedResult = new JSObject();
|
|
432
|
+
@SuppressLint("RestrictedApi")
|
|
433
|
+
String res = imageAnalysis.getAttachedSurfaceResolution().getWidth() + "x" + imageAnalysis.getAttachedSurfaceResolution().getHeight();
|
|
434
|
+
onPlayedResult.put("resolution", res);
|
|
435
|
+
notifyListeners("onPlayed", onPlayedResult);
|
|
436
|
+
} catch (Exception e) {
|
|
437
|
+
e.printStackTrace();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
@SuppressLint("RestrictedApi")
|
|
442
|
+
@PluginMethod
|
|
443
|
+
public void getAllCameras(PluginCall call) {
|
|
444
|
+
JSObject result = new JSObject();
|
|
445
|
+
JSArray cameras = new JSArray();
|
|
446
|
+
cameras.put("Back-Facing Camera");
|
|
447
|
+
cameras.put("Front-Facing Camera");
|
|
448
|
+
result.put("cameras", cameras);
|
|
449
|
+
call.resolve(result);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
@SuppressLint("RestrictedApi")
|
|
453
|
+
@PluginMethod
|
|
454
|
+
public void getSelectedCamera(PluginCall call) {
|
|
455
|
+
if (cameraSelector == null) {
|
|
456
|
+
call.reject("not initialized");
|
|
457
|
+
} else {
|
|
458
|
+
JSObject result = new JSObject();
|
|
459
|
+
String cameraID = "Back-Facing Camera";
|
|
460
|
+
if (cameraSelector.getLensFacing() == CameraSelector.LENS_FACING_FRONT) {
|
|
461
|
+
cameraID = "Front-Facing Camera";
|
|
462
|
+
}
|
|
463
|
+
result.put("selectedCamera", cameraID);
|
|
464
|
+
call.resolve(result);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
@PluginMethod
|
|
469
|
+
public void setResolution(PluginCall call) {
|
|
470
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
471
|
+
@RequiresApi(api = Build.VERSION_CODES.P)
|
|
472
|
+
public void run() {
|
|
473
|
+
if (call.hasOption("resolution")) {
|
|
474
|
+
try {
|
|
475
|
+
int res = call.getInt("resolution");
|
|
476
|
+
int width = 1280;
|
|
477
|
+
int height = 720;
|
|
478
|
+
if (res == 1) {
|
|
479
|
+
width = 640;
|
|
480
|
+
height = 480;
|
|
481
|
+
} else if (res == 2) {
|
|
482
|
+
width = 1280;
|
|
483
|
+
height = 720;
|
|
484
|
+
} else if (res == 3) {
|
|
485
|
+
width = 1920;
|
|
486
|
+
height = 1080;
|
|
487
|
+
} else if (res == 4) {
|
|
488
|
+
width = 2560;
|
|
489
|
+
height = 1440;
|
|
490
|
+
} else if (res == 5) {
|
|
491
|
+
width = 3840;
|
|
492
|
+
height = 2160;
|
|
493
|
+
}
|
|
494
|
+
desiredHeight = height;
|
|
495
|
+
desiredWidth = width;
|
|
496
|
+
CameraState.Type status = null;
|
|
497
|
+
if (camera != null) {
|
|
498
|
+
status = camera.getCameraInfo().getCameraState().getValue().getType();
|
|
499
|
+
if (status == CameraState.Type.OPEN) {
|
|
500
|
+
cameraProvider.unbindAll();
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
setupUseCases(false);
|
|
504
|
+
if (camera != null) {
|
|
505
|
+
if (status == CameraState.Type.OPEN) {
|
|
506
|
+
camera = cameraProvider.bindToLifecycle((LifecycleOwner) getContext(), cameraSelector, useCaseGroup);
|
|
507
|
+
triggerOnPlayed();
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
} catch (Exception e) {
|
|
511
|
+
e.printStackTrace();
|
|
512
|
+
call.reject(e.getMessage());
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
JSObject result = new JSObject();
|
|
517
|
+
result.put("success", true);
|
|
518
|
+
call.resolve(result);
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
@SuppressLint("RestrictedApi")
|
|
524
|
+
@PluginMethod
|
|
525
|
+
public void getResolution(PluginCall call) {
|
|
526
|
+
if (camera == null) {
|
|
527
|
+
call.reject("Camera not initialized");
|
|
528
|
+
} else {
|
|
529
|
+
try {
|
|
530
|
+
JSObject result = new JSObject();
|
|
531
|
+
result.put("resolution", imageAnalysis.getAttachedSurfaceResolution().getWidth() + "x" + imageAnalysis.getAttachedSurfaceResolution().getHeight());
|
|
532
|
+
call.resolve(result);
|
|
533
|
+
} catch (Exception e) {
|
|
534
|
+
call.reject(e.getMessage());
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
static public Bitmap getBitmap() {
|
|
541
|
+
try {
|
|
542
|
+
return frameTaken;
|
|
543
|
+
} catch (Exception e) {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
@PluginMethod
|
|
549
|
+
public void takeSnapshot(PluginCall call) {
|
|
550
|
+
call.setKeepAlive(true);
|
|
551
|
+
takeSnapshotCall = call;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
@PluginMethod
|
|
555
|
+
public void saveFrame(PluginCall call) {
|
|
556
|
+
call.setKeepAlive(true);
|
|
557
|
+
saveFrameCall = call;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
@PluginMethod
|
|
561
|
+
public void takePhoto(PluginCall call) {
|
|
562
|
+
if (camera == null) {
|
|
563
|
+
call.reject("Camera not initialized.");
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
567
|
+
@RequiresApi(api = Build.VERSION_CODES.P)
|
|
568
|
+
public void run() {
|
|
569
|
+
if (useCaseGroup.getUseCases().contains(imageCapture) == false) {
|
|
570
|
+
cameraProvider.unbindAll();
|
|
571
|
+
setupUseCases(false);
|
|
572
|
+
camera = cameraProvider.bindToLifecycle((LifecycleOwner) getContext(), cameraSelector, useCaseGroup);
|
|
573
|
+
}
|
|
574
|
+
File file;
|
|
575
|
+
if (call.hasOption("pathToSave")) {
|
|
576
|
+
file = new File(call.getString("pathToSave"));
|
|
577
|
+
} else {
|
|
578
|
+
File dir = getContext().getExternalCacheDir();
|
|
579
|
+
file = new File(dir, new Date().getTime() + ".jpg");
|
|
580
|
+
}
|
|
581
|
+
ImageCapture.OutputFileOptions outputFileOptions =
|
|
582
|
+
new ImageCapture.OutputFileOptions.Builder(file).build();
|
|
583
|
+
imageCapture.takePicture(outputFileOptions, exec,
|
|
584
|
+
new ImageCapture.OnImageSavedCallback() {
|
|
585
|
+
@Override
|
|
586
|
+
public void onImageSaved(ImageCapture.OutputFileResults outputFileResults) {
|
|
587
|
+
JSObject result = new JSObject();
|
|
588
|
+
if (call.getBoolean("includeBase64", false)) {
|
|
589
|
+
String base64 = Base64.encodeToString(convertFileToByteArray(file), Base64.DEFAULT);
|
|
590
|
+
result.put("base64", base64);
|
|
591
|
+
}
|
|
592
|
+
result.put("path", file.getAbsolutePath());
|
|
593
|
+
call.resolve(result);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
@Override
|
|
597
|
+
public void onError(@NonNull ImageCaptureException exception) {
|
|
598
|
+
call.reject(exception.getMessage());
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
@RequiresApi(api = Build.VERSION_CODES.P)
|
|
607
|
+
@PluginMethod
|
|
608
|
+
public void startRecording(PluginCall call) {
|
|
609
|
+
if (camera == null) {
|
|
610
|
+
call.reject("Camera not initialized.");
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
614
|
+
@RequiresApi(api = Build.VERSION_CODES.P)
|
|
615
|
+
public void run() {
|
|
616
|
+
cameraProvider.unbindAll();
|
|
617
|
+
setupUseCases(true);
|
|
618
|
+
camera = cameraProvider.bindToLifecycle((LifecycleOwner) getContext(), cameraSelector, useCaseGroup);
|
|
619
|
+
if (recorder != null) {
|
|
620
|
+
// create MediaStoreOutputOptions for our recorder: resulting our recording!
|
|
621
|
+
String name = "CameraX-recording-" + System.currentTimeMillis() + ".mp4";
|
|
622
|
+
ContentValues contentValues = new ContentValues();
|
|
623
|
+
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
|
|
624
|
+
|
|
625
|
+
MediaStoreOutputOptions mediaStoreOutput = new MediaStoreOutputOptions.Builder(
|
|
626
|
+
getContext().getContentResolver(),
|
|
627
|
+
MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
|
|
628
|
+
.setContentValues(contentValues)
|
|
629
|
+
.build();
|
|
630
|
+
|
|
631
|
+
// configure Recorder and Start recording to the mediaStoreOutput.
|
|
632
|
+
|
|
633
|
+
PendingRecording pendingRecording = recorder.prepareRecording(getContext(), mediaStoreOutput);
|
|
634
|
+
|
|
635
|
+
if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
|
|
636
|
+
// TODO: Consider calling
|
|
637
|
+
// ActivityCompat#requestPermissions
|
|
638
|
+
// here to request the missing permissions, and then overriding
|
|
639
|
+
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
640
|
+
// int[] grantResults)
|
|
641
|
+
// to handle the case where the user grants the permission. See the documentation
|
|
642
|
+
// for ActivityCompat#requestPermissions for more details.
|
|
643
|
+
}else{
|
|
644
|
+
pendingRecording.withAudioEnabled();
|
|
645
|
+
}
|
|
646
|
+
Consumer<VideoRecordEvent> captureListener = new Consumer<VideoRecordEvent>() {
|
|
647
|
+
@Override
|
|
648
|
+
public void accept(VideoRecordEvent videoRecordEvent) {
|
|
649
|
+
Log.d("Camera",videoRecordEvent.toString());
|
|
650
|
+
if (videoRecordEvent instanceof VideoRecordEvent.Finalize) {
|
|
651
|
+
Log.d("Camera","finalize");
|
|
652
|
+
Uri uri = ((VideoRecordEvent.Finalize) videoRecordEvent).getOutputResults().getOutputUri();
|
|
653
|
+
String path = uri.getPath();
|
|
654
|
+
|
|
655
|
+
if (stopRecordingCall != null) {
|
|
656
|
+
JSObject result = new JSObject();
|
|
657
|
+
if (stopRecordingCall.getBoolean("includeBase64",false)) {
|
|
658
|
+
try {
|
|
659
|
+
InputStream iStream = getContext().getContentResolver().openInputStream(uri);
|
|
660
|
+
byte[] inputData = getBytes(iStream);
|
|
661
|
+
String base64 = Base64.encodeToString(inputData, Base64.DEFAULT);
|
|
662
|
+
result.put("base64",base64);
|
|
663
|
+
} catch (IOException e) {
|
|
664
|
+
throw new RuntimeException(e);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
result.put("path",path);
|
|
668
|
+
recorder = null;
|
|
669
|
+
stopRecordingCall.resolve(result);
|
|
670
|
+
stopRecordingCall = null;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
currentRecording = pendingRecording.start(getContext().getMainExecutor(),captureListener);
|
|
676
|
+
call.resolve();
|
|
677
|
+
}else{
|
|
678
|
+
call.reject("Recording is not ready");
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
private byte[] getBytes(InputStream inputStream) throws IOException {
|
|
685
|
+
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
|
|
686
|
+
int bufferSize = 1024;
|
|
687
|
+
byte[] buffer = new byte[bufferSize];
|
|
688
|
+
|
|
689
|
+
int len = 0;
|
|
690
|
+
while ((len = inputStream.read(buffer)) != -1) {
|
|
691
|
+
byteBuffer.write(buffer, 0, len);
|
|
692
|
+
}
|
|
693
|
+
return byteBuffer.toByteArray();
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
@PluginMethod
|
|
697
|
+
public void stopRecording(PluginCall call){
|
|
698
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
699
|
+
|
|
700
|
+
@Override
|
|
701
|
+
public void run() {
|
|
702
|
+
call.setKeepAlive(true);
|
|
703
|
+
stopRecordingCall = call;
|
|
704
|
+
currentRecording.stop();
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
public static byte[] convertFileToByteArray(File f) {
|
|
710
|
+
byte[] byteArray = null;
|
|
711
|
+
try {
|
|
712
|
+
InputStream inputStream = new FileInputStream(f);
|
|
713
|
+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
714
|
+
byte[] b = new byte[1024 * 8];
|
|
715
|
+
int bytesRead = 0;
|
|
716
|
+
while ((bytesRead = inputStream.read(b)) != -1) {
|
|
717
|
+
bos.write(b, 0, bytesRead);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
byteArray = bos.toByteArray();
|
|
721
|
+
} catch (IOException e) {
|
|
722
|
+
e.printStackTrace();
|
|
723
|
+
}
|
|
724
|
+
return byteArray;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
public static String bitmap2Base64(Bitmap bitmap,int quality) {
|
|
728
|
+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
729
|
+
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
|
|
730
|
+
return Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
@PluginMethod
|
|
734
|
+
public void isOpen(PluginCall call){
|
|
735
|
+
if (camera != null) {
|
|
736
|
+
JSObject result = new JSObject();
|
|
737
|
+
if (camera.getCameraInfo().getCameraState().getValue().getType() == CameraState.Type.OPEN) {
|
|
738
|
+
result.put("isOpen",true);
|
|
739
|
+
}else{
|
|
740
|
+
result.put("isOpen",false);
|
|
741
|
+
}
|
|
742
|
+
call.resolve(result);
|
|
743
|
+
}else {
|
|
744
|
+
call.reject("Camera not initialized.");
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
@Override
|
|
749
|
+
protected void handleOnPause() {
|
|
750
|
+
if (camera != null) {
|
|
751
|
+
CameraState cameraStatus = camera.getCameraInfo().getCameraState().getValue();
|
|
752
|
+
previousCameraStatus = cameraStatus;
|
|
753
|
+
if (cameraStatus.getType() == CameraState.Type.OPEN) {
|
|
754
|
+
cameraProvider.unbindAll();
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
super.handleOnPause();
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
@Override
|
|
761
|
+
protected void handleOnResume() {
|
|
762
|
+
if (camera != null) {
|
|
763
|
+
if (previousCameraStatus.getType() == CameraState.Type.OPEN) {
|
|
764
|
+
camera = cameraProvider.bindToLifecycle((LifecycleOwner) getContext(), cameraSelector, useCaseGroup);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
super.handleOnResume();
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
@Override
|
|
771
|
+
protected void handleOnConfigurationChanged(Configuration newConfig) {
|
|
772
|
+
notifyListeners("onOrientationChanged",null);
|
|
773
|
+
super.handleOnConfigurationChanged(newConfig);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
@PluginMethod
|
|
779
|
+
public void requestCameraPermission(PluginCall call) {
|
|
780
|
+
boolean hasCameraPerms = getPermissionState(CAMERA) == PermissionState.GRANTED;
|
|
781
|
+
if (hasCameraPerms == false) {
|
|
782
|
+
Log.d("Camera","no camera permission. request permission.");
|
|
783
|
+
String[] aliases = new String[] { CAMERA };
|
|
784
|
+
requestPermissionForAliases(aliases, call, "cameraPermissionsCallback");
|
|
785
|
+
}else{
|
|
786
|
+
call.resolve();
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
@PermissionCallback
|
|
791
|
+
private void cameraPermissionsCallback(PluginCall call) {
|
|
792
|
+
boolean hasCameraPerms = getPermissionState(CAMERA) == PermissionState.GRANTED;
|
|
793
|
+
if (hasCameraPerms) {
|
|
794
|
+
call.resolve();
|
|
795
|
+
}else {
|
|
796
|
+
call.reject("Permission not granted.");
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
@PluginMethod
|
|
801
|
+
public void requestMicroPhonePermission(PluginCall call) {
|
|
802
|
+
boolean hasCameraPerms = getPermissionState(MICROPHONE) == PermissionState.GRANTED;
|
|
803
|
+
if (hasCameraPerms == false) {
|
|
804
|
+
Log.d("Camera","no microphone permission. request permission.");
|
|
805
|
+
String[] aliases = new String[] { MICROPHONE };
|
|
806
|
+
requestPermissionForAliases(aliases, call, "microphonePermissionsCallback");
|
|
807
|
+
}else{
|
|
808
|
+
call.resolve();
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
@PermissionCallback
|
|
813
|
+
private void microphonePermissionsCallback(PluginCall call) {
|
|
814
|
+
boolean hasPerms = getPermissionState(MICROPHONE) == PermissionState.GRANTED;
|
|
815
|
+
if (hasPerms) {
|
|
816
|
+
call.resolve();
|
|
817
|
+
}else {
|
|
818
|
+
call.reject("Permission not granted.");
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
@PluginMethod
|
|
823
|
+
public void getOrientation(PluginCall call) {
|
|
824
|
+
int orientation = getContext().getResources().getConfiguration().orientation;
|
|
825
|
+
JSObject result = new JSObject();
|
|
826
|
+
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
|
827
|
+
result.put("orientation","PORTRAIT");
|
|
828
|
+
}else{
|
|
829
|
+
result.put("orientation","LANDSCAPE");
|
|
830
|
+
}
|
|
831
|
+
call.resolve(result);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
}
|