From 7dea3aacfe72337b58a7a00f409e8ae400027646 Mon Sep 17 00:00:00 2001 From: Andrew Coutts Date: Thu, 18 Jun 2020 09:47:32 -0400 Subject: [PATCH 1/8] Added ability to toggle flash in torch mode in Android. Works while capturing photos and videos --- .../io/flutter/plugins/camera/Camera.java | 32 ++++++++++ .../plugins/camera/MethodCallHandlerImpl.java | 18 ++++++ packages/camera/example/lib/main.dart | 41 ++++++++++++- packages/camera/lib/camera.dart | 59 +++++++++++++++++++ 4 files changed, 148 insertions(+), 2 deletions(-) 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..58d318aca19f 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 @@ -49,6 +49,8 @@ public class Camera { private final Size captureSize; private final Size previewSize; private final boolean enableAudio; + private final boolean flashSupported; + private boolean torchEnabled = false; private CameraDevice cameraDevice; private CameraCaptureSession cameraCaptureSession; @@ -110,12 +112,37 @@ public void onOrientationChanged(int i) { isFrontFacing = characteristics.get(CameraCharacteristics.LENS_FACING) == CameraMetadata.LENS_FACING_FRONT; ResolutionPreset preset = ResolutionPreset.valueOf(resolutionPreset); + Boolean flashInfoAvailable = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + flashSupported = flashInfoAvailable == null ? false : flashInfoAvailable; recordingProfile = CameraUtils.getBestAvailableCamcorderProfileForResolutionPreset(cameraName, preset); captureSize = new Size(recordingProfile.videoFrameWidth, recordingProfile.videoFrameHeight); previewSize = computeBestPreviewSize(cameraName, preset); } + // Will turn the torch on/off as long as the device and camera supports it + public void toggleTorch(boolean enable, @NonNull final Result result) { + try { + if (!isFrontFacing) { + if (flashSupported) { + if (enable) { + captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH); + torchEnabled = true; + } else { + captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); + torchEnabled = false; + } + cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null); + result.success(null); + } else { + result.error("flashFailed", "Flash is not supported on this device", ""); + } + } + } catch (CameraAccessException e) { + result.error("cameraAccess", e.getMessage(), null); + } + } + private void prepareMediaRecorder(String outputFilePath) throws IOException { if (mediaRecorder != null) { mediaRecorder.release(); @@ -292,6 +319,11 @@ private void createCaptureSession( // Create a new capture builder. captureRequestBuilder = cameraDevice.createCaptureRequest(templateType); + // When starting a video recording, re-enable flash torch if we had it enabled before starting + if (torchEnabled) { + captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH); + } + // Build Flutter surface to render to SurfaceTexture surfaceTexture = flutterTexture.surfaceTexture(); surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); 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..f58c957e67d7 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 @@ -123,6 +123,24 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) } break; } + case "enableTorch": + { + try { + camera.toggleTorch(true, result); + } catch (Exception e) { + handleException(e, result); + } + break; + } + case "disableTorch": + { + try { + camera.toggleTorch(false, result); + } catch (Exception e) { + handleException(e, result); + } + break; + } case "dispose": { if (camera != null) { diff --git a/packages/camera/example/lib/main.dart b/packages/camera/example/lib/main.dart index ce8d37457123..75c4dbd0bf86 100644 --- a/packages/camera/example/lib/main.dart +++ b/packages/camera/example/lib/main.dart @@ -43,6 +43,7 @@ class _CameraExampleHomeState extends State VideoPlayerController videoController; VoidCallback videoPlayerListener; bool enableAudio = true; + bool enableTorch = false; @override void initState() { @@ -102,7 +103,12 @@ class _CameraExampleHomeState extends State ), ), _captureControlRowWidget(), - _toggleAudioWidget(), + Row( + children: [ + _toggleAudioWidget(), + _toggleTorchWidget(), + ], + ), Padding( padding: const EdgeInsets.all(5.0), child: Row( @@ -158,6 +164,36 @@ class _CameraExampleHomeState extends State ); } + /// Toggle torch mode + Widget _toggleTorchWidget() { + return Opacity( + opacity: controller != null ? 1.0 : 0.2, + child: Padding( + padding: const EdgeInsets.only(left: 25), + child: Row( + children: [ + const Text('Toggle Torch:'), + Switch( + value: enableTorch, + onChanged: (bool value) async { + if (controller == null) { + return; + } + + setState(() => enableTorch = value); + if (enableTorch) { + await controller.enableTorch(); + } else { + await controller.disableTorch(); + } + }, + ), + ], + ), + ), + ); + } + /// Display the thumbnail of the captured image or video. Widget _thumbnailWidget() { return Expanded( @@ -237,7 +273,7 @@ class _CameraExampleHomeState extends State controller.value.isRecordingVideo ? onStopButtonPressed : null, - ) + ), ], ); } @@ -278,6 +314,7 @@ class _CameraExampleHomeState extends State void onNewCameraSelected(CameraDescription cameraDescription) async { if (controller != null) { await controller.dispose(); + setState(() => enableTorch = false); } controller = CameraController( cameraDescription, diff --git a/packages/camera/lib/camera.dart b/packages/camera/lib/camera.dart index ce9fd9430dde..8ad718d3bcde 100644 --- a/packages/camera/lib/camera.dart +++ b/packages/camera/lib/camera.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:typed_data'; +import 'package:camera/new/camera.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; @@ -158,6 +159,7 @@ class CameraValue { this.isRecordingVideo, this.isTakingPicture, this.isStreamingImages, + this.torchEnabled, bool isRecordingPaused, }) : _isRecordingPaused = isRecordingPaused; @@ -168,6 +170,7 @@ class CameraValue { isTakingPicture: false, isStreamingImages: false, isRecordingPaused: false, + torchEnabled: false, ); /// True after [CameraController.initialize] has completed successfully. @@ -184,6 +187,9 @@ class CameraValue { final bool _isRecordingPaused; + /// True when flash torch is enabled. + final bool torchEnabled; + /// True when camera [isRecordingVideo] and recording is paused. bool get isRecordingPaused => isRecordingVideo && _isRecordingPaused; @@ -209,6 +215,7 @@ class CameraValue { String errorDescription, Size previewSize, bool isRecordingPaused, + bool torchEnabled, }) { return CameraValue( isInitialized: isInitialized ?? this.isInitialized, @@ -218,6 +225,7 @@ class CameraValue { isTakingPicture: isTakingPicture ?? this.isTakingPicture, isStreamingImages: isStreamingImages ?? this.isStreamingImages, isRecordingPaused: isRecordingPaused ?? _isRecordingPaused, + torchEnabled: torchEnabled ?? this.torchEnabled, ); } @@ -229,6 +237,7 @@ class CameraValue { 'isInitialized: $isInitialized, ' 'errorDescription: $errorDescription, ' 'previewSize: $previewSize, ' + 'torchEnabled: $torchEnabled, ' 'isStreamingImages: $isStreamingImages)'; } } @@ -569,6 +578,56 @@ class CameraController extends ValueNotifier { } } + /// Enables flash torch mode + Future enableTorch() async { + if (!value.isInitialized || _isDisposed) { + throw CameraException( + 'Uninitialized CameraController', + 'enableTorch was called on uninitialized CameraController', + ); + } + if (value.isTakingPicture) { + throw CameraException( + 'Previous capture has not returned yet.', + 'takePicture was called before the previous capture returned.', + ); + } + if (value.torchEnabled) { + return; + } + try { + await _channel.invokeMethod('enableTorch'); + value = value.copyWith(torchEnabled: true); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + /// Disables flash torch mode + Future disableTorch() async { + if (!value.isInitialized || _isDisposed) { + throw CameraException( + 'Uninitialized CameraController', + 'disableTorch was called on uninitialized CameraController', + ); + } + if (value.isTakingPicture) { + throw CameraException( + 'Previous capture has not returned yet.', + 'takePicture was called before the previous capture returned.', + ); + } + if (!value.torchEnabled) { + return; + } + try { + await _channel.invokeMethod('disableTorch'); + value = value.copyWith(torchEnabled: false); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + /// Releases the resources of this camera. @override Future dispose() async { From b8f530135bb8d6ea3b8945493969e96367f3c645 Mon Sep 17 00:00:00 2001 From: Andrew Coutts Date: Thu, 18 Jun 2020 11:06:32 -0400 Subject: [PATCH 2/8] Added camera torch support for iOS- works during video recording as well --- packages/camera/ios/Classes/CameraPlugin.m | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/packages/camera/ios/Classes/CameraPlugin.m b/packages/camera/ios/Classes/CameraPlugin.m index 42cdb6d5fdf9..0bc5b6f5f314 100644 --- a/packages/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/ios/Classes/CameraPlugin.m @@ -185,6 +185,7 @@ @interface FLTCam : NSObject Date: Thu, 18 Jun 2020 12:05:19 -0400 Subject: [PATCH 3/8] Added hasTorch() to check if camera has a torch. Example app will disable the torch toggle if a selected camera doesn't support it --- .../io/flutter/plugins/camera/Camera.java | 27 ++++++++++--------- .../plugins/camera/MethodCallHandlerImpl.java | 9 +++++++ .../camera/example/ios/Flutter/.last_build_id | 1 + packages/camera/example/lib/main.dart | 7 +++-- packages/camera/ios/Classes/CameraPlugin.m | 6 +++++ packages/camera/lib/camera.dart | 21 +++++++++++++++ 6 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 packages/camera/example/ios/Flutter/.last_build_id 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 58d318aca19f..14a9a9dde409 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 @@ -123,26 +123,29 @@ public void onOrientationChanged(int i) { // Will turn the torch on/off as long as the device and camera supports it public void toggleTorch(boolean enable, @NonNull final Result result) { try { - if (!isFrontFacing) { - if (flashSupported) { - if (enable) { - captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH); - torchEnabled = true; - } else { - captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); - torchEnabled = false; - } - cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null); - result.success(null); + if (flashSupported) { + if (enable) { + captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH); + torchEnabled = true; } else { - result.error("flashFailed", "Flash is not supported on this device", ""); + captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); + torchEnabled = false; } + cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null); + result.success(null); + } else { + result.error("flashFailed", "Flash is not supported on this device", ""); } } catch (CameraAccessException e) { result.error("cameraAccess", e.getMessage(), null); } } + // Returns true if camera supports the torch + public void hasTorch(@NonNull final Result result) { + result.success(flashSupported); + } + private void prepareMediaRecorder(String outputFilePath) throws IOException { if (mediaRecorder != null) { mediaRecorder.release(); 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 f58c957e67d7..e89d73fb69f4 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 @@ -141,6 +141,15 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) } break; } + case "hasTorch": + { + try { + camera.hasTorch(result); + } catch (Exception e) { + handleException(e, result); + } + break; + } case "dispose": { if (camera != null) { diff --git a/packages/camera/example/ios/Flutter/.last_build_id b/packages/camera/example/ios/Flutter/.last_build_id new file mode 100644 index 000000000000..133c323a68b8 --- /dev/null +++ b/packages/camera/example/ios/Flutter/.last_build_id @@ -0,0 +1 @@ +ca4deb5e1e4d8fdaf00843cb47ee20c7 \ No newline at end of file diff --git a/packages/camera/example/lib/main.dart b/packages/camera/example/lib/main.dart index 75c4dbd0bf86..cf20da97d6bb 100644 --- a/packages/camera/example/lib/main.dart +++ b/packages/camera/example/lib/main.dart @@ -44,6 +44,7 @@ class _CameraExampleHomeState extends State VoidCallback videoPlayerListener; bool enableAudio = true; bool enableTorch = false; + bool torchSupported = false; @override void initState() { @@ -167,7 +168,7 @@ class _CameraExampleHomeState extends State /// Toggle torch mode Widget _toggleTorchWidget() { return Opacity( - opacity: controller != null ? 1.0 : 0.2, + opacity: torchSupported ? 1.0 : 0.2, child: Padding( padding: const EdgeInsets.only(left: 25), child: Row( @@ -176,7 +177,7 @@ class _CameraExampleHomeState extends State Switch( value: enableTorch, onChanged: (bool value) async { - if (controller == null) { + if (controller == null || !torchSupported) { return; } @@ -332,6 +333,8 @@ class _CameraExampleHomeState extends State try { await controller.initialize(); + final hasTorch = await controller.hasTorch(); + setState(() => torchSupported = hasTorch); } on CameraException catch (e) { _showCameraException(e); } diff --git a/packages/camera/ios/Classes/CameraPlugin.m b/packages/camera/ios/Classes/CameraPlugin.m index 0bc5b6f5f314..da43ab63e6cb 100644 --- a/packages/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/ios/Classes/CameraPlugin.m @@ -867,6 +867,12 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)re [_camera toggleTorch:true :result :_camera.captureDevice]; } else if ([@"disableTorch" isEqualToString:call.method]) { [_camera toggleTorch:false :result :_camera.captureDevice]; + } else if ([@"hasTorch" isEqualToString:call.method]) { + if ([_camera.captureDevice hasTorch]) { + result(@(YES)); + } else { + result(@(NO)); + } } else if ([@"initialize" isEqualToString:call.method]) { NSString *cameraName = call.arguments[@"cameraName"]; NSString *resolutionPreset = call.arguments[@"resolutionPreset"]; diff --git a/packages/camera/lib/camera.dart b/packages/camera/lib/camera.dart index 8ad718d3bcde..5e9caa8661d7 100644 --- a/packages/camera/lib/camera.dart +++ b/packages/camera/lib/camera.dart @@ -628,6 +628,27 @@ class CameraController extends ValueNotifier { } } + /// Check if the camera supports torch mode + Future hasTorch() async { + if (!value.isInitialized || _isDisposed) { + throw CameraException( + 'Uninitialized CameraController', + 'disableTorch was called on uninitialized CameraController', + ); + } + if (value.isTakingPicture) { + throw CameraException( + 'Previous capture has not returned yet.', + 'takePicture was called before the previous capture returned.', + ); + } + try { + return _channel.invokeMethod('hasTorch'); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + /// Releases the resources of this camera. @override Future dispose() async { From e9b7030da65898dc4bcceab219ed76f2fe1c11d9 Mon Sep 17 00:00:00 2001 From: Andrew Coutts Date: Thu, 18 Jun 2020 12:14:30 -0400 Subject: [PATCH 4/8] Bump version and update changelog. --- packages/camera/CHANGELOG.md | 3 +++ packages/camera/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/camera/CHANGELOG.md b/packages/camera/CHANGELOG.md index 43e5f463604c..09e7d7767e39 100644 --- a/packages/camera/CHANGELOG.md +++ b/packages/camera/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.5.9 +* Added `enableTorch`, `disableTorch`, and `hasTorch` to `CameraController` to enable the use of the flash in torch mode (continuous on). + ## 0.5.8+3 * Fix bug in usage example in README.md diff --git a/packages/camera/pubspec.yaml b/packages/camera/pubspec.yaml index a1ce42370191..2678569faf9a 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.8+3 +version: 0.5.9 homepage: https://github.com/flutter/plugins/tree/master/packages/camera From 4667cbc2e3e4a07def2285b811730ce4b8c88fa2 Mon Sep 17 00:00:00 2001 From: Andrew Coutts Date: Thu, 18 Jun 2020 13:20:49 -0400 Subject: [PATCH 5/8] Fix formatting and analyzer errors --- packages/camera/ios/Classes/CameraPlugin.m | 23 +++++++++++----------- packages/camera/lib/camera.dart | 1 - 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/camera/ios/Classes/CameraPlugin.m b/packages/camera/ios/Classes/CameraPlugin.m index da43ab63e6cb..54f1ba4591f3 100644 --- a/packages/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/ios/Classes/CameraPlugin.m @@ -754,11 +754,12 @@ - (BOOL)setupWriterForPath:(NSString *)path { [_audioOutput setSampleBufferDelegate:self queue:_dispatchQueue]; } - // When starting video capture the torch will be turned off, so re-enable it here so it's started in time for recording to start. + // When starting video capture the torch will be turned off, so re-enable it here so it's started + // in time for recording to start. if (_isTorchEnabled) { - [self.captureDevice lockForConfiguration:nil]; - [self.captureDevice setTorchModeOnWithLevel:AVCaptureMaxAvailableTorchLevel error:nil]; - [self.captureDevice unlockForConfiguration]; + [self.captureDevice lockForConfiguration:nil]; + [self.captureDevice setTorchModeOnWithLevel:AVCaptureMaxAvailableTorchLevel error:nil]; + [self.captureDevice unlockForConfiguration]; } [_videoWriter addInput:_videoWriterInput]; @@ -864,15 +865,15 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)re } result(reply); } else if ([@"enableTorch" isEqualToString:call.method]) { - [_camera toggleTorch:true :result :_camera.captureDevice]; + [_camera toggleTorch:true :result :_camera.captureDevice]; } else if ([@"disableTorch" isEqualToString:call.method]) { - [_camera toggleTorch:false :result :_camera.captureDevice]; + [_camera toggleTorch:false :result :_camera.captureDevice]; } else if ([@"hasTorch" isEqualToString:call.method]) { - if ([_camera.captureDevice hasTorch]) { - result(@(YES)); - } else { - result(@(NO)); - } + if ([_camera.captureDevice hasTorch]) { + result(@(YES)); + } else { + result(@(NO)); + } } else if ([@"initialize" isEqualToString:call.method]) { NSString *cameraName = call.arguments[@"cameraName"]; NSString *resolutionPreset = call.arguments[@"resolutionPreset"]; diff --git a/packages/camera/lib/camera.dart b/packages/camera/lib/camera.dart index 5e9caa8661d7..658db268360b 100644 --- a/packages/camera/lib/camera.dart +++ b/packages/camera/lib/camera.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:camera/new/camera.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; From 0a926532c312101916e25d128898459fb7f3216c Mon Sep 17 00:00:00 2001 From: Andrew Coutts Date: Thu, 18 Jun 2020 13:38:51 -0400 Subject: [PATCH 6/8] Fixed formatting following requirements --- .../plugins/camera/MethodCallHandlerImpl.java | 42 +++++++------- packages/camera/ios/Classes/CameraPlugin.m | 55 +++++++++---------- .../ios/Classes/FLTConnectivityPlugin.m | 4 +- 3 files changed, 51 insertions(+), 50 deletions(-) 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 e89d73fb69f4..68732a96f2d9 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 @@ -124,32 +124,32 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result) break; } case "enableTorch": - { - try { - camera.toggleTorch(true, result); - } catch (Exception e) { - handleException(e, result); + { + try { + camera.toggleTorch(true, result); + } catch (Exception e) { + handleException(e, result); + } + break; } - break; - } case "disableTorch": - { - try { - camera.toggleTorch(false, result); - } catch (Exception e) { - handleException(e, result); + { + try { + camera.toggleTorch(false, result); + } catch (Exception e) { + handleException(e, result); + } + break; } - break; - } case "hasTorch": - { - try { - camera.hasTorch(result); - } catch (Exception e) { - handleException(e, result); + { + try { + camera.hasTorch(result); + } catch (Exception e) { + handleException(e, result); + } + break; } - break; - } case "dispose": { if (camera != null) { diff --git a/packages/camera/ios/Classes/CameraPlugin.m b/packages/camera/ios/Classes/CameraPlugin.m index 54f1ba4591f3..2a47ffef26cc 100644 --- a/packages/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/ios/Classes/CameraPlugin.m @@ -670,31 +670,30 @@ - (void)stopImageStream { } } -- (void)toggleTorch:(bool) enabled :(FlutterResult) result :(AVCaptureDevice*) device { - NSLog(@"[toggleTorch] Calling with enabled: %s _isTorchEnabled: %s", enabled == true ? "true" :"false", _isTorchEnabled == true ? "true" :"false"); - if ([device hasTorch]) { - [device lockForConfiguration:nil]; - if (!enabled) { - [device setTorchMode:AVCaptureTorchModeOff]; - _isTorchEnabled = false; - result(nil); - } - else { - NSError *anyError; - BOOL success = [device setTorchModeOnWithLevel:AVCaptureMaxAvailableTorchLevel error:&anyError]; - [device unlockForConfiguration]; - if (!success) { - result(getFlutterError(anyError)); - } else { - _isTorchEnabled = true; - result(nil); - } - } +- (void)toggleTorch:(bool)enabled:(FlutterResult)result:(AVCaptureDevice *)device { + NSLog(@"[toggleTorch] Calling with enabled: %s _isTorchEnabled: %s", + enabled == true ? "true" : "false", _isTorchEnabled == true ? "true" : "false"); + if ([device hasTorch]) { + [device lockForConfiguration:nil]; + if (!enabled) { + [device setTorchMode:AVCaptureTorchModeOff]; + _isTorchEnabled = false; + result(nil); } else { - result([FlutterError errorWithCode:@"UNAVAILABLE" - message:@"Torch is unavailable" - details:nil]); + NSError *anyError; + BOOL success = [device setTorchModeOnWithLevel:AVCaptureMaxAvailableTorchLevel + error:&anyError]; + [device unlockForConfiguration]; + if (!success) { + result(getFlutterError(anyError)); + } else { + _isTorchEnabled = true; + result(nil); + } } + } else { + result([FlutterError errorWithCode:@"UNAVAILABLE" message:@"Torch is unavailable" details:nil]); + } } - (BOOL)setupWriterForPath:(NSString *)path { @@ -753,7 +752,7 @@ - (BOOL)setupWriterForPath:(NSString *)path { [_videoWriter addInput:_audioWriterInput]; [_audioOutput setSampleBufferDelegate:self queue:_dispatchQueue]; } - + // When starting video capture the torch will be turned off, so re-enable it here so it's started // in time for recording to start. if (_isTorchEnabled) { @@ -865,14 +864,14 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)re } result(reply); } else if ([@"enableTorch" isEqualToString:call.method]) { - [_camera toggleTorch:true :result :_camera.captureDevice]; + [_camera toggleTorch:true:result:_camera.captureDevice]; } else if ([@"disableTorch" isEqualToString:call.method]) { - [_camera toggleTorch:false :result :_camera.captureDevice]; + [_camera toggleTorch:false:result:_camera.captureDevice]; } else if ([@"hasTorch" isEqualToString:call.method]) { if ([_camera.captureDevice hasTorch]) { - result(@(YES)); + result(@(YES)); } else { - result(@(NO)); + result(@(NO)); } } else if ([@"initialize" isEqualToString:call.method]) { NSString *cameraName = call.arguments[@"cameraName"]; diff --git a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m b/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m index 526bee25d561..0a65409b3828 100644 --- a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m +++ b/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m @@ -156,7 +156,9 @@ - (NSString*)convertCLAuthorizationStatusToString:(CLAuthorizationStatus)status case kCLAuthorizationStatusAuthorizedWhenInUse: { return @"authorizedWhenInUse"; } - default: { return @"unknown"; } + default: { + return @"unknown"; + } } } From bc141f6299df41af7f66953f63f354cad5de13d9 Mon Sep 17 00:00:00 2001 From: Andrew Coutts Date: Thu, 18 Jun 2020 14:03:25 -0400 Subject: [PATCH 7/8] Revert formatting fix in connectivity lib (unrelated to this PR) --- .../connectivity/ios/Classes/FLTConnectivityPlugin.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m b/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m index 0a65409b3828..526bee25d561 100644 --- a/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m +++ b/packages/connectivity/connectivity/ios/Classes/FLTConnectivityPlugin.m @@ -156,9 +156,7 @@ - (NSString*)convertCLAuthorizationStatusToString:(CLAuthorizationStatus)status case kCLAuthorizationStatusAuthorizedWhenInUse: { return @"authorizedWhenInUse"; } - default: { - return @"unknown"; - } + default: { return @"unknown"; } } } From 8a2e1cda840dd8bc54c2f8f52b8a1cda0fa64e34 Mon Sep 17 00:00:00 2001 From: Andrew Coutts Date: Mon, 19 Oct 2020 21:14:29 -0400 Subject: [PATCH 8/8] Fix compilation errors --- .../example/ios/Flutter/Flutter.podspec | 18 ++++++++++++++++++ .../ios/Runner.xcodeproj/project.pbxproj | 19 ++++--------------- .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++++++++ packages/camera/ios/Classes/CameraPlugin.m | 3 +-- 4 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 packages/camera/example/ios/Flutter/Flutter.podspec create mode 100644 packages/camera/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/packages/camera/example/ios/Flutter/Flutter.podspec b/packages/camera/example/ios/Flutter/Flutter.podspec new file mode 100644 index 000000000000..5ca30416bac0 --- /dev/null +++ b/packages/camera/example/ios/Flutter/Flutter.podspec @@ -0,0 +1,18 @@ +# +# NOTE: This podspec is NOT to be published. It is only used as a local source! +# + +Pod::Spec.new do |s| + s.name = 'Flutter' + s.version = '1.0.0' + s.summary = 'High-performance, high-fidelity mobile apps.' + s.description = <<-DESC +Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. + DESC + s.homepage = 'https://flutter.io' + s.license = { :type => 'MIT' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } + s.ios.deployment_target = '8.0' + s.vendored_frameworks = 'Flutter.framework' +end diff --git a/packages/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/example/ios/Runner.xcodeproj/project.pbxproj index 862ee64fb666..0096f9662da9 100644 --- a/packages/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,11 +9,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 75201D617916C49BDEDF852A /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 620DDA07C00B5FF2F937CB5B /* libPods-Runner.a */; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -28,8 +24,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -40,7 +34,6 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 483D985F075B951ADBAD218E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 620DDA07C00B5FF2F937CB5B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -48,7 +41,6 @@ 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -63,8 +55,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 75201D617916C49BDEDF852A /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -83,9 +73,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -229,7 +217,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 3E30118C54AB12C3EB9EDF27 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -269,9 +257,12 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -315,7 +306,6 @@ /* Begin XCBuildConfiguration section */ 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; @@ -372,7 +362,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; diff --git a/packages/camera/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/camera/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/camera/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/camera/ios/Classes/CameraPlugin.m b/packages/camera/ios/Classes/CameraPlugin.m index 60425cfced31..485acfaaf648 100644 --- a/packages/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/ios/Classes/CameraPlugin.m @@ -657,7 +657,7 @@ - (void)stopImageStream { } } -- (void)toggleTorch:(bool)enabled:(FlutterResult)result:(AVCaptureDevice *)device { +- (void)toggleTorch:(bool)enabled :(FlutterResult)result :(AVCaptureDevice *)device { NSLog(@"[toggleTorch] Calling with enabled: %s _isTorchEnabled: %s", enabled == true ? "true" : "false", _isTorchEnabled == true ? "true" : "false"); if ([device hasTorch]) { @@ -854,7 +854,6 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)re } else { result(FlutterMethodNotImplemented); } - result(reply); } else if ([@"enableTorch" isEqualToString:call.method]) { [_camera toggleTorch:true:result:_camera.captureDevice]; } else if ([@"disableTorch" isEqualToString:call.method]) {