@capgo/camera-preview 7.3.12 → 7.4.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CapgoCameraPreview.podspec +16 -13
  2. package/README.md +467 -73
  3. package/android/.gradle/8.14.2/checksums/checksums.lock +0 -0
  4. package/android/.gradle/8.14.2/checksums/md5-checksums.bin +0 -0
  5. package/android/.gradle/8.14.2/checksums/sha1-checksums.bin +0 -0
  6. package/android/.gradle/8.14.2/executionHistory/executionHistory.bin +0 -0
  7. package/android/.gradle/8.14.2/executionHistory/executionHistory.lock +0 -0
  8. package/android/.gradle/8.14.2/fileChanges/last-build.bin +0 -0
  9. package/android/.gradle/8.14.2/fileHashes/fileHashes.bin +0 -0
  10. package/android/.gradle/8.14.2/fileHashes/fileHashes.lock +0 -0
  11. package/android/.gradle/8.14.2/fileHashes/resourceHashesCache.bin +0 -0
  12. package/android/.gradle/8.14.2/gc.properties +0 -0
  13. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  14. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  15. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  16. package/android/.gradle/file-system.probe +0 -0
  17. package/android/.gradle/vcs-1/gc.properties +0 -0
  18. package/android/build.gradle +11 -0
  19. package/android/gradle/wrapper/gradle-wrapper.properties +1 -1
  20. package/android/src/main/AndroidManifest.xml +5 -3
  21. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +472 -541
  22. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +1648 -0
  23. package/android/src/main/java/com/ahm/capacitor/camera/preview/GridOverlayView.java +82 -0
  24. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraDevice.java +54 -0
  25. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraLens.java +70 -0
  26. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraSessionConfiguration.java +79 -0
  27. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/LensInfo.java +34 -0
  28. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/ZoomFactors.java +34 -0
  29. package/dist/docs.json +934 -154
  30. package/dist/esm/definitions.d.ts +445 -83
  31. package/dist/esm/definitions.js +10 -1
  32. package/dist/esm/definitions.js.map +1 -1
  33. package/dist/esm/web.d.ts +73 -3
  34. package/dist/esm/web.js +492 -68
  35. package/dist/esm/web.js.map +1 -1
  36. package/dist/plugin.cjs.js +498 -68
  37. package/dist/plugin.cjs.js.map +1 -1
  38. package/dist/plugin.js +498 -68
  39. package/dist/plugin.js.map +1 -1
  40. package/ios/{Plugin → Sources/CapgoCameraPreview}/CameraController.swift +601 -59
  41. package/ios/Sources/CapgoCameraPreview/GridOverlayView.swift +65 -0
  42. package/ios/Sources/CapgoCameraPreview/Plugin.swift +1369 -0
  43. package/ios/Tests/CameraPreviewPluginTests/CameraPreviewPluginTests.swift +15 -0
  44. package/package.json +1 -1
  45. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraActivity.java +0 -1279
  46. package/android/src/main/java/com/ahm/capacitor/camera/preview/CustomSurfaceView.java +0 -29
  47. package/android/src/main/java/com/ahm/capacitor/camera/preview/CustomTextureView.java +0 -39
  48. package/android/src/main/java/com/ahm/capacitor/camera/preview/Preview.java +0 -461
  49. package/android/src/main/java/com/ahm/capacitor/camera/preview/TapGestureDetector.java +0 -24
  50. package/ios/Plugin/Info.plist +0 -24
  51. package/ios/Plugin/Plugin.h +0 -10
  52. package/ios/Plugin/Plugin.m +0 -18
  53. package/ios/Plugin/Plugin.swift +0 -511
  54. package/ios/Plugin.xcodeproj/project.pbxproj +0 -593
  55. package/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  56. package/ios/Plugin.xcworkspace/contents.xcworkspacedata +0 -10
  57. package/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  58. package/ios/PluginTests/Info.plist +0 -22
  59. package/ios/PluginTests/PluginTests.swift +0 -83
  60. package/ios/Podfile +0 -13
  61. package/ios/Podfile.lock +0 -23
