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

Commit

Permalink
Added torch mode functionality for Android and iOS.
Browse files Browse the repository at this point in the history
  • Loading branch information
BeMacized committed Dec 21, 2020
1 parent b4466e7 commit d5fe2a9
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 33 deletions.
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

* Add zoom support for Android and iOS implementations.
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
117 changes: 88 additions & 29 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,28 +721,54 @@ - (void)resumeVideoRecordingWithResult:(FlutterResult)result {
}

- (void)setFlashModeWithResult:(FlutterResult)result mode:(NSString *)modeStr {
AVCaptureFlashMode 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;
}
_flashMode = mode;
result(nil);
FlashMode mode;
@try {
mode = getFlashModeForString(modeStr);
} @catch (NSError *e) {
result(getFlutterError(e));
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);
}

- (void)startImageStreamWithMessenger:(NSObject<FlutterBinaryMessenger> *)messenger {
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
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

0 comments on commit d5fe2a9

Please sign in to comment.