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

[camera] android-rework part 8: Supporting modules for final implementation #4054

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
2b7aa9b
Base classes to support Android camera features
mvanbeusekom Apr 8, 2021
f780742
Fixed formatting
mvanbeusekom Apr 8, 2021
76bc5bd
Applied feedback from PR
mvanbeusekom Apr 20, 2021
0bbed99
Added Android FPS range, resolution and sensor orientation features
mvanbeusekom Apr 8, 2021
1ba738d
Use mockito-inline
mvanbeusekom Apr 9, 2021
de4e70f
Merge remote-tracking branch 'upstream/master' into camera-android/fp…
mvanbeusekom Apr 21, 2021
728346a
Fix issue Pixel 4A
mvanbeusekom May 26, 2021
c014fe3
Merge remote-tracking branch 'upstream/master' into camera-android/fp…
mvanbeusekom May 31, 2021
84f5e73
Added API documentation
mvanbeusekom May 31, 2021
f763f77
Processed feedback on PR
mvanbeusekom May 31, 2021
4a7c73a
Fix formatting
mvanbeusekom May 31, 2021
a890919
Fix formatting
mvanbeusekom May 31, 2021
f60a0d3
Merge remote-tracking branch 'upstream/master'
BeMacized Jun 8, 2021
55a6702
Only exclude 60 FPS limit for Pixel 4a
mvanbeusekom Jun 8, 2021
cd53321
Removed redundant empty line
mvanbeusekom Jun 8, 2021
35831d3
Fixed comment
mvanbeusekom Jun 8, 2021
a9f3142
Test Pixel 4a workaround
mvanbeusekom Jun 8, 2021
551800e
Add tests for orientation updates
mvanbeusekom Jun 10, 2021
68cbc56
Fix formatting
mvanbeusekom Jun 10, 2021
1b137c2
Fix formatting
mvanbeusekom Jun 10, 2021
6514a00
Added missing license header
mvanbeusekom Jun 10, 2021
baae5f6
Add feature classes for exposure- and focus point functionality.
BeMacized Jun 10, 2021
cae8a90
Merge remote-tracking branch 'upstream/master'
BeMacized Jun 11, 2021
94fed08
Added noise reduction feature
BeMacized Jun 14, 2021
be05e67
Merge branch 'camera-android/noise_reduction_feature' into camera-and…
BeMacized Jun 14, 2021
a68b9fb
Merge branch 'camera-android/exposure_focus_point_features' into came…
BeMacized Jun 14, 2021
e5253c1
Merge remote-tracking branch 'origin/camera-android/fps_resolution_se…
BeMacized Jun 14, 2021
4f169ab
Implemented PR feedback
BeMacized Jun 15, 2021
3a68294
Implemented PR feedback
BeMacized Jun 15, 2021
70dbbd5
Add supporting functionality for android refactor
BeMacized Jun 15, 2021
8e65f33
Merge branch 'camera-android/exposure_focus_point_features' into came…
BeMacized Jun 16, 2021
2c64ea2
Merge branch 'camera-android/noise_reduction_feature' into camera-and…
BeMacized Jun 16, 2021
ab332ee
Update packages/camera/camera/android/src/main/java/io/flutter/plugin…
BeMacized Jun 16, 2021
bc16dd3
Update packages/camera/camera/android/src/main/java/io/flutter/plugin…
BeMacized Jun 16, 2021
a85e312
Update packages/camera/camera/android/src/main/java/io/flutter/plugin…
BeMacized Jun 16, 2021
84d6db2
Update packages/camera/camera/android/src/main/java/io/flutter/plugin…
BeMacized Jun 16, 2021
071e879
Update packages/camera/camera/android/src/main/java/io/flutter/plugin…
BeMacized Jun 16, 2021
273762b
Update packages/camera/camera/android/src/test/java/io/flutter/plugin…
BeMacized Jun 16, 2021
e9e9bc5
Update packages/camera/camera/android/src/test/java/io/flutter/plugin…
BeMacized Jun 16, 2021
1af5437
Update packages/camera/camera/android/src/test/java/io/flutter/plugin…
BeMacized Jun 16, 2021
b49b743
Added documentation to DartMessenger
mvanbeusekom Jun 16, 2021
5b8c0d8
Update packages/camera/camera/android/src/main/java/io/flutter/plugin…
BeMacized Jun 16, 2021
62a2f21
Add missing CaptureTimeoutsWrapper documentation
BeMacized Jun 16, 2021
43c0716
Update packages/camera/camera/android/src/main/java/io/flutter/plugin…
BeMacized Jun 16, 2021
24c6c9a
Added documentation to the CameraCaptureCallback
mvanbeusekom Jun 16, 2021
fc2d78b
Merge branch 'camera-android/supporting_functionality' of github.com:…
mvanbeusekom Jun 16, 2021
b2536d4
Add missing documentation for ImageSaver runnable
BeMacized Jun 16, 2021
1748503
Merge remote-tracking branch 'origin/camera-android/supporting_functi…
BeMacized Jun 16, 2021
3118680
Update packages/camera/camera/android/src/main/java/io/flutter/plugin…
BeMacized Jun 16, 2021
7dbbdce
Added documentation to the CameraFeatureFactory
mvanbeusekom Jun 16, 2021
2b2bea3
Configure log tag in one place and optimised loggin
mvanbeusekom Jun 16, 2021
7f0180e
Accept cameraName as String
mvanbeusekom Jun 16, 2021
24af367
Format
mvanbeusekom Jun 16, 2021
a987711
Changes required for integrating main camera class
BeMacized Jun 16, 2021
66a713d
Merge branch 'camera-android/fps_resolution_sensor_features' into cam…
BeMacized Jun 16, 2021
ce9df6c
Changes required for integrating main camera class
BeMacized Jun 16, 2021
32cb969
Remove debug statement
BeMacized Jun 16, 2021
d215ac7
Merge branch 'master' into camera-android/supporting_functionality
BeMacized Jun 22, 2021
914b187
Merge branch 'master' into camera-android/supporting_functionality
BeMacized Jun 24, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera;

