Skip to content

Commit 3be3ec1

Browse files
authored
[camerax] Implements setFocusMode (#6176)
Implements `setFocusMode` (also adds support for disabling auto-cancel to `FocusMeteringAction` host API implementation to accomplish this) + some minor documentation improvements based on discoveries I made while working on this :) Fixes flutter/flutter#120467. ~To be landed after: #6110 Done :)
1 parent 52ed702 commit 3be3ec1

18 files changed

+1223
-176
lines changed

packages/camera/camera_android_camerax/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.6.0
2+
3+
* Implements `setFocusMode`, which makes this plugin reach feature parity with camera_android.
4+
* Fixes `setExposureCompensationIndex` return value to use index returned by CameraX.
5+
16
## 0.5.0+36
27

38
* Implements `setExposureMode`.

packages/camera/camera_android_camerax/README.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
An Android implementation of [`camera`][1] that uses the [CameraX library][2].
44

55
*Note*: This package is under development, so please note the
6-
[missing features and limitations](#missing-features-and-limitations), but
6+
[missing features and limitations](#limitations), but
77
otherwise feel free to try out the current implementation and provide any
88
feedback by filing issues under [`flutter/flutter`][5] with `[camerax]` in
99
the title, which will be actively triaged.
@@ -22,18 +22,14 @@ dependencies:
2222
camera_android_camerax: ^0.5.0
2323
```
2424
25-
## Missing features and limitations
25+
## Limitations
2626
2727
### 240p resolution configuration for video recording
2828
2929
240p resolution configuration for video recording is unsupported by CameraX,
3030
and thus, the plugin will fall back to 480p if configured with a
3131
`ResolutionPreset`.
3232

33-
### Focus mode configuration \[[Issue #120467][120467]\]
34-
35-
`setFocusMode` is unimplemented.
36-
3733
### Setting maximum duration and stream options for video capture
3834

3935
Calling `startVideoCapturing` with `VideoCaptureOptions` configured with

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package io.flutter.plugins.camerax;
66

77
import androidx.annotation.NonNull;
8+
import androidx.annotation.Nullable;
89
import androidx.annotation.VisibleForTesting;
910
import androidx.camera.core.FocusMeteringAction;
1011
import androidx.camera.core.MeteringPoint;
@@ -29,7 +30,9 @@ public class FocusMeteringActionHostApiImpl implements FocusMeteringActionHostAp
2930
public static class FocusMeteringActionProxy {
3031
/** Creates an instance of {@link FocusMeteringAction}. */
3132
public @NonNull FocusMeteringAction create(
32-
@NonNull List<MeteringPoint> meteringPoints, @NonNull List<Integer> meteringPointModes) {
33+
@NonNull List<MeteringPoint> meteringPoints,
34+
@NonNull List<Integer> meteringPointModes,
35+
@Nullable Boolean disableAutoCancel) {
3336
if (meteringPoints.size() >= 1 && meteringPoints.size() != meteringPointModes.size()) {
3437
throw new IllegalArgumentException(
3538
"One metering point must be specified and the number of specified metering points must match the number of specified metering point modes.");
@@ -59,6 +62,10 @@ public static class FocusMeteringActionProxy {
5962
}
6063
}
6164

65+
if (disableAutoCancel != null && disableAutoCancel == true) {
66+
focusMeteringActionBuilder.disableAutoCancel();
67+
}
68+
6269
return focusMeteringActionBuilder.build();
6370
}
6471

@@ -100,7 +107,9 @@ public FocusMeteringActionHostApiImpl(@NonNull InstanceManager instanceManager)
100107

101108
@Override
102109
public void create(
103-
@NonNull Long identifier, @NonNull List<MeteringPointInfo> meteringPointInfos) {
110+
@NonNull Long identifier,
111+
@NonNull List<MeteringPointInfo> meteringPointInfos,
112+
@Nullable Boolean disableAutoCancel) {
104113
final List<MeteringPoint> meteringPoints = new ArrayList<MeteringPoint>();
105114
final List<Integer> meteringPointModes = new ArrayList<Integer>();
106115
for (MeteringPointInfo meteringPointInfo : meteringPointInfos) {
@@ -110,6 +119,6 @@ public void create(
110119
}
111120

112121
instanceManager.addDartCreatedInstance(
113-
proxy.create(meteringPoints, meteringPointModes), identifier);
122+
proxy.create(meteringPoints, meteringPointModes, disableAutoCancel), identifier);
114123
}
115124
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3796,7 +3796,10 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
37963796
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
37973797
public interface FocusMeteringActionHostApi {
37983798

3799-
void create(@NonNull Long identifier, @NonNull List<MeteringPointInfo> meteringPointInfos);
3799+
void create(
3800+
@NonNull Long identifier,
3801+
@NonNull List<MeteringPointInfo> meteringPointInfos,
3802+
@Nullable Boolean disableAutoCancel);
38003803

38013804
/** The codec used by FocusMeteringActionHostApi. */
38023805
static @NonNull MessageCodec<Object> getCodec() {
@@ -3822,10 +3825,12 @@ static void setup(
38223825
Number identifierArg = (Number) args.get(0);
38233826
List<MeteringPointInfo> meteringPointInfosArg =
38243827
(List<MeteringPointInfo>) args.get(1);
3828+
Boolean disableAutoCancelArg = (Boolean) args.get(2);
38253829
try {
38263830
api.create(
38273831
(identifierArg == null) ? null : identifierArg.longValue(),
3828-
meteringPointInfosArg);
3832+
meteringPointInfosArg,
3833+
disableAutoCancelArg);
38293834
wrapped.add(0, null);
38303835
} catch (Throwable exception) {
38313836
ArrayList<Object> wrappedError = wrapError(exception);

packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FocusMeteringActionTest.java

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import static org.junit.Assert.assertEquals;
88
import static org.mockito.Mockito.mock;
9+
import static org.mockito.Mockito.never;
910
import static org.mockito.Mockito.spy;
1011
import static org.mockito.Mockito.verify;
1112
import static org.mockito.Mockito.when;
@@ -43,7 +44,7 @@ public void tearDown() {
4344
}
4445

4546
@Test
46-
public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatHasMode() {
47+
public void hostApiCreate_createsExpectedFocusMeteringActionWithInitialPointThatHasMode() {
4748
FocusMeteringActionHostApiImpl.FocusMeteringActionProxy proxySpy =
4849
spy(new FocusMeteringActionHostApiImpl.FocusMeteringActionProxy());
4950
FocusMeteringActionHostApiImpl hostApi =
@@ -89,7 +90,7 @@ public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatH
8990
List<MeteringPointInfo> mockMeteringPointInfos =
9091
Arrays.asList(fakeMeteringPointInfo1, fakeMeteringPointInfo2, fakeMeteringPointInfo3);
9192

92-
hostApi.create(focusMeteringActionIdentifier, mockMeteringPointInfos);
93+
hostApi.create(focusMeteringActionIdentifier, mockMeteringPointInfos, null);
9394

9495
verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint2, mockMeteringPoint2Mode);
9596
verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint3);
@@ -98,7 +99,8 @@ public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatH
9899
}
99100

100101
@Test
101-
public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatDoesNotHaveMode() {
102+
public void
103+
hostApiCreate_createsExpectedFocusMeteringActionWithInitialPointThatDoesNotHaveMode() {
102104
FocusMeteringActionHostApiImpl.FocusMeteringActionProxy proxySpy =
103105
spy(new FocusMeteringActionHostApiImpl.FocusMeteringActionProxy());
104106
FocusMeteringActionHostApiImpl hostApi =
@@ -142,11 +144,49 @@ public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatD
142144
List<MeteringPointInfo> mockMeteringPointInfos =
143145
Arrays.asList(fakeMeteringPointInfo1, fakeMeteringPointInfo2, fakeMeteringPointInfo3);
144146

145-
hostApi.create(focusMeteringActionIdentifier, mockMeteringPointInfos);
147+
hostApi.create(focusMeteringActionIdentifier, mockMeteringPointInfos, null);
146148

147149
verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint2, mockMeteringPoint2Mode);
148150
verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint3);
149151
assertEquals(
150152
testInstanceManager.getInstance(focusMeteringActionIdentifier), focusMeteringAction);
151153
}
154+
155+
@Test
156+
public void hostApiCreate_disablesAutoCancelAsExpected() {
157+
FocusMeteringActionHostApiImpl.FocusMeteringActionProxy proxySpy =
158+
spy(new FocusMeteringActionHostApiImpl.FocusMeteringActionProxy());
159+
FocusMeteringActionHostApiImpl hostApi =
160+
new FocusMeteringActionHostApiImpl(testInstanceManager, proxySpy);
161+
162+
FocusMeteringAction.Builder mockFocusMeteringActionBuilder =
163+
mock(FocusMeteringAction.Builder.class);
164+
final MeteringPoint mockMeteringPoint = mock(MeteringPoint.class);
165+
final Long mockMeteringPointId = 47L;
166+
167+
MeteringPointInfo fakeMeteringPointInfo =
168+
new MeteringPointInfo.Builder()
169+
.setMeteringPointId(mockMeteringPointId)
170+
.setMeteringMode(null)
171+
.build();
172+
173+
testInstanceManager.addDartCreatedInstance(mockMeteringPoint, mockMeteringPointId);
174+
175+
when(proxySpy.getFocusMeteringActionBuilder(mockMeteringPoint))
176+
.thenReturn(mockFocusMeteringActionBuilder);
177+
when(mockFocusMeteringActionBuilder.build()).thenReturn(focusMeteringAction);
178+
179+
List<MeteringPointInfo> mockMeteringPointInfos = Arrays.asList(fakeMeteringPointInfo);
180+
181+
// Test not disabling auto cancel.
182+
hostApi.create(73L, mockMeteringPointInfos, /* disableAutoCancel */ null);
183+
verify(mockFocusMeteringActionBuilder, never()).disableAutoCancel();
184+
185+
hostApi.create(74L, mockMeteringPointInfos, /* disableAutoCancel */ false);
186+
verify(mockFocusMeteringActionBuilder, never()).disableAutoCancel();
187+
188+
// Test disabling auto cancel.
189+
hostApi.create(75L, mockMeteringPointInfos, /* disableAutoCancel */ true);
190+
verify(mockFocusMeteringActionBuilder).disableAutoCancel();
191+
}
152192
}

packages/camera/camera_android_camerax/example/lib/main.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,9 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
477477
children: <Widget>[
478478
TextButton(
479479
style: styleAuto,
480-
onPressed:
481-
() {}, // TODO(camsim99): Add functionality back here.
480+
onPressed: controller != null
481+
? () => onSetFocusModeButtonPressed(FocusMode.auto)
482+
: null,
482483
onLongPress: () {
483484
if (controller != null) {
484485
CameraPlatform.instance
@@ -490,8 +491,9 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
490491
),
491492
TextButton(
492493
style: styleLocked,
493-
onPressed:
494-
() {}, // TODO(camsim99): Add functionality back here.
494+
onPressed: controller != null
495+
? () => onSetFocusModeButtonPressed(FocusMode.locked)
496+
: null,
495497
child: const Text('LOCKED'),
496498
),
497499
],

0 commit comments

Comments
 (0)