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.
@@ -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
+ }