@@ -1,1279 +0,0 @@
1
- package com.ahm.capacitor.camera.preview;
2
-
3
- import android.app.Activity;
4
- import android.app.Fragment;
5
- import android.content.Context;
6
- import android.content.pm.PackageManager;
7
- import android.content.res.Configuration;
8
- import android.graphics.Bitmap;
9
- import android.graphics.Bitmap.CompressFormat;
10
- import android.graphics.BitmapFactory;
11
- import android.graphics.Matrix;
12
- import android.graphics.Rect;
13
- import android.graphics.YuvImage;
14
- import android.hardware.Camera;
15
- import android.hardware.Camera.PictureCallback;
16
- import android.hardware.Camera.ShutterCallback;
17
- import android.media.AudioManager;
18
- import android.media.CamcorderProfile;
19
- import android.media.MediaRecorder;
20
- import android.os.Bundle;
21
- import android.util.Base64;
22
- import android.util.DisplayMetrics;
23
- import android.util.Log;
24
- import android.view.GestureDetector;
25
- import android.view.Gravity;
26
- import android.view.LayoutInflater;
27
- import android.view.MotionEvent;
28
- import android.view.Surface;
29
- import android.view.View;
30
- import android.view.ViewGroup;
31
- import android.view.ViewTreeObserver;
32
- import android.widget.FrameLayout;
33
- import android.widget.RelativeLayout;
34
- import androidx.exifinterface.media.ExifInterface;
35
- import java.io.ByteArrayInputStream;
36
- import java.io.ByteArrayOutputStream;
37
- import java.io.File;
38
- import java.io.FileOutputStream;
39
- import java.io.IOException;
40
- import java.util.Arrays;
41
- import java.util.Collections;
42
- import java.util.List;
43
- import java.util.Objects;
44
- import java.util.UUID;
45
-
46
- public class CameraActivity extends Fragment {
47
-
48
- public interface CameraPreviewListener {
49
- void onPictureTaken(String originalPicture);
50
- void onPictureTakenError(String message);
51
- void onSnapshotTaken(String originalPicture);
52
- void onSnapshotTakenError(String message);
53
- void onFocusSet(int pointX, int pointY);
54
- void onFocusSetError(String message);
55
- void onBackButton();
56
- void onCameraStarted();
57
- void onStartRecordVideo();
58
- void onStartRecordVideoError(String message);
59
- void onStopRecordVideo(String file);
60
- void onStopRecordVideoError(String error);
61
- }
62
-
63
- private CameraPreviewListener eventListener;
64
- private static final String TAG = "CameraActivity";
65
- public FrameLayout mainLayout;
66
- public FrameLayout frameContainerLayout;
67
-
68
- private Preview mPreview;
69
- private boolean canTakePicture = true;
70
-
71
- private View view;
72
- private Camera.Parameters cameraParameters;
73
- private Camera mCamera;
74
- private int numberOfCameras;
75
- private int cameraCurrentlyLocked;
76
- private int currentQuality;
77
-
78
- private enum RecordingState {
79
- INITIALIZING,
80
- STARTED,
81
- STOPPED,
82
- }
83
-
84
- private final RecordingState mRecordingState = RecordingState.INITIALIZING;
85
- private MediaRecorder mRecorder = null;
86
- private String recordFilePath;
87
-
88
- // The first rear facing camera
89
- private int defaultCameraId;
90
- public String defaultCamera;
91
- public boolean tapToTakePicture;
92
- public boolean dragEnabled;
93
- public boolean tapToFocus;
94
- public boolean disableExifHeaderStripping;
95
- public boolean storeToFile;
96
- public boolean toBack;
97
- public boolean enableOpacity = false;
98
- public boolean enableZoom = false;
99
- public boolean disableAudio = false;
100
-
101
- public int width;
102
- public int height;
103
- public int x;
104
- public int y;
105
-
106
- public void setEventListener(CameraPreviewListener listener) {
107
- eventListener = listener;
108
- }
109
-
110
- private String appResourcesPackage;
111
-
112
- @Override
113
- public View onCreateView(
114
- LayoutInflater inflater,
115
- ViewGroup container,
116
- Bundle savedInstanceState
117
- ) {
118
- appResourcesPackage = getActivity().getPackageName();
119
-
120
- // Inflate the layout for this fragment
121
- view = inflater.inflate(
122
- getResources()
123
- .getIdentifier("camera_activity", "layout", appResourcesPackage),
124
- container,
125
- false
126
- );
127
- createCameraPreview();
128
- return view;
129
- }
130
-
131
- public void setRect(int x, int y, int width, int height) {
132
- this.x = x;
133
- this.y = y;
134
- this.width = width;
135
- this.height = height;
136
- }
137
-
138
- private void createCameraPreview() {
139
- if (mPreview == null) {
140
- setDefaultCameraId();
141
-
142
- //set box position and size
143
- FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
144
- width,
145
- height
146
- );
147
- layoutParams.setMargins(x, y, 0, 0);
148
- frameContainerLayout = (FrameLayout) view.findViewById(
149
- getResources()
150
- .getIdentifier("frame_container", "id", appResourcesPackage)
151
- );
152
- frameContainerLayout.setLayoutParams(layoutParams);
153
-
154
- //video view
155
- mPreview = new Preview(getActivity(), enableOpacity);
156
- mainLayout = (FrameLayout) view.findViewById(
157
- getResources().getIdentifier("video_view", "id", appResourcesPackage)
158
- );
159
- mainLayout.setLayoutParams(
160
- new RelativeLayout.LayoutParams(
161
- RelativeLayout.LayoutParams.MATCH_PARENT,
162
- RelativeLayout.LayoutParams.MATCH_PARENT
163
- )
164
- );
165
- mainLayout.addView(mPreview);
166
- mainLayout.setEnabled(false);
167
-
168
- if (enableZoom) {
169
- this.setupTouchAndBackButton();
170
- }
171
- }
172
- }
173
-
174
- private void setupTouchAndBackButton() {
175
- final GestureDetector gestureDetector = new GestureDetector(
176
- getActivity().getApplicationContext(),
177
- new TapGestureDetector()
178
- );
179
-
180
- getActivity()
181
- .runOnUiThread(
182
- new Runnable() {
183
- @Override
184
- public void run() {
185
- frameContainerLayout.setClickable(true);
186
- frameContainerLayout.setOnTouchListener(
187
- new View.OnTouchListener() {
188
- private int mLastTouchX;
189
- private int mLastTouchY;
190
- private int mPosX = 0;
191
- private int mPosY = 0;
192
-
193
- @Override
194
- public boolean onTouch(View v, MotionEvent event) {
195
- FrameLayout.LayoutParams layoutParams =
196
- (FrameLayout.LayoutParams) frameContainerLayout.getLayoutParams();
197
-
198
- boolean isSingleTapTouch = gestureDetector.onTouchEvent(
199
- event
200
- );
201
- int action = event.getAction();
202
- int eventCount = event.getPointerCount();
203
- Log.d(
204
- TAG,
205
- "onTouch event, action, count: " +
206
- event +
207
- ", " +
208
- action +
209
- ", " +
210
- eventCount
211
- );
212
- if (eventCount > 1) {
213
- // handle multi-touch events
214
- Camera.Parameters params = mCamera.getParameters();
215
- if (action == MotionEvent.ACTION_POINTER_DOWN) {
216
- mDist = getFingerSpacing(event);
217
- } else if (
218
- action == MotionEvent.ACTION_MOVE &&
219
- params.isZoomSupported()
220
- ) {
221
- handleZoom(event, params);
222
- }
223
- } else {
224
- if (action != MotionEvent.ACTION_MOVE && isSingleTapTouch) {
225
- if (tapToTakePicture && tapToFocus) {
226
- setFocusArea(
227
- (int) event.getX(0),
228
- (int) event.getY(0),
229
- new Camera.AutoFocusCallback() {
230
- public void onAutoFocus(
231
- boolean success,
232
- Camera camera
233
- ) {
234
- if (success) {
235
- takePicture(0, 0, 85);
236
- } else {
237
- Log.d(
238
- TAG,
239
- "onTouch:" + " setFocusArea() did not suceed"
240
- );
241
- }
242
- }
243
- }
244
- );
245
- } else if (tapToTakePicture) {
246
- takePicture(0, 0, 85);
247
- } else if (tapToFocus) {
248
- setFocusArea(
249
- (int) event.getX(0),
250
- (int) event.getY(0),
251
- new Camera.AutoFocusCallback() {
252
- public void onAutoFocus(
253
- boolean success,
254
- Camera camera
255
- ) {
256
- if (success) {
257
- // A callback to JS might make sense here.
258
- } else {
259
- Log.d(
260
- TAG,
261
- "onTouch:" + " setFocusArea() did not suceed"
262
- );
263
- }
264
- }
265
- }
266
- );
267
- }
268
- return true;
269
- } else {
270
- if (dragEnabled) {
271
- int x;
272
- int y;
273
-
274
- switch (event.getAction()) {
275
- case MotionEvent.ACTION_DOWN:
276
- if (mLastTouchX == 0 || mLastTouchY == 0) {
277
- mLastTouchX =
278
- (int) event.getRawX() - layoutParams.leftMargin;
279
- mLastTouchY =
280
- (int) event.getRawY() - layoutParams.topMargin;
281
- } else {
282
- mLastTouchX = (int) event.getRawX();
283
- mLastTouchY = (int) event.getRawY();
284
- }
285
- break;
286
- case MotionEvent.ACTION_MOVE:
287
- x = (int) event.getRawX();
288
- y = (int) event.getRawY();
289
-
290
- final float dx = x - mLastTouchX;
291
- final float dy = y - mLastTouchY;
292
-
293
- mPosX += dx;
294
- mPosY += dy;
295
-
296
- layoutParams.leftMargin = mPosX;
297
- layoutParams.topMargin = mPosY;
298
-
299
- frameContainerLayout.setLayoutParams(layoutParams);
300
-
301
- // Remember this touch position for the next move event
302
- mLastTouchX = x;
303
- mLastTouchY = y;
304
-
305
- break;
306
- default:
307
- break;
308
- }
309
- }
310
- }
311
- }
312
- return true;
313
- }
314
- }
315
- );
316
- frameContainerLayout.setFocusableInTouchMode(true);
317
- frameContainerLayout.requestFocus();
318
- frameContainerLayout.setOnKeyListener(
319
- new View.OnKeyListener() {
320
- @Override
321
- public boolean onKey(
322
- View v,
323
- int keyCode,
324
- android.view.KeyEvent event
325
- ) {
326
- if (keyCode == android.view.KeyEvent.KEYCODE_BACK) {
327
- eventListener.onBackButton();
328
- return true;
329
- }
330
- return false;
331
- }
332
- }
333
- );
334
- }
335
-
336
- private float mDist = 0F;
337
-
338
- private void handleZoom(MotionEvent event, Camera.Parameters params) {
339
- if (mCamera != null) {
340
- mCamera.cancelAutoFocus();
341
- int maxZoom = params.getMaxZoom();
342
- int zoom = params.getZoom();
343
- float newDist = getFingerSpacing(event);
344
- if (newDist > mDist) {
345
- //zoom in
346
- if (zoom < maxZoom) zoom++;
347
- } else if (newDist < mDist) {
348
- //zoom out
349
- if (zoom > 0) zoom--;
350
- }
351
- mDist = newDist;
352
- params.setZoom(zoom);
353
- mCamera.setParameters(params);
354
- }
355
- }
356
- }
357
- );
358
- }
359
-
360
- private int getNumberOfCameras() {
361
- if (numberOfCameras == 0) {
362
- numberOfCameras = Camera.getNumberOfCameras();
363
- }
364
-
365
- return numberOfCameras;
366
- }
367
-
368
- private void setDefaultCameraId() {
369
- int facing = "front".equals(defaultCamera)
370
- ? Camera.CameraInfo.CAMERA_FACING_FRONT
371
- : Camera.CameraInfo.CAMERA_FACING_BACK;
372
-
373
- // Find the ID of the default camera
374
- Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
375
- for (int i = 0; i < getNumberOfCameras(); i++) {
376
- Camera.getCameraInfo(i, cameraInfo);
377
- if (cameraInfo.facing == facing) {
378
- defaultCameraId = i;
379
- break;
380
- }
381
- }
382
- }
383
-
384
- @Override
385
- public void onResume() {
386
- // This gets called when getting started AND after the app gets resumed.
387
- super.onResume();
388
-
389
- // Make sure that we load the currently "locked" camera. If the camera gets changed
390
- // during use, we want to "restore" that camera back as soon as we come back to the app.
391
- mCamera = Camera.open(cameraCurrentlyLocked);
392
-
393
- if (cameraCurrentlyLocked == 0 && cameraParameters != null) {
394
- mCamera.setParameters(cameraParameters);
395
- }
396
-
397
- if (mPreview.mPreviewSize == null) {
398
- mPreview.setCamera(mCamera, cameraCurrentlyLocked);
399
- eventListener.onCameraStarted();
400
- } else {
401
- mPreview.switchCamera(mCamera, cameraCurrentlyLocked);
402
- mCamera.startPreview();
403
- }
404
-
405
- Log.d(TAG, "cameraCurrentlyLocked:" + cameraCurrentlyLocked);
406
-
407
- final FrameLayout frameContainerLayout = (FrameLayout) view.findViewById(
408
- getResources().getIdentifier("frame_container", "id", appResourcesPackage)
409
- );
410
-
411
- ViewTreeObserver viewTreeObserver =
412
- frameContainerLayout.getViewTreeObserver();
413
-
414
- if (viewTreeObserver.isAlive()) {
415
- viewTreeObserver.addOnGlobalLayoutListener(
416
- new ViewTreeObserver.OnGlobalLayoutListener() {
417
- @Override
418
- public void onGlobalLayout() {
419
- frameContainerLayout
420
- .getViewTreeObserver()
421
- .removeGlobalOnLayoutListener(this);
422
- frameContainerLayout.measure(
423
- View.MeasureSpec.UNSPECIFIED,
424
- View.MeasureSpec.UNSPECIFIED
425
- );
426
- Activity activity = getActivity();
427
- if (isAdded() && activity != null) {
428
- final RelativeLayout frameCamContainerLayout =
429
- (RelativeLayout) view.findViewById(
430
- getResources()
431
- .getIdentifier(
432
- "frame_camera_cont",
433
- "id",
434
- appResourcesPackage
435
- )
436
- );
437
-
438
- FrameLayout.LayoutParams camViewLayout =
439
- new FrameLayout.LayoutParams(
440
- frameContainerLayout.getWidth(),
441
- frameContainerLayout.getHeight()
442
- );
443
- camViewLayout.gravity =
444
- Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL;
445
- frameCamContainerLayout.setLayoutParams(camViewLayout);
446
- }
447
- }
448
- }
449
- );
450
- }
451
- }
452
-
453
- @Override
454
- public void onPause() {
455
- super.onPause();
456
-
457
- // Because the Camera object is a shared resource, it's very important to release it when the activity is paused.
458
- if (mCamera != null) {
459
- setDefaultCameraId();
460
- mPreview.setCamera(null, -1);
461
- mCamera.release();
462
- mCamera = null;
463
- }
464
- }
465
-
466
- @Override
467
- public void onConfigurationChanged(Configuration newConfig) {
468
- super.onConfigurationChanged(newConfig);
469
-
470
- final FrameLayout frameContainerLayout = (FrameLayout) view.findViewById(
471
- getResources().getIdentifier("frame_container", "id", appResourcesPackage)
472
- );
473
-
474
- final int previousOrientation = frameContainerLayout.getHeight() >
475
- frameContainerLayout.getWidth()
476
- ? Configuration.ORIENTATION_PORTRAIT
477
- : Configuration.ORIENTATION_LANDSCAPE;
478
- // Checks if the orientation of the screen has changed
479
- if (newConfig.orientation != previousOrientation) {
480
- final RelativeLayout frameCamContainerLayout =
481
- (RelativeLayout) view.findViewById(
482
- getResources()
483
- .getIdentifier("frame_camera_cont", "id", appResourcesPackage)
484
- );
485
-
486
- frameContainerLayout.getLayoutParams().width =
487
- frameCamContainerLayout.getHeight();
488
- frameContainerLayout.getLayoutParams().height =
489
- frameCamContainerLayout.getWidth();
490
-
491
- frameCamContainerLayout.getLayoutParams().width =
492
- frameCamContainerLayout.getHeight();
493
- frameCamContainerLayout.getLayoutParams().height =
494
- frameCamContainerLayout.getWidth();
495
-
496
- frameContainerLayout.invalidate();
497
- frameContainerLayout.requestLayout();
498
-
499
- frameCamContainerLayout.forceLayout();
500
-
501
- mPreview.setCameraDisplayOrientation();
502
- }
503
- }
504
-
505
- public Camera getCamera() {
506
- return mCamera;
507
- }
508
-
509
- public void switchCamera() {
510
- // check for availability of multiple cameras
511
- if (numberOfCameras == 1) {
512
- //There is only one camera available
513
- } else {
514
- Log.d(TAG, "numberOfCameras: " + getNumberOfCameras());
515
-
516
- // OK, we have multiple cameras. Release this camera -> cameraCurrentlyLocked
517
- if (mCamera != null) {
518
- mCamera.stopPreview();
519
- mPreview.setCamera(null, -1);
520
- mCamera.release();
521
- mCamera = null;
522
- }
523
-
524
- Log.d(
525
- TAG,
526
- "cameraCurrentlyLocked := " + Integer.toString(cameraCurrentlyLocked)
527
- );
528
- try {
529
- cameraCurrentlyLocked =
530
- (cameraCurrentlyLocked + 1) % getNumberOfCameras();
531
- Log.d(TAG, "cameraCurrentlyLocked new: " + cameraCurrentlyLocked);
532
- } catch (Exception exception) {
533
- Log.d(TAG, Objects.requireNonNull(exception.getMessage()));
534
- }
535
-
536
- // Acquire the next camera and request Preview to reconfigure parameters.
537
- mCamera = Camera.open(cameraCurrentlyLocked);
538
-
539
- if (cameraParameters != null) {
540
- Log.d(TAG, "camera parameter not null");
541
-
542
- // Check for flashMode as well to prevent error on frontward facing camera.
543
- List<String> supportedFlashModesNewCamera = mCamera
544
- .getParameters()
545
- .getSupportedFlashModes();
546
- String currentFlashModePreviousCamera = cameraParameters.getFlashMode();
547
- if (
548
- supportedFlashModesNewCamera != null &&
549
- supportedFlashModesNewCamera.contains(currentFlashModePreviousCamera)
550
- ) {
551
- Log.d(
552
- TAG,
553
- "current flash mode supported on new camera. setting params"
554
- );
555
- /* mCamera.setParameters(cameraParameters);
556
- The line above is disabled because parameters that can actually be changed are different from one device to another. Makes less sense trying to reconfigure them when changing camera device while those settings gan be changed using plugin methods.
557
- */
558
- } else {
559
- Log.d(TAG, "current flash mode NOT supported on new camera");
560
- }
561
- } else {
562
- Log.d(TAG, "camera parameter NULL");
563
- }
564
-
565
- mPreview.switchCamera(mCamera, cameraCurrentlyLocked);
566
-
567
- mCamera.startPreview();
568
- }
569
- }
570
-
571
- public void setCameraParameters(Camera.Parameters params) {
572
- cameraParameters = params;
573
-
574
- if (mCamera != null && cameraParameters != null) {
575
- mCamera.setParameters(cameraParameters);
576
- }
577
- }
578
-
579
- public boolean hasFrontCamera() {
580
- return getActivity()
581
- .getApplicationContext()
582
- .getPackageManager()
583
- .hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
584
- }
585
-
586
- public static Bitmap applyMatrix(Bitmap source, Matrix matrix) {
587
- return Bitmap.createBitmap(
588
- source,
589
- 0,
590
- 0,
591
- source.getWidth(),
592
- source.getHeight(),
593
- matrix,
594
- true
595
- );
596
- }
597
-
598
- ShutterCallback shutterCallback = new ShutterCallback() {
599
- public void onShutter() {
600
- // do nothing, availabilty of this callback causes default system shutter sound to work
601
- }
602
- };
603
-
604
- private static int exifToDegrees(int exifOrientation) {
605
- if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
606
- return 90;
607
- } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
608
- return 180;
609
- } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
610
- return 270;
611
- }
612
- return 0;
613
- }
614
-
615
- private String getTempDirectoryPath() {
616
- File cache = null;
617
-
618
- // Use internal storage
619
- cache = getActivity().getCacheDir();
620
-
621
- // Create the cache directory if it doesn't exist
622
- final boolean mkdirs = cache.mkdirs();
623
- return cache.getAbsolutePath();
624
- }
625
-
626
- private String getTempFilePath() {
627
- return (
628
- getTempDirectoryPath() +
629
- "/cpcp_capture_" +
630
- UUID.randomUUID().toString().replace("-", "").substring(0, 8) +
631
- ".jpg"
632
- );
633
- }
634
-
635
- PictureCallback jpegPictureCallback = new PictureCallback() {
636
- public void onPictureTaken(byte[] data, Camera arg1) {
637
- Log.d(TAG, "CameraPreview jpegPictureCallback");
638
-
639
- try {
640
- Log.d(TAG, "Inside jpegPictureCallback");
641
-
642
- if (!disableExifHeaderStripping) {
643
- try {
644
- ExifInterface exifInterface = new ExifInterface(
645
- new ByteArrayInputStream(data)
646
- );
647
- int orientation = exifInterface.getAttributeInt(
648
- ExifInterface.TAG_ORIENTATION,
649
- ExifInterface.ORIENTATION_NORMAL
650
- );
651
- Log.d(TAG, "EXIF Orientation: " + orientation);
652
-
653
- int rotation = 0;
654
- switch (orientation) {
655
- case ExifInterface.ORIENTATION_ROTATE_90:
656
- rotation = 90;
657
- break;
658
- case ExifInterface.ORIENTATION_ROTATE_180:
659
- rotation = 180;
660
- break;
661
- case ExifInterface.ORIENTATION_ROTATE_270:
662
- rotation = 270;
663
- break;
664
- }
665
- Log.d(
666
- TAG,
667
- "Camera facing: " +
668
- (cameraCurrentlyLocked == Camera.CameraInfo.CAMERA_FACING_FRONT
669
- ? "front"
670
- : "rear")
671
- );
672
- Log.d(TAG, "Image rotation: " + rotation);
673
- Log.d(
674
- TAG,
675
- "Image flipped: " +
676
- (cameraCurrentlyLocked == Camera.CameraInfo.CAMERA_FACING_FRONT)
677
- );
678
-
679
- if (
680
- cameraCurrentlyLocked == Camera.CameraInfo.CAMERA_FACING_FRONT
681
- ) {
682
- Log.d(TAG, "Front camera EXIF Orientation: " + orientation);
683
- // Rotate the bitmap based on the orientation value
684
- Matrix matrix = new Matrix();
685
- switch (orientation) {
686
- case ExifInterface.ORIENTATION_ROTATE_90:
687
- matrix.postRotate(270);
688
- break;
689
- case ExifInterface.ORIENTATION_ROTATE_180:
690
- matrix.postRotate(180);
691
- break;
692
- case ExifInterface.ORIENTATION_ROTATE_270:
693
- matrix.postRotate(90);
694
- break;
695
- }
696
- // Flip the bitmap horizontally for the front camera
697
- matrix.postScale(-1, 1);
698
- Bitmap bitmap = BitmapFactory.decodeByteArray(
699
- data,
700
- 0,
701
- data.length
702
- );
703
- bitmap = Bitmap.createBitmap(
704
- bitmap,
705
- 0,
706
- 0,
707
- bitmap.getWidth(),
708
- bitmap.getHeight(),
709
- matrix,
710
- true
711
- );
712
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
713
- bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
714
- data = outputStream.toByteArray();
715
- } else if (rotation != 0) {
716
- Matrix matrix = new Matrix();
717
- matrix.postRotate(rotation);
718
- Bitmap bitmap = BitmapFactory.decodeByteArray(
719
- data,
720
- 0,
721
- data.length
722
- );
723
- bitmap = Bitmap.createBitmap(
724
- bitmap,
725
- 0,
726
- 0,
727
- bitmap.getWidth(),
728
- bitmap.getHeight(),
729
- matrix,
730
- true
731
- );
732
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
733
- bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
734
- data = outputStream.toByteArray();
735
- }
736
- } catch (IOException e) {
737
- Log.e(TAG, "Failed to read EXIF data", e);
738
- }
739
- }
740
-
741
- if (!storeToFile) {
742
- String encodedImage = Base64.encodeToString(data, Base64.NO_WRAP);
743
- eventListener.onPictureTaken(encodedImage);
744
- } else {
745
- String path = getTempFilePath();
746
- FileOutputStream out = new FileOutputStream(path);
747
- out.write(data);
748
- out.close();
749
- eventListener.onPictureTaken(path);
750
- }
751
- Log.d(TAG, "CameraPreview pictureTakenHandler called back");
752
- } catch (OutOfMemoryError e) {
753
- // most likely failed to allocate memory for rotateBitmap
754
- Log.d(TAG, "CameraPreview OutOfMemoryError");
755
- // failed to allocate memory
756
- eventListener.onPictureTakenError("Picture too large (memory)");
757
- } catch (IOException e) {
758
- Log.d(TAG, "CameraPreview IOException");
759
- eventListener.onPictureTakenError("IO Error when extracting exif");
760
- } catch (Exception e) {
761
- Log.d(TAG, "CameraPreview onPictureTaken general exception");
762
- } finally {
763
- canTakePicture = true;
764
- mCamera.startPreview();
765
- }
766
- }
767
- };
768
-
769
- static byte[] rotateNV21(
770
- final byte[] yuv,
771
- final int width,
772
- final int height,
773
- final int rotation
774
- ) {
775
- if (rotation == 0) return yuv;
776
- if (rotation % 90 != 0 || rotation < 0 || rotation > 270) {
777
- throw new IllegalArgumentException(
778
- "0 <= rotation < 360, rotation % 90 == 0"
779
- );
780
- }
781
-
782
- final byte[] output = new byte[yuv.length];
783
- final int frameSize = width * height;
784
- final boolean swap = rotation % 180 != 0;
785
- final boolean xflip = rotation % 270 != 0;
786
- final boolean yflip = rotation >= 180;
787
-
788
- for (int j = 0; j < height; j++) {
789
- for (int i = 0; i < width; i++) {
790
- final int yIn = j * width + i;
791
- final int uIn = frameSize + (j >> 1) * width + (i & ~1);
792
- final int vIn = uIn + 1;
793
-
794
- final int wOut = swap ? height : width;
795
- final int hOut = swap ? width : height;
796
- final int iSwapped = swap ? j : i;
797
- final int jSwapped = swap ? i : j;
798
- final int iOut = xflip ? wOut - iSwapped - 1 : iSwapped;
799
- final int jOut = yflip ? hOut - jSwapped - 1 : jSwapped;
800
-
801
- final int yOut = jOut * wOut + iOut;
802
- final int uOut = frameSize + (jOut >> 1) * wOut + (iOut & ~1);
803
- final int vOut = uOut + 1;
804
-
805
- output[yOut] = (byte) (0xff & yuv[yIn]);
806
- output[uOut] = (byte) (0xff & yuv[uIn]);
807
- output[vOut] = (byte) (0xff & yuv[vIn]);
808
- }
809
- }
810
- return output;
811
- }
812
-
813
- public void setOpacity(final float opacity) {
814
- Log.d(TAG, "set opacity:" + opacity);
815
- mPreview.setOpacity(opacity);
816
- }
817
-
818
- public void takeSnapshot(final int quality) {
819
- mCamera.setPreviewCallback(
820
- new Camera.PreviewCallback() {
821
- @Override
822
- public void onPreviewFrame(byte[] bytes, Camera camera) {
823
- try {
824
- Camera.Parameters parameters = camera.getParameters();
825
- Camera.Size size = parameters.getPreviewSize();
826
- int orientation = mPreview.getDisplayOrientation();
827
- if (
828
- mPreview.getCameraFacing() ==
829
- Camera.CameraInfo.CAMERA_FACING_FRONT
830
- ) {
831
- bytes = rotateNV21(
832
- bytes,
833
- size.width,
834
- size.height,
835
- (360 - orientation) % 360
836
- );
837
- } else {
838
- bytes = rotateNV21(bytes, size.width, size.height, orientation);
839
- }
840
- // switch width/height when rotating 90/270 deg
841
- Rect rect = orientation == 90 || orientation == 270
842
- ? new Rect(0, 0, size.height, size.width)
843
- : new Rect(0, 0, size.width, size.height);
844
- YuvImage yuvImage = new YuvImage(
845
- bytes,
846
- parameters.getPreviewFormat(),
847
- rect.width(),
848
- rect.height(),
849
- null
850
- );
851
- ByteArrayOutputStream byteArrayOutputStream =
852
- new ByteArrayOutputStream();
853
- yuvImage.compressToJpeg(rect, quality, byteArrayOutputStream);
854
- byte[] data = byteArrayOutputStream.toByteArray();
855
- byteArrayOutputStream.close();
856
- eventListener.onSnapshotTaken(
857
- Base64.encodeToString(data, Base64.NO_WRAP)
858
- );
859
- } catch (IOException e) {
860
- Log.d(TAG, "CameraPreview IOException");
861
- eventListener.onSnapshotTakenError("IO Error");
862
- } finally {
863
- mCamera.setPreviewCallback(null);
864
- }
865
- }
866
- }
867
- );
868
- }
869
-
870
- private Camera.Size getOptimalPictureSizeForPreview(
871
- int width,
872
- int height,
873
- Camera.Size previewSize,
874
- List<Camera.Size> supportedSizes
875
- ) {
876
- Log.d(TAG, "Requested picture size: " + width + "x" + height);
877
- Log.d(TAG, "Preview size: " + previewSize.width + "x" + previewSize.height);
878
-
879
- // If width and height are provided and non-zero, find an exact match
880
- if (width > 0 && height > 0) {
881
- for (Camera.Size size : supportedSizes) {
882
- if (size.width == width && size.height == height) {
883
- Log.d(TAG, "Exact match found: " + size.width + "x" + size.height);
884
- return size;
885
- }
886
- }
887
- }
888
-
889
- // If no exact match found, find the optimal size based on aspect ratio and max pixels
890
- double targetRatio = (double) previewSize.width / previewSize.height;
891
- Camera.Size optimalSize = null;
892
- double minDiff = Double.MAX_VALUE;
893
- long maxPixels = 0;
894
-
895
- for (Camera.Size size : supportedSizes) {
896
- double ratio = (double) size.width / size.height;
897
- if (Math.abs(ratio - targetRatio) > 0.1) continue;
898
-
899
- long pixels = (long) size.width * size.height;
900
- if (pixels > maxPixels) {
901
- maxPixels = pixels;
902
- optimalSize = size;
903
- } else if (pixels == maxPixels) {
904
- if (Math.abs(size.height - height) < minDiff) {
905
- optimalSize = size;
906
- minDiff = Math.abs(size.height - height);
907
- }
908
- }
909
- }
910
-
911
- if (optimalSize == null) {
912
- Log.d(TAG, "No picture size matches the aspect ratio");
913
- minDiff = Double.MAX_VALUE;
914
- for (Camera.Size size : supportedSizes) {
915
- if (Math.abs(size.height - height) < minDiff) {
916
- optimalSize = size;
917
- minDiff = Math.abs(size.height - height);
918
- }
919
- }
920
- }
921
- Log.d(
922
- TAG,
923
- "Optimal picture size: " + optimalSize.width + "x" + optimalSize.height
924
- );
925
- return optimalSize;
926
- }
927
-
928
- public void takePicture(
929
- final int width,
930
- final int height,
931
- final int quality
932
- ) {
933
- Log.d(
934
- TAG,
935
- "CameraPreview takePicture width: " +
936
- width +
937
- ", height: " +
938
- height +
939
- ", quality: " +
940
- quality
941
- );
942
-
943
- if (mPreview != null) {
944
- if (!canTakePicture) {
945
- return;
946
- }
947
-
948
- canTakePicture = false;
949
-
950
- new Thread() {
951
- public void run() {
952
- Camera.Parameters params = mCamera.getParameters();
953
-
954
- Camera.Size size = getOptimalPictureSizeForPreview(
955
- width,
956
- height,
957
- params.getPreviewSize(),
958
- params.getSupportedPictureSizes()
959
- );
960
- params.setPictureSize(size.width, size.height);
961
- currentQuality = quality;
962
-
963
- if (
964
- cameraCurrentlyLocked == Camera.CameraInfo.CAMERA_FACING_FRONT &&
965
- !storeToFile
966
- ) {
967
- // The image will be recompressed in the callback
968
- params.setJpegQuality(99);
969
- } else {
970
- params.setJpegQuality(quality);
971
- }
972
-
973
- if (
974
- cameraCurrentlyLocked == Camera.CameraInfo.CAMERA_FACING_FRONT &&
975
- disableExifHeaderStripping
976
- ) {
977
- Activity activity = getActivity();
978
- int rotation = activity
979
- .getWindowManager()
980
- .getDefaultDisplay()
981
- .getRotation();
982
- int degrees = 0;
983
- switch (rotation) {
984
- case Surface.ROTATION_0:
985
- degrees = 0;
986
- break;
987
- case Surface.ROTATION_90:
988
- degrees = 180;
989
- break;
990
- case Surface.ROTATION_180:
991
- degrees = 270;
992
- break;
993
- case Surface.ROTATION_270:
994
- degrees = 0;
995
- break;
996
- }
997
- int orientation;
998
- Camera.CameraInfo info = new Camera.CameraInfo();
999
- if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
1000
- orientation = (info.orientation + degrees) % 360;
1001
- if (degrees != 0) {
1002
- orientation = (360 - orientation) % 360;
1003
- }
1004
- } else {
1005
- orientation = (info.orientation - degrees + 360) % 360;
1006
- }
1007
- params.setRotation(orientation);
1008
- } else {
1009
- params.setRotation(mPreview.getDisplayOrientation());
1010
- }
1011
-
1012
- mCamera.setParameters(params);
1013
- mCamera.takePicture(shutterCallback, null, jpegPictureCallback);
1014
- }
1015
- }
1016
- .start();
1017
- } else {
1018
- canTakePicture = true;
1019
- }
1020
- }
1021
-
1022
- public void startRecord(
1023
- final String filePath,
1024
- final String camera,
1025
- final int width,
1026
- final int height,
1027
- final int quality,
1028
- final boolean withFlash,
1029
- final int maxDuration
1030
- ) {
1031
- Log.d(
1032
- TAG,
1033
- "CameraPreview startRecord camera: " +
1034
- camera +
1035
- " width: " +
1036
- width +
1037
- ", height: " +
1038
- height +
1039
- ", quality: " +
1040
- quality
1041
- );
1042
- Activity activity = getActivity();
1043
- muteStream(true, activity);
1044
- if (this.mRecordingState == RecordingState.STARTED) {
1045
- Log.d(TAG, "Already Recording");
1046
- return;
1047
- }
1048
-
1049
- this.recordFilePath = filePath;
1050
- int mOrientationHint = calculateOrientationHint();
1051
- int videoWidth = 0; //set whatever
1052
- int videoHeight = 0; //set whatever
1053
-
1054
- Camera.Parameters cameraParams = mCamera.getParameters();
1055
- if (withFlash) {
1056
- cameraParams.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
1057
- mCamera.setParameters(cameraParams);
1058
- mCamera.startPreview();
1059
- }
1060
-
1061
- mCamera.unlock();
1062
- mRecorder = new MediaRecorder();
1063
-
1064
- try {
1065
- mRecorder.setCamera(mCamera);
1066
-
1067
- CamcorderProfile profile;
1068
- if (
1069
- CamcorderProfile.hasProfile(
1070
- defaultCameraId,
1071
- CamcorderProfile.QUALITY_HIGH
1072
- )
1073
- ) {
1074
- profile = CamcorderProfile.get(
1075
- defaultCameraId,
1076
- CamcorderProfile.QUALITY_HIGH
1077
- );
1078
- } else {
1079
- if (
1080
- CamcorderProfile.hasProfile(
1081
- defaultCameraId,
1082
- CamcorderProfile.QUALITY_480P
1083
- )
1084
- ) {
1085
- profile = CamcorderProfile.get(
1086
- defaultCameraId,
1087
- CamcorderProfile.QUALITY_480P
1088
- );
1089
- } else {
1090
- if (
1091
- CamcorderProfile.hasProfile(
1092
- defaultCameraId,
1093
- CamcorderProfile.QUALITY_720P
1094
- )
1095
- ) {
1096
- profile = CamcorderProfile.get(
1097
- defaultCameraId,
1098
- CamcorderProfile.QUALITY_720P
1099
- );
1100
- } else {
1101
- if (
1102
- CamcorderProfile.hasProfile(
1103
- defaultCameraId,
1104
- CamcorderProfile.QUALITY_1080P
1105
- )
1106
- ) {
1107
- profile = CamcorderProfile.get(
1108
- defaultCameraId,
1109
- CamcorderProfile.QUALITY_1080P
1110
- );
1111
- } else {
1112
- profile = CamcorderProfile.get(
1113
- defaultCameraId,
1114
- CamcorderProfile.QUALITY_LOW
1115
- );
1116
- }
1117
- }
1118
- }
1119
- }
1120
-
1121
- if (disableAudio) {
1122
- mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
1123
- mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
1124
- } else {
1125
- mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION);
1126
- }
1127
-
1128
- mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
1129
- mRecorder.setProfile(profile);
1130
- mRecorder.setOutputFile(filePath);
1131
- mRecorder.setOrientationHint(mOrientationHint);
1132
- mRecorder.setMaxDuration(maxDuration);
1133
-
1134
- mRecorder.prepare();
1135
- Log.d(TAG, "Starting recording");
1136
- mRecorder.start();
1137
- eventListener.onStartRecordVideo();
1138
- } catch (IOException e) {
1139
- eventListener.onStartRecordVideoError(e.getMessage());
1140
- }
1141
- }
1142
-
1143
- public int calculateOrientationHint() {
1144
- DisplayMetrics dm = new DisplayMetrics();
1145
- Camera.CameraInfo info = new Camera.CameraInfo();
1146
- Camera.getCameraInfo(defaultCameraId, info);
1147
- int cameraRotationOffset = info.orientation;
1148
- Activity activity = getActivity();
1149
-
1150
- activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
1151
- int currentScreenRotation = activity
1152
- .getWindowManager()
1153
- .getDefaultDisplay()
1154
- .getRotation();
1155
-
1156
- int degrees = 0;
1157
- switch (currentScreenRotation) {
1158
- case Surface.ROTATION_90:
1159
- degrees = 90;
1160
- break;
1161
- case Surface.ROTATION_180:
1162
- degrees = 180;
1163
- break;
1164
- case Surface.ROTATION_270:
1165
- degrees = 270;
1166
- break;
1167
- default:
1168
- break;
1169
- }
1170
-
1171
- int orientation;
1172
- if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
1173
- orientation = (cameraRotationOffset + degrees) % 360;
1174
- if (degrees != 0) {
1175
- orientation = (360 - orientation) % 360;
1176
- }
1177
- } else {
1178
- orientation = (cameraRotationOffset - degrees + 360) % 360;
1179
- }
1180
- Log.w(TAG, "************orientationHint ***********= " + orientation);
1181
-
1182
- return orientation;
1183
- }
1184
-
1185
- public void stopRecord() {
1186
- Log.d(TAG, "stopRecord");
1187
-
1188
- try {
1189
- mRecorder.stop();
1190
- mRecorder.reset(); // clear recorder configuration
1191
- mRecorder.release(); // release the recorder object
1192
- mRecorder = null;
1193
- mCamera.lock();
1194
- Camera.Parameters cameraParams = mCamera.getParameters();
1195
- cameraParams.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
1196
- mCamera.setParameters(cameraParams);
1197
- mCamera.startPreview();
1198
- eventListener.onStopRecordVideo(this.recordFilePath);
1199
- } catch (Exception e) {
1200
- eventListener.onStopRecordVideoError(e.getMessage());
1201
- }
1202
- }
1203
-
1204
- public void muteStream(boolean mute, Activity activity) {
1205
- AudioManager audioManager =
1206
- ((AudioManager) activity
1207
- .getApplicationContext()
1208
- .getSystemService(Context.AUDIO_SERVICE));
1209
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
1210
- int direction = mute
1211
- ? AudioManager.ADJUST_MUTE
1212
- : AudioManager.ADJUST_UNMUTE;
1213
- }
1214
- }
1215
-
1216
- public void setFocusArea(
1217
- final int pointX,
1218
- final int pointY,
1219
- final Camera.AutoFocusCallback callback
1220
- ) {
1221
- if (mCamera != null) {
1222
- mCamera.cancelAutoFocus();
1223
-
1224
- Camera.Parameters parameters = mCamera.getParameters();
1225
-
1226
- Rect focusRect = calculateTapArea(pointX, pointY, 1f);
1227
- parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
1228
- parameters.setFocusAreas(
1229
- Collections.singletonList(new Camera.Area(focusRect, 1000))
1230
- );
1231
-
1232
- if (parameters.getMaxNumMeteringAreas() > 0) {
1233
- Rect meteringRect = calculateTapArea(pointX, pointY, 1.5f);
1234
- parameters.setMeteringAreas(
1235
- Collections.singletonList(new Camera.Area(meteringRect, 1000))
1236
- );
1237
- }
1238
-
1239
- try {
1240
- setCameraParameters(parameters);
1241
- mCamera.autoFocus(callback);
1242
- } catch (Exception e) {
1243
- Log.d(TAG, Objects.requireNonNull(e.getMessage()));
1244
- callback.onAutoFocus(false, this.mCamera);
1245
- }
1246
- }
1247
- }
1248
-
1249
- private Rect calculateTapArea(float x, float y, float coefficient) {
1250
- if (x < 100) {
1251
- x = 100;
1252
- }
1253
- if (x > width - 100) {
1254
- x = width - 100;
1255
- }
1256
- if (y < 100) {
1257
- y = 100;
1258
- }
1259
- if (y > height - 100) {
1260
- y = height - 100;
1261
- }
1262
- return new Rect(
1263
- Math.round(((x - 100) * 2000) / width - 1000),
1264
- Math.round(((y - 100) * 2000) / height - 1000),
1265
- Math.round(((x + 100) * 2000) / width - 1000),
1266
- Math.round(((y + 100) * 2000) / height - 1000)
1267
- );
1268
- }
1269
-
1270
- /**
1271
- * Determine the space between the first two fingers
1272
- */
1273
- private static float getFingerSpacing(MotionEvent event) {
1274
- // ...
1275
- float x = event.getX(0) - event.getX(1);
1276
- float y = event.getY(0) - event.getY(1);
1277
- return (float) Math.sqrt(x * x + y * y);
1278
- }
1279
- }