Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

[camera] Add implementations for the torch flash mode. #3338

Merged
merged 3 commits into from Dec 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/camera/CHANGELOG.md
@@ -1,3 +1,7 @@
## 0.6.3

* Adds torch mode as a flash mode for Android and iOS implementations.

## 0.6.2+1

* Fix the API documentation for the `CameraController.takePicture` method.
Expand Down
Expand Up @@ -532,7 +532,6 @@ public void setFlashMode(@NonNull final Result result, FlashMode mode)
return;
}
// Get flash

this.flashMode = mode;
initPreviewCaptureBuilder();
this.cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
Expand All @@ -553,11 +552,16 @@ private void initPreviewCaptureBuilder() {
captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
break;
case always:
default:
captureRequestBuilder.set(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
break;
case torch:
default:
captureRequestBuilder.set(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
break;
}
}

Expand Down
Expand Up @@ -4,7 +4,8 @@
public enum FlashMode {
off,
auto,
always;
always,
torch;

public static FlashMode getValueForString(String modeStr) {
try {
Expand Down
Expand Up @@ -16,6 +16,10 @@ public void getValueForString_returns_correct_values() {
"Returns FlashMode.always for 'always'",
FlashMode.getValueForString("always"),
FlashMode.always);
assertEquals(
"Returns FlashMode.torch for 'torch'",
FlashMode.getValueForString("torch"),
FlashMode.torch);
}

@Test
Expand Down
9 changes: 9 additions & 0 deletions packages/camera/camera/example/lib/main.dart
Expand Up @@ -256,6 +256,15 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
? () => onFlashModeButtonPressed(FlashMode.always)
: null,
),
IconButton(
icon: const Icon(Icons.highlight),
color: controller?.value?.flashMode == FlashMode.torch
? Colors.orange
: Colors.blue,
onPressed: controller != null
? () => onFlashModeButtonPressed(FlashMode.torch)
: null,
),
],
);
}
Expand Down
99 changes: 79 additions & 20 deletions packages/camera/camera/ios/Classes/CameraPlugin.m
Expand Up @@ -134,13 +134,23 @@ - (UIImageOrientation)getImageRotation {
}
@end

