Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/camera_android_camerax/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.6.28

* Adds more descriptive error to camera error stream when image capture fails.

## 0.6.27

* Changes `availableCameras` to get the camera name from `Camera2CameraInfo.getCameraId`.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v26.1.4), do not edit directly.
// Autogenerated from Pigeon (v26.1.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")

Expand Down Expand Up @@ -4257,6 +4257,7 @@ abstract class PigeonApiImageCapture(
/** Captures a new still image for in memory access. */
abstract fun takePicture(
pigeon_instance: androidx.camera.core.ImageCapture,
systemServicesManager: SystemServicesManager,
callback: (Result<String>) -> Unit
)

Expand Down Expand Up @@ -4331,7 +4332,9 @@ abstract class PigeonApiImageCapture(
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val pigeon_instanceArg = args[0] as androidx.camera.core.ImageCapture
api.takePicture(pigeon_instanceArg) { result: Result<String> ->
val systemServicesManagerArg = args[1] as SystemServicesManager
api.takePicture(pigeon_instanceArg, systemServicesManagerArg) { result: Result<String>
->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(CameraXLibraryPigeonUtils.wrapError(error))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public void setFlashMode(
@Override
public void takePicture(
@NonNull ImageCapture pigeonInstance,
@NonNull SystemServicesManager systemServicesManager,
@NonNull Function1<? super Result<String>, Unit> callback) {
final File outputDir = getPigeonRegistrar().getContext().getCacheDir();
File temporaryCaptureFile;
Expand All @@ -98,7 +99,7 @@ public void takePicture(
final ImageCapture.OutputFileOptions outputFileOptions =
createImageCaptureOutputFileOptions(temporaryCaptureFile);
final ImageCapture.OnImageSavedCallback onImageSavedCallback =
createOnImageSavedCallback(temporaryCaptureFile, callback);
createOnImageSavedCallback(temporaryCaptureFile, systemServicesManager, callback);

pigeonInstance.takePicture(
outputFileOptions, Executors.newSingleThreadExecutor(), onImageSavedCallback);
Expand All @@ -121,7 +122,9 @@ ImageCapture.OutputFileOptions createImageCaptureOutputFileOptions(@NonNull File

@NonNull
ImageCapture.OnImageSavedCallback createOnImageSavedCallback(
@NonNull File file, @NonNull Function1<? super Result<String>, Unit> callback) {
@NonNull File file,
@NonNull SystemServicesManager systemServicesManager,
@NonNull Function1<? super Result<String>, Unit> callback) {
return new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
Expand All @@ -130,8 +133,32 @@ public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResul

@Override
public void onError(@NonNull ImageCaptureException exception) {
systemServicesManager.onCameraError(
getImageCaptureExceptionDescription(exception.getImageCaptureError()));
ResultCompat.failure(exception, callback);
}
};
}

/**
* Returns an error description for each {@link ImageCaptureException} error code.
*
* <p>See
* https://developer.android.com/reference/androidx/camera/core/ImageCaptureException#getImageCaptureError()
* for details on each error type.
*/
String getImageCaptureExceptionDescription(int imageCaptureErrorCode) {
switch (imageCaptureErrorCode) {
case ImageCapture.ERROR_FILE_IO:
return "An error occurred while attempting to save the captured image to a file.";
case ImageCapture.ERROR_CAPTURE_FAILED:
return "The camera framework failed to fulfill the image capture request.";
case ImageCapture.ERROR_CAMERA_CLOSED:
return "Image capture failed due to the camera being closed.";
case ImageCapture.ERROR_INVALID_CAMERA:
return "The ImageCapture use case was bound to an invalid camera by the Flutter camera plugin. If you see this error, please file an issue if you cannot find one that already exists: https://github.com/flutter/flutter/issues/.";
default:
return "An unknown error has occurred while attempting to take a picture. Check the logs for more details.";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,13 @@ ImageCapture.OutputFileOptions createImageCaptureOutputFileOptions(@NonNull File
@NonNull
@Override
ImageCapture.OnImageSavedCallback createOnImageSavedCallback(
@NonNull File file, @NonNull Function1<? super Result<String>, Unit> callback) {
@NonNull File file,
@NonNull SystemServicesManager systemServicesManager,
@NonNull Function1<? super Result<String>, Unit> callback) {
final File mockFile = mock(File.class);
when(mockFile.getAbsolutePath()).thenReturn(filename);
final ImageCapture.OnImageSavedCallback imageSavedCallback =
super.createOnImageSavedCallback(mockFile, callback);
super.createOnImageSavedCallback(mockFile, systemServicesManager, callback);
imageSavedCallback.onImageSaved(mock(ImageCapture.OutputFileResults.class));
return imageSavedCallback;
}
Expand All @@ -114,6 +116,7 @@ ImageCapture.OnImageSavedCallback createOnImageSavedCallback(
final String[] result = {null};
api.takePicture(
instance,
mock(SystemServicesManager.class),
ResultCompat.asCompatCallback(
reply -> {
result[0] = reply.getOrNull();
Expand Down Expand Up @@ -155,6 +158,7 @@ public void takePicture_sendsErrorWhenTemporaryFileCannotBeCreated() {
final Throwable[] result = {null};
api.takePicture(
instance,
mock(SystemServicesManager.class),
ResultCompat.asCompatCallback(
reply -> {
result[0] = reply.exceptionOrNull();
Expand All @@ -167,14 +171,16 @@ public void takePicture_sendsErrorWhenTemporaryFileCannotBeCreated() {
}

@Test
public void takePicture_onImageSavedCallbackCanSendsError() {
public void takePicture_onImageSavedCallbackSendsErrorAndReportsException() {
final ProxyApiRegistrar mockApiRegistrar = mock(ProxyApiRegistrar.class);
final Context mockContext = mock(Context.class);
final File mockOutputDir = mock(File.class);
final SystemServicesManager mockSystemServicesManager = mock(SystemServicesManager.class);
when(mockContext.getCacheDir()).thenReturn(mockOutputDir);
when(mockApiRegistrar.getContext()).thenReturn(mockContext);

final ImageCaptureException captureException = mock(ImageCaptureException.class);
when(captureException.getImageCaptureError()).thenReturn(ImageCapture.ERROR_CAPTURE_FAILED);
final ImageCaptureProxyApi api =
new ImageCaptureProxyApi(mockApiRegistrar) {
@Override
Expand All @@ -185,9 +191,11 @@ ImageCapture.OutputFileOptions createImageCaptureOutputFileOptions(@NonNull File
@NonNull
@Override
ImageCapture.OnImageSavedCallback createOnImageSavedCallback(
@NonNull File file, @NonNull Function1<? super Result<String>, Unit> callback) {
@NonNull File file,
@NonNull SystemServicesManager systemServicesManager,
@NonNull Function1<? super Result<String>, Unit> callback) {
final ImageCapture.OnImageSavedCallback imageSavedCallback =
super.createOnImageSavedCallback(mock(File.class), callback);
super.createOnImageSavedCallback(mock(File.class), systemServicesManager, callback);
imageSavedCallback.onError(captureException);
return imageSavedCallback;
}
Expand All @@ -209,6 +217,7 @@ ImageCapture.OnImageSavedCallback createOnImageSavedCallback(
final Throwable[] result = {null};
api.takePicture(
instance,
mockSystemServicesManager,
ResultCompat.asCompatCallback(
reply -> {
result[0] = reply.exceptionOrNull();
Expand All @@ -220,6 +229,8 @@ ImageCapture.OnImageSavedCallback createOnImageSavedCallback(
any(ImageCapture.OutputFileOptions.class),
any(Executor.class),
any(ImageCapture.OnImageSavedCallback.class));
verify(mockSystemServicesManager)
.onCameraError("The camera framework failed to fulfill the image capture request.");
assertEquals(result[0], captureException);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1040,7 +1040,9 @@ class AndroidCameraCameraX extends CameraPlatform {
);
}

final String picturePath = await imageCapture!.takePicture();
final String picturePath = await imageCapture!.takePicture(
systemServicesManager,
);
return XFile(picturePath);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v26.1.4), do not edit directly.
// Autogenerated from Pigeon (v26.1.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers

Expand Down Expand Up @@ -5258,7 +5258,9 @@ class ImageCapture extends UseCase {
}

/// Captures a new still image for in memory access.
Future<String> takePicture() async {
Future<String> takePicture(
SystemServicesManager systemServicesManager,
) async {
final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec =
_pigeonVar_codecImageCapture;
final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger;
Expand All @@ -5270,7 +5272,7 @@ class ImageCapture extends UseCase {
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(
<Object?>[this],
<Object?>[this, systemServicesManager],
);
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ abstract class ImageCapture extends UseCase {

/// Captures a new still image for in memory access.
@async
String takePicture();
String takePicture(SystemServicesManager systemServicesManager);

/// Sets the desired rotation of the output image.
void setTargetRotation(int rotation);
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera_android_camerax/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: camera_android_camerax
description: Android implementation of the camera plugin using the CameraX library.
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
version: 0.6.27
version: 0.6.28

environment:
sdk: ^3.9.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3854,21 +3854,28 @@ void main() {
final mockProcessCameraProvider = MockProcessCameraProvider();
final mockCamera = MockCamera();
final mockCameraInfo = MockCameraInfo();
final mockImageCapture = MockImageCapture();
const testPicturePath = 'test/absolute/path/to/picture';

// Set directly for test versus calling createCamera.
camera.imageCapture = MockImageCapture();
camera.imageCapture = mockImageCapture;
camera.processCameraProvider = mockProcessCameraProvider;
camera.cameraSelector = MockCameraSelector();

// Ignore setting target rotation for this test; tested seprately.
camera.captureOrientationLocked = true;

// Tell plugin to create detached camera state observers.
// Tell plugin to create detached camera state observers and mock systemServicesManager.
GenericsPigeonOverrides.observerNew =
<T>({required void Function(Observer<T>, T) onChanged}) {
return Observer<T>.detached(onChanged: onChanged);
};
PigeonOverrides.systemServicesManager_new =
({
required void Function(SystemServicesManager, String) onCameraError,
}) {
return MockSystemServicesManager();
};

when(
mockProcessCameraProvider.isBound(camera.imageCapture),
Expand All @@ -3884,7 +3891,7 @@ void main() {
mockCameraInfo.getCameraState(),
).thenAnswer((_) async => MockLiveCameraState());
when(
camera.imageCapture!.takePicture(),
mockImageCapture.takePicture(argThat(isA<SystemServicesManager>())),
).thenAnswer((_) async => testPicturePath);

final XFile imageFile = await camera.takePicture(3);
Expand All @@ -3907,7 +3914,7 @@ void main() {
camera.imageCapture = mockImageCapture;
camera.processCameraProvider = mockProcessCameraProvider;

// Tell plugin to mock call to get current photo orientation.
// Tell plugin to mock call to get current photo orientation and systemServicesManager.
PigeonOverrides.deviceOrientationManager_new =
({
required void Function(DeviceOrientationManager, String)
Expand All @@ -3919,12 +3926,18 @@ void main() {
).thenAnswer((_) async => defaultTargetRotation);
return mockDeviceOrientationManager;
};
PigeonOverrides.systemServicesManager_new =
({
required void Function(SystemServicesManager, String) onCameraError,
}) {
return MockSystemServicesManager();
};

when(
mockProcessCameraProvider.isBound(camera.imageCapture),
).thenAnswer((_) async => true);
when(
camera.imageCapture!.takePicture(),
mockImageCapture.takePicture(argThat(isA<SystemServicesManager>())),
).thenAnswer((_) async => 'test/absolute/path/to/picture');

// Orientation is unlocked and plugin does not need to set default target
Expand Down Expand Up @@ -3969,6 +3982,14 @@ void main() {
// Ignore setting target rotation for this test; tested seprately.
camera.captureOrientationLocked = true;

// Tell plugin to mock call to get systemServicesManager.
PigeonOverrides.systemServicesManager_new =
({
required void Function(SystemServicesManager, String) onCameraError,
}) {
return MockSystemServicesManager();
};

when(
mockProcessCameraProvider.isBound(camera.imageCapture),
).thenAnswer((_) async => true);
Expand All @@ -3995,6 +4016,14 @@ void main() {
camera.captureOrientationLocked = true;
camera.processCameraProvider = mockProcessCameraProvider;

// Tell plugin to mock call to get current photo orientation and systemServicesManager.
PigeonOverrides.systemServicesManager_new =
({
required void Function(SystemServicesManager, String) onCameraError,
}) {
return MockSystemServicesManager();
};

when(
mockProcessCameraProvider.isBound(camera.imageCapture),
).thenAnswer((_) async => true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1444,14 +1444,22 @@ class MockImageCapture extends _i1.Mock implements _i2.ImageCapture {
as _i5.Future<void>);

@override
_i5.Future<String> takePicture() =>
_i5.Future<String> takePicture(
_i2.SystemServicesManager? systemServicesManager,
) =>
(super.noSuchMethod(
Invocation.method(#takePicture, []),
Invocation.method(#takePicture, [systemServicesManager]),
returnValue: _i5.Future<String>.value(
_i6.dummyValue<String>(this, Invocation.method(#takePicture, [])),
_i6.dummyValue<String>(
this,
Invocation.method(#takePicture, [systemServicesManager]),
),
),
returnValueForMissingStub: _i5.Future<String>.value(
_i6.dummyValue<String>(this, Invocation.method(#takePicture, [])),
_i6.dummyValue<String>(
this,
Invocation.method(#takePicture, [systemServicesManager]),
),
),
)
as _i5.Future<String>);
Expand Down
Loading