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

Commit 622ba57

Browse files
authored
[camera] Add implementations for the torch flash mode. (#3338)
* Added torch mode functionality for Android and iOS. * Format objective c code
1 parent 7bc9aa2 commit 622ba57

File tree

8 files changed

+108
-24
lines changed

8 files changed

+108
-24
lines changed

packages/camera/camera/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.6.3
2+
3+
* Adds torch mode as a flash mode for Android and iOS implementations.
4+
15
## 0.6.2+1
26

37
* Fix the API documentation for the `CameraController.takePicture` method.

packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,6 @@ public void setFlashMode(@NonNull final Result result, FlashMode mode)
532532
return;
533533
}
534534
// Get flash
535-
536535
this.flashMode = mode;
537536
initPreviewCaptureBuilder();
538537
this.cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
@@ -553,11 +552,16 @@ private void initPreviewCaptureBuilder() {
553552
captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
554553
break;
555554
case always:
556-
default:
557555
captureRequestBuilder.set(
558556
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
559557
captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
560558
break;
559+
case torch:
560+
default:
561+
captureRequestBuilder.set(
562+
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
563+
captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
564+
break;
561565
}
562566
}
563567

packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/FlashMode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
public enum FlashMode {
55
off,
66
auto,
7-
always;
7+
always,
8+
torch;
89

910
public static FlashMode getValueForString(String modeStr) {
1011
try {

packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/FlashModeTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ public void getValueForString_returns_correct_values() {
1616
"Returns FlashMode.always for 'always'",
1717
FlashMode.getValueForString("always"),
1818
FlashMode.always);
19+
assertEquals(
20+
"Returns FlashMode.torch for 'torch'",
21+
FlashMode.getValueForString("torch"),
22+
FlashMode.torch);
1923
}
2024

2125
@Test

packages/camera/camera/example/lib/main.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,15 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
256256
? () => onFlashModeButtonPressed(FlashMode.always)
257257
: null,
258258
),
259+
IconButton(
260+
icon: const Icon(Icons.highlight),
261+
color: controller?.value?.flashMode == FlashMode.torch
262+
? Colors.orange
263+
: Colors.blue,
264+
onPressed: controller != null
265+
? () => onFlashModeButtonPressed(FlashMode.torch)
266+
: null,
267+
),
259268
],
260269
);
261270
}

packages/camera/camera/ios/Classes/CameraPlugin.m

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,23 @@ - (UIImageOrientation)getImageRotation {
134134
}
135135
@end
136136

