Skip to content

[camerax] Wrap Android classes/methods required for implementing setting focus & exposure points and offset #5659

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
743b38d
Merge remote-tracking branch 'upstream/main' into camx_occ
camsim99 May 1, 2023
4427ac6
Undo changes
camsim99 May 10, 2023
3f9cf35
Start wrapping
camsim99 Nov 27, 2023
c37e8cd
Progress, left todo as marker
camsim99 Dec 13, 2023
ad9c29c
More wrapping, start with meteringpoint
camsim99 Dec 13, 2023
8472b7c
Wrapping
camsim99 Dec 26, 2023
886706b
New files
camsim99 Dec 27, 2023
51da157
Finish non cam2 wrpaping
camsim99 Jan 2, 2024
d157b46
Adding tests + docs
camsim99 Jan 3, 2024
4a0872c
More wrapping and tests
camsim99 Jan 4, 2024
aa0f947
More wrapping, tests
camsim99 Jan 5, 2024
05b784d
Final inital wrapping
camsim99 Jan 5, 2024
36257d4
java formatting + fix dart tests
camsim99 Jan 5, 2024
a9af98d
Fix Java classes
camsim99 Jan 8, 2024
4af2266
Get tests running
camsim99 Jan 8, 2024
fb7927b
Fix java tests
camsim99 Jan 8, 2024
b5e974c
Bump version
camsim99 Jan 8, 2024
ba95c41
Self review
camsim99 Jan 9, 2024
c435903
Merge remote-tracking branch 'upstream/main' into camx_cc
camsim99 Jan 9, 2024
be193db
Merge branch 'main' into camx_cc
camsim99 Jan 9, 2024
2e69a9c
Nits
camsim99 Jan 9, 2024
cc4ecee
Merge remote-tracking branch 'refs/remotes/origin/camx_cc' into camx_cc
camsim99 Jan 9, 2024
643fabb
lint
camsim99 Jan 9, 2024
36d9a2e
Update packages/camera/camera_android_camerax/pigeons/camerax_library…
camsim99 Jan 24, 2024
92157c4
Update packages/camera/camera_android_camerax/android/src/test/java/i…
camsim99 Jan 24, 2024
9977463
Update packages/camera/camera_android_camerax/lib/src/camera_control.…
camsim99 Jan 24, 2024
469d0cd
Merge remote-tracking branch 'upstream/main' into camx_cc
camsim99 Jan 24, 2024
cdfd98c
format
camsim99 Jan 24, 2024
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
3 changes: 2 additions & 1 deletion packages/camera/camera_android_camerax/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 0.5.0+28

* Wraps CameraX classes needed to implement setting focus and exposure points and exposure offset.
* Updates compileSdk version to 34.

## 0.5.0+27
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ public void setUp(
binaryMessenger, new FallbackStrategyHostApiImpl(instanceManager));
GeneratedCameraXLibrary.QualitySelectorHostApi.setup(
binaryMessenger, new QualitySelectorHostApiImpl(instanceManager));
cameraControlHostApiImpl = new CameraControlHostApiImpl(instanceManager, context);
cameraControlHostApiImpl =
new CameraControlHostApiImpl(binaryMessenger, instanceManager, context);
GeneratedCameraXLibrary.CameraControlHostApi.setup(binaryMessenger, cameraControlHostApiImpl);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.CameraControl;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.FocusMeteringResult;
import androidx.core.content.ContextCompat;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraControlHostApi;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.Result;
import java.util.Objects;

/**
Expand All @@ -29,6 +33,8 @@ public class CameraControlHostApiImpl implements CameraControlHostApi {
@VisibleForTesting
public static class CameraControlProxy {
Context context;
BinaryMessenger binaryMessenger;
InstanceManager instanceManager;

/** Enables or disables the torch of the specified {@link CameraControl} instance. */
@NonNull
Expand Down Expand Up @@ -82,6 +88,85 @@ public void onFailure(Throwable t) {
},
ContextCompat.getMainExecutor(context));
}

