Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[camerax] Implements torch mode #4903

Merged
merged 53 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
dec3d69
Merge remote-tracking branch 'upstream/main' into camx_occ
camsim99 May 1, 2023
0e0333b
Merge remote-tracking branch 'upstream/main'
camsim99 May 2, 2023
bd7ac99
Merge remote-tracking branch 'upstream/main'
camsim99 May 3, 2023
5c3363b
Merge remote-tracking branch 'upstream/main'
camsim99 May 10, 2023
fed9621
Undo changes
camsim99 May 10, 2023
5aabe34
Merge remote-tracking branch 'upstream/main'
camsim99 May 12, 2023
2b9a352
Merge remote-tracking branch 'upstream/main'
camsim99 May 25, 2023
a1173da
Merge remote-tracking branch 'upstream/main'
camsim99 May 30, 2023
cbc3d6b
Merge remote-tracking branch 'upstream/main'
camsim99 May 30, 2023
cae5a4c
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 1, 2023
72283db
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 5, 2023
166a77c
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 5, 2023
399780e
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 14, 2023
8d5d0e7
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 26, 2023
084d960
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 12, 2023
d2a59ac
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 17, 2023
a1422bf
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 17, 2023
bdd87a6
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 18, 2023
137a28b
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 19, 2023
bc0db5a
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 20, 2023
d04b466
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 21, 2023
a9cfe87
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 24, 2023
a32def1
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 2, 2023
4785148
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 14, 2023
7a8fc69
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 15, 2023
b02e15f
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 15, 2023
c6e5868
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 17, 2023
0c0065a
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 28, 2023
9dfe259
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 29, 2023
bfcc0df
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 30, 2023
b80cc86
Merge remote-tracking branch 'upstream/main'
camsim99 Sep 5, 2023
915332e
Start impl
camsim99 Sep 5, 2023
22ea65f
More impl
camsim99 Sep 6, 2023
ad46f47
More impl
camsim99 Sep 6, 2023
6396ec5
More impl
camsim99 Sep 7, 2023
cd0dc35
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 11, 2023
9c9922b
Add dart tests
camsim99 Sep 11, 2023
0f64164
Add back example code
camsim99 Sep 11, 2023
dc5f95b
Add tests, update docs
camsim99 Sep 12, 2023
743c853
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 12, 2023
85e3da7
Fix analysis errors
camsim99 Sep 12, 2023
476d4de
Remove redundant context setting
camsim99 Sep 12, 2023
b385f1a
Fix img analysis test
camsim99 Sep 12, 2023
fb7986d
Nit
camsim99 Sep 12, 2023
87a4438
Modify torch tests
camsim99 Sep 12, 2023
12ce00c
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 12, 2023
a6f0968
Fix java tests
camsim99 Sep 12, 2023
cef5f31
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 12, 2023
81e6cbc
Delete unused proxy
camsim99 Sep 13, 2023
2534eae
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 13, 2023
bb0e9d7
Merge remote-tracking branch 'upstream/main' into camx_torch
camsim99 Sep 18, 2023
93317eb
Address review
camsim99 Sep 21, 2023
54a69ea
Add documentation
camsim99 Oct 10, 2023
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
4 changes: 4 additions & 0 deletions packages/camera/camera_android_camerax/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.5.0+19

* Implements torch flash mode.

## 0.5.0+18

* Implements `startVideoCapturing`.
Expand Down
4 changes: 0 additions & 4 deletions packages/camera/camera_android_camerax/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ and thus, the plugin will fall back to 480p if configured with a

`lockCaptureOrientation` & `unLockCaptureOrientation` are unimplemented.

### Torch mode \[[Issue #120715][120715]\]

Calling `setFlashMode` with mode `FlashMode.torch` currently does nothing.

### Exposure mode, point, & offset configuration \[[Issue #120468][120468]\]

`setExposureMode`, `setExposurePoint`, & `setExposureOffset` are unimplemented.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity
private VideoCaptureHostApiImpl videoCaptureHostApiImpl;
private ImageAnalysisHostApiImpl imageAnalysisHostApiImpl;
private ImageCaptureHostApiImpl imageCaptureHostApiImpl;
private CameraControlHostApiImpl cameraControlHostApiImpl;
public @Nullable SystemServicesHostApiImpl systemServicesHostApiImpl;