137-
static AVCaptureFlashMode getFlashModeForString(NSString *mode) {
137+
// Mirrors FlashMode in flash_mode.dart
138+
typedef enum {
139+
FlashModeOff,
140+
FlashModeAuto,
141+
FlashModeAlways,
142+
FlashModeTorch,
143+
} FlashMode;
144+
145+
static FlashMode getFlashModeForString(NSString *mode) {
138146
if ([mode isEqualToString:@"off"]) {
139-
return AVCaptureFlashModeOff;
147+
return FlashModeOff;
140148
} else if ([mode isEqualToString:@"auto"]) {
141-
return AVCaptureFlashModeAuto;
149+
return FlashModeAuto;
142150
} else if ([mode isEqualToString:@"always"]) {
143-
return AVCaptureFlashModeOn;
151+
return FlashModeAlways;
152+
} else if ([mode isEqualToString:@"torch"]) {
153+
return FlashModeTorch;
144154
} else {
145155
NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
146156
code:NSURLErrorUnknown
@@ -152,6 +162,20 @@ static AVCaptureFlashMode getFlashModeForString(NSString *mode) {
152162
}
153163
}
154164

165+
static AVCaptureFlashMode getAVCaptureFlashModeForFlashMode(FlashMode mode) {
166+
switch (mode) {
167+
case FlashModeOff:
168+
return AVCaptureFlashModeOff;
169+
case FlashModeAuto:
170+
return AVCaptureFlashModeAuto;
171+
case FlashModeAlways:
172+
return AVCaptureFlashModeOn;
173+
case FlashModeTorch:
174+
default:
175+
return -1;
176+
}
177+
}
178+
155179
// Mirrors ResolutionPreset in camera.dart
156180
typedef enum {
157181
veryLow,
@@ -219,7 +243,7 @@ @interface FLTCam : NSObject <FlutterTexture,
219243
@property(assign, nonatomic) BOOL isAudioSetup;
220244
@property(assign, nonatomic) BOOL isStreamingImages;
221245
@property(assign, nonatomic) ResolutionPreset resolutionPreset;
222-
@property(assign, nonatomic) AVCaptureFlashMode flashMode;
246+
@property(assign, nonatomic) FlashMode flashMode;
223247
@property(assign, nonatomic) CMTime lastVideoSampleTime;
224248
@property(assign, nonatomic) CMTime lastAudioSampleTime;
225249
@property(assign, nonatomic) CMTime videoTimeOffset;
@@ -250,7 +274,7 @@ - (instancetype)initWithCameraName:(NSString *)cameraName
250274
_enableAudio = enableAudio;
251275
_dispatchQueue = dispatchQueue;
252276
_captureSession = [[AVCaptureSession alloc] init];
253-
_flashMode = AVCaptureFlashModeAuto;
277+
_flashMode = FlashModeAuto;
254278

255279
_captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName];
256280
NSError *localError = nil;
@@ -303,7 +327,10 @@ - (void)captureToFile:(FlutterResult)result API_AVAILABLE(ios(10)) {
303327
if (_resolutionPreset == max) {
304328
[settings setHighResolutionPhotoEnabled:YES];
305329
}
306-
[settings setFlashMode:_flashMode];
330+
AVCaptureFlashMode avFlashMode = getAVCaptureFlashModeForFlashMode(_flashMode);
331+
if (avFlashMode != -1) {
332+
[settings setFlashMode:avFlashMode];
333+
}
307334
NSError *error;
308335
NSString *path = [self getTemporaryFilePathWithExtension:@"jpg"
309336
subfolder:@"pictures"
@@ -694,25 +721,51 @@ - (void)resumeVideoRecordingWithResult:(FlutterResult)result {
694721
}
695722

696723
- (void)setFlashModeWithResult:(FlutterResult)result mode:(NSString *)modeStr {
697-
AVCaptureFlashMode mode;
724+
FlashMode mode;
698725
@try {
699726
mode = getFlashModeForString(modeStr);
700727
} @catch (NSError *e) {
701728
result(getFlutterError(e));
702729
return;
703730
}
704-
if (!_captureDevice.hasFlash) {
705-
result([FlutterError errorWithCode:@"setFlashModeFailed"
706-
message:@"Device does not have flash capabilities"
707-
details:nil]);
708-
return;
709-
}
710-
if (![_capturePhotoOutput.supportedFlashModes
711-
containsObject:[NSNumber numberWithInt:((int)mode)]]) {
712-
result([FlutterError errorWithCode:@"setFlashModeFailed"
713-
message:@"Device does not support this specific flash mode"
714-
details:nil]);
715-
return;
731+
if (mode == FlashModeTorch) {
732+
if (!_captureDevice.hasTorch) {
733+
result([FlutterError errorWithCode:@"setFlashModeFailed"
734+
message:@"Device does not support torch mode"
735+
details:nil]);
736+
return;
737+
}
738+
if (!_captureDevice.isTorchAvailable) {
739+
result([FlutterError errorWithCode:@"setFlashModeFailed"
740+
message:@"Torch mode is currently not available"
741+
details:nil]);
742+
return;
743+
}
744+
if (_captureDevice.torchMode != AVCaptureTorchModeOn) {
745+
[_captureDevice lockForConfiguration:nil];
746+
[_captureDevice setTorchMode:AVCaptureTorchModeOn];
747+
[_captureDevice unlockForConfiguration];
748+
}
749+
} else {
750+
if (!_captureDevice.hasFlash) {
751+
result([FlutterError errorWithCode:@"setFlashModeFailed"
752+
message:@"Device does not have flash capabilities"
753+
details:nil]);
754+
return;
755+
}
756+
AVCaptureFlashMode avFlashMode = getAVCaptureFlashModeForFlashMode(mode);
757+
if (![_capturePhotoOutput.supportedFlashModes
758+
containsObject:[NSNumber numberWithInt:((int)avFlashMode)]]) {
759+
result([FlutterError errorWithCode:@"setFlashModeFailed"
760+
message:@"Device does not support this specific flash mode"
761+
details:nil]);
762+
return;
763+
}
764+
if (_captureDevice.torchMode != AVCaptureTorchModeOff) {
765+
[_captureDevice lockForConfiguration:nil];
766+
[_captureDevice setTorchMode:AVCaptureTorchModeOff];
767+
[_captureDevice unlockForConfiguration];
768+
}
716769
}
717770
_flashMode = mode;
718771
result(nil);
@@ -854,6 +907,12 @@ - (BOOL)setupWriterForPath:(NSString *)path {
854907
[_audioOutput setSampleBufferDelegate:self queue:_dispatchQueue];
855908
}
856909

910+
if (_flashMode == FlashModeTorch) {
911+
[self.captureDevice lockForConfiguration:nil];
912+
[self.captureDevice setTorchMode:AVCaptureTorchModeOn];
913+
[self.captureDevice unlockForConfiguration];
914+
}
915+
857916
[_videoWriter addInput:_videoWriterInput];
858917
[_captureVideoOutput setSampleBufferDelegate:self queue:_dispatchQueue];
859918

packages/camera/camera/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: camera
22
description: A Flutter plugin for getting information about and controlling the
33
camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video,
44
and streaming image buffers to dart.
5-
version: 0.6.2+1
5+
version: 0.6.3
66
homepage: https://github.com/flutter/plugins/tree/master/packages/camera/camera
77

88
dependencies:

packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,12 +478,15 @@ void main() {
478478
);
479479

480480
// Act
481+
await camera.setFlashMode(cameraId, FlashMode.torch);
481482
await camera.setFlashMode(cameraId, FlashMode.always);
482483
await camera.setFlashMode(cameraId, FlashMode.auto);
483484
await camera.setFlashMode(cameraId, FlashMode.off);
484485

485486
// Assert
486487
expect(channel.log, <Matcher>[
488+
isMethodCall('setFlashMode',
489+
arguments: {'cameraId': cameraId, 'mode': 'torch'}),
487490
isMethodCall('setFlashMode',
488491
arguments: {'cameraId': cameraId, 'mode': 'always'}),
489492
isMethodCall('setFlashMode',

0 commit comments

Comments
 (0)