Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit e2541ca

Browse files
authored
[camera] android-rework part 8: Supporting modules for final implementation (#4054)
1 parent 744730c commit e2541ca

File tree

12 files changed

+1418
-6
lines changed

12 files changed

+1418
-6
lines changed
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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;
6+
7+
import android.hardware.camera2.CameraCaptureSession;
8+
import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
9+
import android.hardware.camera2.CaptureRequest;
10+
import android.hardware.camera2.CaptureResult;
11+
import android.hardware.camera2.TotalCaptureResult;
12+
import android.util.Log;
13+
import androidx.annotation.NonNull;
14+
import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper;
15+
16+
/**
17+
* A callback object for tracking the progress of a {@link android.hardware.camera2.CaptureRequest}
18+
* submitted to the camera device.
19+
*/
20+
class CameraCaptureCallback extends CaptureCallback {
21+
private static final String TAG = "CameraCaptureCallback";
22+
private final CameraCaptureStateListener cameraStateListener;
23+
private CameraState cameraState;
24+
private final CaptureTimeoutsWrapper captureTimeouts;
25+
26+
private CameraCaptureCallback(
27+
@NonNull CameraCaptureStateListener cameraStateListener,
28+
@NonNull CaptureTimeoutsWrapper captureTimeouts) {
29+
cameraState = CameraState.STATE_PREVIEW;
30+
this.cameraStateListener = cameraStateListener;
31+
this.captureTimeouts = captureTimeouts;
32+
}
33+
34+
/**
35+
* Creates a new instance of the {@link CameraCaptureCallback} class.
36+
*
37+
* @param cameraStateListener instance which will be called when the camera state changes.
38+
* @param captureTimeouts specifying the different timeout counters that should be taken into
39+
* account.
40+
* @return a configured instance of the {@link CameraCaptureCallback} class.
41+
*/
42+
public static CameraCaptureCallback create(
43+
@NonNull CameraCaptureStateListener cameraStateListener,
44+
@NonNull CaptureTimeoutsWrapper captureTimeouts) {
45+
return new CameraCaptureCallback(cameraStateListener, captureTimeouts);
46+
}
47+
48+
/**
49+
* Gets the current {@link CameraState}.
50+
*
51+
* @return the current {@link CameraState}.
52+
*/
53+
public CameraState getCameraState() {
54+
return cameraState;
55+
}
56+
57+
/**
58+
* Sets the {@link CameraState}.
59+
*
60+
* @param state the camera is currently in.
61+
*/
62+
public void setCameraState(@NonNull CameraState state) {
63+
cameraState = state;
64+
}
65+
66+
private void process(CaptureResult result) {
67+
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
68+
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
69+
70+
if (cameraState != CameraState.STATE_PREVIEW) {
71+
Log.d(
72+
TAG,
73+
"CameraCaptureCallback | state: "
74+
+ cameraState
75+
+ " | afState: "
76+
+ afState
77+
+ " | aeState: "
78+
+ aeState);
79+
}
80+
81+
switch (cameraState) {
82+
case STATE_PREVIEW:
83+
{
84+
// We have nothing to do when the camera preview is working normally.
85+
break;
86+
}
87+
case STATE_WAITING_FOCUS:
88+
{
89+
if (afState == null) {
90+
return;
91+
} else if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED
92+
|| afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
93+
handleWaitingFocusState(aeState);
94+
} else if (captureTimeouts.getPreCaptureFocusing().getIsExpired()) {
95+
Log.w(TAG, "Focus timeout, moving on with capture");
96+
handleWaitingFocusState(aeState);
97+
}
98+
99+
break;
100+
}
101+
case STATE_WAITING_PRECAPTURE_START:
102+
{
103+
// CONTROL_AE_STATE can be null on some devices
104+
if (aeState == null
105+
|| aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED
106+
|| aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE
107+
|| aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED) {
108+
setCameraState(CameraState.STATE_WAITING_PRECAPTURE_DONE);
109+
} else if (captureTimeouts.getPreCaptureMetering().getIsExpired()) {
110+
Log.w(TAG, "Metering timeout waiting for pre-capture to start, moving on with capture");
111+
112+
setCameraState(CameraState.STATE_WAITING_PRECAPTURE_DONE);
113+
}
114+
break;
115+
}
116+
case STATE_WAITING_PRECAPTURE_DONE:
117+
{
118+
// CONTROL_AE_STATE can be null on some devices
119+
if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
120+
cameraStateListener.onConverged();
121+
} else if (captureTimeouts.getPreCaptureMetering().getIsExpired()) {
122+
Log.w(
123+
TAG, "Metering timeout waiting for pre-capture to finish, moving on with capture");
124+
cameraStateListener.onConverged();
125+
}
126+
127+
break;
128+
}
129+
}
130+
}
131+
132+
private void handleWaitingFocusState(Integer aeState) {
133+
// CONTROL_AE_STATE can be null on some devices
134+
if (aeState == null || aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED) {
135+
cameraStateListener.onConverged();
136+
} else {
137+
cameraStateListener.onPrecapture();
138+
}
139+
}
140+
141+
@Override
142+
public void onCaptureProgressed(
143+
@NonNull CameraCaptureSession session,
144+
@NonNull CaptureRequest request,
145+
@NonNull CaptureResult partialResult) {
146+
process(partialResult);
147+
}
148+
149+
@Override
150+
public void onCaptureCompleted(
151+
@NonNull CameraCaptureSession session,
152+
@NonNull CaptureRequest request,
153+
@NonNull TotalCaptureResult result) {
154+
process(result);
155+
}
156+
157+
/** An interface that describes the different state changes implementers can be informed about. */
158+
interface CameraCaptureStateListener {
159+
160+
/** Called when the {@link android.hardware.camera2.CaptureRequest} has been converged. */
161+
void onConverged();
162+
163+
/**
164+
* Called when the {@link android.hardware.camera2.CaptureRequest} enters the pre-capture state.
165+
*/
166+
void onPrecapture();
167+
}
168+
}

packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public interface CameraProperties {
124124
* <li>@see android.hardware.camera2.CameraMetadata.LENS_FACING_EXTERNAL
125125
* </ul>
126126
*
127-
* By default maps to the @see android.hardware.camera2.CameraCharacteristics.LENS_FACING key.
127+
* <p>By default maps to the @see android.hardware.camera2.CameraCharacteristics.LENS_FACING key.
128128
*
129129
* @return int Direction the camera faces relative to device screen.
130130
*/
@@ -216,7 +216,7 @@ public interface CameraProperties {
216216
* <li>@see android.hardware.camera2.CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL
217217
* </ul>
218218
*
219-
* By default maps to the @see
219+
* <p>By default maps to the @see
220220
* android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL key.
221221
*
222222
* @return int Level which generally classifies the overall set of the camera device
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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;
6+
7+
/**
8+
* These are the states that the camera can be in. The camera can only take one photo at a time so
9+
* this state describes the state of the camera itself. The camera works like a pipeline where we
10+
* feed it requests through. It can only process one tasks at a time.
11+
*/
12+
public enum CameraState {
13+
/** Idle, showing preview and not capturing anything. */
14+
STATE_PREVIEW,
15+
16+
/** Starting and waiting for autofocus to complete. */
17+
STATE_WAITING_FOCUS,
18+
19+
/** Start performing autoexposure. */
20+
STATE_WAITING_PRECAPTURE_START,
21+
22+
/** waiting for autoexposure to complete. */
23+
STATE_WAITING_PRECAPTURE_DONE,
24+
25+
/** Capturing an image. */
26+
STATE_CAPTURING,
27+
}

packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
import java.util.HashMap;
1717
import java.util.Map;
1818

19+
/** Utility class that facilitates communication to the Flutter client */
1920
public class DartMessenger {
2021
@NonNull private final Handler handler;
2122
@Nullable private MethodChannel cameraChannel;
2223
@Nullable private MethodChannel deviceChannel;
2324

25+
/** Specifies the different device related message types. */
2426
enum DeviceEventType {
27+
/** Indicates the device's orientation has changed. */
2528
ORIENTATION_CHANGED("orientation_changed");
2629
private final String method;
2730

@@ -30,24 +33,47 @@ enum DeviceEventType {
3033
}
3134
}
3235

36+
/** Specifies the different camera related message types. */
3337
enum CameraEventType {
38+
/** Indicates that an error occurred while interacting with the camera. */
3439
ERROR("error"),
40+
/** Indicates that the camera is closing. */
3541
CLOSING("camera_closing"),
42+
/** Indicates that the camera is initialized. */
3643
INITIALIZED("initialized");
3744

3845
private final String method;
3946

47+
/**
48+
* Converts the supplied method name to the matching {@link CameraEventType}.
49+
*
50+
* @param method name to be converted into a {@link CameraEventType}.
51+
*/
4052
CameraEventType(String method) {
4153
this.method = method;
4254
}
4355
}
4456

57+
/**
58+
* Creates a new instance of the {@link DartMessenger} class.
59+
*
60+
* @param messenger is the {@link BinaryMessenger} that is used to communicate with Flutter.
61+
* @param cameraId identifies the camera which is the source of the communication.
62+
* @param handler the handler used to manage the thread's message queue. This should always be a
63+
* handler managing the main thread since communication with Flutter should always happen on
64+
* the main thread. The handler is mainly supplied so it will be easier test this class.
65+
*/
4566
DartMessenger(BinaryMessenger messenger, long cameraId, @NonNull Handler handler) {
4667
cameraChannel = new MethodChannel(messenger, "flutter.io/cameraPlugin/camera" + cameraId);
4768
deviceChannel = new MethodChannel(messenger, "flutter.io/cameraPlugin/device");
4869
this.handler = handler;
4970
}
5071

72+
/**
73+
* Sends a message to the Flutter client informing the orientation of the device has been changed.
74+
*
75+
* @param orientation specifies the new orientation of the device.
76+
*/
5177
public void sendDeviceOrientationChangeEvent(PlatformChannel.DeviceOrientation orientation) {
5278
assert (orientation != null);
5379
this.send(
@@ -59,6 +85,16 @@ public void sendDeviceOrientationChangeEvent(PlatformChannel.DeviceOrientation o
5985
});
6086
}
6187

88+
/**
89+
* Sends a message to the Flutter client informing that the camera has been initialized.
90+
*
91+
* @param previewWidth describes the preview width that is supported by the camera.
92+
* @param previewHeight describes the preview height that is supported by the camera.
93+
* @param exposureMode describes the current exposure mode that is set on the camera.
94+
* @param focusMode describes the current focus mode that is set on the camera.
95+
* @param exposurePointSupported indicates if the camera supports setting an exposure point.
96+
* @param focusPointSupported indicates if the camera supports setting a focus point.
97+
*/
6298
void sendCameraInitializedEvent(
6399
Integer previewWidth,
64100
Integer previewHeight,
@@ -86,10 +122,17 @@ void sendCameraInitializedEvent(
86122
});
87123
}
88124

125+
/** Sends a message to the Flutter client informing that the camera is closing. */
89126
void sendCameraClosingEvent() {
90127
send(CameraEventType.CLOSING);
91128
}
92129

130+
/**
131+
* Sends a message to the Flutter client informing that an error occurred while interacting with
132+
* the camera.
133+
*
134+
* @param description contains details regarding the error that occurred.
135+
*/
93136
void sendCameraErrorEvent(@Nullable String description) {
94137
this.send(
95138
CameraEventType.ERROR,
@@ -100,11 +143,11 @@ void sendCameraErrorEvent(@Nullable String description) {
100143
});
101144
}
102145

103-
void send(CameraEventType eventType) {
146+
private void send(CameraEventType eventType) {
104147
send(eventType, new HashMap<>());
105148
}
106149

107-
void send(CameraEventType eventType, Map<String, Object> args) {
150+
private void send(CameraEventType eventType, Map<String, Object> args) {
108151
if (cameraChannel == null) {
109152
return;
110153
}
@@ -118,11 +161,11 @@ public void run() {
118161
});
119162
}
120163

121-
void send(DeviceEventType eventType) {
164+
private void send(DeviceEventType eventType) {
122165
send(eventType, new HashMap<>());
123166
}
124167

125-
void send(DeviceEventType eventType, Map<String, Object> args) {
168+
private void send(DeviceEventType eventType, Map<String, Object> args) {
126169
if (deviceChannel == null) {
127170
return;
128171
}

0 commit comments

Comments
 (0)