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

[camera] Add ProcessCameraProvider class to CameraX plugin #6469

Merged
merged 14 commits into from
Sep 27, 2022
Merged
1 change: 1 addition & 0 deletions packages/camera/camera_android_camerax/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
* Creates camera_android_camerax plugin for development.
* Adds CameraInfo class and removes unnecessary code from plugin.
* Adds CameraSelector class.
* Adds ProcessCameraProvider class.
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ android {

dependencies {
// CameraX core library using the camera2 implementation must use same version number.
def camerax_version = "1.2.0-beta01"
def camerax_version = "1.2.0-beta02"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation 'com.google.guava:guava:28.1-android'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-inline:4.7.0'
testImplementation 'androidx.test:core:1.4.0'
testImplementation 'org.robolectric:robolectric:4.3'
}
testOptions {
unitTests.includeAndroidResources = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware {
private InstanceManager instanceManager;
private FlutterPluginBinding pluginBinding;
private ProcessCameraProviderHostApiImpl processCameraProviderHostApi;

/**
* Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment.
Expand All @@ -39,6 +40,10 @@ void setUp(BinaryMessenger binaryMessenger, Context context) {
binaryMessenger, new JavaObjectHostApiImpl(instanceManager));
GeneratedCameraXLibrary.CameraSelectorHostApi.setup(
binaryMessenger, new CameraSelectorHostApiImpl(binaryMessenger, instanceManager));
processCameraProviderHostApi =
new ProcessCameraProviderHostApiImpl(binaryMessenger, instanceManager, context);
GeneratedCameraXLibrary.ProcessCameraProviderHostApi.setup(
binaryMessenger, processCameraProviderHostApi);
}

@Override
Expand All @@ -60,15 +65,33 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
// Activity Lifecycle methods:

@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {}
public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {
updateContext(activityPluginBinding.getActivity());
}

@Override
public void onDetachedFromActivityForConfigChanges() {}
public void onDetachedFromActivityForConfigChanges() {
updateContext(pluginBinding.getApplicationContext());
}

@Override
public void onReattachedToActivityForConfigChanges(
@NonNull ActivityPluginBinding activityPluginBinding) {}
@NonNull ActivityPluginBinding activityPluginBinding) {
updateContext(activityPluginBinding.getActivity());
}

@Override
public void onDetachedFromActivity() {}
public void onDetachedFromActivity() {
updateContext(pluginBinding.getApplicationContext());
}

/**
* Updates context that is used to fetch the corresponding instance of a {@code
* ProcessCameraProvider}.
*/
private void updateContext(Context context) {
if (processCameraProviderHostApi != null) {
processCameraProviderHostApi.setContext(context);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public CameraInfoFlutterApiImpl(
}

void create(CameraInfo cameraInfo, Reply<Void> reply) {
instanceManager.addHostCreatedInstance(cameraInfo);
create(instanceManager.getIdentifierForStrongReference(cameraInfo), reply);
create(instanceManager.addHostCreatedInstance(cameraInfo), reply);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
/** Generated class from Pigeon. */
@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"})
public class GeneratedCameraXLibrary {

public interface Result<T> {
void success(T result);

void error(Throwable error);
}

private static class JavaObjectHostApiCodec extends StandardMessageCodec {
public static final JavaObjectHostApiCodec INSTANCE = new JavaObjectHostApiCodec();

Expand Down Expand Up @@ -311,6 +318,133 @@ public void create(
}
}

private static class ProcessCameraProviderHostApiCodec extends StandardMessageCodec {
public static final ProcessCameraProviderHostApiCodec INSTANCE =
new ProcessCameraProviderHostApiCodec();

private ProcessCameraProviderHostApiCodec() {}
}

/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
public interface ProcessCameraProviderHostApi {
void getInstance(Result<Long> result);

@NonNull
List<Long> getAvailableCameraInfos(@NonNull Long identifier);

/** The codec used by ProcessCameraProviderHostApi. */
static MessageCodec<Object> getCodec() {
return ProcessCameraProviderHostApiCodec.INSTANCE;
}

/**
* Sets up an instance of `ProcessCameraProviderHostApi` to handle messages through the
* `binaryMessenger`.
*/
static void setup(BinaryMessenger binaryMessenger, ProcessCameraProviderHostApi api) {
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
Map<String, Object> wrapped = new HashMap<>();
try {
Result<Long> resultCallback =
new Result<Long>() {
public void success(Long result) {
wrapped.put("result", result);
reply.reply(wrapped);
}

public void error(Throwable error) {
wrapped.put("error", wrapError(error));
reply.reply(wrapped);
}
};

api.getInstance(resultCallback);
} catch (Error | RuntimeException exception) {
wrapped.put("error", wrapError(exception));
reply.reply(wrapped);
}
});
} else {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
Map<String, Object> wrapped = new HashMap<>();
try {
ArrayList<Object> args = (ArrayList<Object>) message;
Number identifierArg = (Number) args.get(0);
if (identifierArg == null) {
throw new NullPointerException("identifierArg unexpectedly null.");
}
List<Long> output =
api.getAvailableCameraInfos(
(identifierArg == null) ? null : identifierArg.longValue());
wrapped.put("result", output);
} catch (Error | RuntimeException exception) {
wrapped.put("error", wrapError(exception));
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}

private static class ProcessCameraProviderFlutterApiCodec extends StandardMessageCodec {
public static final ProcessCameraProviderFlutterApiCodec INSTANCE =
new ProcessCameraProviderFlutterApiCodec();

private ProcessCameraProviderFlutterApiCodec() {}
}

/** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
public static class ProcessCameraProviderFlutterApi {
private final BinaryMessenger binaryMessenger;

public ProcessCameraProviderFlutterApi(BinaryMessenger argBinaryMessenger) {
this.binaryMessenger = argBinaryMessenger;
}

public interface Reply<T> {
void reply(T reply);
}

static MessageCodec<Object> getCodec() {
return ProcessCameraProviderFlutterApiCodec.INSTANCE;
}

public void create(@NonNull Long identifierArg, Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create",
getCodec());
channel.send(
new ArrayList<Object>(Arrays.asList(identifierArg)),
channelReply -> {
callback.reply(null);
});
}
}

private static Map<String, Object> wrapError(Throwable exception) {
Map<String, Object> errorMap = new HashMap<>();
errorMap.put("message", exception.toString());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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.camera.lifecycle.ProcessCameraProvider;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderFlutterApi;

public class ProcessCameraProviderFlutterApiImpl extends ProcessCameraProviderFlutterApi {
public ProcessCameraProviderFlutterApiImpl(
BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
super(binaryMessenger);
this.instanceManager = instanceManager;
}

private final InstanceManager instanceManager;

void create(ProcessCameraProvider processCameraProvider, Reply<Void> reply) {
create(instanceManager.addHostCreatedInstance(processCameraProvider), reply);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// 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.camera.core.CameraInfo;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat;
import com.google.common.util.concurrent.ListenableFuture;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderHostApi;
import java.util.ArrayList;
import java.util.List;

public class ProcessCameraProviderHostApiImpl implements ProcessCameraProviderHostApi {
private final BinaryMessenger binaryMessenger;
private final InstanceManager instanceManager;

private Context context;

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

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

/**
* Returns the instance of the ProcessCameraProvider to manage the lifecycle of the camera for the
* current {@code Context}.
*/
@Override
public void getInstance(GeneratedCameraXLibrary.Result<Long> result) {
ListenableFuture<ProcessCameraProvider> processCameraProviderFuture =
ProcessCameraProvider.getInstance(context);

processCameraProviderFuture.addListener(
() -> {
try {
// Camera provider is now guaranteed to be available.
ProcessCameraProvider processCameraProvider = processCameraProviderFuture.get();

if (!instanceManager.containsInstance(processCameraProvider)) {
final ProcessCameraProviderFlutterApiImpl flutterApi =
new ProcessCameraProviderFlutterApiImpl(binaryMessenger, instanceManager);
flutterApi.create(processCameraProvider, reply -> {});
}
result.success(instanceManager.getIdentifierForStrongReference(processCameraProvider));
} catch (Exception e) {
result.error(e);
}
},
ContextCompat.getMainExecutor(context));
}

/** Returns cameras available to the ProcessCameraProvider. */
@Override
public List<Long> getAvailableCameraInfos(@NonNull Long identifier) {
ProcessCameraProvider processCameraProvider =
(ProcessCameraProvider) instanceManager.getInstance(identifier);

List<CameraInfo> availableCameras = processCameraProvider.getAvailableCameraInfos();
List<Long> availableCamerasIds = new ArrayList<Long>();
final CameraInfoFlutterApiImpl cameraInfoFlutterApi =
new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager);

for (CameraInfo cameraInfo : availableCameras) {
cameraInfoFlutterApi.create(cameraInfo, result -> {});
availableCamerasIds.add(instanceManager.getIdentifierForStrongReference(cameraInfo));
}
return availableCamerasIds;
}
}
Loading