From 0850316c24a81c4e85e6ccecc7a190db62d3fcf5 Mon Sep 17 00:00:00 2001 From: dinitri ragoo Date: Wed, 6 Mar 2024 16:33:15 +0400 Subject: [PATCH 01/21] configure ios to choose wide angle cam --- src/ios/CameraSessionManager.m | 14 ++++++++++---- src/ios/SimpleCameraPreview.m | 12 ++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/ios/CameraSessionManager.m b/src/ios/CameraSessionManager.m index bc8791c..dd35c73 100644 --- a/src/ios/CameraSessionManager.m +++ b/src/ios/CameraSessionManager.m @@ -53,8 +53,14 @@ - (void) setupSession:(NSString *)defaultCamera completion:(void(^)(BOOL started } else { self.defaultCamera = AVCaptureDevicePositionBack; } - - AVCaptureDevice *videoDevice = [self cameraWithPosition: self.defaultCamera]; + AVCaptureDevice *videoDevice; + if ([options[@"ulw"] isEqual: @"active"]) { + // need to add check for ios version first + videoDevice = [self cameraWithPosition: self.defaultCamera captureDeviceType: AVCaptureDeviceTypeBuiltInUltraWideCamera]; + } + else { + videoDevice = [self cameraWithPosition: self.defaultCamera captureDeviceType: AVCaptureDeviceTypeBuiltInWideAngleCamera]; + } if ([videoDevice hasFlash]) { if ([videoDevice lockForConfiguration:&error]) { @@ -186,8 +192,8 @@ - (void)setFlashMode:(NSInteger)flashMode photoSettings:(AVCapturePhotoSettings } } // Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found -- (AVCaptureDevice *) cameraWithPosition:(AVCaptureDevicePosition) position { - AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:self.defaultCamera]; +- (AVCaptureDevice *) cameraWithPosition:(AVCaptureDevicePosition) position captureDeviceType:(AVCaptureDeviceType) captureDeviceType { + AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[ captureDeviceType] mediaType:AVMediaTypeVideo position:self.defaultCamera]; NSArray *devices = [captureDeviceDiscoverySession devices]; for (AVCaptureDevice *device in devices){ if ([device position] == position) diff --git a/src/ios/SimpleCameraPreview.m b/src/ios/SimpleCameraPreview.m index e2dd4a1..faeb562 100644 --- a/src/ios/SimpleCameraPreview.m +++ b/src/ios/SimpleCameraPreview.m @@ -65,13 +65,17 @@ - (void) enable:(CDVInvokedUrlCommand*)command { // Setup session self.sessionManager.delegate = self.cameraRenderController; - NSDictionary *setupSessionOptions; + NSMutableDictionary *setupSessionOptions = [NSMutableDictionary dictionary]; if (command.arguments.count > 0) { NSDictionary* config = command.arguments[0]; @try { if (config[@"targetSize"] != [NSNull null] && ![config[@"targetSize"] isEqual: @"null"]) { NSInteger targetSize = ((NSNumber*)config[@"targetSize"]).intValue; - setupSessionOptions = @{ @"targetSize" : [NSNumber numberWithInteger:targetSize] }; + [setupSessionOptions setValue:[NSNumber numberWithInteger:targetSize] forKey:@"targetSize"]; + } + NSString *ulw = config[@"ulw"]; + if (ulw != [NSNull null] && ![ulw isEqual: @""]) { + [setupSessionOptions setValue:ulw forKey:@"ulw"]; } } @catch(NSException *exception) { [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"targetSize not well defined"] callbackId:command.callbackId]; @@ -79,14 +83,14 @@ - (void) enable:(CDVInvokedUrlCommand*)command { } self.photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:@{AVVideoCodecKey : AVVideoCodecTypeJPEG}]; - + NSDictionary *immutableSetupSessionOptions = [setupSessionOptions copy]; [self.sessionManager setupSession:@"back" completion:^(BOOL started) { dispatch_async(dispatch_get_main_queue(), ^{ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; [pluginResult setKeepCallbackAsBool:true]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }); - } options:setupSessionOptions photoSettings:self.photoSettings]; + } options:immutableSetupSessionOptions photoSettings:self.photoSettings]; } - (void) sessionNotInterrupted:(NSNotification *)notification { From edfbc7952fb658870572542e3996b8c24e561e50 Mon Sep 17 00:00:00 2001 From: dinitri ragoo Date: Wed, 6 Mar 2024 18:07:58 +0400 Subject: [PATCH 02/21] change option name to captureDevice --- src/ios/CameraSessionManager.m | 2 +- src/ios/SimpleCameraPreview.m | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ios/CameraSessionManager.m b/src/ios/CameraSessionManager.m index dd35c73..2a4909b 100644 --- a/src/ios/CameraSessionManager.m +++ b/src/ios/CameraSessionManager.m @@ -54,7 +54,7 @@ - (void) setupSession:(NSString *)defaultCamera completion:(void(^)(BOOL started self.defaultCamera = AVCaptureDevicePositionBack; } AVCaptureDevice *videoDevice; - if ([options[@"ulw"] isEqual: @"active"]) { + if ([options[@"captureDevice"] isEqual: @"wide-angle"]) { // need to add check for ios version first videoDevice = [self cameraWithPosition: self.defaultCamera captureDeviceType: AVCaptureDeviceTypeBuiltInUltraWideCamera]; } diff --git a/src/ios/SimpleCameraPreview.m b/src/ios/SimpleCameraPreview.m index faeb562..546694b 100644 --- a/src/ios/SimpleCameraPreview.m +++ b/src/ios/SimpleCameraPreview.m @@ -73,9 +73,9 @@ - (void) enable:(CDVInvokedUrlCommand*)command { NSInteger targetSize = ((NSNumber*)config[@"targetSize"]).intValue; [setupSessionOptions setValue:[NSNumber numberWithInteger:targetSize] forKey:@"targetSize"]; } - NSString *ulw = config[@"ulw"]; - if (ulw != [NSNull null] && ![ulw isEqual: @""]) { - [setupSessionOptions setValue:ulw forKey:@"ulw"]; + NSString *captureDevice = config[@"captureDevice"]; + if (captureDevice != [NSNull null] && ![captureDevice isEqual: @""]) { + [setupSessionOptions setValue:captureDevice forKey:@"captureDevice"]; } } @catch(NSException *exception) { [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"targetSize not well defined"] callbackId:command.callbackId]; From 30bbf55830dbb2084ad90c53ef4f977853ce0537 Mon Sep 17 00:00:00 2001 From: dinitri ragoo Date: Mon, 11 Mar 2024 18:18:38 +0400 Subject: [PATCH 03/21] switch between camera --- src/ios/CameraSessionManager.h | 1 + src/ios/CameraSessionManager.m | 65 +++++++++++++++++++++++++++++++--- src/ios/SimpleCameraPreview.h | 1 + src/ios/SimpleCameraPreview.m | 9 ++++- www/SimpleCameraPreview.js | 4 +++ 5 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/ios/CameraSessionManager.h b/src/ios/CameraSessionManager.h index c0b006b..8856e52 100644 --- a/src/ios/CameraSessionManager.h +++ b/src/ios/CameraSessionManager.h @@ -8,6 +8,7 @@ - (void) setupSession:(NSString *)defaultCamera completion:(void(^)(BOOL started))completion options:(NSDictionary *)options photoSettings:(AVCapturePhotoSettings *)photoSettings; - (void) setFlashMode:(NSInteger)flashMode photoSettings:(AVCapturePhotoSettings *)photoSettings; - (void) torchSwitch:(NSInteger)torchState; +- (BOOL) switchToUltraWideCamera; - (void) updateOrientation:(AVCaptureVideoOrientation)orientation; - (AVCaptureVideoOrientation) getCurrentOrientation:(UIInterfaceOrientation)toInterfaceOrientation; + (AVCaptureSessionPreset) calculateResolution:(NSInteger)targetSize; diff --git a/src/ios/CameraSessionManager.m b/src/ios/CameraSessionManager.m index 2a4909b..b356e1d 100644 --- a/src/ios/CameraSessionManager.m +++ b/src/ios/CameraSessionManager.m @@ -53,13 +53,16 @@ - (void) setupSession:(NSString *)defaultCamera completion:(void(^)(BOOL started } else { self.defaultCamera = AVCaptureDevicePositionBack; } + AVCaptureDevice *videoDevice; + videoDevice = [self cameraWithPosition: self.defaultCamera captureDeviceType: AVCaptureDeviceTypeBuiltInWideAngleCamera]; if ([options[@"captureDevice"] isEqual: @"wide-angle"]) { - // need to add check for ios version first - videoDevice = [self cameraWithPosition: self.defaultCamera captureDeviceType: AVCaptureDeviceTypeBuiltInUltraWideCamera]; - } - else { - videoDevice = [self cameraWithPosition: self.defaultCamera captureDeviceType: AVCaptureDeviceTypeBuiltInWideAngleCamera]; + if ([self deviceHasUltraWideCamera]) { + if (@available(iOS 13.0, *)) { + videoDevice = [self cameraWithPosition: self.defaultCamera captureDeviceType: AVCaptureDeviceTypeBuiltInUltraWideCamera]; + } + } + } if ([videoDevice hasFlash]) { @@ -173,6 +176,58 @@ - (void) torchSwitch:(NSInteger)torchState{ } } +- (BOOL)switchToUltraWideCamera { + if (![self deviceHasUltraWideCamera]) return FALSE; + + dispatch_async(self.sessionQueue, ^{ + if (@available(iOS 13.0, *)) { + AVCaptureDevice *ultraWideCamera = [self cameraWithPosition: self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInUltraWideCamera]; + if (ultraWideCamera) { + // Remove the current input + [self.session removeInput:self.videoDeviceInput]; + + // Create a new input with the ultra-wide camera + NSError *error = nil; + AVCaptureDeviceInput *ultraWideVideoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:ultraWideCamera error:&error]; + + if (!error) { + // Add the new input to the session + if ([self.session canAddInput:ultraWideVideoDeviceInput]) { + [self.session addInput:ultraWideVideoDeviceInput]; + self.videoDeviceInput = ultraWideVideoDeviceInput; + __block AVCaptureVideoOrientation orientation; + dispatch_sync(dispatch_get_main_queue(), ^{ + orientation=[self getCurrentOrientation]; + }); + [self updateOrientation:orientation]; + } else { + NSLog(@"Failed to add ultra-wide input to session"); + } + } else { + NSLog(@"Error creating ultra-wide device input: %@", error.localizedDescription); + } + } else { + NSLog(@"Ultra-wide camera not found"); + } + } else { + // Fallback on earlier versions + } + }); + return TRUE; +} + +- (BOOL)deviceHasUltraWideCamera { + NSArray *devices; + if (@available(iOS 13.0, *)) { + AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInUltraWideCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified]; + devices = discoverySession.devices; + } else { + // Fallback on earlier versions + } + + return devices.count > 0; +} + - (void)setFlashMode:(NSInteger)flashMode photoSettings:(AVCapturePhotoSettings *)photoSettings { NSError *error = nil; // Let's save the setting even if we can't set it up on this camera. diff --git a/src/ios/SimpleCameraPreview.h b/src/ios/SimpleCameraPreview.h index 018bb90..bdc81a6 100644 --- a/src/ios/SimpleCameraPreview.h +++ b/src/ios/SimpleCameraPreview.h @@ -16,6 +16,7 @@ - (void) capture:(CDVInvokedUrlCommand*)command; - (void) setSize:(CDVInvokedUrlCommand*)command; - (void) torchSwitch: (CDVInvokedUrlCommand*)command; +- (void) switchToUltraWideCamera: (CDVInvokedUrlCommand*) command; - (void) deviceHasFlash: (CDVInvokedUrlCommand*)command; @property (nonatomic) CameraSessionManager *sessionManager; @property (nonatomic) CameraRenderController *cameraRenderController; diff --git a/src/ios/SimpleCameraPreview.m b/src/ios/SimpleCameraPreview.m index 546694b..1a3ea35 100644 --- a/src/ios/SimpleCameraPreview.m +++ b/src/ios/SimpleCameraPreview.m @@ -156,6 +156,14 @@ - (void) torchSwitch:(CDVInvokedUrlCommand*)command{ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } +- (void) switchToUltraWideCamera:(CDVInvokedUrlCommand*)command{ + if (self.sessionManager != nil) { + [self.sessionManager switchToUltraWideCamera]; + } + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + - (void) deviceHasFlash:(CDVInvokedUrlCommand*)command{ AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo @@ -192,7 +200,6 @@ - (void) capture:(CDVInvokedUrlCommand*)command { } } - - (NSDictionary *)getGPSDictionaryForLocation { if (!currentLocation) return nil; diff --git a/www/SimpleCameraPreview.js b/www/SimpleCameraPreview.js index b1f4d96..4eb57bd 100755 --- a/www/SimpleCameraPreview.js +++ b/www/SimpleCameraPreview.js @@ -28,6 +28,10 @@ SimpleCameraPreview.torchSwitch = function (options, onSuccess, onError) { exec(onSuccess, onError, PLUGIN_NAME, "torchSwitch", [options]); }; +SimpleCameraPreview.switchToUltraWideCamera = function (onSuccess, onError) { + exec(onSuccess, onError, PLUGIN_NAME, "switchToUltraWideCamera", []); +}; + SimpleCameraPreview.deviceHasFlash = function (onSuccess, onError) { exec(onSuccess, onError, PLUGIN_NAME, "deviceHasFlash", []); }; From 4e556087da3f45d91a43e2da8a12f18fb247a9c6 Mon Sep 17 00:00:00 2001 From: HashirRajah Date: Tue, 12 Mar 2024 15:12:45 +0400 Subject: [PATCH 04/21] android zoom --- src/android/CameraPreviewFragment.java | 25 ++++++++++++++++ src/android/SimpleCameraPreview.java | 40 ++++++++++++++++++++++++++ www/SimpleCameraPreview.js | 10 +++++++ 3 files changed, 75 insertions(+) diff --git a/src/android/CameraPreviewFragment.java b/src/android/CameraPreviewFragment.java index a808b31..f9197f3 100644 --- a/src/android/CameraPreviewFragment.java +++ b/src/android/CameraPreviewFragment.java @@ -55,6 +55,10 @@ interface HasFlashCallback { void onResult(boolean result); } +interface ZoomCallback { + void onZoom(Exception err); +} + public class CameraPreviewFragment extends Fragment { private PreviewView viewFinder; @@ -69,6 +73,8 @@ public class CameraPreviewFragment extends Fragment { private static float ratio = (4 / (float) 3); private static final String TAG = "SimpleCameraPreview"; + private float minZoomRatio; + private float maxZoomRatio; public CameraPreviewFragment() { @@ -148,6 +154,9 @@ public void startCamera() { preview, imageCapture ); + } finally { + minZoomRatio = camera.getCameraInfo().getZoomState().getValue().getMinZoomRatio(); + maxZoomRatio = camera.getCameraInfo().getZoomState().getValue().getMaxZoomRatio(); } preview.setSurfaceProvider(viewFinder.getSurfaceProvider()); @@ -309,4 +318,20 @@ public void setLocation(Location loc) { this.location = loc; } } + + public float getMinZoomRatio() { + return minZoomRatio; + } + + public void setZoomRatio(float zoomRatio, ZoomCallback zoomCallback) { + if ((zoomRatio < minZoomRatio) || (zoomRatio > maxZoomRatio)) { + zoomCallback.onZoom(new Exception("Unsupported zoom ratio")); + return; + } + try { + camera.getCameraControl().setZoomRatio(zoomRatio); + } catch (Exception e) { + zoomCallback.onZoom(new Exception("Failed to set zoom ratio to " + zoomRatio, e)); + } + } } diff --git a/src/android/SimpleCameraPreview.java b/src/android/SimpleCameraPreview.java index be44226..b4409eb 100644 --- a/src/android/SimpleCameraPreview.java +++ b/src/android/SimpleCameraPreview.java @@ -74,6 +74,12 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo case "deviceHasFlash": return deviceHasFlash(callbackContext); + + case "getMinZoomRatio": + return getMinZoomRatio(callbackContext); + + case "setZoomRatio": + return setZoomRatio(Double.valueOf(args.getDouble(0)).floatValue(), callbackContext); default: break; } @@ -323,6 +329,39 @@ public void run() { } } + private boolean getMinZoomRatio(CallbackContext callbackContext) { + if (fragment == null) { + callbackContext.error("Camera is closed, cannot get minimum zoom ratio."); + return true; + } + try { + float minZoomRatio = fragment.getMinZoomRatio(); + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, minZoomRatio); + callbackContext.sendPluginResult(pluginResult); + return true; + } catch (Exception e) { + e.printStackTrace(); + callbackContext.error(e.getMessage()); + return false; + } + } + + private boolean setZoomRatio(float zoomRatio, CallbackContext callbackContext) { + if (fragment == null) { + callbackContext.error("Camera is closed, cannot set zoom ratio"); + return true; + } + + fragment.setZoom(zoomRatio, (Exception err) -> { + if (err == null) { + callbackContext.success(); + } else { + callbackContext.error(err.getMessage()); + } + }); + return true; + } + public boolean hasAllPermissions() { for(String p : REQUIRED_PERMISSIONS) { if(!PermissionHelper.hasPermission(this, p)) { @@ -395,6 +434,7 @@ public void onRequestPermissionResult(int requestCode, String[] permissions, int } } } + @Override public void onDestroy() { diff --git a/www/SimpleCameraPreview.js b/www/SimpleCameraPreview.js index 4eb57bd..4366c4c 100755 --- a/www/SimpleCameraPreview.js +++ b/www/SimpleCameraPreview.js @@ -36,4 +36,14 @@ SimpleCameraPreview.deviceHasFlash = function (onSuccess, onError) { exec(onSuccess, onError, PLUGIN_NAME, "deviceHasFlash", []); }; +SimpleCameraPreview.getMinZoomRatio = function (onSuccess, onError) { + exec(onSuccess, onError, PLUGIN_NAME, "getMinZoomRatio", []); +}; + +SimpleCameraPreview.setZoomRatio = function (options, onSuccess, onError) { + options = options || {}; + options.zoomRatio = options.zoomRatio || 1; + exec(onSuccess, onError, PLUGIN_NAME, "setZoomRatio", [options.zoomRatio]); +}; + module.exports = SimpleCameraPreview; From 44b7fe0ddaade579ffd0b7019c5ccf91721f58d1 Mon Sep 17 00:00:00 2001 From: HashirRajah Date: Tue, 12 Mar 2024 15:33:58 +0400 Subject: [PATCH 05/21] method name correction --- src/android/SimpleCameraPreview.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/android/SimpleCameraPreview.java b/src/android/SimpleCameraPreview.java index b4409eb..2a032dc 100644 --- a/src/android/SimpleCameraPreview.java +++ b/src/android/SimpleCameraPreview.java @@ -352,7 +352,7 @@ private boolean setZoomRatio(float zoomRatio, CallbackContext callbackContext) { return true; } - fragment.setZoom(zoomRatio, (Exception err) -> { + fragment.setZoomRatio(zoomRatio, (Exception err) -> { if (err == null) { callbackContext.success(); } else { From 68b033252b818ee1eea75f3397d99f50fe628d10 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Mon, 18 Mar 2024 16:34:48 +0400 Subject: [PATCH 06/21] handle wide and ultra wide angle camera ios --- src/ios/CameraSessionManager.h | 3 ++- src/ios/CameraSessionManager.m | 9 +++++++-- src/ios/SimpleCameraPreview.h | 1 + src/ios/SimpleCameraPreview.m | 12 +++++++++++- www/SimpleCameraPreview.js | 10 ++++++++-- 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/ios/CameraSessionManager.h b/src/ios/CameraSessionManager.h index 8856e52..b449940 100644 --- a/src/ios/CameraSessionManager.h +++ b/src/ios/CameraSessionManager.h @@ -8,7 +8,8 @@ - (void) setupSession:(NSString *)defaultCamera completion:(void(^)(BOOL started))completion options:(NSDictionary *)options photoSettings:(AVCapturePhotoSettings *)photoSettings; - (void) setFlashMode:(NSInteger)flashMode photoSettings:(AVCapturePhotoSettings *)photoSettings; - (void) torchSwitch:(NSInteger)torchState; -- (BOOL) switchToUltraWideCamera; +- (BOOL) switchToUltraWideCamera:(NSString *)cameraMode; +- (BOOL) deviceHasUltraWideCamera; - (void) updateOrientation:(AVCaptureVideoOrientation)orientation; - (AVCaptureVideoOrientation) getCurrentOrientation:(UIInterfaceOrientation)toInterfaceOrientation; + (AVCaptureSessionPreset) calculateResolution:(NSInteger)targetSize; diff --git a/src/ios/CameraSessionManager.m b/src/ios/CameraSessionManager.m index b356e1d..30dc1d6 100644 --- a/src/ios/CameraSessionManager.m +++ b/src/ios/CameraSessionManager.m @@ -176,12 +176,17 @@ - (void) torchSwitch:(NSInteger)torchState{ } } -- (BOOL)switchToUltraWideCamera { +- (BOOL)switchToUltraWideCamera:(NSString*)cameraMode { if (![self deviceHasUltraWideCamera]) return FALSE; dispatch_async(self.sessionQueue, ^{ if (@available(iOS 13.0, *)) { - AVCaptureDevice *ultraWideCamera = [self cameraWithPosition: self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInUltraWideCamera]; + AVCaptureDevice *ultraWideCamera; + if([cameraMode isEqual: @"default"]){ + ultraWideCamera = [self cameraWithPosition: self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera]; + } else { + ultraWideCamera = [self cameraWithPosition: self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera]; + } if (ultraWideCamera) { // Remove the current input [self.session removeInput:self.videoDeviceInput]; diff --git a/src/ios/SimpleCameraPreview.h b/src/ios/SimpleCameraPreview.h index bdc81a6..8ddbc69 100644 --- a/src/ios/SimpleCameraPreview.h +++ b/src/ios/SimpleCameraPreview.h @@ -17,6 +17,7 @@ - (void) setSize:(CDVInvokedUrlCommand*)command; - (void) torchSwitch: (CDVInvokedUrlCommand*)command; - (void) switchToUltraWideCamera: (CDVInvokedUrlCommand*) command; +- (void) deviceHasUltraWideCamera: (CDVInvokedUrlCommand*) command; - (void) deviceHasFlash: (CDVInvokedUrlCommand*)command; @property (nonatomic) CameraSessionManager *sessionManager; @property (nonatomic) CameraRenderController *cameraRenderController; diff --git a/src/ios/SimpleCameraPreview.m b/src/ios/SimpleCameraPreview.m index 1a3ea35..efd5bcf 100644 --- a/src/ios/SimpleCameraPreview.m +++ b/src/ios/SimpleCameraPreview.m @@ -157,13 +157,23 @@ - (void) torchSwitch:(CDVInvokedUrlCommand*)command{ } - (void) switchToUltraWideCamera:(CDVInvokedUrlCommand*)command{ + NSString *device = [command.arguments objectAtIndex:0]; if (self.sessionManager != nil) { - [self.sessionManager switchToUltraWideCamera]; + [self.sessionManager switchToUltraWideCamera: device]; } CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } +- (void) deviceHasUltraWideCamera:(CDVInvokedUrlCommand *)command{ + BOOL hasUltraWideCamera = NO; + if (self.sessionManager != nil) { + hasUltraWideCamera = [self.sessionManager deviceHasUltraWideCamera]; + } + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:hasUltraWideCamera]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + - (void) deviceHasFlash:(CDVInvokedUrlCommand*)command{ AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo diff --git a/www/SimpleCameraPreview.js b/www/SimpleCameraPreview.js index 4366c4c..4bf5f1f 100755 --- a/www/SimpleCameraPreview.js +++ b/www/SimpleCameraPreview.js @@ -28,14 +28,20 @@ SimpleCameraPreview.torchSwitch = function (options, onSuccess, onError) { exec(onSuccess, onError, PLUGIN_NAME, "torchSwitch", [options]); }; -SimpleCameraPreview.switchToUltraWideCamera = function (onSuccess, onError) { - exec(onSuccess, onError, PLUGIN_NAME, "switchToUltraWideCamera", []); +SimpleCameraPreview.switchToUltraWideCamera = function (options, onSuccess, onError) { + options = options || {}; + options.captureDevice = options.captureDevice || "default"; + exec(onSuccess, onError, PLUGIN_NAME, "switchToUltraWideCamera", [options.captureDevice]); }; SimpleCameraPreview.deviceHasFlash = function (onSuccess, onError) { exec(onSuccess, onError, PLUGIN_NAME, "deviceHasFlash", []); }; +SimpleCameraPreview.deviceHasUltraWideCamera = function (onSuccess, onError) { + exec(onSuccess, onError, PLUGIN_NAME, "deviceHasUltraWideCamera", []); +}; + SimpleCameraPreview.getMinZoomRatio = function (onSuccess, onError) { exec(onSuccess, onError, PLUGIN_NAME, "getMinZoomRatio", []); }; From 01408aa809a0e0262a8aae64af6262b6b453bdf6 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Mon, 18 Mar 2024 17:00:38 +0400 Subject: [PATCH 07/21] fix ultra wide camera if --- src/ios/CameraSessionManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ios/CameraSessionManager.m b/src/ios/CameraSessionManager.m index 30dc1d6..6958403 100644 --- a/src/ios/CameraSessionManager.m +++ b/src/ios/CameraSessionManager.m @@ -185,7 +185,7 @@ - (BOOL)switchToUltraWideCamera:(NSString*)cameraMode { if([cameraMode isEqual: @"default"]){ ultraWideCamera = [self cameraWithPosition: self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera]; } else { - ultraWideCamera = [self cameraWithPosition: self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera]; + ultraWideCamera = [self cameraWithPosition: self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInUltraWideCamera]; } if (ultraWideCamera) { // Remove the current input From 7a8c1188b2e9a6a248932b3ed36a1f9a2baebfb3 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Mon, 18 Mar 2024 17:21:55 +0400 Subject: [PATCH 08/21] return result after switching camera --- src/ios/SimpleCameraPreview.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ios/SimpleCameraPreview.m b/src/ios/SimpleCameraPreview.m index efd5bcf..757a0d4 100644 --- a/src/ios/SimpleCameraPreview.m +++ b/src/ios/SimpleCameraPreview.m @@ -158,10 +158,11 @@ - (void) torchSwitch:(CDVInvokedUrlCommand*)command{ - (void) switchToUltraWideCamera:(CDVInvokedUrlCommand*)command{ NSString *device = [command.arguments objectAtIndex:0]; + BOOL cameraSwitched = FALSE; if (self.sessionManager != nil) { - [self.sessionManager switchToUltraWideCamera: device]; + cameraSwitched = [self.sessionManager switchToUltraWideCamera: device]; } - CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:cameraSwitched]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } From 3d3590e904eb92a5e5ab3af114392079b2d06012 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Mon, 18 Mar 2024 18:10:31 +0400 Subject: [PATCH 09/21] completion handlers --- src/ios/CameraSessionManager.h | 2 +- src/ios/CameraSessionManager.m | 33 ++++++++++++++++++++++++++------- src/ios/SimpleCameraPreview.m | 14 +++++++++++--- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/ios/CameraSessionManager.h b/src/ios/CameraSessionManager.h index b449940..cfb1f05 100644 --- a/src/ios/CameraSessionManager.h +++ b/src/ios/CameraSessionManager.h @@ -8,7 +8,7 @@ - (void) setupSession:(NSString *)defaultCamera completion:(void(^)(BOOL started))completion options:(NSDictionary *)options photoSettings:(AVCapturePhotoSettings *)photoSettings; - (void) setFlashMode:(NSInteger)flashMode photoSettings:(AVCapturePhotoSettings *)photoSettings; - (void) torchSwitch:(NSInteger)torchState; -- (BOOL) switchToUltraWideCamera:(NSString *)cameraMode; +- (void) switchToUltraWideCamera:(NSString *)cameraMode completion:(void (^)(BOOL success))completion; - (BOOL) deviceHasUltraWideCamera; - (void) updateOrientation:(AVCaptureVideoOrientation)orientation; - (AVCaptureVideoOrientation) getCurrentOrientation:(UIInterfaceOrientation)toInterfaceOrientation; diff --git a/src/ios/CameraSessionManager.m b/src/ios/CameraSessionManager.m index 6958403..39d3a33 100644 --- a/src/ios/CameraSessionManager.m +++ b/src/ios/CameraSessionManager.m @@ -176,16 +176,21 @@ - (void) torchSwitch:(NSInteger)torchState{ } } -- (BOOL)switchToUltraWideCamera:(NSString*)cameraMode { - if (![self deviceHasUltraWideCamera]) return FALSE; +- (void)switchToUltraWideCamera:(NSString*)cameraMode completion:(void (^)(BOOL success))completion { + if (![self deviceHasUltraWideCamera]) { + if (completion) { + completion(NO); + } + return; + } dispatch_async(self.sessionQueue, ^{ if (@available(iOS 13.0, *)) { AVCaptureDevice *ultraWideCamera; - if([cameraMode isEqual: @"default"]){ - ultraWideCamera = [self cameraWithPosition: self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera]; + if([cameraMode isEqualToString:@"default"]) { + ultraWideCamera = [self cameraWithPosition:self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera]; } else { - ultraWideCamera = [self cameraWithPosition: self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInUltraWideCamera]; + ultraWideCamera = [self cameraWithPosition:self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInUltraWideCamera]; } if (ultraWideCamera) { // Remove the current input @@ -202,23 +207,37 @@ - (BOOL)switchToUltraWideCamera:(NSString*)cameraMode { self.videoDeviceInput = ultraWideVideoDeviceInput; __block AVCaptureVideoOrientation orientation; dispatch_sync(dispatch_get_main_queue(), ^{ - orientation=[self getCurrentOrientation]; + orientation = [self getCurrentOrientation]; }); [self updateOrientation:orientation]; + if (completion) { + completion(YES); + } } else { NSLog(@"Failed to add ultra-wide input to session"); + if (completion) { + completion(NO); + } } } else { NSLog(@"Error creating ultra-wide device input: %@", error.localizedDescription); + if (completion) { + completion(NO); + } } } else { NSLog(@"Ultra-wide camera not found"); + if (completion) { + completion(NO); + } } } else { // Fallback on earlier versions + if (completion) { + completion(NO); + } } }); - return TRUE; } - (BOOL)deviceHasUltraWideCamera { diff --git a/src/ios/SimpleCameraPreview.m b/src/ios/SimpleCameraPreview.m index 757a0d4..6f4856c 100644 --- a/src/ios/SimpleCameraPreview.m +++ b/src/ios/SimpleCameraPreview.m @@ -160,10 +160,18 @@ - (void) switchToUltraWideCamera:(CDVInvokedUrlCommand*)command{ NSString *device = [command.arguments objectAtIndex:0]; BOOL cameraSwitched = FALSE; if (self.sessionManager != nil) { - cameraSwitched = [self.sessionManager switchToUltraWideCamera: device]; + [self.sessionManager switchToUltraWideCamera: device completion:^(BOOL success) { + if (success) { + NSLog(@"Camera switched successfully"); + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:TRUE]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + NSLog(@"Failed to switch camera"); + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:FALSE]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + }]; } - CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:cameraSwitched]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } - (void) deviceHasUltraWideCamera:(CDVInvokedUrlCommand *)command{ From 0a2984f09f4f6ebd586991e156d3b2a37044f3d1 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Tue, 19 Mar 2024 17:46:25 +0400 Subject: [PATCH 10/21] android logic for wide angle and ultra wide angle camera --- src/android/CameraPreviewFragment.java | 159 ++++++++++++++++++++++--- src/android/SimpleCameraPreview.java | 39 +++--- www/SimpleCameraPreview.js | 10 -- 3 files changed, 156 insertions(+), 52 deletions(-) diff --git a/src/android/CameraPreviewFragment.java b/src/android/CameraPreviewFragment.java index f9197f3..4023473 100644 --- a/src/android/CameraPreviewFragment.java +++ b/src/android/CameraPreviewFragment.java @@ -4,9 +4,12 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; +import android.hardware.camera2.CameraCharacteristics; import android.location.Location; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.util.Size; import android.view.Display; @@ -18,7 +21,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.camera.camera2.internal.Camera2CameraInfoImpl; import androidx.camera.core.Camera; +import androidx.camera.core.CameraInfo; import androidx.camera.core.CameraSelector; import androidx.camera.core.ImageCapture; import androidx.camera.core.ImageCaptureException; @@ -36,6 +41,9 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -55,8 +63,12 @@ interface HasFlashCallback { void onResult(boolean result); } -interface ZoomCallback { - void onZoom(Exception err); +interface CameraSwitchedCallback { + void onSwitch(Exception err); +} + +interface HasUltraWideCameraCallback { + void onResult(boolean result); } public class CameraPreviewFragment extends Fragment { @@ -154,9 +166,6 @@ public void startCamera() { preview, imageCapture ); - } finally { - minZoomRatio = camera.getCameraInfo().getZoomState().getValue().getMinZoomRatio(); - maxZoomRatio = camera.getCameraInfo().getZoomState().getValue().getMaxZoomRatio(); } preview.setSurfaceProvider(viewFinder.getSurfaceProvider()); @@ -166,6 +175,48 @@ public void startCamera() { } } + @SuppressLint("RestrictedApi") + public void deviceHasUltraWideCamera(HasUltraWideCameraCallback hasUltraWideCameraCallback) { + ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(getActivity()); + ProcessCameraProvider cameraProvider = null; + + try { + cameraProvider = cameraProviderFuture.get(); + } catch (ExecutionException | InterruptedException e) { + Log.e(TAG, "startCamera: " + e.getMessage()); + e.printStackTrace(); + return; + } + List cameraInfos = cameraProvider.getAvailableCameraInfos(); + + boolean defaultCamera = false; + boolean ultraWideCamera = false; + List backCameras = new ArrayList<>(); + for (CameraInfo cameraInfo : cameraInfos) { + if (cameraInfo instanceof Camera2CameraInfoImpl) { + Camera2CameraInfoImpl camera2CameraInfo = (Camera2CameraInfoImpl) cameraInfo; + if (camera2CameraInfo.getLensFacing() == CameraSelector.LENS_FACING_BACK) { + backCameras.add(camera2CameraInfo); + } + } + } + + for (Camera2CameraInfoImpl backCamera : backCameras) { + if (backCamera.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0] >= 2.4) { + defaultCamera = true; + } else if( backCamera.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0] < 2.4 || + backCamera.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0] >= 1.0) { + ultraWideCamera = true; + } + } + + if (defaultCamera == true && ultraWideCamera == true) { + hasUltraWideCameraCallback.onResult(true); + } else { + hasUltraWideCameraCallback.onResult(false); + } + } + public static Size calculateResolution(Context context, int targetSize) { Size calculatedSize; if (getScreenOrientation(context) == Configuration.ORIENTATION_PORTRAIT) { @@ -319,19 +370,91 @@ public void setLocation(Location loc) { } } - public float getMinZoomRatio() { - return minZoomRatio; - } - public void setZoomRatio(float zoomRatio, ZoomCallback zoomCallback) { - if ((zoomRatio < minZoomRatio) || (zoomRatio > maxZoomRatio)) { - zoomCallback.onZoom(new Exception("Unsupported zoom ratio")); - return; - } - try { - camera.getCameraControl().setZoomRatio(zoomRatio); - } catch (Exception e) { - zoomCallback.onZoom(new Exception("Failed to set zoom ratio to " + zoomRatio, e)); - } + public void switchToUltraWideCamera(String device, CameraSwitchedCallback cameraSwitchedCallback) { + Handler mainHandler = new Handler(Looper.getMainLooper()); + mainHandler.post(new Runnable() { + @SuppressLint("RestrictedApi") + @Override + public void run() { + ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(getActivity()); + ProcessCameraProvider cameraProvider = null; + CameraSelector cameraSelector; + try { + cameraProvider = cameraProviderFuture.get(); + } catch (ExecutionException | InterruptedException e) { + Log.e(TAG, "startCamera: " + e.getMessage()); + e.printStackTrace(); + cameraSwitchedCallback.onSwitch(new Exception("Unable to start camera")); + return; + } + + if (device.equals("default")) { + cameraSelector = new CameraSelector.Builder() + .requireLensFacing(direction) + .build(); + } else { + cameraSelector = new CameraSelector.Builder() + .addCameraFilter(cameraInfos -> { + List backCameras = new ArrayList<>(); + for (CameraInfo cameraInfo : cameraInfos) { + if (cameraInfo instanceof Camera2CameraInfoImpl) { + Camera2CameraInfoImpl camera2CameraInfo = (Camera2CameraInfoImpl) cameraInfo; + if (camera2CameraInfo.getLensFacing() == CameraSelector.LENS_FACING_BACK) { + backCameras.add(camera2CameraInfo); + } + } + } + + Camera2CameraInfoImpl selectedCamera = Collections.min(backCameras, (o1, o2) -> { + Float focalLength1 = o1.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0]; + Float focalLength2 = o2.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0]; + return Float.compare(focalLength1, focalLength2); + }); + + if (selectedCamera != null) { + return Collections.singletonList(selectedCamera); + } else { + return cameraInfos; + } + }) + .build(); + } + + Size targetResolution = null; + if (targetSize > 0) { + targetResolution = CameraPreviewFragment.calculateResolution(getContext(), targetSize); + } + + preview = new Preview.Builder().build(); + imageCapture = new ImageCapture.Builder() + .setTargetResolution(targetResolution) + .build(); + + cameraProvider.unbindAll(); + try { + camera = cameraProvider.bindToLifecycle( + getActivity(), + cameraSelector, + preview, + imageCapture + ); + } catch (IllegalArgumentException e) { + // Error with result in capturing image with default resolution + e.printStackTrace(); + imageCapture = new ImageCapture.Builder() + .build(); + camera = cameraProvider.bindToLifecycle( + getActivity(), + cameraSelector, + preview, + imageCapture + ); + } + + preview.setSurfaceProvider(viewFinder.getSurfaceProvider()); + } + }); + cameraSwitchedCallback.onSwitch(null); } } diff --git a/src/android/SimpleCameraPreview.java b/src/android/SimpleCameraPreview.java index 2a032dc..b80b9b0 100644 --- a/src/android/SimpleCameraPreview.java +++ b/src/android/SimpleCameraPreview.java @@ -75,11 +75,11 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo case "deviceHasFlash": return deviceHasFlash(callbackContext); - case "getMinZoomRatio": - return getMinZoomRatio(callbackContext); + case "deviceHasUltraWideCamera": + return deviceHasUltraWideCamera(callbackContext); - case "setZoomRatio": - return setZoomRatio(Double.valueOf(args.getDouble(0)).floatValue(), callbackContext); + case "switchToUltraWideCamera": + return switchToUltraWideCamera(args.getString(0), callbackContext); default: break; } @@ -278,6 +278,14 @@ private boolean deviceHasFlash(CallbackContext callbackContext) { return true; } + private boolean deviceHasUltraWideCamera(CallbackContext callbackContext) { + fragment.deviceHasUltraWideCamera((boolean result) -> { + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result); + callbackContext.sendPluginResult(pluginResult); + }); + return true; + } + private boolean torchSwitch(boolean torchState, CallbackContext callbackContext) { if (fragment == null) { callbackContext.error("Camera is closed, cannot switch " + torchState + " torch"); @@ -329,30 +337,13 @@ public void run() { } } - private boolean getMinZoomRatio(CallbackContext callbackContext) { - if (fragment == null) { - callbackContext.error("Camera is closed, cannot get minimum zoom ratio."); - return true; - } - try { - float minZoomRatio = fragment.getMinZoomRatio(); - PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, minZoomRatio); - callbackContext.sendPluginResult(pluginResult); - return true; - } catch (Exception e) { - e.printStackTrace(); - callbackContext.error(e.getMessage()); - return false; - } - } - - private boolean setZoomRatio(float zoomRatio, CallbackContext callbackContext) { + private boolean switchToUltraWideCamera(String device, CallbackContext callbackContext) { if (fragment == null) { - callbackContext.error("Camera is closed, cannot set zoom ratio"); + callbackContext.error("Camera is closed, cannot switch camera"); return true; } - fragment.setZoomRatio(zoomRatio, (Exception err) -> { + fragment.switchToUltraWideCamera(device, (Exception err) -> { if (err == null) { callbackContext.success(); } else { diff --git a/www/SimpleCameraPreview.js b/www/SimpleCameraPreview.js index 4bf5f1f..352b794 100755 --- a/www/SimpleCameraPreview.js +++ b/www/SimpleCameraPreview.js @@ -42,14 +42,4 @@ SimpleCameraPreview.deviceHasUltraWideCamera = function (onSuccess, onError) { exec(onSuccess, onError, PLUGIN_NAME, "deviceHasUltraWideCamera", []); }; -SimpleCameraPreview.getMinZoomRatio = function (onSuccess, onError) { - exec(onSuccess, onError, PLUGIN_NAME, "getMinZoomRatio", []); -}; - -SimpleCameraPreview.setZoomRatio = function (options, onSuccess, onError) { - options = options || {}; - options.zoomRatio = options.zoomRatio || 1; - exec(onSuccess, onError, PLUGIN_NAME, "setZoomRatio", [options.zoomRatio]); -}; - module.exports = SimpleCameraPreview; From 911d8607daa8fbcc6ee978302b0340bfffabc1a2 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Wed, 20 Mar 2024 10:10:00 +0400 Subject: [PATCH 11/21] return true or false when switching camera --- src/android/CameraPreviewFragment.java | 6 +++--- src/android/SimpleCameraPreview.java | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/android/CameraPreviewFragment.java b/src/android/CameraPreviewFragment.java index 4023473..6378d58 100644 --- a/src/android/CameraPreviewFragment.java +++ b/src/android/CameraPreviewFragment.java @@ -64,7 +64,7 @@ interface HasFlashCallback { } interface CameraSwitchedCallback { - void onSwitch(Exception err); + void onSwitch(boolean result); } interface HasUltraWideCameraCallback { @@ -385,7 +385,7 @@ public void run() { } catch (ExecutionException | InterruptedException e) { Log.e(TAG, "startCamera: " + e.getMessage()); e.printStackTrace(); - cameraSwitchedCallback.onSwitch(new Exception("Unable to start camera")); + cameraSwitchedCallback.onSwitch(false); return; } @@ -453,8 +453,8 @@ public void run() { } preview.setSurfaceProvider(viewFinder.getSurfaceProvider()); + cameraSwitchedCallback.onSwitch(true); } }); - cameraSwitchedCallback.onSwitch(null); } } diff --git a/src/android/SimpleCameraPreview.java b/src/android/SimpleCameraPreview.java index b80b9b0..10f3443 100644 --- a/src/android/SimpleCameraPreview.java +++ b/src/android/SimpleCameraPreview.java @@ -343,12 +343,9 @@ private boolean switchToUltraWideCamera(String device, CallbackContext callbackC return true; } - fragment.switchToUltraWideCamera(device, (Exception err) -> { - if (err == null) { - callbackContext.success(); - } else { - callbackContext.error(err.getMessage()); - } + fragment.switchToUltraWideCamera(device, (boolean result) -> { + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result); + callbackContext.sendPluginResult(pluginResult); }); return true; } From 8c1042fc1badb247ddf7866e883ddc343af9a6b5 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Fri, 22 Mar 2024 09:51:50 +0400 Subject: [PATCH 12/21] android plugin refactoring --- src/android/CameraPreviewFragment.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/android/CameraPreviewFragment.java b/src/android/CameraPreviewFragment.java index 6378d58..b904749 100644 --- a/src/android/CameraPreviewFragment.java +++ b/src/android/CameraPreviewFragment.java @@ -183,7 +183,7 @@ public void deviceHasUltraWideCamera(HasUltraWideCameraCallback hasUltraWideCame try { cameraProvider = cameraProviderFuture.get(); } catch (ExecutionException | InterruptedException e) { - Log.e(TAG, "startCamera: " + e.getMessage()); + Log.e(TAG, "Error occurred while trying to obtain the camera provider: " + e.getMessage()); e.printStackTrace(); return; } @@ -204,8 +204,7 @@ public void deviceHasUltraWideCamera(HasUltraWideCameraCallback hasUltraWideCame for (Camera2CameraInfoImpl backCamera : backCameras) { if (backCamera.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0] >= 2.4) { defaultCamera = true; - } else if( backCamera.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0] < 2.4 || - backCamera.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0] >= 1.0) { + } else if( backCamera.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0] < 2.4) { ultraWideCamera = true; } } @@ -383,7 +382,7 @@ public void run() { try { cameraProvider = cameraProviderFuture.get(); } catch (ExecutionException | InterruptedException e) { - Log.e(TAG, "startCamera: " + e.getMessage()); + Log.e(TAG, "Error occurred while trying to obtain the camera provider: " + e.getMessage()); e.printStackTrace(); cameraSwitchedCallback.onSwitch(false); return; From a2e45c5b6e2c757f37c2d1dfda402f0dc42e880e Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Fri, 22 Mar 2024 12:27:19 +0400 Subject: [PATCH 13/21] ios plugin refactoring --- src/ios/CameraSessionManager.m | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/ios/CameraSessionManager.m b/src/ios/CameraSessionManager.m index 39d3a33..0183542 100644 --- a/src/ios/CameraSessionManager.m +++ b/src/ios/CameraSessionManager.m @@ -185,12 +185,13 @@ - (void)switchToUltraWideCamera:(NSString*)cameraMode completion:(void (^)(BOOL } dispatch_async(self.sessionQueue, ^{ + BOOL cameraSwitched = FALSE; if (@available(iOS 13.0, *)) { AVCaptureDevice *ultraWideCamera; - if([cameraMode isEqualToString:@"default"]) { - ultraWideCamera = [self cameraWithPosition:self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera]; - } else { + if([cameraMode isEqualToString:@"wide-angle"]) { ultraWideCamera = [self cameraWithPosition:self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInUltraWideCamera]; + } else { + ultraWideCamera = [self cameraWithPosition:self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera]; } if (ultraWideCamera) { // Remove the current input @@ -210,33 +211,21 @@ - (void)switchToUltraWideCamera:(NSString*)cameraMode completion:(void (^)(BOOL orientation = [self getCurrentOrientation]; }); [self updateOrientation:orientation]; - if (completion) { - completion(YES); - } + cameraSwitched = TRUE; } else { NSLog(@"Failed to add ultra-wide input to session"); - if (completion) { - completion(NO); - } } } else { NSLog(@"Error creating ultra-wide device input: %@", error.localizedDescription); - if (completion) { - completion(NO); - } } } else { NSLog(@"Ultra-wide camera not found"); - if (completion) { - completion(NO); - } } } else { // Fallback on earlier versions - if (completion) { - completion(NO); - } } + + completion ? completion(cameraSwitched): NULL; }); } From 4ba0edaf7639e10b6e56e240727c6f593d824db5 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Tue, 2 Apr 2024 09:50:09 +0400 Subject: [PATCH 14/21] rename method to switchCameraTo --- src/android/CameraPreviewFragment.java | 2 +- src/android/SimpleCameraPreview.java | 8 ++++---- src/ios/CameraSessionManager.h | 2 +- src/ios/CameraSessionManager.m | 4 +--- src/ios/SimpleCameraPreview.h | 2 +- src/ios/SimpleCameraPreview.m | 4 ++-- www/SimpleCameraPreview.js | 4 ++-- 7 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/android/CameraPreviewFragment.java b/src/android/CameraPreviewFragment.java index b904749..94eaa52 100644 --- a/src/android/CameraPreviewFragment.java +++ b/src/android/CameraPreviewFragment.java @@ -370,7 +370,7 @@ public void setLocation(Location loc) { } - public void switchToUltraWideCamera(String device, CameraSwitchedCallback cameraSwitchedCallback) { + public void switchCameraTo(String device, CameraSwitchedCallback cameraSwitchedCallback) { Handler mainHandler = new Handler(Looper.getMainLooper()); mainHandler.post(new Runnable() { @SuppressLint("RestrictedApi") diff --git a/src/android/SimpleCameraPreview.java b/src/android/SimpleCameraPreview.java index 10f3443..98658df 100644 --- a/src/android/SimpleCameraPreview.java +++ b/src/android/SimpleCameraPreview.java @@ -78,8 +78,8 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo case "deviceHasUltraWideCamera": return deviceHasUltraWideCamera(callbackContext); - case "switchToUltraWideCamera": - return switchToUltraWideCamera(args.getString(0), callbackContext); + case "switchCameraTo": + return switchCameraTo(args.getString(0), callbackContext); default: break; } @@ -337,13 +337,13 @@ public void run() { } } - private boolean switchToUltraWideCamera(String device, CallbackContext callbackContext) { + private boolean switchCameraTo(String device, CallbackContext callbackContext) { if (fragment == null) { callbackContext.error("Camera is closed, cannot switch camera"); return true; } - fragment.switchToUltraWideCamera(device, (boolean result) -> { + fragment.switchCameraTo(device, (boolean result) -> { PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result); callbackContext.sendPluginResult(pluginResult); }); diff --git a/src/ios/CameraSessionManager.h b/src/ios/CameraSessionManager.h index cfb1f05..d3c82be 100644 --- a/src/ios/CameraSessionManager.h +++ b/src/ios/CameraSessionManager.h @@ -8,7 +8,7 @@ - (void) setupSession:(NSString *)defaultCamera completion:(void(^)(BOOL started))completion options:(NSDictionary *)options photoSettings:(AVCapturePhotoSettings *)photoSettings; - (void) setFlashMode:(NSInteger)flashMode photoSettings:(AVCapturePhotoSettings *)photoSettings; - (void) torchSwitch:(NSInteger)torchState; -- (void) switchToUltraWideCamera:(NSString *)cameraMode completion:(void (^)(BOOL success))completion; +- (void) switchCameraTo:(NSString *)cameraMode completion:(void (^)(BOOL success))completion; - (BOOL) deviceHasUltraWideCamera; - (void) updateOrientation:(AVCaptureVideoOrientation)orientation; - (AVCaptureVideoOrientation) getCurrentOrientation:(UIInterfaceOrientation)toInterfaceOrientation; diff --git a/src/ios/CameraSessionManager.m b/src/ios/CameraSessionManager.m index 0183542..fe1b3de 100644 --- a/src/ios/CameraSessionManager.m +++ b/src/ios/CameraSessionManager.m @@ -176,7 +176,7 @@ - (void) torchSwitch:(NSInteger)torchState{ } } -- (void)switchToUltraWideCamera:(NSString*)cameraMode completion:(void (^)(BOOL success))completion { +- (void)switchCameraTo:(NSString*)cameraMode completion:(void (^)(BOOL success))completion { if (![self deviceHasUltraWideCamera]) { if (completion) { completion(NO); @@ -221,8 +221,6 @@ - (void)switchToUltraWideCamera:(NSString*)cameraMode completion:(void (^)(BOOL } else { NSLog(@"Ultra-wide camera not found"); } - } else { - // Fallback on earlier versions } completion ? completion(cameraSwitched): NULL; diff --git a/src/ios/SimpleCameraPreview.h b/src/ios/SimpleCameraPreview.h index 8ddbc69..0e77694 100644 --- a/src/ios/SimpleCameraPreview.h +++ b/src/ios/SimpleCameraPreview.h @@ -16,7 +16,7 @@ - (void) capture:(CDVInvokedUrlCommand*)command; - (void) setSize:(CDVInvokedUrlCommand*)command; - (void) torchSwitch: (CDVInvokedUrlCommand*)command; -- (void) switchToUltraWideCamera: (CDVInvokedUrlCommand*) command; +- (void) switchCameraTo: (CDVInvokedUrlCommand*) command; - (void) deviceHasUltraWideCamera: (CDVInvokedUrlCommand*) command; - (void) deviceHasFlash: (CDVInvokedUrlCommand*)command; @property (nonatomic) CameraSessionManager *sessionManager; diff --git a/src/ios/SimpleCameraPreview.m b/src/ios/SimpleCameraPreview.m index 6f4856c..feb5f45 100644 --- a/src/ios/SimpleCameraPreview.m +++ b/src/ios/SimpleCameraPreview.m @@ -156,11 +156,11 @@ - (void) torchSwitch:(CDVInvokedUrlCommand*)command{ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } -- (void) switchToUltraWideCamera:(CDVInvokedUrlCommand*)command{ +- (void) switchCameraTo:(CDVInvokedUrlCommand*)command{ NSString *device = [command.arguments objectAtIndex:0]; BOOL cameraSwitched = FALSE; if (self.sessionManager != nil) { - [self.sessionManager switchToUltraWideCamera: device completion:^(BOOL success) { + [self.sessionManager switchCameraTo: device completion:^(BOOL success) { if (success) { NSLog(@"Camera switched successfully"); CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:TRUE]; diff --git a/www/SimpleCameraPreview.js b/www/SimpleCameraPreview.js index 352b794..7b1b9e8 100755 --- a/www/SimpleCameraPreview.js +++ b/www/SimpleCameraPreview.js @@ -28,10 +28,10 @@ SimpleCameraPreview.torchSwitch = function (options, onSuccess, onError) { exec(onSuccess, onError, PLUGIN_NAME, "torchSwitch", [options]); }; -SimpleCameraPreview.switchToUltraWideCamera = function (options, onSuccess, onError) { +SimpleCameraPreview.switchCameraTo = function (options, onSuccess, onError) { options = options || {}; options.captureDevice = options.captureDevice || "default"; - exec(onSuccess, onError, PLUGIN_NAME, "switchToUltraWideCamera", [options.captureDevice]); + exec(onSuccess, onError, PLUGIN_NAME, "switchCameraTo", [options.captureDevice]); }; SimpleCameraPreview.deviceHasFlash = function (onSuccess, onError) { From 694e6ce2d603706bf799364580034e08643dc30b Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Tue, 2 Apr 2024 09:58:09 +0400 Subject: [PATCH 15/21] use ultra-wide-angle instead of wide-angle --- src/android/CameraPreviewFragment.java | 10 +++++----- src/ios/CameraSessionManager.m | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/android/CameraPreviewFragment.java b/src/android/CameraPreviewFragment.java index 94eaa52..df6b73d 100644 --- a/src/android/CameraPreviewFragment.java +++ b/src/android/CameraPreviewFragment.java @@ -388,11 +388,7 @@ public void run() { return; } - if (device.equals("default")) { - cameraSelector = new CameraSelector.Builder() - .requireLensFacing(direction) - .build(); - } else { + if (device.equals("ultra-wide-angle")) { cameraSelector = new CameraSelector.Builder() .addCameraFilter(cameraInfos -> { List backCameras = new ArrayList<>(); @@ -418,6 +414,10 @@ public void run() { } }) .build(); + } else { + cameraSelector = new CameraSelector.Builder() + .requireLensFacing(direction) + .build(); } Size targetResolution = null; diff --git a/src/ios/CameraSessionManager.m b/src/ios/CameraSessionManager.m index fe1b3de..4bde7ef 100644 --- a/src/ios/CameraSessionManager.m +++ b/src/ios/CameraSessionManager.m @@ -56,7 +56,7 @@ - (void) setupSession:(NSString *)defaultCamera completion:(void(^)(BOOL started AVCaptureDevice *videoDevice; videoDevice = [self cameraWithPosition: self.defaultCamera captureDeviceType: AVCaptureDeviceTypeBuiltInWideAngleCamera]; - if ([options[@"captureDevice"] isEqual: @"wide-angle"]) { + if ([options[@"captureDevice"] isEqual: @"ultra-wide-angle"]) { if ([self deviceHasUltraWideCamera]) { if (@available(iOS 13.0, *)) { videoDevice = [self cameraWithPosition: self.defaultCamera captureDeviceType: AVCaptureDeviceTypeBuiltInUltraWideCamera]; @@ -188,7 +188,7 @@ - (void)switchCameraTo:(NSString*)cameraMode completion:(void (^)(BOOL success)) BOOL cameraSwitched = FALSE; if (@available(iOS 13.0, *)) { AVCaptureDevice *ultraWideCamera; - if([cameraMode isEqualToString:@"wide-angle"]) { + if([cameraMode isEqualToString:@"ultra-wide-angle"]) { ultraWideCamera = [self cameraWithPosition:self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInUltraWideCamera]; } else { ultraWideCamera = [self cameraWithPosition:self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera]; From 9705d2903ac9b1d36e312a8a865057bdacf223a7 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Thu, 4 Apr 2024 14:22:59 +0400 Subject: [PATCH 16/21] refactor android part(camera selector) and capture device value into consideration when enabling camera --- src/android/CameraPreviewFragment.java | 163 +++++++++++++------------ src/android/SimpleCameraPreview.java | 15 +++ 2 files changed, 97 insertions(+), 81 deletions(-) diff --git a/src/android/CameraPreviewFragment.java b/src/android/CameraPreviewFragment.java index df6b73d..4513669 100644 --- a/src/android/CameraPreviewFragment.java +++ b/src/android/CameraPreviewFragment.java @@ -85,8 +85,7 @@ public class CameraPreviewFragment extends Fragment { private static float ratio = (4 / (float) 3); private static final String TAG = "SimpleCameraPreview"; - private float minZoomRatio; - private float maxZoomRatio; + private String captureDevice; public CameraPreviewFragment() { @@ -100,6 +99,11 @@ public CameraPreviewFragment(int cameraDirection, CameraStartedCallback cameraSt } catch (JSONException e) { e.printStackTrace(); } + try { + this.captureDevice = options.getString("captureDevice"); + } catch (JSONException e) { + e.printStackTrace(); + } startCameraCallback = cameraStartedCallback; } @@ -132,10 +136,7 @@ public void startCamera() { startCameraCallback.onCameraStarted(new Exception("Unable to start camera")); return; } - - CameraSelector cameraSelector = new CameraSelector.Builder() - .requireLensFacing(direction) - .build(); + CameraSelector cameraSelector = getCameraSelector(captureDevice); Size targetResolution = null; if (targetSize > 0) { @@ -372,88 +373,88 @@ public void setLocation(Location loc) { public void switchCameraTo(String device, CameraSwitchedCallback cameraSwitchedCallback) { Handler mainHandler = new Handler(Looper.getMainLooper()); - mainHandler.post(new Runnable() { - @SuppressLint("RestrictedApi") - @Override - public void run() { - ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(getActivity()); - ProcessCameraProvider cameraProvider = null; - CameraSelector cameraSelector; - try { - cameraProvider = cameraProviderFuture.get(); - } catch (ExecutionException | InterruptedException e) { - Log.e(TAG, "Error occurred while trying to obtain the camera provider: " + e.getMessage()); - e.printStackTrace(); - cameraSwitchedCallback.onSwitch(false); - return; - } - - if (device.equals("ultra-wide-angle")) { - cameraSelector = new CameraSelector.Builder() - .addCameraFilter(cameraInfos -> { - List backCameras = new ArrayList<>(); - for (CameraInfo cameraInfo : cameraInfos) { - if (cameraInfo instanceof Camera2CameraInfoImpl) { - Camera2CameraInfoImpl camera2CameraInfo = (Camera2CameraInfoImpl) cameraInfo; - if (camera2CameraInfo.getLensFacing() == CameraSelector.LENS_FACING_BACK) { - backCameras.add(camera2CameraInfo); - } - } - } + mainHandler.post(() -> { + ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(getActivity()); + ProcessCameraProvider cameraProvider = null; + try { + cameraProvider = cameraProviderFuture.get(); + } catch (ExecutionException | InterruptedException e) { + Log.e(TAG, "Error occurred while trying to obtain the camera provider: " + e.getMessage()); + e.printStackTrace(); + cameraSwitchedCallback.onSwitch(false); + return; + } - Camera2CameraInfoImpl selectedCamera = Collections.min(backCameras, (o1, o2) -> { - Float focalLength1 = o1.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0]; - Float focalLength2 = o2.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0]; - return Float.compare(focalLength1, focalLength2); - }); + CameraSelector cameraSelector = getCameraSelector(device); - if (selectedCamera != null) { - return Collections.singletonList(selectedCamera); - } else { - return cameraInfos; - } - }) - .build(); - } else { - cameraSelector = new CameraSelector.Builder() - .requireLensFacing(direction) - .build(); - } + Size targetResolution = null; + if (targetSize > 0) { + targetResolution = CameraPreviewFragment.calculateResolution(getContext(), targetSize); + } - Size targetResolution = null; - if (targetSize > 0) { - targetResolution = CameraPreviewFragment.calculateResolution(getContext(), targetSize); - } + preview = new Preview.Builder().build(); + imageCapture = new ImageCapture.Builder() + .setTargetResolution(targetResolution) + .build(); - preview = new Preview.Builder().build(); + cameraProvider.unbindAll(); + try { + camera = cameraProvider.bindToLifecycle( + getActivity(), + cameraSelector, + preview, + imageCapture + ); + } catch (IllegalArgumentException e) { + // Error with result in capturing image with default resolution + e.printStackTrace(); imageCapture = new ImageCapture.Builder() - .setTargetResolution(targetResolution) .build(); - - cameraProvider.unbindAll(); - try { - camera = cameraProvider.bindToLifecycle( - getActivity(), - cameraSelector, - preview, - imageCapture - ); - } catch (IllegalArgumentException e) { - // Error with result in capturing image with default resolution - e.printStackTrace(); - imageCapture = new ImageCapture.Builder() - .build(); - camera = cameraProvider.bindToLifecycle( - getActivity(), - cameraSelector, - preview, - imageCapture - ); - } - - preview.setSurfaceProvider(viewFinder.getSurfaceProvider()); - cameraSwitchedCallback.onSwitch(true); + camera = cameraProvider.bindToLifecycle( + getActivity(), + cameraSelector, + preview, + imageCapture + ); } + + preview.setSurfaceProvider(viewFinder.getSurfaceProvider()); + cameraSwitchedCallback.onSwitch(true); }); } + + @SuppressLint("RestrictedApi") + public CameraSelector getCameraSelector(String captureDevice) { + if (captureDevice.equals("ultra-wide-angle")) { + return new CameraSelector.Builder() + .addCameraFilter(cameraInfos -> { + List backCameras = new ArrayList<>(); + for (CameraInfo cameraInfo : cameraInfos) { + if (cameraInfo instanceof Camera2CameraInfoImpl) { + Camera2CameraInfoImpl camera2CameraInfo = (Camera2CameraInfoImpl) cameraInfo; + if (camera2CameraInfo.getLensFacing() == CameraSelector.LENS_FACING_BACK) { + backCameras.add(camera2CameraInfo); + } + } + } + + Camera2CameraInfoImpl selectedCamera = Collections.min(backCameras, (o1, o2) -> { + Float focalLength1 = o1.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0]; + Float focalLength2 = o2.getCameraCharacteristicsCompat().get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0]; + return Float.compare(focalLength1, focalLength2); + }); + + if (selectedCamera != null) { + return Collections.singletonList(selectedCamera); + } else { + return cameraInfos; + } + }) + .build(); + } else { + return new CameraSelector.Builder() + .requireLensFacing(direction) + .build(); + } + } } diff --git a/src/android/SimpleCameraPreview.java b/src/android/SimpleCameraPreview.java index 98658df..6eea021 100644 --- a/src/android/SimpleCameraPreview.java +++ b/src/android/SimpleCameraPreview.java @@ -152,6 +152,15 @@ private boolean enable(JSONObject options, CallbackContext callbackContext) { e.printStackTrace(); } + String captureDevice = ""; + try { + if (options.getString("captureDevice") != null && !options.getString("captureDevice").equals("null")) { + captureDevice = options.getString("captureDevice"); + } + } catch (JSONException | NumberFormatException e) { + e.printStackTrace(); + } + JSONObject cameraPreviewOptions = new JSONObject(); try { cameraPreviewOptions.put("targetSize", targetSize); @@ -159,6 +168,12 @@ private boolean enable(JSONObject options, CallbackContext callbackContext) { e.printStackTrace(); } + try { + cameraPreviewOptions.put("captureDevice", captureDevice); + } catch (JSONException e) { + e.printStackTrace(); + } + fragment = new CameraPreviewFragment(cameraDirection, (err) -> { if (err != null) { callbackContext.error(err.getMessage()); From df81319f7685ae64b3a648a2830e5041479154f1 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Fri, 5 Apr 2024 17:26:55 +0400 Subject: [PATCH 17/21] refactor --- src/android/CameraPreviewFragment.java | 105 +++++++++---------------- src/ios/SimpleCameraPreview.m | 8 +- 2 files changed, 41 insertions(+), 72 deletions(-) diff --git a/src/android/CameraPreviewFragment.java b/src/android/CameraPreviewFragment.java index 4513669..cb85361 100644 --- a/src/android/CameraPreviewFragment.java +++ b/src/android/CameraPreviewFragment.java @@ -136,38 +136,7 @@ public void startCamera() { startCameraCallback.onCameraStarted(new Exception("Unable to start camera")); return; } - CameraSelector cameraSelector = getCameraSelector(captureDevice); - - Size targetResolution = null; - if (targetSize > 0) { - targetResolution = CameraPreviewFragment.calculateResolution(getContext(), targetSize); - } - - preview = new Preview.Builder().build(); - imageCapture = new ImageCapture.Builder() - .setTargetResolution(targetResolution) - .build(); - - cameraProvider.unbindAll(); - try { - camera = cameraProvider.bindToLifecycle( - this, - cameraSelector, - preview, - imageCapture - ); - } catch (IllegalArgumentException e) { - // Error with result in capturing image with default resolution - e.printStackTrace(); - imageCapture = new ImageCapture.Builder() - .build(); - camera = cameraProvider.bindToLifecycle( - this, - cameraSelector, - preview, - imageCapture - ); - } + setUpCamera(captureDevice,cameraProvider); preview.setSurfaceProvider(viewFinder.getSurfaceProvider()); @@ -385,38 +354,7 @@ public void switchCameraTo(String device, CameraSwitchedCallback cameraSwitchedC return; } - CameraSelector cameraSelector = getCameraSelector(device); - - Size targetResolution = null; - if (targetSize > 0) { - targetResolution = CameraPreviewFragment.calculateResolution(getContext(), targetSize); - } - - preview = new Preview.Builder().build(); - imageCapture = new ImageCapture.Builder() - .setTargetResolution(targetResolution) - .build(); - - cameraProvider.unbindAll(); - try { - camera = cameraProvider.bindToLifecycle( - getActivity(), - cameraSelector, - preview, - imageCapture - ); - } catch (IllegalArgumentException e) { - // Error with result in capturing image with default resolution - e.printStackTrace(); - imageCapture = new ImageCapture.Builder() - .build(); - camera = cameraProvider.bindToLifecycle( - getActivity(), - cameraSelector, - preview, - imageCapture - ); - } + setUpCamera(device,cameraProvider); preview.setSurfaceProvider(viewFinder.getSurfaceProvider()); cameraSwitchedCallback.onSwitch(true); @@ -424,9 +362,10 @@ public void switchCameraTo(String device, CameraSwitchedCallback cameraSwitchedC } @SuppressLint("RestrictedApi") - public CameraSelector getCameraSelector(String captureDevice) { + public void setUpCamera(String captureDevice, ProcessCameraProvider cameraProvider) { + CameraSelector cameraSelector; if (captureDevice.equals("ultra-wide-angle")) { - return new CameraSelector.Builder() + cameraSelector = new CameraSelector.Builder() .addCameraFilter(cameraInfos -> { List backCameras = new ArrayList<>(); for (CameraInfo cameraInfo : cameraInfos) { @@ -452,9 +391,41 @@ public CameraSelector getCameraSelector(String captureDevice) { }) .build(); } else { - return new CameraSelector.Builder() + cameraSelector = new CameraSelector.Builder() .requireLensFacing(direction) .build(); } + + Size targetResolution = null; + if (targetSize > 0) { + targetResolution = CameraPreviewFragment.calculateResolution(getContext(), targetSize); + } + + preview = new Preview.Builder().build(); + imageCapture = new ImageCapture.Builder() + .setTargetResolution(targetResolution) + .build(); + + cameraProvider.unbindAll(); + try { + camera = cameraProvider.bindToLifecycle( + getActivity(), + cameraSelector, + preview, + imageCapture + ); + } catch (IllegalArgumentException e) { + // Error with result in capturing image with default resolution + e.printStackTrace(); + imageCapture = new ImageCapture.Builder() + .build(); + camera = cameraProvider.bindToLifecycle( + getActivity(), + cameraSelector, + preview, + imageCapture + ); + } + } } diff --git a/src/ios/SimpleCameraPreview.m b/src/ios/SimpleCameraPreview.m index feb5f45..9455d12 100644 --- a/src/ios/SimpleCameraPreview.m +++ b/src/ios/SimpleCameraPreview.m @@ -74,7 +74,7 @@ - (void) enable:(CDVInvokedUrlCommand*)command { [setupSessionOptions setValue:[NSNumber numberWithInteger:targetSize] forKey:@"targetSize"]; } NSString *captureDevice = config[@"captureDevice"]; - if (captureDevice != [NSNull null] && ![captureDevice isEqual: @""]) { + if (captureDevice && [captureDevice length] > 0) { [setupSessionOptions setValue:captureDevice forKey:@"captureDevice"]; } } @catch(NSException *exception) { @@ -82,15 +82,13 @@ - (void) enable:(CDVInvokedUrlCommand*)command { } } - self.photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:@{AVVideoCodecKey : AVVideoCodecTypeJPEG}]; - NSDictionary *immutableSetupSessionOptions = [setupSessionOptions copy]; - [self.sessionManager setupSession:@"back" completion:^(BOOL started) { + self.photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:@{AVVideoCodecKey : AVVideoCodecTypeJPEG}]; [self.sessionManager setupSession:@"back" completion:^(BOOL started) { dispatch_async(dispatch_get_main_queue(), ^{ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; [pluginResult setKeepCallbackAsBool:true]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }); - } options:immutableSetupSessionOptions photoSettings:self.photoSettings]; + } options:setupSessionOptions photoSettings:self.photoSettings]; } - (void) sessionNotInterrupted:(NSNotification *)notification { From 716451e97d7a930029906f97d21d88c04c081cb1 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Fri, 12 Apr 2024 09:31:11 +0400 Subject: [PATCH 18/21] fix camera bug on ios --- src/ios/CameraSessionManager.m | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ios/CameraSessionManager.m b/src/ios/CameraSessionManager.m index 4bde7ef..c098019 100644 --- a/src/ios/CameraSessionManager.m +++ b/src/ios/CameraSessionManager.m @@ -211,6 +211,7 @@ - (void)switchCameraTo:(NSString*)cameraMode completion:(void (^)(BOOL success)) orientation = [self getCurrentOrientation]; }); [self updateOrientation:orientation]; + self.device = ultraWideCamera; cameraSwitched = TRUE; } else { NSLog(@"Failed to add ultra-wide input to session"); From a8b8d2b9c41f512de2bfd4ccd32c2063cbd1820f Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Fri, 26 Apr 2024 09:40:38 +0400 Subject: [PATCH 19/21] refactor --- src/android/CameraPreviewFragment.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/android/CameraPreviewFragment.java b/src/android/CameraPreviewFragment.java index cb85361..c0a299f 100644 --- a/src/android/CameraPreviewFragment.java +++ b/src/android/CameraPreviewFragment.java @@ -155,6 +155,7 @@ public void deviceHasUltraWideCamera(HasUltraWideCameraCallback hasUltraWideCame } catch (ExecutionException | InterruptedException e) { Log.e(TAG, "Error occurred while trying to obtain the camera provider: " + e.getMessage()); e.printStackTrace(); + hasUltraWideCameraCallback.onResult(false); return; } List cameraInfos = cameraProvider.getAvailableCameraInfos(); @@ -179,11 +180,7 @@ public void deviceHasUltraWideCamera(HasUltraWideCameraCallback hasUltraWideCame } } - if (defaultCamera == true && ultraWideCamera == true) { - hasUltraWideCameraCallback.onResult(true); - } else { - hasUltraWideCameraCallback.onResult(false); - } + hasUltraWideCameraCallback.onResult(defaultCamera == true && ultraWideCamera == true); } public static Size calculateResolution(Context context, int targetSize) { From e40283fa6296e6d2667ceb03636791dff6ad1b84 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Mon, 6 May 2024 10:26:51 +0400 Subject: [PATCH 20/21] refactor code --- src/android/SimpleCameraPreview.java | 2 +- src/ios/CameraSessionManager.m | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/android/SimpleCameraPreview.java b/src/android/SimpleCameraPreview.java index 6eea021..4bc7b40 100644 --- a/src/android/SimpleCameraPreview.java +++ b/src/android/SimpleCameraPreview.java @@ -152,7 +152,7 @@ private boolean enable(JSONObject options, CallbackContext callbackContext) { e.printStackTrace(); } - String captureDevice = ""; + String captureDevice = "default"; try { if (options.getString("captureDevice") != null && !options.getString("captureDevice").equals("null")) { captureDevice = options.getString("captureDevice"); diff --git a/src/ios/CameraSessionManager.m b/src/ios/CameraSessionManager.m index c098019..b81042d 100644 --- a/src/ios/CameraSessionManager.m +++ b/src/ios/CameraSessionManager.m @@ -55,14 +55,11 @@ - (void) setupSession:(NSString *)defaultCamera completion:(void(^)(BOOL started } AVCaptureDevice *videoDevice; - videoDevice = [self cameraWithPosition: self.defaultCamera captureDeviceType: AVCaptureDeviceTypeBuiltInWideAngleCamera]; - if ([options[@"captureDevice"] isEqual: @"ultra-wide-angle"]) { - if ([self deviceHasUltraWideCamera]) { - if (@available(iOS 13.0, *)) { - videoDevice = [self cameraWithPosition: self.defaultCamera captureDeviceType: AVCaptureDeviceTypeBuiltInUltraWideCamera]; - } + videoDevice = [self cameraWithPosition:self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera]; + if ([options[@"captureDevice"] isEqual:@"ultra-wide-angle"] && [self deviceHasUltraWideCamera]) { + if (@available(iOS 13.0, *)) { + videoDevice = [self cameraWithPosition:self.defaultCamera captureDeviceType:AVCaptureDeviceTypeBuiltInUltraWideCamera]; } - } if ([videoDevice hasFlash]) { @@ -229,15 +226,12 @@ - (void)switchCameraTo:(NSString*)cameraMode completion:(void (^)(BOOL success)) } - (BOOL)deviceHasUltraWideCamera { - NSArray *devices; if (@available(iOS 13.0, *)) { AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInUltraWideCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified]; - devices = discoverySession.devices; + return discoverySession.devices.count > 0; } else { - // Fallback on earlier versions + return NO; } - - return devices.count > 0; } - (void)setFlashMode:(NSInteger)flashMode photoSettings:(AVCapturePhotoSettings *)photoSettings { From 0dda35ea8697e934e6210ccb8a839ecd31d5d918 Mon Sep 17 00:00:00 2001 From: parveshneedhoo Date: Mon, 13 May 2024 17:09:06 +0400 Subject: [PATCH 21/21] Bump plugin from version 2.0.17 to 2.0.18 --- CHANGELOG.md | 7 +++++++ README.md | 43 +++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- plugin.xml | 2 +- 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 971b377..656bf04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [2.0.18](https://github.com/spoonconsulting/cordova-plugin-simple-camera-preview/compare/v2.0.17...v2.0.18) (2024-05-12) + +* **iOS:** Added a method "switchCameraTo" to help switch between ultra-wide camera and default camera. ([#68](https://github.com/spoonconsulting/cordova-plugin-simple-camera-preview/pull/68)) +* **Android:** Added a method "switchCameraTo" to help switch between ultra-wide camera and default camera. ([#68](https://github.com/spoonconsulting/cordova-plugin-simple-camera-preview/pull/68)) +* **iOS:** Added a method "deviceHasUltraWideCamera" to check if device has ultra-wide camera. ([#68](https://github.com/spoonconsulting/cordova-plugin-simple-camera-preview/pull/68)) +* **Android:** Added a method "deviceHasUltraWideCamera" to check if device has ultra-wide camera. ([#68](https://github.com/spoonconsulting/cordova-plugin-simple-camera-preview/pull/68)) + ## [2.0.17](https://github.com/spoonconsulting/cordova-plugin-simple-camera-preview/compare/v2.0.16...v2.0.17) (2023-07-04) * **iOS:** Use interfaceOrientation for ios 13+. ([#67](https://github.com/spoonconsulting/cordova-plugin-simple-camera-preview/pull/67)) diff --git a/README.md b/README.md index 393eb5b..d7c493d 100644 --- a/README.md +++ b/README.md @@ -99,3 +99,46 @@ SimpleCameraPreview.setSize(size, () => { console.log("Camera frame size set"); }); ``` + +### deviceHasUltraWideCamera(successCallback, errorCallback) + +Check if device has ultra-wide camera +
+ +```javascript + +SimpleCameraPreview.deviceHasUltraWideCamera(size, (value: boolean) => { + console.log("Device has ultra-wide camera?: ", value); +}); +``` + +### switchCameraTo(option, successCallback, errorCallback) + +Switch camera between ultra-wide or default + +The variable captureDevice can take two values: +```javascript + "ultra-wide-angle" + + or + + "default" +``` +
+ +```javascript + +const params = { + captureDevice: "ultra-wide-angle", +} + +SimpleCameraPreview.switchCameraTo( + params, + (value: unknown) => { + return (typeof value === "boolean" ? value : false); + }, + (e: unknown) => { + console.log("cannot switch camera: ", e); + } +); +``` diff --git a/package-lock.json b/package-lock.json index a2cf1fb..1403cb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@spoonconsulting/cordova-plugin-simple-camera-preview", - "version": "2.0.17", + "version": "2.0.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@spoonconsulting/cordova-plugin-simple-camera-preview", - "version": "2.0.17", + "version": "2.0.18", "license": "Apache 2.0", "devDependencies": {} } diff --git a/package.json b/package.json index f793732..6e0239b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@spoonconsulting/cordova-plugin-simple-camera-preview", - "version": "2.0.17", + "version": "2.0.18", "description": "Cordova plugin that allows camera interaction from HTML code for showing camera preview below or on top of the HTML.", "keywords": [ "cordova", diff --git a/plugin.xml b/plugin.xml index 6d78762..297a332 100644 --- a/plugin.xml +++ b/plugin.xml @@ -1,6 +1,6 @@ - + cordova-plugin-simple-camera-preview Cordova plugin that allows camera interaction from HTML code. Show camera preview popup on top of the HTML.