diff --git a/packages/camera/CHANGELOG.md b/packages/camera/CHANGELOG.md index 8a3f248433eb..2411c10c8a98 100644 --- a/packages/camera/CHANGELOG.md +++ b/packages/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.7 + +* Add zoom feature. + ## 0.5.6+4 * Android: Use CameraDevice.TEMPLATE_RECORD to improve image streaming. diff --git a/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java index 0fcda278d836..947a2e316213 100644 --- a/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -7,6 +7,7 @@ import android.app.Activity; import android.content.Context; import android.graphics.ImageFormat; +import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; @@ -61,6 +62,10 @@ public class Camera { private CamcorderProfile recordingProfile; private int currentOrientation = ORIENTATION_UNKNOWN; + public float zoomLevel = 1f; + private Rect zoom; + protected CameraCharacteristics cameraCharacteristics; + // Mirrors camera.dart public enum ResolutionPreset { low, @@ -106,6 +111,8 @@ public void onOrientationChanged(int i) { characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); //noinspection ConstantConditions sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); + cameraCharacteristics = characteristics; + //noinspection ConstantConditions isFrontFacing = characteristics.get(CameraCharacteristics.LENS_FACING) == CameraMetadata.LENS_FACING_FRONT; @@ -249,6 +256,7 @@ public void takePicture(String filePath, @NonNull final Result result) { cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(pictureImageReader.getSurface()); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getMediaOrientation()); + setScalerCropRegion(captureBuilder, zoom); cameraCaptureSession.capture( captureBuilder.build(), @@ -320,6 +328,7 @@ public void onConfigured(@NonNull CameraCaptureSession session) { cameraCaptureSession = session; captureRequestBuilder.set( CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); + setScalerCropRegion(captureRequestBuilder, zoom); cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null); if (onSuccessCallback != null) { onSuccessCallback.run(); @@ -510,6 +519,41 @@ public void dispose() { orientationEventListener.disable(); } + public void zoom(int step) throws CameraAccessException { + changeZoom(step); + } + + private void changeZoom(int step) throws CameraAccessException { + calculateZoom(step); + setScalerCropRegion(captureRequestBuilder, zoom); + cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null); + } + + private void calculateZoom(int step) { + zoomLevel += step; + + if (zoomLevel < 1f) { + zoomLevel = 1f; + return; + } + + Rect rect = cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); + + float ratio = (float) 1 / zoomLevel; + int croppedWidth = rect.width() - Math.round((float) rect.width() * ratio); + int croppedHeight = rect.height() - Math.round((float) rect.height() * ratio); + zoom = + new Rect( + croppedWidth / 2, + croppedHeight / 2, + rect.width() - croppedWidth / 2, + rect.height() - croppedHeight / 2); + } + + private void setScalerCropRegion(CaptureRequest.Builder captureRequestBuilder, Rect zoom) { + captureRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom); + } + private int getMediaOrientation() { final int sensorOrientationOffset = (currentOrientation == ORIENTATION_UNKNOWN) diff --git a/packages/camera/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index cb58d19a9a02..4567e372a3d5 100644 --- a/packages/camera/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -77,6 +77,36 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) camera.takePicture(call.argument("path"), result); break; } + case "zoom": + { + try { + camera.zoom(call.argument("step")); + result.success(null); + } catch (CameraAccessException e) { + result.error("CameraAccess", e.getMessage(), null); + } + break; + } + case "zoomIn": + { + try { + camera.zoom(1); + result.success(null); + } catch (CameraAccessException e) { + result.error("CameraAccess", e.getMessage(), null); + } + break; + } + case "zoomOut": + { + try { + camera.zoom(-1); + result.success(null); + } catch (CameraAccessException e) { + result.error("CameraAccess", e.getMessage(), null); + } + break; + } case "prepareForVideoRecording": { // This optimization is not required for Android. diff --git a/packages/camera/example/lib/main.dart b/packages/camera/example/lib/main.dart index 1c4b11672530..5ec7739993ec 100644 --- a/packages/camera/example/lib/main.dart +++ b/packages/camera/example/lib/main.dart @@ -235,7 +235,17 @@ class _CameraExampleHomeState extends State controller.value.isRecordingVideo ? onStopButtonPressed : null, - ) + ), + IconButton( + icon: const Icon(Icons.zoom_in), + color: Colors.blue, + onPressed: controller != null ? onZoomInButtonPressed : null, + ), + IconButton( + icon: const Icon(Icons.zoom_out), + color: Colors.blue, + onPressed: controller != null ? onZoomOutButtonPressed : null, + ), ], ); } @@ -329,6 +339,20 @@ class _CameraExampleHomeState extends State }); } + void onZoomInButtonPressed() { + if (controller == null) { + return; + } + controller.zoomIn(); + } + + void onZoomOutButtonPressed() { + if (controller == null) { + return; + } + controller.zoomOut(); + } + void onPauseButtonPressed() { pauseVideoRecording().then((_) { if (mounted) setState(() {}); diff --git a/packages/camera/ios/Classes/CameraPlugin.m b/packages/camera/ios/Classes/CameraPlugin.m index 42cdb6d5fdf9..08a5cb32ef91 100644 --- a/packages/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/ios/Classes/CameraPlugin.m @@ -197,6 +197,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName enableAudio:(BOOL)enableAudio dispatchQueue:(dispatch_queue_t)dispatchQueue error:(NSError **)error; +@property(nonatomic, assign) int zoom; - (void)start; - (void)stop; @@ -205,6 +206,7 @@ - (void)stopVideoRecordingWithResult:(FlutterResult)result; - (void)startImageStreamWithMessenger:(NSObject *)messenger; - (void)stopImageStream; - (void)captureToFile:(NSString *)filename result:(FlutterResult)result; +- (void)zoom:(NSUInteger *)step; @end @implementation FLTCam { @@ -219,6 +221,8 @@ - (instancetype)initWithCameraName:(NSString *)cameraName dispatchQueue:(dispatch_queue_t)dispatchQueue error:(NSError **)error { self = [super init]; + _zoom = 1; + NSAssert(self, @"super init cannot be nil"); @try { _resolutionPreset = getResolutionPresetForString(resolutionPreset); @@ -272,6 +276,19 @@ - (void)stop { [_captureSession stopRunning]; } +- (void)zoom:(NSUInteger *)step { + _zoom += step; + + if (_zoom < 1) { + _zoom = 1; + return; + } + + [_captureDevice lockForConfiguration:NULL]; + [_captureDevice setVideoZoomFactor:_zoom]; + [_captureDevice unlockForConfiguration]; +} + - (void)captureToFile:(NSString *)path result:(FlutterResult)result { AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; if (_resolutionPreset == max) { @@ -871,6 +888,16 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)re } else if ([@"stopImageStream" isEqualToString:call.method]) { [_camera stopImageStream]; result(nil); + } else if ([@"zoomIn" isEqualToString:call.method]) { + [_camera zoom:1]; + result(nil); + } else if ([@"zoomOut" isEqualToString:call.method]) { + [_camera zoom:-1]; + result(nil); + } else if ([@"zoom" isEqualToString:call.method]) { + NSUInteger step = ((NSNumber *)call.arguments[@"step"]).unsignedIntegerValue; + [_camera zoom:step]; + result(nil); } else if ([@"pauseVideoRecording" isEqualToString:call.method]) { [_camera pauseVideoRecording]; result(nil); diff --git a/packages/camera/lib/camera.dart b/packages/camera/lib/camera.dart index ee1892c4cbc0..687e72f127fc 100644 --- a/packages/camera/lib/camera.dart +++ b/packages/camera/lib/camera.dart @@ -585,4 +585,20 @@ class CameraController extends ValueNotifier { await _eventSubscription?.cancel(); } } + + Future zoomIn() async { + await _channel.invokeMethod('zoomIn'); + } + + /// + /// change zoom by specific [step]. + /// with a negative step, the zoom will be 1 + /// + Future zoom(int step) async { + await _channel.invokeMethod('zoom', {'step': step}); + } + + Future zoomOut() async { + await _channel.invokeMethod('zoomOut'); + } } diff --git a/packages/camera/pubspec.yaml b/packages/camera/pubspec.yaml index aae2d6e9112b..431e3344f6da 100644 --- a/packages/camera/pubspec.yaml +++ b/packages/camera/pubspec.yaml @@ -2,7 +2,7 @@ name: camera description: A Flutter plugin for getting information about and controlling the camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video, and streaming image buffers to dart. -version: 0.5.6+3 +version: 0.5.7 authors: - Flutter Team diff --git a/packages/connectivity/ios/Classes/ConnectivityPlugin.m b/packages/connectivity/ios/Classes/ConnectivityPlugin.m index c69871175b01..346e5f1777e2 100644 --- a/packages/connectivity/ios/Classes/ConnectivityPlugin.m +++ b/packages/connectivity/ios/Classes/ConnectivityPlugin.m @@ -155,7 +155,9 @@ - (NSString*)convertCLAuthorizationStatusToString:(CLAuthorizationStatus)status case kCLAuthorizationStatusAuthorizedWhenInUse: { return @"authorizedWhenInUse"; } - default: { return @"unknown"; } + default: { + return @"unknown"; + } } }