/**
* Starts a focus and metering action configured by the {@code FocusMeteringAction}.
*
* <p>Will trigger an auto focus action and enable auto focus/auto exposure/auto white balance
* metering regions.
*/
public void startFocusAndMetering(
@NonNull CameraControl cameraControl,
@NonNull FocusMeteringAction focusMeteringAction,
@NonNull GeneratedCameraXLibrary.Result<Long> result) {
ListenableFuture<FocusMeteringResult> focusMeteringResultFuture =
cameraControl.startFocusAndMetering(focusMeteringAction);

Futures.addCallback(
focusMeteringResultFuture,
new FutureCallback<FocusMeteringResult>() {
public void onSuccess(FocusMeteringResult focusMeteringResult) {
final FocusMeteringResultFlutterApiImpl flutterApi =
new FocusMeteringResultFlutterApiImpl(binaryMessenger, instanceManager);
flutterApi.create(focusMeteringResult, reply -> {});
result.success(instanceManager.getIdentifierForStrongReference(focusMeteringResult));
}

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

/**
* Cancels current {@code FocusMeteringAction} and clears auto focus/auto exposure/auto white
* balance regions.
*/
public void cancelFocusAndMetering(
@NonNull CameraControl cameraControl, @NonNull Result<Void> result) {
ListenableFuture<Void> cancelFocusAndMeteringFuture = cameraControl.cancelFocusAndMetering();

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

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

/**
* Sets the exposure compensation index for the specified {@link CameraControl} instance and
* returns the new target exposure value.
*
* <p>The exposure compensation value set on the camera must be within the range of {@code
* ExposureState#getExposureCompensationRange()} for the current {@code ExposureState} for the
* call to succeed.
*/
public void setExposureCompensationIndex(
@NonNull CameraControl cameraControl, @NonNull Long index, @NonNull Result<Long> result) {
ListenableFuture<Integer> setExposureCompensationIndexFuture =
cameraControl.setExposureCompensationIndex(index.intValue());

Futures.addCallback(
setExposureCompensationIndexFuture,
new FutureCallback<Integer>() {
public void onSuccess(Integer integerResult) {
result.success(integerResult.longValue());
}

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

/**
Expand All @@ -90,8 +175,10 @@ public void onFailure(Throwable t) {
* @param instanceManager maintains instances stored to communicate with attached Dart objects
*/
public CameraControlHostApiImpl(
@NonNull InstanceManager instanceManager, @NonNull Context context) {
this(instanceManager, new CameraControlProxy(), context);
@NonNull BinaryMessenger binaryMessenger,
@NonNull InstanceManager instanceManager,
@NonNull Context context) {
this(binaryMessenger, instanceManager, new CameraControlProxy(), context);
}

/**
Expand All @@ -103,12 +190,16 @@ public CameraControlHostApiImpl(
*/
@VisibleForTesting
CameraControlHostApiImpl(
@NonNull BinaryMessenger binaryMessenger,
@NonNull InstanceManager instanceManager,
@NonNull CameraControlProxy proxy,
@NonNull Context context) {
this.instanceManager = instanceManager;
this.proxy = proxy;
proxy.context = context;
// proxy.startFocusAndMetering needs to access these to create a FocusMeteringResult when it becomes available:
proxy.instanceManager = instanceManager;
proxy.binaryMessenger = binaryMessenger;
}

/**
Expand All @@ -127,16 +218,38 @@ public void enableTorch(
@NonNull Long identifier,
@NonNull Boolean torch,
@NonNull GeneratedCameraXLibrary.Result<Void> result) {
proxy.enableTorch(
Objects.requireNonNull(instanceManager.getInstance(identifier)), torch, result);
proxy.enableTorch(getCameraControlInstance(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);
proxy.setZoomRatio(getCameraControlInstance(identifier), ratio, result);
}

@Override
public void startFocusAndMetering(
@NonNull Long identifier, @NonNull Long focusMeteringActionId, @NonNull Result<Long> result) {
proxy.startFocusAndMetering(
getCameraControlInstance(identifier),
Objects.requireNonNull(instanceManager.getInstance(focusMeteringActionId)),
result);
}

@Override
public void cancelFocusAndMetering(@NonNull Long identifier, @NonNull Result<Void> result) {
proxy.cancelFocusAndMetering(getCameraControlInstance(identifier), result);
}

@Override
public void setExposureCompensationIndex(
@NonNull Long identifier, @NonNull Long index, @NonNull Result<Long> result) {
proxy.setExposureCompensationIndex(getCameraControlInstance(identifier), index, result);
}

private CameraControl getCameraControlInstance(@NonNull Long identifier) {
return Objects.requireNonNull(instanceManager.getInstance(identifier));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camerax;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.MeteringPoint;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FocusMeteringActionHostApi;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.MeteringPointInfo;
import java.util.ArrayList;
import java.util.List;

/**
* Host API implementation for {@link FocusMeteringAction}.
*
* <p>This class may handle instantiating and adding native object instances that are attached to a
* Dart instance or handle method calls on the associated native class or an instance of the class.
*/
public class FocusMeteringActionHostApiImpl implements FocusMeteringActionHostApi {
private final InstanceManager instanceManager;

private final FocusMeteringActionProxy proxy;

/** Proxy for constructors and static method of {@link FocusMeteringAction}. */
@VisibleForTesting
public static class FocusMeteringActionProxy {
/** Creates an instance of {@link FocusMeteringAction}. */
public @NonNull FocusMeteringAction create(
@NonNull List<MeteringPoint> meteringPoints, @NonNull List<Integer> meteringPointModes) {
if (meteringPoints.size() >= 1 && meteringPoints.size() != meteringPointModes.size()) {
throw new IllegalArgumentException(
"One metering point must be specified and the number of specified metering points must match the number of specified metering point modes.");
}

FocusMeteringAction.Builder focusMeteringActionBuilder;

// Create builder to potentially add more MeteringPoints to.
MeteringPoint firstMeteringPoint = meteringPoints.get(0);
Integer firstMeteringPointMode = meteringPointModes.get(0);
if (firstMeteringPointMode == null) {
focusMeteringActionBuilder = getFocusMeteringActionBuilder(firstMeteringPoint);
} else {
focusMeteringActionBuilder =
getFocusMeteringActionBuilder(firstMeteringPoint, firstMeteringPointMode);
}

// Add any additional metering points in order as specified by input lists.
for (int i = 1; i < meteringPoints.size(); i++) {
MeteringPoint meteringPoint = meteringPoints.get(i);
Integer meteringMode = meteringPointModes.get(i);

if (meteringMode == null) {
focusMeteringActionBuilder.addPoint(meteringPoint);
} else {
focusMeteringActionBuilder.addPoint(meteringPoint, meteringMode);
}
}

return focusMeteringActionBuilder.build();
}

@VisibleForTesting
@NonNull
public FocusMeteringAction.Builder getFocusMeteringActionBuilder(
@NonNull MeteringPoint meteringPoint) {
return new FocusMeteringAction.Builder(meteringPoint);
}

@VisibleForTesting
@NonNull
public FocusMeteringAction.Builder getFocusMeteringActionBuilder(
@NonNull MeteringPoint meteringPoint, int meteringMode) {
return new FocusMeteringAction.Builder(meteringPoint, meteringMode);
}
}

/**
* Constructs a {@link FocusMeteringActionHostApiImpl}.
*
* @param instanceManager maintains instances stored to communicate with attached Dart objects
*/
public FocusMeteringActionHostApiImpl(@NonNull InstanceManager instanceManager) {
this(instanceManager, new FocusMeteringActionProxy());
}

/**
* Constructs a {@link FocusMeteringActionHostApiImpl}.
*
* @param instanceManager maintains instances stored to communicate with attached Dart objects
* @param proxy proxy for constructors and static method of {@link FocusMeteringAction}
*/
FocusMeteringActionHostApiImpl(
@NonNull InstanceManager instanceManager, @NonNull FocusMeteringActionProxy proxy) {
this.instanceManager = instanceManager;
this.proxy = proxy;
}

@Override
public void create(
@NonNull Long identifier, @NonNull List<MeteringPointInfo> meteringPointInfos) {
final List<MeteringPoint> meteringPoints = new ArrayList<MeteringPoint>();
final List<Integer> meteringPointModes = new ArrayList<Integer>();
for (MeteringPointInfo meteringPointInfo : meteringPointInfos) {
meteringPoints.add(instanceManager.getInstance(meteringPointInfo.getMeteringPointId()));
Long meteringPointMode = meteringPointInfo.getMeteringMode();
meteringPointModes.add(meteringPointMode == null ? null : meteringPointMode.intValue());
}

instanceManager.addDartCreatedInstance(
proxy.create(meteringPoints, meteringPointModes), identifier);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camerax;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.FocusMeteringResult;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FocusMeteringResultFlutterApi;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FocusMeteringResultFlutterApi.Reply;

/**
* Flutter API implementation for {@link FocusMeteringResult}.
*
* <p>This class may handle adding native instances that are attached to a Dart instance or passing
* arguments of callbacks methods to a Dart instance.
*/
public class FocusMeteringResultFlutterApiImpl {
private final BinaryMessenger binaryMessenger;
private final InstanceManager instanceManager;
private FocusMeteringResultFlutterApi focusMeteringResultFlutterApi;

/**
* Constructs a {@link FocusMeteringResultFlutterApiImpl}.
*
* @param binaryMessenger used to communicate with Dart over asynchronous messages
* @param instanceManager maintains instances stored to communicate with attached Dart objects
*/
public FocusMeteringResultFlutterApiImpl(
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
this.binaryMessenger = binaryMessenger;
this.instanceManager = instanceManager;
focusMeteringResultFlutterApi = new FocusMeteringResultFlutterApi(binaryMessenger);
}

/**
* Stores the {@link FocusMeteringResult} instance and notifies Dart to create and store a new
* {@link FocusMeteringResult} instance that is attached to this one. If {@code instance} has
* already been added, this method does nothing.
*/
public void create(@NonNull FocusMeteringResult instance, @NonNull Reply<Void> callback) {
if (!instanceManager.containsInstance(instance)) {
focusMeteringResultFlutterApi.create(
instanceManager.addHostCreatedInstance(instance), callback);
}
}

/** Sets the Flutter API used to send messages to Dart. */
@VisibleForTesting
void setApi(@NonNull FocusMeteringResultFlutterApi api) {
this.focusMeteringResultFlutterApi = api;
}
}
Loading