Skip to content

Commit dced730

Browse files
authored
[camerax] Wrap Android classes/methods required for implementing setting focus & exposure points and offset (flutter#5659)
Wraps CameraX APIs needed to at least implement setting focus and exposure points and offset (`setFocusPoint`,`setExposurePoint`, `setExposureOffset`). Listed by class: `CameraControl` - [`startFocusAndMetering`](https://developer.android.com/reference/androidx/camera/core/CameraControl#startFocusAndMetering(androidx.camera.core.FocusMeteringAction)) - [`cancelFocusAndMetering`](https://developer.android.com/reference/androidx/camera/core/CameraControl#cancelFocusAndMetering()) - [`setExposureCompensationIndex`](https://developer.android.com/reference/androidx/camera/core/CameraControl#setExposureCompensationIndex(int)) `FocusMeteringAction` - `create` method for [this class](https://developer.android.com/reference/androidx/camera/core/FocusMeteringAction.Builder#Builder(androidx.camera.core.MeteringPoint,int)) `FocusMeteringResult` - `create` method for [this class](https://developer.android.com/reference/androidx/camera/core/FocusMeteringResult) - [`isFocusSuccessful`](https://developer.android.com/reference/kotlin/androidx/camera/core/FocusMeteringResult#isFocusSuccessful()) `MeteringPoint` - `create` method for the points that [`SurfaceOrientedMeteringPointFactory`](https://developer.android.com/reference/androidx/camera/core/SurfaceOrientedMeteringPointFactory) creates - [`getDefaultPointSize`](https://developer.android.com/reference/androidx/camera/core/MeteringPointFactory#getDefaultPointSize()) Part of flutter#120468 and flutter#120467.
1 parent e83f9f7 commit dced730

30 files changed

+3448
-62
lines changed

packages/camera/camera_android_camerax/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 0.5.0+28
22

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

56
## 0.5.0+27

packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ public void setUp(
116116
binaryMessenger, new FallbackStrategyHostApiImpl(instanceManager));
117117
GeneratedCameraXLibrary.QualitySelectorHostApi.setup(
118118
binaryMessenger, new QualitySelectorHostApiImpl(instanceManager));
119-
cameraControlHostApiImpl = new CameraControlHostApiImpl(instanceManager, context);
119+
cameraControlHostApiImpl =
120+
new CameraControlHostApiImpl(binaryMessenger, instanceManager, context);
120121
GeneratedCameraXLibrary.CameraControlHostApi.setup(binaryMessenger, cameraControlHostApiImpl);
121122
}
122123

packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlHostApiImpl.java

Lines changed: 119 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@
88
import androidx.annotation.NonNull;
99
import androidx.annotation.VisibleForTesting;
1010
import androidx.camera.core.CameraControl;
11+
import androidx.camera.core.FocusMeteringAction;
12+
import androidx.camera.core.FocusMeteringResult;
1113
import androidx.core.content.ContextCompat;
1214
import com.google.common.util.concurrent.FutureCallback;
1315
import com.google.common.util.concurrent.Futures;
1416
import com.google.common.util.concurrent.ListenableFuture;
17+
import io.flutter.plugin.common.BinaryMessenger;
1518
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraControlHostApi;
19+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.Result;
1620
import java.util.Objects;
1721

1822
/**
@@ -29,6 +33,8 @@ public class CameraControlHostApiImpl implements CameraControlHostApi {
2933
@VisibleForTesting
3034
public static class CameraControlProxy {
3135
Context context;
36+
BinaryMessenger binaryMessenger;
37+
InstanceManager instanceManager;
3238

3339
/** Enables or disables the torch of the specified {@link CameraControl} instance. */
3440
@NonNull
@@ -82,6 +88,85 @@ public void onFailure(Throwable t) {
8288
},
8389
ContextCompat.getMainExecutor(context));
8490
}
91+
92+
/**
93+
* Starts a focus and metering action configured by the {@code FocusMeteringAction}.
94+
*
95+
* <p>Will trigger an auto focus action and enable auto focus/auto exposure/auto white balance
96+
* metering regions.
97+
*/
98+
public void startFocusAndMetering(
99+
@NonNull CameraControl cameraControl,
100+
@NonNull FocusMeteringAction focusMeteringAction,
101+
@NonNull GeneratedCameraXLibrary.Result<Long> result) {
102+
ListenableFuture<FocusMeteringResult> focusMeteringResultFuture =
103+
cameraControl.startFocusAndMetering(focusMeteringAction);
104+
105+
Futures.addCallback(
106+
focusMeteringResultFuture,
107+
new FutureCallback<FocusMeteringResult>() {
108+
public void onSuccess(FocusMeteringResult focusMeteringResult) {
109+
final FocusMeteringResultFlutterApiImpl flutterApi =
110+
new FocusMeteringResultFlutterApiImpl(binaryMessenger, instanceManager);
111+
flutterApi.create(focusMeteringResult, reply -> {});
112+
result.success(instanceManager.getIdentifierForStrongReference(focusMeteringResult));
113+
}
114+
115+
public void onFailure(Throwable t) {
116+
result.error(t);
117+
}
118+
},
119+
ContextCompat.getMainExecutor(context));
120+
}
121+
122+
/**
123+
* Cancels current {@code FocusMeteringAction} and clears auto focus/auto exposure/auto white
124+
* balance regions.
125+
*/
126+
public void cancelFocusAndMetering(
127+
@NonNull CameraControl cameraControl, @NonNull Result<Void> result) {
128+
ListenableFuture<Void> cancelFocusAndMeteringFuture = cameraControl.cancelFocusAndMetering();
129+
130+
Futures.addCallback(
131+
cancelFocusAndMeteringFuture,
132+
new FutureCallback<Void>() {
133+
public void onSuccess(Void voidResult) {
134+
result.success(null);
135+
}
136+
137+
public void onFailure(Throwable t) {
138+
result.error(t);
139+
}
140+
},
141+
ContextCompat.getMainExecutor(context));
142+
}
143+
144+
/**
145+
* Sets the exposure compensation index for the specified {@link CameraControl} instance and
146+
* returns the new target exposure value.
147+
*
148+
* <p>The exposure compensation value set on the camera must be within the range of {@code
149+
* ExposureState#getExposureCompensationRange()} for the current {@code ExposureState} for the
150+
* call to succeed.
151+
*/
152+
public void setExposureCompensationIndex(
153+
@NonNull CameraControl cameraControl, @NonNull Long index, @NonNull Result<Long> result) {
154+
ListenableFuture<Integer> setExposureCompensationIndexFuture =
155+
cameraControl.setExposureCompensationIndex(index.intValue());
156+
157+
Futures.addCallback(
158+
setExposureCompensationIndexFuture,
159+
new FutureCallback<Integer>() {
160+
public void onSuccess(Integer integerResult) {
161+
result.success(integerResult.longValue());
162+
}
163+
164+
public void onFailure(Throwable t) {
165+
result.error(t);
166+
}
167+
},
168+
ContextCompat.getMainExecutor(context));
169+
}
85170
}
86171

