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

[camera] Flash functionality for Android and iOS #3314

Merged
merged 108 commits into from
Dec 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
2394d40
Move camera to camera/camera
mvanbeusekom Nov 5, 2020
8a361ba
Fix relative path after move
mvanbeusekom Nov 9, 2020
0662dc4
First suggestion for camera platform interface
mvanbeusekom Nov 6, 2020
fdac99f
Remove test coverage folder
mvanbeusekom Nov 6, 2020
c92210f
Update the version to 1.0.0
mvanbeusekom Nov 9, 2020
4d78819
Remove redundant analysis overrides
mvanbeusekom Nov 9, 2020
d98a51e
Renamed onLatestImageAvailableHandler definition
mvanbeusekom Nov 9, 2020
96b40fc
Merge branch 'master' into camera_federated
mvanbeusekom Nov 11, 2020
69f573b
Split CameraEvents into separate streams
mvanbeusekom Nov 12, 2020
613a92b
Updated platform interface to have recording methods return XFile ins…
BeMacized Nov 12, 2020
247d9c9
Update documentation and unit tests to match platform interface changes
BeMacized Nov 12, 2020
51c25b5
Make file input optional for recording methods in platform interface.…
BeMacized Nov 12, 2020
1b06322
Add missing full stop in docs.
BeMacized Nov 12, 2020
8e94057
Run dartfmt. Wrapped docs after max 80 cols. Added missing full stop.
BeMacized Nov 12, 2020
05a8f83
Implemented & tested first parts of method channel implementation
BeMacized Nov 13, 2020
5206bff
Remove unused EventChannelMock class
BeMacized Nov 13, 2020
492fae0
Add missing unit tests
BeMacized Nov 13, 2020
6b381ef
Add availableCameras to method channel implementation
BeMacized Nov 13, 2020
96bccd4
Updated platform interface
BeMacized Nov 13, 2020
79b58c8
Update packages/camera/camera_platform_interface/lib/src/platform_int…
BeMacized Nov 13, 2020
6c32fa9
Update packages/camera/camera_platform_interface/lib/src/platform_int…
BeMacized Nov 13, 2020
994e342
Merge pull request #9 from Baseflow/subpr/camera_federated/platform-i…
mvanbeusekom Nov 13, 2020
7a38720
Added placeholders in default method channel implementation
BeMacized Nov 13, 2020
15fed4b
Merge branch 'camera_federated' into subpr/camera_federated/default_m…
BeMacized Nov 13, 2020
5b90760
Add missing implementations to default method channel implementation
BeMacized Nov 13, 2020
6eb0e38
Fix formatting
mvanbeusekom Nov 13, 2020
af21985
Fix PR feedback
BeMacized Nov 13, 2020
7ba6b1d
Add unit test for availableCameras
BeMacized Nov 13, 2020
a9cd0aa
Expand availableCameras unit test. Added unit test for takePicture.
BeMacized Nov 13, 2020
e56dd6c
Add unit test for startVideoRecording
BeMacized Nov 13, 2020
ea263a7
Add unit test for prepareForVideoRecording
BeMacized Nov 13, 2020
ae8cfc0
Add unit test for stopVideoRecording
BeMacized Nov 13, 2020
782fe6a
Add unit test for pauseVideoRecording
BeMacized Nov 13, 2020
d701a76
Add unit test for buildView
BeMacized Nov 13, 2020
debb8bc
Remove TODO comment
BeMacized Nov 13, 2020
55782fd
Update packages/camera/camera_platform_interface/lib/src/method_chann…
BeMacized Nov 13, 2020
576ef94
Update packages/camera/camera_platform_interface/lib/src/method_chann…
BeMacized Nov 13, 2020
8e7e954
Update packages/camera/camera_platform_interface/lib/src/method_chann…
BeMacized Nov 13, 2020
5c743b2
Merge branch 'camera_federated' into subpr/camera_federated/default_m…
BeMacized Nov 19, 2020
821710c
WIP: Dart and Android implementation
danielroek Nov 19, 2020
6902112
Have resolution stream replay last value on subscription. Replace str…
BeMacized Nov 19, 2020
bebe969
Merge branch 'subpr/camera_federated/default_method_channel_impl' int…
BeMacized Nov 19, 2020
4c217ed
Added reverse method channel to replace event channel. Updated initia…
BeMacized Nov 19, 2020
01fcfe7
Fixed example app for Android. Removed isRecordingVideo and isStreami…
danielroek Nov 19, 2020
918033e
iOS implementation: Removed standard event channel. Added reverse met…
BeMacized Nov 20, 2020
f5c0615
Added some first tests for camera/camera
danielroek Nov 23, 2020
837cef7
Started splitting initialize method
mvanbeusekom Nov 26, 2020
b7b33e5
More tests and some feedback
danielroek Nov 26, 2020
8bd9718
Finish splitting up initialize for iOS
BeMacized Nov 26, 2020
c7ffb49
Update unit tests
BeMacized Nov 26, 2020
b8f286c
Remove unused listener in plugin
BeMacized Nov 26, 2020
349b26f
Fix takePicture method on iOS
BeMacized Nov 26, 2020
31cf90f
Merged iOS - split initialize method
mvanbeusekom Nov 27, 2020
efcfcb4
Fix video recording on iOS. Updated platform interface.
BeMacized Nov 27, 2020
8b22d10
Update unit tests
BeMacized Nov 27, 2020
f11850a
Update error handling of video methods in iOS code. Make iOS code mor…
BeMacized Nov 27, 2020
7f14ff7
Split initialize method on Android
mvanbeusekom Nov 27, 2020
dea57c7
Merge branch 'subpr/camera_federated/default_method_channel_impl_ios'…
mvanbeusekom Nov 27, 2020
a6f8985
Updated startVideoRecording documentation
mvanbeusekom Nov 27, 2020
3e53a35
Make sure file is returned by stopVideoRecording
mvanbeusekom Nov 27, 2020
418abc0
Change cast
BeMacized Nov 27, 2020
d36789b
Use correct event-type after initializing
mvanbeusekom Nov 27, 2020
2a5cc5a
Fix DartMessenger unit-tests
mvanbeusekom Nov 27, 2020
fc3c160
Merge branch 'subpr/camera_federated/default_method_channel_impl_ios'…
mvanbeusekom Nov 27, 2020
885b960
Added Android and iOS implementation
mvanbeusekom Nov 27, 2020
1e7d19a
Fix formatting
mvanbeusekom Nov 27, 2020
6b93430
Fixed tests, formatting and analysis warnings
mvanbeusekom Nov 27, 2020
5d58410
Added missing documentation public APIs
mvanbeusekom Nov 30, 2020
8ee0442
Added missing license to Dart files
mvanbeusekom Nov 30, 2020
987eba1
Fix formatting issues
mvanbeusekom Nov 30, 2020
2b2f207
Updated CHANGELOG and version
mvanbeusekom Nov 30, 2020
1f9c343
Added more tests
danielroek Nov 30, 2020
23dd28d
Merge remote-tracking branch 'origin/camera_federated' into camera_fe…
danielroek Nov 30, 2020
1c8a1a1
Formatted code
danielroek Nov 30, 2020
cac52fc
Added additional unit-tests to platform_interface
mvanbeusekom Nov 30, 2020
9c065e1
Merge branch 'camera_federated' of github.com:Baseflow/flutter-plugin…
mvanbeusekom Nov 30, 2020
3e991dd
Fix formatting issues
mvanbeusekom Nov 30, 2020
d613724
Re-added the CameraPreview widget
mvanbeusekom Dec 2, 2020
343294a
Refactored CameraException not to use
mvanbeusekom Dec 3, 2020
f385a29
Use import/export instead of part implementation
mvanbeusekom Dec 3, 2020
557d056
fixed formatting
mvanbeusekom Dec 3, 2020
ab30e1d
Resolved additional feedback
mvanbeusekom Dec 3, 2020
22ec1d7
Resolved additional feedback
mvanbeusekom Dec 3, 2020
4cbb191
Flash WIP
BeMacized Dec 3, 2020
884a24e
Implement flash modes for Android
BeMacized Dec 3, 2020
413934f
Merge branch 'camera_federated_implementation' into feature/camera_flash
BeMacized Dec 3, 2020
33788a1
Rebased on master
mvanbeusekom Dec 4, 2020
b38bfa9
Update dependency to git repo
mvanbeusekom Dec 4, 2020
d7b2e1c
Add missing PictureCaptureRequest class
BeMacized Dec 4, 2020
7448e04
Add PR feedback
BeMacized Dec 4, 2020
c469adb
Move enums out of Camera.java
BeMacized Dec 4, 2020
b9ece62
Merge branch 'camera_federated_implementation' into feature/camera_flash
mvanbeusekom Dec 4, 2020
6fc1593
Expanded platform interface so support setting flash mode
BeMacized Dec 9, 2020
ab5a338
Formatted dart code
BeMacized Dec 9, 2020
6902ea8
Manually serialize flash mode enum rather than relying on stringifica…
BeMacized Dec 10, 2020
9c0d015
Add default to flash mode serialization
BeMacized Dec 10, 2020
a2b3020
Merge branch 'feature/camera_flash_platform_interface' into feature/c…
BeMacized Dec 10, 2020
fc0f410
Fix for unit tests and reformatting
BeMacized Dec 10, 2020
292db7a
Merge branch 'master' into feature/camera_flash
BeMacized Dec 11, 2020
85ade8a
Merged with master
mvanbeusekom Dec 14, 2020
048c7be
Fixed CHANGELOG and remove redundant iOS files
mvanbeusekom Dec 14, 2020
1694bcc
Expose FlashMode through camera package
mvanbeusekom Dec 14, 2020
b68cc80
Add reqeusted unit tests
BeMacized Dec 15, 2020
0c7f391
Clean up new tests
BeMacized Dec 15, 2020
04cca39
Add unit tests for Android implementation
BeMacized Dec 17, 2020
97ed5d9
Update platform interface dependency to point to pub.dev release.
BeMacized Dec 17, 2020
53af749
Merge branch 'master' into feature/camera_flash
BeMacized Dec 18, 2020
2a31b5e
Update version number
mvanbeusekom Dec 19, 2020
File filter