@VisibleForTesting
Expand Down Expand Up @@ -81,7 +82,8 @@ public void setUp(
GeneratedCameraXLibrary.LiveDataHostApi.setup(binaryMessenger, liveDataHostApiImpl);
GeneratedCameraXLibrary.ObserverHostApi.setup(
binaryMessenger, new ObserverHostApiImpl(binaryMessenger, instanceManager));
imageAnalysisHostApiImpl = new ImageAnalysisHostApiImpl(binaryMessenger, instanceManager);
imageAnalysisHostApiImpl =
new ImageAnalysisHostApiImpl(binaryMessenger, instanceManager, context);
Copy link
Contributor Author

@camsim99 camsim99 Sep 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR also makes a fix to this implementation by adding a Context to the ImageAnalysisHostApiImpl constructor to remove an unnecessary call to update the context for the Host API implementations after running this method also setting the Context for those implementations (see line 131).

GeneratedCameraXLibrary.ImageAnalysisHostApi.setup(binaryMessenger, imageAnalysisHostApiImpl);
GeneratedCameraXLibrary.AnalyzerHostApi.setup(
binaryMessenger, new AnalyzerHostApiImpl(binaryMessenger, instanceManager));
Expand All @@ -107,6 +109,8 @@ public void setUp(
binaryMessenger, new FallbackStrategyHostApiImpl(instanceManager));
GeneratedCameraXLibrary.QualitySelectorHostApi.setup(
binaryMessenger, new QualitySelectorHostApiImpl(instanceManager));
cameraControlHostApiImpl = new CameraControlHostApiImpl(instanceManager, context);
GeneratedCameraXLibrary.CameraControlHostApi.setup(binaryMessenger, cameraControlHostApiImpl);
}

@Override
Expand All @@ -128,7 +132,6 @@ public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBi
Activity activity = activityPluginBinding.getActivity();

setUp(pluginBinding.getBinaryMessenger(), activity, pluginBinding.getTextureRegistry());
updateContext(activity);

