Skip to content

Commit 1a5a7ce

Browse files
authored
[camerax] Wrap Android classes/methods required to set the exposure mode (#5966)
Wraps classes/methods needed to set capture request options that will allow us to implement setting the exposure mode, namely: **Camera2CameraControl** - `create` method using [`from`](https://developer.android.com/reference/androidx/camera/camera2/interop/Camera2CameraControl#from(androidx.camera.core.CameraControl)) - [`setCaptureRequestOptions`](https://developer.android.com/reference/androidx/camera/camera2/interop/Camera2CameraControl#setCaptureRequestOptions(androidx.camera.camera2.interop.CaptureRequestOptions)) **CaptureRequestOptions** - `create` method using its [builder](https://developer.android.com/reference/androidx/camera/camera2/interop/CaptureRequestOptions.Builder)* Part of flutter/flutter#120468. *Note that this required that I specify types of supported capture request options due to the Dart/native split. I took inspiration from our previous implementation of supported `LiveData` types (see [pigeon file](https://github.com/flutter/packages/blob/73c9bdc59b8d51ee558bff113bcdcdb53485cd08/packages/camera/camera_android_camerax/pigeons/camerax_library.dart#L80) for details).
1 parent d63629d commit 1a5a7ce

27 files changed

+1668
-150
lines changed

packages/camera/camera_android_camerax/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.5.0+31
2+
3+
* Wraps CameraX classes needed to set capture request options, which is needed to implement setting the exposure mode.
4+
15
## 0.5.0+30
26

37
* Adds documentation to clarify how the plugin uses resolution presets as target resolutions for CameraX.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class AnalyzerHostApiImpl implements AnalyzerHostApi {
2222
private final InstanceManager instanceManager;
2323
private final AnalyzerProxy proxy;
2424

25-
/** Proxy for constructors and static method of {@link ImageAnalysis.Analyzer}. */
25+
/** Proxy for constructor of {@link ImageAnalysis.Analyzer}. */
2626
@VisibleForTesting
2727
public static class AnalyzerProxy {
2828

@@ -95,7 +95,7 @@ public AnalyzerHostApiImpl(
9595
*
9696
* @param binaryMessenger used to communicate with Dart over asynchronous messages
9797
* @param instanceManager maintains instances stored to communicate with attached Dart objects
98-
* @param proxy proxy for constructors and static method of {@link ImageAnalysis.Analyzer}
98+
* @param proxy proxy for constructor of {@link ImageAnalysis.Analyzer}
9999
*/
100100
@VisibleForTesting
101101
AnalyzerHostApiImpl(

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class AspectRatioStrategyHostApiImpl implements AspectRatioStrategyHostAp
1919
private final InstanceManager instanceManager;
2020
private final AspectRatioStrategyProxy proxy;
2121

22-
/** Proxy for constructors and static method of {@link AspectRatioStrategy}. */
22+
/** Proxy for constructor of {@link AspectRatioStrategy}. */
2323
@VisibleForTesting
2424
public static class AspectRatioStrategyProxy {
2525
/** Creates an instance of {@link AspectRatioStrategy}. */
@@ -43,7 +43,7 @@ public AspectRatioStrategyHostApiImpl(@NonNull InstanceManager instanceManager)
4343
* Constructs an {@link AspectRatioStrategyHostApiImpl}.
4444
*
4545
* @param instanceManager maintains instances stored to communicate with attached Dart objects
46-
* @param proxy proxy for constructors and static method of {@link AspectRatioStrategy}
46+
* @param proxy proxy for constructor of {@link AspectRatioStrategy}
4747
*/
4848
@VisibleForTesting
4949
AspectRatioStrategyHostApiImpl(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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 android.content.Context;
8+
import androidx.annotation.NonNull;
9+
import androidx.annotation.OptIn;
10+
import androidx.annotation.VisibleForTesting;
11+
import androidx.camera.camera2.interop.Camera2CameraControl;
12+
import androidx.camera.camera2.interop.CaptureRequestOptions;
13+
import androidx.camera.core.CameraControl;
14+
import androidx.core.content.ContextCompat;
15+
import com.google.common.util.concurrent.FutureCallback;
16+
import com.google.common.util.concurrent.Futures;
17+
import com.google.common.util.concurrent.ListenableFuture;
18+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.Camera2CameraControlHostApi;
19+
import java.util.Objects;
20+
21+
/**
22+
* Host API implementation for {@link Camera2CameraControl}.
23+
*
24+
* <p>This class may handle instantiating and adding native object instances that are attached to a
25+
* Dart instance or handle method calls on the associated native class or an instance of the class.
26+
*/
27+
public class Camera2CameraControlHostApiImpl implements Camera2CameraControlHostApi {
28+
private final InstanceManager instanceManager;
29+
private final Camera2CameraControlProxy proxy;
30+
31+
/** Proxy for constructor and methods of {@link Camera2CameraControl}. */
32+
@VisibleForTesting
33+
public static class Camera2CameraControlProxy {
34+
Context context;
35+
36+
/**
37+
* Creates an instance of {@link Camera2CameraControl} derived from specified {@link
38+
* CameraControl} instance.
39+
*/
40+
@OptIn(markerClass = androidx.camera.camera2.interop.ExperimentalCamera2Interop.class)
41+
public @NonNull Camera2CameraControl create(@NonNull CameraControl cameraControl) {
42+
return Camera2CameraControl.from(cameraControl);
43+
}
44+
45+
/**
46+
* Adds a {@link CaptureRequestOptions} to update the capture session with the options it
47+
* contains.
48+
*/
49+
@OptIn(markerClass = androidx.camera.camera2.interop.ExperimentalCamera2Interop.class)
50+
public void addCaptureRequestOptions(
51+
@NonNull Camera2CameraControl camera2CameraControl,
52+
@NonNull CaptureRequestOptions bundle,
53+
@NonNull GeneratedCameraXLibrary.Result<Void> result) {
54+
if (context == null) {
55+
throw new IllegalStateException("Context must be set to add capture request options.");
56+
}
57+
58+
ListenableFuture<Void> addCaptureRequestOptionsFuture =
59+
camera2CameraControl.addCaptureRequestOptions(bundle);
60+
61+
Futures.addCallback(
62+
addCaptureRequestOptionsFuture,
63+
new FutureCallback<Void>() {
64+
public void onSuccess(Void voidResult) {
65+
result.success(null);
66+
}
67+
68+
public void onFailure(Throwable t) {
69+
result.error(t);
70+
}
71+
},
72+
ContextCompat.getMainExecutor(context));
73+
}
74+
}
75+
76+
/**
77+
* Constructs a {@link Camera2CameraControlHostApiImpl}.
78+
*
79+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
80+
* @param context {@link Context} used to retrieve {@code Executor}
81+
*/
82+
public Camera2CameraControlHostApiImpl(
83+
@NonNull InstanceManager instanceManager, @NonNull Context context) {
84+
this(instanceManager, new Camera2CameraControlProxy(), context);
85+
}
86+
87+
/**
88+
* Constructs a {@link Camera2CameraControlHostApiImpl}.
89+
*
90+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
91+
* @param proxy proxy for constructor and methods of {@link Camera2CameraControl}
92+
* @param context {@link Context} used to retrieve {@code Executor}
93+
*/
94+
@VisibleForTesting
95+
Camera2CameraControlHostApiImpl(
96+
@NonNull InstanceManager instanceManager,
97+
@NonNull Camera2CameraControlProxy proxy,
98+
@NonNull Context context) {
99+
this.instanceManager = instanceManager;
100+
this.proxy = proxy;
101+
proxy.context = context;
102+
}
103+
104+
/**
105+
* Sets the context that the {@code Camera2CameraControl} will use to listen for the result of
106+
* setting capture request options.
107+
*
108+
* <p>If using the camera plugin in an add-to-app context, ensure that this is called anytime that
109+
* the context changes.
110+
*/
111+
public void setContext(@NonNull Context context) {
112+
this.proxy.context = context;
113+
}
114+
115+
@Override
116+
public void create(@NonNull Long identifier, @NonNull Long cameraControlIdentifier) {
117+
instanceManager.addDartCreatedInstance(
118+
proxy.create(Objects.requireNonNull(instanceManager.getInstance(cameraControlIdentifier))),
119+
identifier);
120+
}
121+
122+
@Override
123+
public void addCaptureRequestOptions(
124+
@NonNull Long identifier,
125+
@NonNull Long captureRequestOptionsIdentifier,
126+
@NonNull GeneratedCameraXLibrary.Result<Void> result) {
127+
proxy.addCaptureRequestOptions(
128+
getCamera2CameraControlInstance(identifier),
129+
Objects.requireNonNull(instanceManager.getInstance(captureRequestOptionsIdentifier)),
130+
result);
131+
}
132+
133+
/**
134+
* Retrieves the {@link Camera2CameraControl} instance associated with the specified {@code
135+
* identifier}.
136+
*/
137+
private Camera2CameraControl getCamera2CameraControlInstance(@NonNull Long identifier) {
138+
return Objects.requireNonNull(instanceManager.getInstance(identifier));
139+
}
140+
}

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

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class CameraControlHostApiImpl implements CameraControlHostApi {
2929
private final InstanceManager instanceManager;
3030
private final CameraControlProxy proxy;
3131

32-
/** Proxy for constructors and static method of {@link CameraControl}. */
32+
/** Proxy for methods of {@link CameraControl}. */
3333
@VisibleForTesting
3434
public static class CameraControlProxy {
3535
Context context;
@@ -99,6 +99,10 @@ public void startFocusAndMetering(
9999
@NonNull CameraControl cameraControl,
100100
@NonNull FocusMeteringAction focusMeteringAction,
101101
@NonNull GeneratedCameraXLibrary.Result<Long> result) {
102+
if (context == null) {
103+
throw new IllegalStateException("Context must be set to set zoom ratio.");
104+
}
105+
102106
ListenableFuture<FocusMeteringResult> focusMeteringResultFuture =
103107
cameraControl.startFocusAndMetering(focusMeteringAction);
104108

@@ -173,6 +177,7 @@ public void onFailure(Throwable t) {
173177
* Constructs an {@link CameraControlHostApiImpl}.
174178
*
175179
* @param instanceManager maintains instances stored to communicate with attached Dart objects
180+
* @param context {@link Context} used to retrieve {@code Executor}
176181
*/
177182
public CameraControlHostApiImpl(
178183
@NonNull BinaryMessenger binaryMessenger,
@@ -185,8 +190,8 @@ public CameraControlHostApiImpl(
185190
* Constructs an {@link CameraControlHostApiImpl}.
186191
*
187192
* @param instanceManager maintains instances stored to communicate with attached Dart objects
188-
* @param proxy proxy for constructors and static method of {@link CameraControl}
189-
* @param context {@link Context} used to retrieve {@code Executor} used to enable torch mode
193+
* @param proxy proxy for methods of {@link CameraControl}
194+
* @param context {@link Context} used to retrieve {@code Executor}
190195
*/
191196
@VisibleForTesting
192197
CameraControlHostApiImpl(
@@ -203,11 +208,11 @@ public CameraControlHostApiImpl(
203208
}
204209

205210
/**
206-
* Sets the context that the {@code ProcessCameraProvider} will use to enable/disable torch mode
207-
* and set the zoom ratio.
211+
* Sets the context that the {@code CameraControl} will use to enable/disable torch mode and set
212+
* the zoom ratio.
208213
*
209-
* <p>If using the camera plugin in an add-to-app context, ensure that a new instance of the
210-
* {@code CameraControl} is fetched via {@code #enableTorch} anytime the context changes.
214+
* <p>If using the camera plugin in an add-to-app context, ensure that this is called anytime that
215+
* the context changes.
211216
*/
212217
public void setContext(@NonNull Context context) {
213218
this.proxy.context = context;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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 android.hardware.camera2.CaptureRequest;
8+
import androidx.annotation.NonNull;
9+
import androidx.annotation.OptIn;
10+
import androidx.annotation.VisibleForTesting;
11+
import androidx.camera.camera2.interop.CaptureRequestOptions;
12+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CaptureRequestKeySupportedType;
13+
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CaptureRequestOptionsHostApi;
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
17+
/**
18+
* Host API implementation for {@link CaptureRequestOptions}.
19+
*
20+
* <p>This class may handle instantiating and adding native object instances that are attached to a
21+
* Dart instance or handle method calls on the associated native class or an instance of the class.
22+
*/
23+
public class CaptureRequestOptionsHostApiImpl implements CaptureRequestOptionsHostApi {
24+
private final InstanceManager instanceManager;
25+
private final CaptureRequestOptionsProxy proxy;
26+
27+
/** Proxy for constructor of {@link CaptureRequestOptions}. */
28+
@VisibleForTesting
29+
public static class CaptureRequestOptionsProxy {
30+
/** Creates an instance of {@link CaptureRequestOptions}. */
31+
// Suppression is safe because the type shared between the key and value pairs that
32+
// represent capture request options is checked on the Dart side.
33+
@SuppressWarnings("unchecked")
34+
@OptIn(markerClass = androidx.camera.camera2.interop.ExperimentalCamera2Interop.class)
35+
public @NonNull CaptureRequestOptions create(
36+
@NonNull Map<CaptureRequestKeySupportedType, Object> options) {
37+
CaptureRequestOptions.Builder builder = getCaptureRequestOptionsBuilder();
38+
39+
for (Map.Entry<CaptureRequestKeySupportedType, Object> option : options.entrySet()) {
40+
CaptureRequestKeySupportedType optionKeyType = option.getKey();
41+
CaptureRequest.Key<? extends Object> optionKey = getCaptureRequestKey(optionKeyType);
42+
Object optionValue = option.getValue();
43+
44+
if (optionValue == null) {
45+
builder.clearCaptureRequestOption(optionKey);
46+
continue;
47+
}
48+
49+
switch (optionKeyType) {
50+
case CONTROL_AE_LOCK:
51+
builder.setCaptureRequestOption(
52+
(CaptureRequest.Key<Boolean>) optionKey, (Boolean) optionValue);
53+
break;
54+
default:
55+
throw new IllegalArgumentException(
56+
"The capture request key "
57+
+ optionKeyType.toString()
58+
+ "is not currently supported by the plugin.");
59+
}
60+
}
61+
62+
return builder.build();
63+
}
64+
65+
private CaptureRequest.Key<? extends Object> getCaptureRequestKey(
66+
CaptureRequestKeySupportedType type) {
67+
CaptureRequest.Key<? extends Object> key;
68+
switch (type) {
69+
case CONTROL_AE_LOCK:
70+
key = CaptureRequest.CONTROL_AE_LOCK;
71+
break;
72+
default:
73+
throw new IllegalArgumentException(
74+
"The capture request key is not currently supported by the plugin.");
75+
}
76+
return key;
77+
}
78+
79+
@VisibleForTesting
80+
@OptIn(markerClass = androidx.camera.camera2.interop.ExperimentalCamera2Interop.class)
81+
public @NonNull CaptureRequestOptions.Builder getCaptureRequestOptionsBuilder() {
82+
return new CaptureRequestOptions.Builder();
83+
}
84+
}
85+
86+
/**
87+
* Constructs a {@link CaptureRequestOptionsHostApiImpl}.
88+
*
89+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
90+
*/
91+
public CaptureRequestOptionsHostApiImpl(@NonNull InstanceManager instanceManager) {
92+
this(instanceManager, new CaptureRequestOptionsProxy());
93+
}
94+
95+
/**
96+
* Constructs a {@link CaptureRequestOptionsHostApiImpl}.
97+
*
98+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
99+
* @param proxy proxy for constructor of {@link CaptureRequestOptions}
100+
*/
101+
@VisibleForTesting
102+
CaptureRequestOptionsHostApiImpl(
103+
@NonNull InstanceManager instanceManager, @NonNull CaptureRequestOptionsProxy proxy) {
104+
this.instanceManager = instanceManager;
105+
this.proxy = proxy;
106+
}
107+
108+
@Override
109+
public void create(@NonNull Long identifier, @NonNull Map<Long, Object> options) {
110+
Map<CaptureRequestKeySupportedType, Object> decodedOptions =
111+
new HashMap<CaptureRequestKeySupportedType, Object>();
112+
for (Map.Entry<Long, Object> option : options.entrySet()) {
113+
decodedOptions.put(
114+
CaptureRequestKeySupportedType.values()[option.getKey().intValue()], option.getValue());
115+
}
116+
instanceManager.addDartCreatedInstance(proxy.create(decodedOptions), identifier);
117+
}
118+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class FallbackStrategyHostApiImpl implements FallbackStrategyHostApi {
2323

2424
private final FallbackStrategyProxy proxy;
2525

26-
/** Proxy for constructors and static method of {@link FallbackStrategy}. */
26+
/** Proxy for constructor of {@link FallbackStrategy}. */
2727
@VisibleForTesting
2828
public static class FallbackStrategyProxy {
2929
/** Creates an instance of {@link FallbackStrategy}. */
@@ -59,7 +59,7 @@ public FallbackStrategyHostApiImpl(@NonNull InstanceManager instanceManager) {
5959
* Constructs a {@link FallbackStrategyHostApiImpl}.
6060
*
6161
* @param instanceManager maintains instances stored to communicate with attached Dart objects
62-
* @param proxy proxy for constructors and static method of {@link FallbackStrategy}
62+
* @param proxy proxy for constructor of {@link FallbackStrategy}
6363
*/
6464
FallbackStrategyHostApiImpl(
6565
@NonNull InstanceManager instanceManager, @NonNull FallbackStrategyProxy proxy) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class FocusMeteringActionHostApiImpl implements FocusMeteringActionHostAp
2424

2525
private final FocusMeteringActionProxy proxy;
2626

27-
/** Proxy for constructors and static method of {@link FocusMeteringAction}. */
27+
/** Proxy for constructor of {@link FocusMeteringAction}. */
2828
@VisibleForTesting
2929
public static class FocusMeteringActionProxy {
3030
/** Creates an instance of {@link FocusMeteringAction}. */
@@ -90,7 +90,7 @@ public FocusMeteringActionHostApiImpl(@NonNull InstanceManager instanceManager)
9090
* Constructs a {@link FocusMeteringActionHostApiImpl}.
9191
*
9292
* @param instanceManager maintains instances stored to communicate with attached Dart objects
93-
* @param proxy proxy for constructors and static method of {@link FocusMeteringAction}
93+
* @param proxy proxy for constructor of {@link FocusMeteringAction}
9494
*/
9595
FocusMeteringActionHostApiImpl(
9696
@NonNull InstanceManager instanceManager, @NonNull FocusMeteringActionProxy proxy) {

0 commit comments

Comments
 (0)