static AVCaptureFlashMode getFlashModeForString(NSString *mode) {
// Mirrors FlashMode in flash_mode.dart
typedef enum {
FlashModeOff,
FlashModeAuto,
FlashModeAlways,
FlashModeTorch,
} FlashMode;

static FlashMode getFlashModeForString(NSString *mode) {
if ([mode isEqualToString:@"off"]) {
return AVCaptureFlashModeOff;
return FlashModeOff;
} else if ([mode isEqualToString:@"auto"]) {
return AVCaptureFlashModeAuto;
return FlashModeAuto;
} else if ([mode isEqualToString:@"always"]) {
return AVCaptureFlashModeOn;
return FlashModeAlways;
} else if ([mode isEqualToString:@"torch"]) {
return FlashModeTorch;
} else {
NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
code:NSURLErrorUnknown
Expand All @@ -152,6 +162,20 @@ static AVCaptureFlashMode getFlashModeForString(NSString *mode) {
}
}

static AVCaptureFlashMode getAVCaptureFlashModeForFlashMode(FlashMode mode) {
switch (mode) {
case FlashModeOff:
return AVCaptureFlashModeOff;
case FlashModeAuto:
return AVCaptureFlashModeAuto;
case FlashModeAlways:
return AVCaptureFlashModeOn;
case FlashModeTorch:
default:
return -1;
}
}

// Mirrors ResolutionPreset in camera.dart
typedef enum {
veryLow,
Expand Down Expand Up @@ -219,7 +243,7 @@ @interface FLTCam : NSObject <FlutterTexture,
@property(assign, nonatomic) BOOL isAudioSetup;
@property(assign, nonatomic) BOOL isStreamingImages;
@property(assign, nonatomic) ResolutionPreset resolutionPreset;
@property(assign, nonatomic) AVCaptureFlashMode flashMode;
@property(assign, nonatomic) FlashMode flashMode;
@property(assign, nonatomic) CMTime lastVideoSampleTime;
@property(assign, nonatomic) CMTime lastAudioSampleTime;
@property(assign, nonatomic) CMTime videoTimeOffset;
Expand Down Expand Up @@ -250,7 +274,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName
_enableAudio = enableAudio;
_dispatchQueue = dispatchQueue;
_captureSession = [[AVCaptureSession alloc] init];
_flashMode = AVCaptureFlashModeAuto;
_flashMode = FlashModeAuto;

_captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName];
NSError *localError = nil;
Expand Down Expand Up @@ -303,7 +327,10 @@ - (void)captureToFile:(FlutterResult)result API_AVAILABLE(ios(10)) {
if (_resolutionPreset == max) {
[settings setHighResolutionPhotoEnabled:YES];
}
[settings setFlashMode:_flashMode];
AVCaptureFlashMode avFlashMode = getAVCaptureFlashModeForFlashMode(_flashMode);
if (avFlashMode != -1) {
[settings setFlashMode:avFlashMode];
}
NSError *error;
NSString *path = [self getTemporaryFilePathWithExtension:@"jpg"
subfolder:@"pictures"
Expand Down Expand Up @@ -694,25 +721,51 @@ - (void)resumeVideoRecordingWithResult:(FlutterResult)result {
}

- (void)setFlashModeWithResult:(FlutterResult)result mode:(NSString *)modeStr {
AVCaptureFlashMode mode;
FlashMode mode;
@try {
mode = getFlashModeForString(modeStr);
} @catch (NSError *e) {
result(getFlutterError(e));
return;
}
if (!_captureDevice.hasFlash) {
result([FlutterError errorWithCode:@"setFlashModeFailed"
message:@"Device does not have flash capabilities"
details:nil]);
return;
}
if (![_capturePhotoOutput.supportedFlashModes
containsObject:[NSNumber numberWithInt:((int)mode)]]) {
result([FlutterError errorWithCode:@"setFlashModeFailed"
message:@"Device does not support this specific flash mode"
details:nil]);
return;
if (mode == FlashModeTorch) {
if (!_captureDevice.hasTorch) {
result([FlutterError errorWithCode:@"setFlashModeFailed"
message:@"Device does not support torch mode"
details:nil]);
return;
}
if (!_captureDevice.isTorchAvailable) {
result([FlutterError errorWithCode:@"setFlashModeFailed"
message:@"Torch mode is currently not available"
details:nil]);
return;
}
if (_captureDevice.torchMode != AVCaptureTorchModeOn) {
[_captureDevice lockForConfiguration:nil];
[_captureDevice setTorchMode:AVCaptureTorchModeOn];
[_captureDevice unlockForConfiguration];
}
} else {
if (!_captureDevice.hasFlash) {
result([FlutterError errorWithCode:@"setFlashModeFailed"
message:@"Device does not have flash capabilities"
details:nil]);
return;
}
AVCaptureFlashMode avFlashMode = getAVCaptureFlashModeForFlashMode(mode);
if (![_capturePhotoOutput.supportedFlashModes
containsObject:[NSNumber numberWithInt:((int)avFlashMode)]]) {
result([FlutterError errorWithCode:@"setFlashModeFailed"
message:@"Device does not support this specific flash mode"
details:nil]);
return;
}
if (_captureDevice.torchMode != AVCaptureTorchModeOff) {
[_captureDevice lockForConfiguration:nil];
[_captureDevice setTorchMode:AVCaptureTorchModeOff];
[_captureDevice unlockForConfiguration];
}
}
_flashMode = mode;
result(nil);
Expand Down Expand Up @@ -854,6 +907,12 @@ - (BOOL)setupWriterForPath:(NSString *)path {
[_audioOutput setSampleBufferDelegate:self queue:_dispatchQueue];
}

if (_flashMode == FlashModeTorch) {
[self.captureDevice lockForConfiguration:nil];
[self.captureDevice setTorchMode:AVCaptureTorchModeOn];
[self.captureDevice unlockForConfiguration];
}

[_videoWriter addInput:_videoWriterInput];
[_captureVideoOutput setSampleBufferDelegate:self queue:_dispatchQueue];

Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera/pubspec.yaml
Expand Up @@ -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.6.2+1
version: 0.6.3
homepage: https://github.com/flutter/plugins/tree/master/packages/camera/camera

dependencies:
Expand Down
Expand Up @@ -478,12 +478,15 @@ void main() {
);

// Act
await camera.setFlashMode(cameraId, FlashMode.torch);
await camera.setFlashMode(cameraId, FlashMode.always);
await camera.setFlashMode(cameraId, FlashMode.auto);
await camera.setFlashMode(cameraId, FlashMode.off);

// Assert
expect(channel.log, <Matcher>[
isMethodCall('setFlashMode',
arguments: {'cameraId': cameraId, 'mode': 'torch'}),
isMethodCall('setFlashMode',
arguments: {'cameraId': cameraId, 'mode': 'always'}),
isMethodCall('setFlashMode',
Expand Down