import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.util.Log;
import androidx.annotation.NonNull;
import io.flutter.plugins.camera.types.CaptureTimeoutsWrapper;

/**
* A callback object for tracking the progress of a {@link android.hardware.camera2.CaptureRequest}
* submitted to the camera device.
*/
class CameraCaptureCallback extends CaptureCallback {
private static final String TAG = "CameraCaptureCallback";
private final CameraCaptureStateListener cameraStateListener;
private CameraState cameraState;
private final CaptureTimeoutsWrapper captureTimeouts;

private CameraCaptureCallback(
@NonNull CameraCaptureStateListener cameraStateListener,
@NonNull CaptureTimeoutsWrapper captureTimeouts) {
cameraState = CameraState.STATE_PREVIEW;
this.cameraStateListener = cameraStateListener;
this.captureTimeouts = captureTimeouts;
}

/**
* Creates a new instance of the {@link CameraCaptureCallback} class.
*
* @param cameraStateListener instance which will be called when the camera state changes.
* @param captureTimeouts specifying the different timeout counters that should be taken into
* account.
* @return a configured instance of the {@link CameraCaptureCallback} class.
*/
public static CameraCaptureCallback create(
@NonNull CameraCaptureStateListener cameraStateListener,
@NonNull CaptureTimeoutsWrapper captureTimeouts) {
return new CameraCaptureCallback(cameraStateListener, captureTimeouts);
}

/**
* Gets the current {@link CameraState}.
*
* @return the current {@link CameraState}.
*/
public CameraState getCameraState() {
return cameraState;
}

/**
* Sets the {@link CameraState}.
*
* @param state the camera is currently in.
*/
public void setCameraState(@NonNull CameraState state) {
cameraState = state;
}

private void process(CaptureResult result) {
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);

if (cameraState != CameraState.STATE_PREVIEW) {
Log.d(
TAG,
"CameraCaptureCallback | state: "
+ cameraState
+ " | afState: "
+ afState
+ " | aeState: "
+ aeState);
}

switch (cameraState) {
case STATE_PREVIEW:
{
// We have nothing to do when the camera preview is working normally.
break;
}
case STATE_WAITING_FOCUS:
{
if (afState == null) {
return;
} else if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED
|| afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
handleWaitingFocusState(aeState);
} else if (captureTimeouts.getPreCaptureFocusing().getIsExpired()) {
Log.w(TAG, "Focus timeout, moving on with capture");
handleWaitingFocusState(aeState);
}

break;
}
case STATE_WAITING_PRECAPTURE_START:
{
// CONTROL_AE_STATE can be null on some devices
if (aeState == null
|| aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED
|| aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE
|| aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED) {
setCameraState(CameraState.STATE_WAITING_PRECAPTURE_DONE);
} else if (captureTimeouts.getPreCaptureMetering().getIsExpired()) {
Log.w(TAG, "Metering timeout waiting for pre-capture to start, moving on with capture");

setCameraState(CameraState.STATE_WAITING_PRECAPTURE_DONE);
}
break;
}
case STATE_WAITING_PRECAPTURE_DONE:
{
// CONTROL_AE_STATE can be null on some devices
if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
cameraStateListener.onConverged();
} else if (captureTimeouts.getPreCaptureMetering().getIsExpired()) {
Log.w(
TAG, "Metering timeout waiting for pre-capture to finish, moving on with capture");
cameraStateListener.onConverged();
}

break;
}
}
}

private void handleWaitingFocusState(Integer aeState) {
// CONTROL_AE_STATE can be null on some devices
if (aeState == null || aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED) {
cameraStateListener.onConverged();
} else {
cameraStateListener.onPrecapture();
}
}

