Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
dec3d69
Merge remote-tracking branch 'upstream/main' into camx_occ
camsim99 May 1, 2023
0e0333b
Merge remote-tracking branch 'upstream/main'
camsim99 May 2, 2023
bd7ac99
Merge remote-tracking branch 'upstream/main'
camsim99 May 3, 2023
5c3363b
Merge remote-tracking branch 'upstream/main'
camsim99 May 10, 2023
fed9621
Undo changes
camsim99 May 10, 2023
5aabe34
Merge remote-tracking branch 'upstream/main'
camsim99 May 12, 2023
2b9a352
Merge remote-tracking branch 'upstream/main'
camsim99 May 25, 2023
a1173da
Merge remote-tracking branch 'upstream/main'
camsim99 May 30, 2023
cbc3d6b
Merge remote-tracking branch 'upstream/main'
camsim99 May 30, 2023
cae5a4c
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 1, 2023
72283db
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 5, 2023
166a77c
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 5, 2023
399780e
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 14, 2023
8d5d0e7
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 26, 2023
084d960
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 12, 2023
d2a59ac
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 17, 2023
a1422bf
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 17, 2023
bdd87a6
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 18, 2023
137a28b
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 19, 2023
bc0db5a
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 20, 2023
d04b466
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 21, 2023
a9cfe87
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 24, 2023
a32def1
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 2, 2023
4785148
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 14, 2023
7a8fc69
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 15, 2023
b02e15f
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 15, 2023
c6e5868
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 17, 2023
0c0065a
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 28, 2023
9dfe259
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 29, 2023
bfcc0df
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 30, 2023
b80cc86
Merge remote-tracking branch 'upstream/main'
camsim99 Sep 5, 2023
915332e
Start impl
camsim99 Sep 5, 2023
22ea65f
More impl
camsim99 Sep 6, 2023
ad46f47
More impl
camsim99 Sep 6, 2023
6396ec5
More impl
camsim99 Sep 7, 2023
cd0dc35
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 11, 2023
9c9922b
Add dart tests
camsim99 Sep 11, 2023
0f64164
Add back example code
camsim99 Sep 11, 2023
dc5f95b
Add tests, update docs
camsim99 Sep 12, 2023
743c853
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 12, 2023
85e3da7
Fix analysis errors
camsim99 Sep 12, 2023
476d4de
Remove redundant context setting
camsim99 Sep 12, 2023
b385f1a
Fix img analysis test
camsim99 Sep 12, 2023
fb7986d
Nit
camsim99 Sep 12, 2023
87a4438
Modify torch tests
camsim99 Sep 12, 2023
12ce00c
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 12, 2023
a6f0968
Fix java tests
camsim99 Sep 12, 2023
cef5f31
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 12, 2023
81e6cbc
Delete unused proxy
camsim99 Sep 13, 2023
2534eae
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 13, 2023
bb0e9d7
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 18, 2023
7133289
Add impl
camsim99 Sep 19, 2023
93317eb
Address review
camsim99 Sep 21, 2023
a08b7a3
Modify readme
camsim99 Sep 25, 2023
f87e367
Merge branch 'camx_torch' into camx_zoom
camsim99 Sep 25, 2023
8b4094b
Add error handling and add plugin test
camsim99 Sep 25, 2023
1ff09a6
Merge remote-tracking branch 'upstream/main' into camx_zoom
camsim99 Oct 11, 2023
96ad199
Modify error and update changelog/pubspec
camsim99 Oct 11, 2023
d3f245e
Remove unimplemented functionality
camsim99 Oct 18, 2023
2aadc7c
Update packages/camera/camera_android_camerax/lib/src/camera_control.…
camsim99 Oct 25, 2023
ae695b5
Specify what happens to error
camsim99 Oct 25, 2023
9644718
Merge remote-tracking branch 'upstream/main' into camx_zoom
camsim99 Oct 25, 2023
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.5.0+20

* Implements `setZoomLevel`.

## 0.5.0+19

* Implements torch flash mode.
Expand Down
4 changes: 0 additions & 4 deletions packages/camera/camera_android_camerax/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ and thus, the plugin will fall back to 480p if configured with a

`setFocusMode` & `setFocusPoint` are unimplemented.

### Zoom configuration \[[Issue #125371][125371]\]

