Skip to content

Commit 371e0a7

Browse files
mvanbeusekomfotiDim
authored andcommitted
[camera] android-rework part 2: Android auto focus feature (flutter#3796)
1 parent 24a6702 commit 371e0a7

File tree

4 files changed

+322
-0
lines changed

4 files changed

+322
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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.camera.features.autofocus;
6+
7+
import android.hardware.camera2.CameraCharacteristics;
8+
import android.hardware.camera2.CaptureRequest;
9+
import io.flutter.plugins.camera.CameraProperties;
10+
import io.flutter.plugins.camera.features.CameraFeature;
11+
12+
/** Controls the auto focus configuration on the {@see anddroid.hardware.camera2} API. */
13+
public class AutoFocusFeature extends CameraFeature<FocusMode> {
14+
private FocusMode currentSetting = FocusMode.auto;
15+
16+
// When switching recording modes this feature is re-created with the appropriate setting here.
17+
private final boolean recordingVideo;
18+
19+
/**
20+
* Creates a new instance of the {@see AutoFocusFeature}.
21+
*
22+
* @param cameraProperties Collection of the characteristics for the current camera device.
23+
* @param recordingVideo Indicates whether the camera is currently recording video.
24+
*/
25+
public AutoFocusFeature(CameraProperties cameraProperties, boolean recordingVideo) {
26+
super(cameraProperties);
27+
this.recordingVideo = recordingVideo;
28+
}
29+
30+
@Override
31+
public String getDebugName() {
32+
return "AutoFocusFeature";
33+
}
34+
35+
@Override
36+
public FocusMode getValue() {
37+
return currentSetting;
38+
}
39+
40+
@Override
41+
public void setValue(FocusMode value) {
42+
this.currentSetting = value;
43+
}
44+
45+
@Override
46+
public boolean checkIsSupported() {
47+
int[] modes = cameraProperties.getControlAutoFocusAvailableModes();
48+
49+
final Float minFocus = cameraProperties.getLensInfoMinimumFocusDistance();
50+
51+
// Check if the focal length of the lens is fixed. If the minimum focus distance == 0, then the
52+
// focal length is fixed. The minimum focus distance can be null on some devices: https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE
53+
boolean isFixedLength = minFocus == null || minFocus == 0;
54+
55+
return !isFixedLength
56+
&& !(modes.length == 0
57+
|| (modes.length == 1 && modes[0] == CameraCharacteristics.CONTROL_AF_MODE_OFF));
58+
}
59+
60+
@Override
61+
public void updateBuilder(CaptureRequest.Builder requestBuilder) {
62+
if (!checkIsSupported()) {
63+
return;
64+
}
65+
66+
switch (currentSetting) {
67+
case locked:
68+
// When locking the auto-focus the camera device should do a one-time focus and afterwards
69+
// set the auto-focus to idle. This is accomplished by setting the CONTROL_AF_MODE to
70+
// CONTROL_AF_MODE_AUTO.
71+
requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
72+
break;
73+
case auto:
74+
requestBuilder.set(
75+
CaptureRequest.CONTROL_AF_MODE,
76+
recordingVideo
77+
? CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO
78+
: CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
79+
default:
80+
break;
81+
}
82+
}
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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.camera.features.autofocus;
6+
7+
// Mirrors focus_mode.dart
8+
public enum FocusMode {
9+
auto("auto"),
10+
locked("locked");
11+
12+
private final String strValue;
13+
14+
FocusMode(String strValue) {
15+
this.strValue = strValue;
16+
}
17+
18+
public static FocusMode getValueForString(String modeStr) {
19+
for (FocusMode value : values()) {
20+
if (value.strValue.equals(modeStr)) return value;
21+
}
22+
return null;
23+
}
24+
25+
@Override
26+
public String toString() {
27+
return strValue;
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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.camera.features.autofocus;
6+
7+
import static org.junit.Assert.assertEquals;
8+
import static org.junit.Assert.assertFalse;
9+
import static org.junit.Assert.assertTrue;
10+
import static org.mockito.ArgumentMatchers.any;
11+
import static org.mockito.Mockito.mock;
12+
import static org.mockito.Mockito.never;
13+
import static org.mockito.Mockito.times;
14+
import static org.mockito.Mockito.verify;
15+
import static org.mockito.Mockito.when;
16+
17+
import android.hardware.camera2.CameraCharacteristics;
18+
import android.hardware.camera2.CaptureRequest;
19+
import io.flutter.plugins.camera.CameraProperties;
20+
import org.junit.Test;
21+
22+
public class AutoFocusFeatureTest {
23+
private static final int[] FOCUS_MODES_ONLY_OFF =
24+
new int[] {CameraCharacteristics.CONTROL_AF_MODE_OFF};
25+
private static final int[] FOCUS_MODES =
26+
new int[] {
27+
CameraCharacteristics.CONTROL_AF_MODE_OFF, CameraCharacteristics.CONTROL_AF_MODE_AUTO
28+
};
29+
30+
@Test
31+
public void getDebugName_should_return_the_name_of_the_feature() {
32+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
33+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, false);
34+
35+
assertEquals("AutoFocusFeature", autoFocusFeature.getDebugName());
36+
}
37+
38+
@Test
39+
public void getValue_should_return_auto_if_not_set() {
40+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
41+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, false);
42+
43+
assertEquals(FocusMode.auto, autoFocusFeature.getValue());
44+
}
45+
46+
@Test
47+
public void getValue_should_echo_the_set_value() {
48+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
49+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, false);
50+
FocusMode expectedValue = FocusMode.locked;
51+
52+
autoFocusFeature.setValue(expectedValue);
53+
FocusMode actualValue = autoFocusFeature.getValue();
54+
55+
assertEquals(expectedValue, actualValue);
56+
}
57+
58+
@Test
59+
public void checkIsSupported_should_return_false_when_minimum_focus_distance_is_zero() {
60+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
61+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, false);
62+
63+
when(mockCameraProperties.getControlAutoFocusAvailableModes()).thenReturn(FOCUS_MODES);
64+
when(mockCameraProperties.getLensInfoMinimumFocusDistance()).thenReturn(0.0F);
65+
66+
assertFalse(autoFocusFeature.checkIsSupported());
67+
}
68+
69+
@Test
70+
public void checkIsSupported_should_return_false_when_minimum_focus_distance_is_null() {
71+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
72+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, false);
73+
74+
when(mockCameraProperties.getControlAutoFocusAvailableModes()).thenReturn(FOCUS_MODES);
75+
when(mockCameraProperties.getLensInfoMinimumFocusDistance()).thenReturn(null);
76+
77+
assertFalse(autoFocusFeature.checkIsSupported());
78+
}
79+
80+
@Test
81+
public void checkIsSupport_should_return_false_when_no_focus_modes_are_available() {
82+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
83+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, false);
84+
85+
when(mockCameraProperties.getControlAutoFocusAvailableModes()).thenReturn(new int[] {});
86+
when(mockCameraProperties.getLensInfoMinimumFocusDistance()).thenReturn(1.0F);
87+
88+
assertFalse(autoFocusFeature.checkIsSupported());
89+
}
90+
91+
@Test
92+
public void checkIsSupport_should_return_false_when_only_focus_off_is_available() {
93+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
94+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, false);
95+
96+
when(mockCameraProperties.getControlAutoFocusAvailableModes()).thenReturn(FOCUS_MODES_ONLY_OFF);
97+
when(mockCameraProperties.getLensInfoMinimumFocusDistance()).thenReturn(1.0F);
98+
99+
assertFalse(autoFocusFeature.checkIsSupported());
100+
}
101+
102+
@Test
103+
public void checkIsSupport_should_return_true_when_only_multiple_focus_modes_are_available() {
104+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
105+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, false);
106+
107+
when(mockCameraProperties.getControlAutoFocusAvailableModes()).thenReturn(FOCUS_MODES);
108+
when(mockCameraProperties.getLensInfoMinimumFocusDistance()).thenReturn(1.0F);
109+
110+
assertTrue(autoFocusFeature.checkIsSupported());
111+
}
112+
113+
@Test
114+
public void updateBuilder_should_return_when_checkIsSupported_is_false() {
115+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
116+
CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
117+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, false);
118+
119+
when(mockCameraProperties.getControlAutoFocusAvailableModes()).thenReturn(FOCUS_MODES);
120+
when(mockCameraProperties.getLensInfoMinimumFocusDistance()).thenReturn(0.0F);
121+
122+
autoFocusFeature.updateBuilder(mockBuilder);
123+
124+
verify(mockBuilder, never()).set(any(), any());
125+
}
126+
127+
@Test
128+
public void updateBuilder_should_set_control_mode_to_auto_when_focus_is_locked() {
129+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
130+
CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
131+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, false);
132+
133+
when(mockCameraProperties.getControlAutoFocusAvailableModes()).thenReturn(FOCUS_MODES);
134+
when(mockCameraProperties.getLensInfoMinimumFocusDistance()).thenReturn(1.0F);
135+
136+
autoFocusFeature.setValue(FocusMode.locked);
137+
autoFocusFeature.updateBuilder(mockBuilder);
138+
139+
verify(mockBuilder, times(1))
140+
.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
141+
}
142+
143+
@Test
144+
public void
145+
updateBuilder_should_set_control_mode_to_continuous_video_when_focus_is_auto_and_recording_video() {
146+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
147+
CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
148+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, true);
149+
150+
when(mockCameraProperties.getControlAutoFocusAvailableModes()).thenReturn(FOCUS_MODES);
151+
when(mockCameraProperties.getLensInfoMinimumFocusDistance()).thenReturn(1.0F);
152+
153+
autoFocusFeature.setValue(FocusMode.auto);
154+
autoFocusFeature.updateBuilder(mockBuilder);
155+
156+
verify(mockBuilder, times(1))
157+
.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO);
158+
}
159+
160+
@Test
161+
public void
162+
updateBuilder_should_set_control_mode_to_continuous_video_when_focus_is_auto_and_not_recording_video() {
163+
CameraProperties mockCameraProperties = mock(CameraProperties.class);
164+
CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
165+
AutoFocusFeature autoFocusFeature = new AutoFocusFeature(mockCameraProperties, false);
166+
167+
when(mockCameraProperties.getControlAutoFocusAvailableModes()).thenReturn(FOCUS_MODES);
168+
when(mockCameraProperties.getLensInfoMinimumFocusDistance()).thenReturn(1.0F);
169+
170+
autoFocusFeature.setValue(FocusMode.auto);
171+
autoFocusFeature.updateBuilder(mockBuilder);
172+
173+
verify(mockBuilder, times(1))
174+
.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
175+
}
176+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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.camera.features.autofocus;
6+
7+
import static org.junit.Assert.assertEquals;
8+
9+
import org.junit.Test;
10+
11+
public class FocusModeTest {
12+
13+
@Test
14+
public void getValueForString_returns_correct_values() {
15+
assertEquals(
16+
"Returns FocusMode.auto for 'auto'", FocusMode.getValueForString("auto"), FocusMode.auto);
17+
assertEquals(
18+
"Returns FocusMode.locked for 'locked'",
19+
FocusMode.getValueForString("locked"),
20+
FocusMode.locked);
21+
}
22+
23+
@Test
24+
public void getValueForString_returns_null_for_nonexistant_value() {
25+
assertEquals(
26+
"Returns null for 'nonexistant'", FocusMode.getValueForString("nonexistant"), null);
27+
}
28+
29+
@Test
30+
public void toString_returns_correct_value() {
31+
assertEquals("Returns 'auto' for FocusMode.auto", FocusMode.auto.toString(), "auto");
32+
assertEquals("Returns 'locked' for FocusMode.locked", FocusMode.locked.toString(), "locked");
33+
}
34+
}

0 commit comments

Comments
 (0)