87172
/**
@@ -90,8 +175,10 @@ public void onFailure(Throwable t) {
90175
* @param instanceManager maintains instances stored to communicate with attached Dart objects
91176
*/
92177
public CameraControlHostApiImpl(
93-
@NonNull InstanceManager instanceManager, @NonNull Context context) {
94-
this(instanceManager, new CameraControlProxy(), context);
178+
@NonNull BinaryMessenger binaryMessenger,
179+
@NonNull InstanceManager instanceManager,
180+
@NonNull Context context) {
181+
this(binaryMessenger, instanceManager, new CameraControlProxy(), context);
95182
}
96183

97184
/**
@@ -103,12 +190,16 @@ public CameraControlHostApiImpl(
103190
*/
104191
@VisibleForTesting
105192
CameraControlHostApiImpl(
193+
@NonNull BinaryMessenger binaryMessenger,
106194
@NonNull InstanceManager instanceManager,
107195
@NonNull CameraControlProxy proxy,
108196
@NonNull Context context) {
109197
this.instanceManager = instanceManager;
110198
this.proxy = proxy;
111199
proxy.context = context;
200+
// proxy.startFocusAndMetering needs to access these to create a FocusMeteringResult when it becomes available:
201+
proxy.instanceManager = instanceManager;
202+
proxy.binaryMessenger = binaryMessenger;
112203
}
113204