`setZoomLevel` is unimplemented.

### Setting maximum duration and stream options for video capture

Calling `startVideoCapturing` with `VideoCaptureOptions` configured with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,29 @@ public void onFailure(Throwable t) {
},
ContextCompat.getMainExecutor(context));
}

/** Sets the zoom ratio of the specified {@link CameraControl} instance. */
@NonNull
public void setZoomRatio(
@NonNull CameraControl cameraControl,
@NonNull Double ratio,
@NonNull GeneratedCameraXLibrary.Result<Void> result) {
float ratioAsFloat = ratio.floatValue();
ListenableFuture<Void> setZoomRatioFuture = cameraControl.setZoomRatio(ratioAsFloat);

Futures.addCallback(
setZoomRatioFuture,
new FutureCallback<Void>() {
public void onSuccess(Void voidResult) {
result.success(null);
}

public void onFailure(Throwable t) {
result.error(t);
}
},
ContextCompat.getMainExecutor(context));
}
}

/**
Expand Down Expand Up @@ -81,7 +104,8 @@ public CameraControlHostApiImpl(
}

/**
* Sets the context that the {@code ProcessCameraProvider} will use to enable/disable torch mode.
* Sets the context that the {@code ProcessCameraProvider} will use to enable/disable torch mode
* and set the zoom ratio.
*
* <p>If using the camera plugin in an add-to-app context, ensure that a new instance of the
* {@code CameraControl} is fetched via {@code #enableTorch} anytime the context changes.
Expand All @@ -98,4 +122,13 @@ public void enableTorch(
proxy.enableTorch(
Objects.requireNonNull(instanceManager.getInstance(identifier)), torch, result);
}

@Override
public void setZoomRatio(
@NonNull Long identifier,
@NonNull Double ratio,
@NonNull GeneratedCameraXLibrary.Result<Void> result) {
proxy.setZoomRatio(
Objects.requireNonNull(instanceManager.getInstance(identifier)), ratio, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3237,6 +3237,9 @@ public interface CameraControlHostApi {
void enableTorch(
@NonNull Long identifier, @NonNull Boolean torch, @NonNull Result<Void> result);

void setZoomRatio(
@NonNull Long identifier, @NonNull Double ratio, @NonNull Result<Void> result);

/** The codec used by CameraControlHostApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
Expand Down Expand Up @@ -3280,6 +3283,41 @@ public void error(Throwable error) {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.CameraControlHostApi.setZoomRatio",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
Number identifierArg = (Number) args.get(0);
Double ratioArg = (Double) args.get(1);
Result<Void> resultCallback =
new Result<Void>() {
public void success(Void result) {
wrapped.add(0, null);
reply.reply(wrapped);
}

public void error(Throwable error) {
ArrayList<Object> wrappedError = wrapError(error);
reply.reply(wrappedError);
}
};

api.setZoomRatio(
(identifierArg == null) ? null : identifierArg.longValue(),
ratioArg,
resultCallback);
});
} else {
channel.setMessageHandler(null);
}
}
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,57 @@ public void enableTorch_turnsTorchModeOnAndOffAsExpected() {
}
}

@Test
public void setZoomRatio_setsZoomAsExpected() {
try (MockedStatic<Futures> mockedFutures = Mockito.mockStatic(Futures.class)) {
final CameraControlHostApiImpl cameraControlHostApiImpl =
new CameraControlHostApiImpl(testInstanceManager, mock(Context.class));
final Long cameraControlIdentifier = 33L;
final Double zoomRatio = 0.2D;

@SuppressWarnings("unchecked")
final ListenableFuture<Void> setZoomRatioFuture = mock(ListenableFuture.class);

testInstanceManager.addDartCreatedInstance(cameraControl, cameraControlIdentifier);

when(cameraControl.setZoomRatio(zoomRatio.floatValue())).thenReturn(setZoomRatioFuture);

@SuppressWarnings("unchecked")
final ArgumentCaptor<FutureCallback<Void>> futureCallbackCaptor =
ArgumentCaptor.forClass(FutureCallback.class);

// Test successful behavior.
@SuppressWarnings("unchecked")
final GeneratedCameraXLibrary.Result<Void> successfulMockResult =
mock(GeneratedCameraXLibrary.Result.class);
cameraControlHostApiImpl.setZoomRatio(
cameraControlIdentifier, zoomRatio, successfulMockResult);
mockedFutures.verify(
() -> Futures.addCallback(eq(setZoomRatioFuture), futureCallbackCaptor.capture(), any()));
mockedFutures.clearInvocations();

FutureCallback<Void> successfulSetZoomRatioCallback = futureCallbackCaptor.getValue();

successfulSetZoomRatioCallback.onSuccess(mock(Void.class));
verify(successfulMockResult).success(null);

// Test failed behavior.
@SuppressWarnings("unchecked")
final GeneratedCameraXLibrary.Result<Void> failedMockResult =
mock(GeneratedCameraXLibrary.Result.class);
final Throwable testThrowable = new Throwable();
cameraControlHostApiImpl.setZoomRatio(cameraControlIdentifier, zoomRatio, failedMockResult);
mockedFutures.verify(
() -> Futures.addCallback(eq(setZoomRatioFuture), futureCallbackCaptor.capture(), any()));
mockedFutures.clearInvocations();

FutureCallback<Void> failedSetZoomRatioCallback = futureCallbackCaptor.getValue();

failedSetZoomRatioCallback.onFailure(testThrowable);
verify(failedMockResult).error(testThrowable);
}
}

@Test
public void flutterApiCreate_makesCallToCreateInstanceOnDartSide() {
final CameraControlFlutterApiImpl spyFlutterApi =
Expand Down
16 changes: 4 additions & 12 deletions packages/camera/camera_android_camerax/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -392,12 +392,8 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
style: styleAuto,
onPressed:
() {}, // TODO(camsim99): Add functionality back here.
onLongPress: () {
if (controller != null) {
controller!.setExposurePoint(null);
showInSnackBar('Resetting exposure point');
}
},
onLongPress:
() {}, // TODO(camsim99): Add functionality back here.,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just taking out for now as they do cause errors.

child: const Text('AUTO'),
),
TextButton(
Expand Down Expand Up @@ -470,12 +466,8 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
style: styleAuto,
onPressed:
() {}, // TODO(camsim99): Add functionality back here.
onLongPress: () {
if (controller != null) {
controller!.setFocusPoint(null);
}
showInSnackBar('Resetting focus point');
},
onLongPress:
() {}, // TODO(camsim99): Add functionality back here.
child: const Text('AUTO'),
),
TextButton(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,17 @@ class AndroidCameraCameraX extends CameraPlatform {
return zoomState.minZoomRatio;
}

/// Set the zoom level for the selected camera.
///
/// The supplied [zoom] value should be between the minimum and the maximum
/// supported zoom level returned by `getMinZoomLevel` and `getMaxZoomLevel`.
/// Throws a `CameraException` when an illegal zoom level is supplied.
@override
Future<void> setZoomLevel(int cameraId, double zoom) async {
final CameraControl cameraControl = await camera!.getCameraControl();
await cameraControl.setZoomRatio(zoom);
}

/// The ui orientation changed.
@override
Stream<DeviceOrientationChangedEvent> onDeviceOrientationChanged() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,22 @@ class CameraControl extends JavaObject {
late final _CameraControlHostApiImpl _api;

/// Enables or disables the torch of related [Camera] instance.
///
/// If the torch mode was unable to be changed, an error message will be
/// added to [SystemServices.cameraErrorStreamController].
Future<void> enableTorch(bool torch) async {
return _api.enableTorchFromInstance(this, torch);
}

/// Sets zoom of related [Camera] by ratio.
///
/// Ratio should be between what the `minZoomRatio` and `maxZoomRatio` of the
/// [ZoomState] of the [CameraInfo] instance that is retrievable from the same
/// [Camera] instance; otherwise, an error message will be added to
/// [SystemServices.cameraErrorStreamController].
Future<void> setZoomRatio(double ratio) async {
return _api.setZoomRatioFromInstance(this, ratio);
}
}

/// Host API implementation of [CameraControl].
Expand Down Expand Up @@ -69,6 +82,18 @@ class _CameraControlHostApiImpl extends CameraControlHostApi {
.add(e.message ?? 'The camera was unable to change torch modes.');
}
}

/// Sets zoom of specified [CameraControl] instance by ratio.
Future<void> setZoomRatioFromInstance(
CameraControl instance, double ratio) async {
final int identifier = instanceManager.getIdentifier(instance)!;
try {
await setZoomRatio(identifier, ratio);
} on PlatformException catch (e) {
SystemServices.cameraErrorStreamController.add(e.message ??
Copy link
Member

@gmackall gmackall Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will lose the stack trace right? It looks like we are doing this in other places as well (I probably missed this in earlier reviews), but do we want to include the stack trace in the message as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is true, but this follows suit from the other camera platform implementations. I would argue that it's okay that we don't include it because we should only be catching camera errors like this when the camera was unable to complete the operation and not due to a code error. If you feel strongly though, I don't think it would hurt to include!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No strong feelings either way!

'Zoom ratio was unable to be set. If ratio was not out of range, newer value may have been set; otherwise, the camera may be closed.');
}
}
}

/// Flutter API implementation of [CameraControl].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2685,6 +2685,28 @@ class CameraControlHostApi {
return;
}
}

Future<void> setZoomRatio(int arg_identifier, double arg_ratio) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.CameraControlHostApi.setZoomRatio', codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = await channel
.send(<Object?>[arg_identifier, arg_ratio]) as List<Object?>?;
if (replyList == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
);
} else if (replyList.length > 1) {
throw PlatformException(
code: replyList[0]! as String,
message: replyList[1] as String?,
details: replyList[2],
);
} else {
return;
}
}
}

abstract class CameraControlFlutterApi {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,9 @@ abstract class FallbackStrategyHostApi {
abstract class CameraControlHostApi {
@async
void enableTorch(int identifier, bool torch);

@async
void setZoomRatio(int identifier, double ratio);
}

@FlutterApi()
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.5.0+19
version: 0.5.0+20

environment:
sdk: ">=2.19.0 <4.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1219,6 +1219,22 @@ void main() {
expect(await camera.getMinZoomLevel(55), minZoomRatio);
});

test('setZoomLevel sets zoom ratio as expected', () async {
final AndroidCameraCameraX camera = AndroidCameraCameraX();
const int cameraId = 44;
const double zoomRatio = 0.3;
final MockCameraControl mockCameraControl = MockCameraControl();

camera.camera = MockCamera();

when(camera.camera!.getCameraControl())
.thenAnswer((_) async => mockCameraControl);

await camera.setZoomLevel(cameraId, zoomRatio);

verify(mockCameraControl.setZoomRatio(zoomRatio));
});

test(
'onStreamedFrameAvailable emits CameraImageData when picked up from CameraImageData stream controller',
() async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,15 @@ class MockCameraControl extends _i1.Mock implements _i3.CameraControl {
returnValue: _i15.Future<void>.value(),
returnValueForMissingStub: _i15.Future<void>.value(),
) as _i15.Future<void>);
@override
_i15.Future<void> setZoomRatio(double? ratio) => (super.noSuchMethod(
Invocation.method(
#setZoomRatio,
[ratio],
),
returnValue: _i15.Future<void>.value(),
returnValueForMissingStub: _i15.Future<void>.value(),
) as _i15.Future<void>);
}

/// A class which mocks [CameraImageData].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,32 @@ void main() {
verify(mockApi.enableTorch(cameraControlIdentifier, enableTorch));
});

test('setZoomRatio makes call on Java side to set zoom ratio', () async {
final MockTestCameraControlHostApi mockApi =
MockTestCameraControlHostApi();
TestCameraControlHostApi.setup(mockApi);

final InstanceManager instanceManager = InstanceManager(
onWeakReferenceRemoved: (_) {},
);

final CameraControl cameraControl = CameraControl.detached(
instanceManager: instanceManager,
);
const int cameraControlIdentifier = 45;

instanceManager.addHostCreatedInstance(
cameraControl,
cameraControlIdentifier,
onCopy: (_) => CameraControl.detached(instanceManager: instanceManager),
);

const double zoom = 0.2;
await cameraControl.setZoomRatio(zoom);

verify(mockApi.setZoomRatio(cameraControlIdentifier, zoom));
});

test('flutterApiCreate makes call to add instance to instance manager', () {
final InstanceManager instanceManager = InstanceManager(
onWeakReferenceRemoved: (_) {},
Expand Down
Loading