Filter by extension

Filter by extension


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

* Add flash support for Android and iOS implementations.

## 0.6.0+2

* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
Expand All @@ -34,6 +36,8 @@
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugins.camera.media.MediaRecorderBuilder;
import io.flutter.plugins.camera.types.FlashMode;
import io.flutter.plugins.camera.types.ResolutionPreset;
import io.flutter.view.TextureRegistry.SurfaceTextureEntry;
import java.io.File;
import java.io.FileOutputStream;
Expand Down Expand Up @@ -69,16 +73,8 @@ public class Camera {
private CamcorderProfile recordingProfile;
private int currentOrientation = ORIENTATION_UNKNOWN;
private Context applicationContext;

// Mirrors camera.dart
public enum ResolutionPreset {
low,
medium,
high,
veryHigh,
ultraHigh,
max,
}
private FlashMode flashMode;
private PictureCaptureRequest pictureCaptureRequest;

public Camera(
final Activity activity,
Expand All @@ -97,6 +93,7 @@ public Camera(
this.dartMessenger = dartMessenger;
this.cameraManager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
this.applicationContext = activity.getApplicationContext();
this.flashMode = FlashMode.auto;
orientationEventListener =
new OrientationEventListener(activity.getApplicationContext()) {
@Override
Expand Down Expand Up @@ -220,58 +217,125 @@ SurfaceTextureEntry getFlutterTexture() {
}

public void takePicture(@NonNull final Result result) {
// Only take 1 picture at a time
if (pictureCaptureRequest != null && !pictureCaptureRequest.isFinished()) {
result.error("captureAlreadyActive", "Picture is currently already being captured", null);
return;
}
// Store the result
this.pictureCaptureRequest = new PictureCaptureRequest(result);

// Create temporary file
final File outputDir = applicationContext.getCacheDir();
final File file;
try {
file = File.createTempFile("CAP", ".jpg", outputDir);
} catch (IOException | SecurityException e) {
result.error("cannotCreateFile", e.getMessage(), null);
pictureCaptureRequest.error("cannotCreateFile", e.getMessage(), null);
return;
}

// Listen for picture being taken
pictureImageReader.setOnImageAvailableListener(
reader -> {
try (Image image = reader.acquireLatestImage()) {
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
writeToFile(buffer, file);
result.success(file.getAbsolutePath());
pictureCaptureRequest.finish(file.getAbsolutePath());
} catch (IOException e) {
result.error("IOError", "Failed saving image", null);
pictureCaptureRequest.error("IOError", "Failed saving image", null);
}
},
null);

runPicturePreCapture();
}

private final CameraCaptureSession.CaptureCallback pictureCaptureCallback =
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(
@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
assert (pictureCaptureRequest != null);
switch (pictureCaptureRequest.getState()) {
case awaitingPreCapture:
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
// Some devices might return null here, in which case we will also continue.
if (aeState == null
|| aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED
|| aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED) {
runPictureCapture();
}
break;
}
}

@Override
public void onCaptureFailed(
@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureFailure failure) {
assert (pictureCaptureRequest != null);
String reason;
switch (failure.getReason()) {
case CaptureFailure.REASON_ERROR:
reason = "An error happened in the framework";
break;
case CaptureFailure.REASON_FLUSHED:
reason = "The capture has failed due to an abortCaptures() call";
break;
default:
reason = "Unknown reason";
}
pictureCaptureRequest.error("captureFailure", reason, null);
}
};

private void runPicturePreCapture() {
assert (pictureCaptureRequest != null);
pictureCaptureRequest.setState(PictureCaptureRequest.State.awaitingPreCapture);

captureRequestBuilder.set(
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
try {
cameraCaptureSession.capture(captureRequestBuilder.build(), pictureCaptureCallback, null);
captureRequestBuilder.set(
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
} catch (CameraAccessException e) {
pictureCaptureRequest.error("cameraAccess", e.getMessage(), null);
}
}

private void runPictureCapture() {
assert (pictureCaptureRequest != null);
pictureCaptureRequest.setState(PictureCaptureRequest.State.capturing);
try {
final CaptureRequest.Builder captureBuilder =
cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(pictureImageReader.getSurface());
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getMediaOrientation());

cameraCaptureSession.capture(
captureBuilder.build(),
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureFailed(
@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureFailure failure) {
String reason;
switch (failure.getReason()) {
case CaptureFailure.REASON_ERROR:
reason = "An error happened in the framework";
break;
case CaptureFailure.REASON_FLUSHED:
reason = "The capture has failed due to an abortCaptures() call";
break;
default:
reason = "Unknown reason";
}
result.error("captureFailure", reason, null);
}
},
null);
switch (flashMode) {
case off:
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
captureBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
break;
case auto:
captureBuilder.set(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
break;
case always:
default:
captureBuilder.set(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
break;
}
cameraCaptureSession.capture(captureBuilder.build(), pictureCaptureCallback, null);
} catch (CameraAccessException e) {
result.error("cameraAccess", e.getMessage(), null);
pictureCaptureRequest.error("cameraAccess", e.getMessage(), null);
}
}

Expand Down Expand Up @@ -314,8 +378,7 @@ public void onConfigured(@NonNull CameraCaptureSession session) {
return;
}
cameraCaptureSession = session;
captureRequestBuilder.set(
CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
initPreviewCaptureBuilder();
cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
if (onSuccessCallback != null) {
onSuccessCallback.run();
Expand Down Expand Up @@ -452,6 +515,54 @@ public void resumeVideoRecording(@NonNull final Result result) {
result.success(null);
}

public void setFlashMode(@NonNull final Result result, FlashMode mode)
throws CameraAccessException {
// Get the flash availability
Boolean flashAvailable;
try {
flashAvailable =
cameraManager
.getCameraCharacteristics(cameraDevice.getId())
.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
} catch (CameraAccessException e) {
result.error("setFlashModeFailed", e.getMessage(), null);
return;
}
// Check if flash is available.
if (flashAvailable == null || !flashAvailable) {
result.error("setFlashModeFailed", "Device does not have flash capabilities", null);
return;
}
// Get flash

this.flashMode = mode;
initPreviewCaptureBuilder();
this.cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
result.success(null);
}

private void initPreviewCaptureBuilder() {
captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
switch (flashMode) {
case off:
captureRequestBuilder.set(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
break;
case auto:
captureRequestBuilder.set(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
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;
}
}

public void startPreview() throws CameraAccessException {
if (pictureImageReader == null || pictureImageReader.getSurface() == null) return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.CamcorderProfile;
import android.util.Size;
import io.flutter.plugins.camera.Camera.ResolutionPreset;
import io.flutter.plugins.camera.types.ResolutionPreset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugins.camera.CameraPermissions.PermissionsRegistry;
import io.flutter.plugins.camera.types.FlashMode;
import io.flutter.view.TextureRegistry;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -122,6 +123,21 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result)
camera.resumeVideoRecording(result);
break;
}
case "setFlashMode":
{
String modeStr = call.argument("mode");
FlashMode mode = FlashMode.getValueForString(modeStr);
if (mode == null) {
result.error("setFlashModeFailed", "Unknown flash mode " + modeStr, null);
return;
}
try {
camera.setFlashMode(result, mode);
} catch (Exception e) {
handleException(e, result);
}
break;
}
case "startImageStream":
{
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.flutter.plugins.camera;

import androidx.annotation.Nullable;
import io.flutter.plugin.common.MethodChannel;

class PictureCaptureRequest {

enum State {
idle,
awaitingPreCapture,
capturing,
finished,
error,
}

private final MethodChannel.Result result;
private State state;

public PictureCaptureRequest(MethodChannel.Result result) {
this.result = result;
state = State.idle;
}

public void setState(State state) {
if (isFinished()) throw new IllegalStateException("Request has already been finished");
this.state = state;
}

public State getState() {
return state;
}

public boolean isFinished() {
return state == State.finished || state == State.error;
}

public void finish(String absolutePath) {
if (isFinished()) throw new IllegalStateException("Request has already been finished");
result.success(absolutePath);
state = State.finished;
}

public void error(
String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
if (isFinished()) throw new IllegalStateException("Request has already been finished");
result.error(errorCode, errorMessage, errorDetails);
state = State.error;
}
}
mvanbeusekom marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.flutter.plugins.camera.types;

// Mirrors flash_mode.dart
public enum FlashMode {
off,
auto,
always;

public static FlashMode getValueForString(String modeStr) {
try {
return valueOf(modeStr);
} catch (IllegalArgumentException | NullPointerException e) {
return null;
}
}
mvanbeusekom marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.flutter.plugins.camera.types;

// Mirrors camera.dart
public enum ResolutionPreset {
low,
medium,
high,
veryHigh,
ultraHigh,
max,
}
Loading