if (activity instanceof LifecycleOwner) {
processCameraProviderHostApiImpl.setLifecycleOwner((LifecycleOwner) activity);
Expand Down Expand Up @@ -183,5 +186,8 @@ public void updateContext(@NonNull Context context) {
if (imageAnalysisHostApiImpl != null) {
imageAnalysisHostApiImpl.setContext(context);
}
if (cameraControlHostApiImpl != null) {
cameraControlHostApiImpl.setContext(context);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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.camerax;

import androidx.annotation.NonNull;
import androidx.camera.core.CameraControl;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraControlFlutterApi;

public class CameraControlFlutterApiImpl extends CameraControlFlutterApi {
private final @NonNull InstanceManager instanceManager;

public CameraControlFlutterApiImpl(
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
super(binaryMessenger);
this.instanceManager = instanceManager;
}

/**
* Creates a {@link CameraControl} instance in Dart. {@code reply} is not used so it can be empty.
*/
void create(CameraControl cameraControl, Reply<Void> reply) {
if (!instanceManager.containsInstance(cameraControl)) {
create(instanceManager.addHostCreatedInstance(cameraControl), reply);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// 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.camerax;

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.CameraControl;
import androidx.core.content.ContextCompat;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraControlHostApi;
import java.util.Objects;

/**
* Host API implementation for {@link CameraControl}.
*
* <p>This class handles instantiating and adding native object instances that are attached to a
* Dart instance or handle method calls on the associated native class or an instance of the class.
*/
public class CameraControlHostApiImpl implements CameraControlHostApi {
private final InstanceManager instanceManager;
private final CameraControlProxy proxy;

/** Proxy for constructors and static method of {@link CameraControl}. */
@VisibleForTesting
public static class CameraControlProxy {
Context context;

/** Enables or disables the torch of the specified {@link CameraControl} instance. */
@NonNull
public void enableTorch(
@NonNull CameraControl cameraControl,
@NonNull Boolean torch,
@NonNull GeneratedCameraXLibrary.Result<Void> result) {
ListenableFuture<Void> enableTorchFuture = cameraControl.enableTorch(torch);

Futures.addCallback(
enableTorchFuture,
new FutureCallback<Void>() {
public void onSuccess(Void voidResult) {
result.success(null);
}

public void onFailure(Throwable t) {
result.error(t);
}
},
ContextCompat.getMainExecutor(context));
}
}

/**
* Constructs an {@link CameraControlHostApiImpl}.
*
* @param instanceManager maintains instances stored to communicate with attached Dart objects
*/
public CameraControlHostApiImpl(
@NonNull InstanceManager instanceManager, @NonNull Context context) {
this(instanceManager, new CameraControlProxy(), context);
}

/**
* Constructs an {@link CameraControlHostApiImpl}.
*
* @param instanceManager maintains instances stored to communicate with attached Dart objects
* @param proxy proxy for constructors and static method of {@link CameraControl}
* @param context {@link Context} used to retrieve {@code Executor} used to enable torch mode
*/
@VisibleForTesting
CameraControlHostApiImpl(
@NonNull InstanceManager instanceManager,
@NonNull CameraControlProxy proxy,
@NonNull Context context) {
this.instanceManager = instanceManager;
this.proxy = proxy;
proxy.context = context;
}

/**
* Sets the context that the {@code ProcessCameraProvider} will use to enable/disable torch mode.
*
* <p>If using the camera plugin in an add-to-app context, ensure that a new instance of the
* {@code CameraControl} is fetched via {@code #enableTorch} anytime the context changes.
*/
public void setContext(@NonNull Context context) {
this.proxy.context = context;
}

@Override
public void enableTorch(
@NonNull Long identifier,
@NonNull Boolean torch,
@NonNull GeneratedCameraXLibrary.Result<Void> result) {
proxy.enableTorch(
Objects.requireNonNull(instanceManager.getInstance(identifier)), torch, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import androidx.annotation.NonNull;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraControl;
import androidx.camera.core.CameraInfo;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraHostApi;
Expand All @@ -28,14 +29,33 @@ public CameraHostApiImpl(
@Override
@NonNull
public Long getCameraInfo(@NonNull Long identifier) {
Camera camera = (Camera) Objects.requireNonNull(instanceManager.getInstance(identifier));
Camera camera = getCameraInstance(identifier);
CameraInfo cameraInfo = camera.getCameraInfo();

if (!instanceManager.containsInstance(cameraInfo)) {
CameraInfoFlutterApiImpl cameraInfoFlutterApiImpl =
new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager);
cameraInfoFlutterApiImpl.create(cameraInfo, reply -> {});
}
CameraInfoFlutterApiImpl cameraInfoFlutterApiImpl =
new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager);
cameraInfoFlutterApiImpl.create(cameraInfo, reply -> {});
return instanceManager.getIdentifierForStrongReference(cameraInfo);
}

/**
* Retrieves the {@link CameraControl} instance that provides access to asynchronous operations
* like zoom and focus & metering on the {@link Camera} instance with the specified identifier.
*/
@Override
@NonNull
public Long getCameraControl(@NonNull Long identifier) {
Camera camera = getCameraInstance(identifier);
CameraControl cameraControl = camera.getCameraControl();

CameraControlFlutterApiImpl cameraControlFlutterApiImpl =
new CameraControlFlutterApiImpl(binaryMessenger, instanceManager);
cameraControlFlutterApiImpl.create(cameraControl, reply -> {});
return instanceManager.getIdentifierForStrongReference(cameraControl);
}

/** Retrieives the {@link Camera} instance associated with the specified {@code identifier}. */
private Camera getCameraInstance(@NonNull Long identifier) {
return (Camera) Objects.requireNonNull(instanceManager.getInstance(identifier));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,9 @@ public interface CameraHostApi {
@NonNull
Long getCameraInfo(@NonNull Long identifier);

@NonNull
Long getCameraControl(@NonNull Long identifier);

/** The codec used by CameraHostApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
Expand Down Expand Up @@ -1166,6 +1169,31 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable CameraHost
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger, "dev.flutter.pigeon.CameraHostApi.getCameraControl", getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
Number identifierArg = (Number) args.get(0);
try {
Long output =
api.getCameraControl(
(identifierArg == null) ? null : identifierArg.longValue());
wrapped.add(0, output);
} catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
Expand Down Expand Up @@ -3203,4 +3231,82 @@ static void setup(
}
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
public interface CameraControlHostApi {

void enableTorch(
@NonNull Long identifier, @NonNull Boolean torch, @NonNull Result<Void> result);

/** The codec used by CameraControlHostApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
}
/**
* Sets up an instance of `CameraControlHostApi` to handle messages through the
* `binaryMessenger`.
*/
static void setup(
@NonNull BinaryMessenger binaryMessenger, @Nullable CameraControlHostApi api) {
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger, "dev.flutter.pigeon.CameraControlHostApi.enableTorch", getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
Number identifierArg = (Number) args.get(0);
Boolean torchArg = (Boolean) args.get(1);
Result<Void> resultCallback =
new Result<Void>() {
public void success(Void result) {
wrapped.add(0, null);
reply.reply(wrapped);
}

public void error(Throwable error) {
ArrayList<Object> wrappedError = wrapError(error);
reply.reply(wrappedError);
}
};

api.enableTorch(
(identifierArg == null) ? null : identifierArg.longValue(),
torchArg,
resultCallback);
});
} else {
channel.setMessageHandler(null);
}
}
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
public static class CameraControlFlutterApi {
private final @NonNull BinaryMessenger binaryMessenger;

public CameraControlFlutterApi(@NonNull BinaryMessenger argBinaryMessenger) {
this.binaryMessenger = argBinaryMessenger;
}

/** Public interface for sending reply. */
@SuppressWarnings("UnknownNullness")
public interface Reply<T> {
void reply(T reply);
}
/** The codec used by CameraControlFlutterApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
}

public void create(@NonNull Long identifierArg, @NonNull Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger, "dev.flutter.pigeon.CameraControlFlutterApi.create", getCodec());
channel.send(
new ArrayList<Object>(Collections.singletonList(identifierArg)),
channelReply -> callback.reply(null));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ public class ImageAnalysisHostApiImpl implements ImageAnalysisHostApi {
@VisibleForTesting @NonNull public CameraXProxy cameraXProxy = new CameraXProxy();

public ImageAnalysisHostApiImpl(
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
@NonNull BinaryMessenger binaryMessenger,
@NonNull InstanceManager instanceManager,
@NonNull Context context) {
this.binaryMessenger = binaryMessenger;
this.instanceManager = instanceManager;
this.context = context;
}

/**
Expand Down
Loading