@Override
public void onCaptureProgressed(
@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureResult partialResult) {
process(partialResult);
}

@Override
public void onCaptureCompleted(
@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
process(result);
}

/** An interface that describes the different state changes implementers can be informed about. */
interface CameraCaptureStateListener {

/** Called when the {@link android.hardware.camera2.CaptureRequest} has been converged. */
void onConverged();

/**
* Called when the {@link android.hardware.camera2.CaptureRequest} enters the pre-capture state.
*/
void onPrecapture();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public interface CameraProperties {
* <li>@see android.hardware.camera2.CameraMetadata.LENS_FACING_EXTERNAL
* </ul>
*
* By default maps to the @see android.hardware.camera2.CameraCharacteristics.LENS_FACING key.
* <p>By default maps to the @see android.hardware.camera2.CameraCharacteristics.LENS_FACING key.
*
* @return int Direction the camera faces relative to device screen.
*/
Expand Down Expand Up @@ -216,7 +216,7 @@ public interface CameraProperties {
* <li>@see android.hardware.camera2.CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL
* </ul>
*
* By default maps to the @see
* <p>By default maps to the @see
* android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL key.
*
* @return int Level which generally classifies the overall set of the camera device
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera;

/**
* These are the states that the camera can be in. The camera can only take one photo at a time so
* this state describes the state of the camera itself. The camera works like a pipeline where we
* feed it requests through. It can only process one tasks at a time.
*/
public enum CameraState {
/** Idle, showing preview and not capturing anything. */
STATE_PREVIEW,

/** Starting and waiting for autofocus to complete. */
STATE_WAITING_FOCUS,

/** Start performing autoexposure. */
STATE_WAITING_PRECAPTURE_START,

/** waiting for autoexposure to complete. */
STATE_WAITING_PRECAPTURE_DONE,

/** Capturing an image. */
STATE_CAPTURING,
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
import java.util.HashMap;
import java.util.Map;

/** Utility class that facilitates communication to the Flutter client */
public class DartMessenger {
@NonNull private final Handler handler;
@Nullable private MethodChannel cameraChannel;
@Nullable private MethodChannel deviceChannel;

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

Expand All @@ -30,24 +33,47 @@ enum DeviceEventType {
}
}

/** Specifies the different camera related message types. */
enum CameraEventType {
/** Indicates that an error occurred while interacting with the camera. */
ERROR("error"),
/** Indicates that the camera is closing. */
CLOSING("camera_closing"),
/** Indicates that the camera is initialized. */
INITIALIZED("initialized");

private final String method;

/**
* Converts the supplied method name to the matching {@link CameraEventType}.
*
* @param method name to be converted into a {@link CameraEventType}.
*/
CameraEventType(String method) {
this.method = method;
}
}

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

/**
* Sends a message to the Flutter client informing the orientation of the device has been changed.
*
* @param orientation specifies the new orientation of the device.
*/
public void sendDeviceOrientationChangeEvent(PlatformChannel.DeviceOrientation orientation) {
assert (orientation != null);
this.send(
Expand All @@ -59,6 +85,16 @@ public void sendDeviceOrientationChangeEvent(PlatformChannel.DeviceOrientation o
});
}

/**
* Sends a message to the Flutter client informing that the camera has been initialized.
*
* @param previewWidth describes the preview width that is supported by the camera.
* @param previewHeight describes the preview height that is supported by the camera.
* @param exposureMode describes the current exposure mode that is set on the camera.
* @param focusMode describes the current focus mode that is set on the camera.
* @param exposurePointSupported indicates if the camera supports setting an exposure point.
* @param focusPointSupported indicates if the camera supports setting a focus point.
*/
void sendCameraInitializedEvent(
Integer previewWidth,
Integer previewHeight,
Expand Down Expand Up @@ -86,10 +122,17 @@ void sendCameraInitializedEvent(
});
}

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

/**
* Sends a message to the Flutter client informing that an error occurred while interacting with
* the camera.
*
* @param description contains details regarding the error that occurred.
*/
void sendCameraErrorEvent(@Nullable String description) {
this.send(
CameraEventType.ERROR,
Expand All @@ -100,11 +143,11 @@ void sendCameraErrorEvent(@Nullable String description) {
});
}

void send(CameraEventType eventType) {
private void send(CameraEventType eventType) {
send(eventType, new HashMap<>());
}

void send(CameraEventType eventType, Map<String, Object> args) {
private void send(CameraEventType eventType, Map<String, Object> args) {
if (cameraChannel == null) {
return;
}
Expand All @@ -118,11 +161,11 @@ public void run() {
});
}

void send(DeviceEventType eventType) {
private void send(DeviceEventType eventType) {
send(eventType, new HashMap<>());
}

void send(DeviceEventType eventType, Map<String, Object> args) {
private void send(DeviceEventType eventType, Map<String, Object> args) {
if (deviceChannel == null) {
return;
}
Expand Down
Loading