114205
/**
@@ -127,16 +218,38 @@ public void enableTorch(
127218
@NonNull Long identifier,
128219
@NonNull Boolean torch,
129220
@NonNull GeneratedCameraXLibrary.Result<Void> result) {
130-
proxy.enableTorch(
131-
Objects.requireNonNull(instanceManager.getInstance(identifier)), torch, result);
221+
proxy.enableTorch(getCameraControlInstance(identifier), torch, result);
132222
}
133223

134224
@Override
135225
public void setZoomRatio(
136226
@NonNull Long identifier,
137227
@NonNull Double ratio,
138228
@NonNull GeneratedCameraXLibrary.Result<Void> result) {
139-
proxy.setZoomRatio(
140-
Objects.requireNonNull(instanceManager.getInstance(identifier)), ratio, result);
229+
proxy.setZoomRatio(getCameraControlInstance(identifier), ratio, result);
230+
}
231+
232+
@Override
233+
public void startFocusAndMetering(
234+
@NonNull Long identifier, @NonNull Long focusMeteringActionId, @NonNull Result<Long> result) {
235+
proxy.startFocusAndMetering(
236+
getCameraControlInstance(identifier),
237+
Objects.requireNonNull(instanceManager.getInstance(focusMeteringActionId)),
238+
result);
239+
}
240+
241+
@Override
242+
public void cancelFocusAndMetering(@NonNull Long identifier, @NonNull Result<Void> result) {
243+
proxy.cancelFocusAndMetering(getCameraControlInstance(identifier), result);
244+
}
245+
246+
@Override
247+
public void setExposureCompensationIndex(
248+
@NonNull Long identifier, @NonNull Long index, @NonNull Result<Long> result) {
249+
proxy.setExposureCompensationIndex(getCameraControlInstance(identifier), index, result);
250+
}
251+
252+
private CameraControl getCameraControlInstance(@NonNull Long identifier) {
253+
return Objects.requireNonNull(instanceManager.getInstance(identifier));
141254
}
142255
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.camerax;
6+
7+
import androidx.annotation.NonNull;
8+
import androidx.annotation.VisibleForTesting;
9+
import androidx.camera.core.FocusMeteringAction;
10+
import androidx.camera.core.MeteringPoint;
11+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FocusMeteringActionHostApi;
12+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.MeteringPointInfo;
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
16+
/**
17+
* Host API implementation for {@link FocusMeteringAction}.
18+
*
19+
* <p>This class may handle instantiating and adding native object instances that are attached to a
20+
* Dart instance or handle method calls on the associated native class or an instance of the class.
21+
*/
22+
public class FocusMeteringActionHostApiImpl implements FocusMeteringActionHostApi {
23+
private final InstanceManager instanceManager;
24+
25+
private final FocusMeteringActionProxy proxy;
26+
27+
/** Proxy for constructors and static method of {@link FocusMeteringAction}. */
28+
@VisibleForTesting
29+
public static class FocusMeteringActionProxy {
30+
/** Creates an instance of {@link FocusMeteringAction}. */
31+
public @NonNull FocusMeteringAction create(
32+
@NonNull List<MeteringPoint> meteringPoints, @NonNull List<Integer> meteringPointModes) {
33+
if (meteringPoints.size() >= 1 && meteringPoints.size() != meteringPointModes.size()) {
34+
throw new IllegalArgumentException(
35+
"One metering point must be specified and the number of specified metering points must match the number of specified metering point modes.");
36+
}
37+
38+
FocusMeteringAction.Builder focusMeteringActionBuilder;
39+
40+
// Create builder to potentially add more MeteringPoints to.
41+
MeteringPoint firstMeteringPoint = meteringPoints.get(0);
42+
Integer firstMeteringPointMode = meteringPointModes.get(0);
43+
if (firstMeteringPointMode == null) {
44+
focusMeteringActionBuilder = getFocusMeteringActionBuilder(firstMeteringPoint);
45+
} else {
46+
focusMeteringActionBuilder =
47+
getFocusMeteringActionBuilder(firstMeteringPoint, firstMeteringPointMode);
48+
}
49+
50+
// Add any additional metering points in order as specified by input lists.
51+
for (int i = 1; i < meteringPoints.size(); i++) {
52+
MeteringPoint meteringPoint = meteringPoints.get(i);
53+
Integer meteringMode = meteringPointModes.get(i);
54+
55+
if (meteringMode == null) {
56+
focusMeteringActionBuilder.addPoint(meteringPoint);
57+
} else {
58+
focusMeteringActionBuilder.addPoint(meteringPoint, meteringMode);
59+
}
60+
}
61+
62+
return focusMeteringActionBuilder.build();
63+
}
64+
65+
@VisibleForTesting
66+
@NonNull
67+
public FocusMeteringAction.Builder getFocusMeteringActionBuilder(
68+
@NonNull MeteringPoint meteringPoint) {
69+
return new FocusMeteringAction.Builder(meteringPoint);
70+
}
71+
72+
@VisibleForTesting
73+
@NonNull
74+
public FocusMeteringAction.Builder getFocusMeteringActionBuilder(
75+
@NonNull MeteringPoint meteringPoint, int meteringMode) {
76+
return new FocusMeteringAction.Builder(meteringPoint, meteringMode);
77+
}
78+
}
79+
80+
/**
81+
* Constructs a {@link FocusMeteringActionHostApiImpl}.
82+
*
83+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
84+
*/
85+
public FocusMeteringActionHostApiImpl(@NonNull InstanceManager instanceManager) {
86+
this(instanceManager, new FocusMeteringActionProxy());
87+
}
88+
89+
/**
90+
* Constructs a {@link FocusMeteringActionHostApiImpl}.
91+
*
92+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
93+
* @param proxy proxy for constructors and static method of {@link FocusMeteringAction}
94+
*/
95+
FocusMeteringActionHostApiImpl(
96+
@NonNull InstanceManager instanceManager, @NonNull FocusMeteringActionProxy proxy) {
97+
this.instanceManager = instanceManager;
98+
this.proxy = proxy;
99+
}
100+
101+
@Override
102+
public void create(
103+
@NonNull Long identifier, @NonNull List<MeteringPointInfo> meteringPointInfos) {
104+
final List<MeteringPoint> meteringPoints = new ArrayList<MeteringPoint>();
105+
final List<Integer> meteringPointModes = new ArrayList<Integer>();
106+
for (MeteringPointInfo meteringPointInfo : meteringPointInfos) {
107+
meteringPoints.add(instanceManager.getInstance(meteringPointInfo.getMeteringPointId()));
108+
Long meteringPointMode = meteringPointInfo.getMeteringMode();
109+
meteringPointModes.add(meteringPointMode == null ? null : meteringPointMode.intValue());
110+
}
111+
112+
instanceManager.addDartCreatedInstance(
113+
proxy.create(meteringPoints, meteringPointModes), identifier);
114+
}
115+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.camerax;
6+
7+
import androidx.annotation.NonNull;
8+
import androidx.annotation.VisibleForTesting;
9+
import androidx.camera.core.FocusMeteringResult;
10+
import io.flutter.plugin.common.BinaryMessenger;
11+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FocusMeteringResultFlutterApi;
12+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FocusMeteringResultFlutterApi.Reply;
13+
14+
/**
15+
* Flutter API implementation for {@link FocusMeteringResult}.
16+
*
17+
* <p>This class may handle adding native instances that are attached to a Dart instance or passing
18+
* arguments of callbacks methods to a Dart instance.
19+
*/
20+
public class FocusMeteringResultFlutterApiImpl {
21+
private final BinaryMessenger binaryMessenger;
22+
private final InstanceManager instanceManager;
23+
private FocusMeteringResultFlutterApi focusMeteringResultFlutterApi;
24+
25+
/**
26+
* Constructs a {@link FocusMeteringResultFlutterApiImpl}.
27+
*
28+
* @param binaryMessenger used to communicate with Dart over asynchronous messages
29+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
30+
*/
31+
public FocusMeteringResultFlutterApiImpl(
32+
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
33+
this.binaryMessenger = binaryMessenger;
34+
this.instanceManager = instanceManager;
35+
focusMeteringResultFlutterApi = new FocusMeteringResultFlutterApi(binaryMessenger);
36+
}
37+
38+
/**
39+
* Stores the {@link FocusMeteringResult} instance and notifies Dart to create and store a new
40+
* {@link FocusMeteringResult} instance that is attached to this one. If {@code instance} has
41+
* already been added, this method does nothing.
42+
*/
43+
public void create(@NonNull FocusMeteringResult instance, @NonNull Reply<Void> callback) {
44+
if (!instanceManager.containsInstance(instance)) {
45+
focusMeteringResultFlutterApi.create(
46+
instanceManager.addHostCreatedInstance(instance), callback);
47+
}
48+
}
49+
50+
/** Sets the Flutter API used to send messages to Dart. */
51+
@VisibleForTesting
52+
void setApi(@NonNull FocusMeteringResultFlutterApi api) {
53+
this.focusMeteringResultFlutterApi = api;
54+
}
55+
}

0 commit comments

Comments
 (0)