Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
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
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ packages/**/*_web/** @ditman

# - Android
packages/camera/camera_android/** @camsim99
packages/camera/camera_android_camerax/** @camsim99
packages/espresso/** @GaryQian
packages/flutter_plugin_android_lifecycle/** @GaryQian
packages/google_maps_flutter/google_maps_flutter_android/** @GaryQian
Expand Down
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 @@ -5,3 +5,4 @@
* Adds CameraSelector class.
* Adds ProcessCameraProvider class.
* Bump CameraX version to 1.3.0-alpha02.
* Adds Camera and UseCase classes, along with methods for binding UseCases to a lifecycle with the ProcessCameraProvider.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleOwner;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
Expand All @@ -15,7 +16,7 @@
public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware {
private InstanceManager instanceManager;
private FlutterPluginBinding pluginBinding;
private ProcessCameraProviderHostApiImpl processCameraProviderHostApi;
public ProcessCameraProviderHostApiImpl processCameraProviderHostApi;

/**
* Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment.
Expand All @@ -36,10 +37,10 @@ void setUp(BinaryMessenger binaryMessenger, Context context) {
// Set up Host APIs.
GeneratedCameraXLibrary.CameraInfoHostApi.setup(
binaryMessenger, new CameraInfoHostApiImpl(instanceManager));
GeneratedCameraXLibrary.JavaObjectHostApi.setup(
binaryMessenger, new JavaObjectHostApiImpl(instanceManager));
GeneratedCameraXLibrary.CameraSelectorHostApi.setup(
binaryMessenger, new CameraSelectorHostApiImpl(binaryMessenger, instanceManager));
GeneratedCameraXLibrary.JavaObjectHostApi.setup(
binaryMessenger, new JavaObjectHostApiImpl(instanceManager));
processCameraProviderHostApi =
new ProcessCameraProviderHostApiImpl(binaryMessenger, instanceManager, context);
GeneratedCameraXLibrary.ProcessCameraProviderHostApi.setup(
Expand All @@ -49,10 +50,6 @@ void setUp(BinaryMessenger binaryMessenger, Context context) {
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
pluginBinding = flutterPluginBinding;
(new CameraAndroidCameraxPlugin())
.setUp(
flutterPluginBinding.getBinaryMessenger(),
flutterPluginBinding.getApplicationContext());
}

@Override
Expand All @@ -66,7 +63,11 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {

@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {
updateContext(activityPluginBinding.getActivity());
CameraAndroidCameraxPlugin plugin = new CameraAndroidCameraxPlugin();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think I missed this in the last PR. But I don't think it is necessary to create a new CameraAndroidCameraxPlugin instance. You should be able to just call setUp on the current instance, right?

plugin.setUp(pluginBinding.getBinaryMessenger(), pluginBinding.getApplicationContext());
plugin.updateContext(pluginBinding.getApplicationContext());
plugin.processCameraProviderHostApi.setLifecycleOwner(
(LifecycleOwner) activityPluginBinding.getActivity());
}

@Override
Expand All @@ -89,7 +90,7 @@ public void onDetachedFromActivity() {
* Updates context that is used to fetch the corresponding instance of a {@code
* ProcessCameraProvider}.
*/
private void updateContext(Context context) {
public void updateContext(Context context) {
if (processCameraProviderHostApi != null) {
processCameraProviderHostApi.setContext(context);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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.core.Camera;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraFlutterApi;

public class CameraFlutterApiImpl extends CameraFlutterApi {
private final InstanceManager instanceManager;

public CameraFlutterApiImpl(BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
super(binaryMessenger);
this.instanceManager = instanceManager;
}

void create(Camera camera, Reply<Void> reply) {
create(instanceManager.addHostCreatedInstance(camera), reply);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,16 @@ public interface ProcessCameraProviderHostApi {
@NonNull
List<Long> getAvailableCameraInfos(@NonNull Long identifier);

@NonNull
Long bindToLifecycle(
@NonNull Long identifier,
@NonNull Long cameraSelectorIdentifier,
@NonNull List<Long> useCaseIds);

void unbind(@NonNull Long identifier, @NonNull List<Long> useCaseIds);

void unbindAll(@NonNull Long identifier);

/** The codec used by ProcessCameraProviderHostApi. */
static MessageCodec<Object> getCodec() {
return ProcessCameraProviderHostApiCodec.INSTANCE;
Expand Down Expand Up @@ -405,6 +415,107 @@ public void error(Throwable error) {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle",
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.");
}
Number cameraSelectorIdentifierArg = (Number) args.get(1);
if (cameraSelectorIdentifierArg == null) {
throw new NullPointerException(
"cameraSelectorIdentifierArg unexpectedly null.");
}
List<Long> useCaseIdsArg = (List<Long>) args.get(2);
if (useCaseIdsArg == null) {
throw new NullPointerException("useCaseIdsArg unexpectedly null.");
}
Long output =
api.bindToLifecycle(
(identifierArg == null) ? null : identifierArg.longValue(),
(cameraSelectorIdentifierArg == null)
? null
: cameraSelectorIdentifierArg.longValue(),
useCaseIdsArg);
wrapped.put("result", output);
} 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.unbind",
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> useCaseIdsArg = (List<Long>) args.get(1);
if (useCaseIdsArg == null) {
throw new NullPointerException("useCaseIdsArg unexpectedly null.");
}
api.unbind(
(identifierArg == null) ? null : identifierArg.longValue(), useCaseIdsArg);
wrapped.put("result", null);
} 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.unbindAll",
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.");
}
api.unbindAll((identifierArg == null) ? null : identifierArg.longValue());
wrapped.put("result", null);
} catch (Error | RuntimeException exception) {
wrapped.put("error", wrapError(exception));
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}

Expand Down Expand Up @@ -445,6 +556,40 @@ public void create(@NonNull Long identifierArg, Reply<Void> callback) {
}
}

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

private CameraFlutterApiCodec() {}
}

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

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

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

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

public void create(@NonNull Long identifierArg, Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger, "dev.flutter.pigeon.CameraFlutterApi.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
Expand Up @@ -6,9 +6,13 @@

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.UseCase;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import com.google.common.util.concurrent.ListenableFuture;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderHostApi;
Expand All @@ -20,6 +24,7 @@ public class ProcessCameraProviderHostApiImpl implements ProcessCameraProviderHo
private final InstanceManager instanceManager;

private Context context;
private LifecycleOwner lifecycleOwner;

public ProcessCameraProviderHostApiImpl(
BinaryMessenger binaryMessenger, InstanceManager instanceManager, Context context) {
Expand All @@ -28,6 +33,10 @@ public ProcessCameraProviderHostApiImpl(
this.context = context;
}

public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
this.lifecycleOwner = lifecycleOwner;
}

/**
* Sets the context that the {@code ProcessCameraProvider} will use to attach the lifecycle of the
* camera to.
Expand All @@ -40,8 +49,8 @@ public void setContext(Context context) {
}

/**
* Returns the instance of the ProcessCameraProvider to manage the lifecycle of the camera for the
* current {@code Context}.
* Returns the instance of the {@code ProcessCameraProvider} to manage the lifecycle of the camera
* for the current {@code Context}.
*/
@Override
public void getInstance(GeneratedCameraXLibrary.Result<Long> result) {
Expand All @@ -54,11 +63,9 @@ public void getInstance(GeneratedCameraXLibrary.Result<Long> result) {
// 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 -> {});
}
final ProcessCameraProviderFlutterApiImpl flutterApi =
new ProcessCameraProviderFlutterApiImpl(binaryMessenger, instanceManager);
flutterApi.create(processCameraProvider, reply -> {});
result.success(instanceManager.getIdentifierForStrongReference(processCameraProvider));
} catch (Exception e) {
result.error(e);
Expand All @@ -67,7 +74,7 @@ public void getInstance(GeneratedCameraXLibrary.Result<Long> result) {
ContextCompat.getMainExecutor(context));
}

/** Returns cameras available to the ProcessCameraProvider. */
/** Returns cameras available to the {@code ProcessCameraProvider}. */
@Override
public List<Long> getAvailableCameraInfos(@NonNull Long identifier) {
ProcessCameraProvider processCameraProvider =
Expand All @@ -84,4 +91,52 @@ public List<Long> getAvailableCameraInfos(@NonNull Long identifier) {
}
return availableCamerasIds;
}

/**
* Binds specified {@code UseCase}s to the lifecycle of the {@code LifecycleOwner} that
* corresponds to this instance and returns the instance of the {@code Camera} whose lifecycle
* that {@code LifecycleOwner} reflects.
*/
@Override
public Long bindToLifecycle(
@NonNull Long identifier,
@NonNull Long cameraSelectorIdentifier,
@NonNull List<Long> useCaseIds) {
ProcessCameraProvider processCameraProvider =
(ProcessCameraProvider) instanceManager.getInstance(identifier);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you wrap instanceManager.getInstance(..) with Objects.requireNonnull(...). It helps with debugging and removes the lint warning. Same for the line below and other apis.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

cc @gmackall just a heads up about this

CameraSelector cameraSelector =
(CameraSelector) instanceManager.getInstance(cameraSelectorIdentifier);
UseCase[] useCases = new UseCase[useCaseIds.size()];
for (int i = 0; i < useCaseIds.size(); i++) {
useCases[i] = (UseCase) instanceManager.getInstance(((Number) useCaseIds.get(i)).longValue());
}

Camera camera =
processCameraProvider.bindToLifecycle(
(LifecycleOwner) lifecycleOwner, cameraSelector, useCases);

final CameraFlutterApiImpl camraFlutterApi =
new CameraFlutterApiImpl(binaryMessenger, instanceManager);
camraFlutterApi.create(camera, result -> {});
Comment thread
camsim99 marked this conversation as resolved.
Outdated

return instanceManager.getIdentifierForStrongReference(camera);
}

@Override
public void unbind(@NonNull Long identifier, @NonNull List<Long> useCaseIds) {
ProcessCameraProvider processCameraProvider =
(ProcessCameraProvider) instanceManager.getInstance(identifier);
UseCase[] useCases = new UseCase[useCaseIds.size()];
for (int i = 0; i < useCaseIds.size(); i++) {
useCases[i] = (UseCase) instanceManager.getInstance(((Number) useCaseIds.get(i)).longValue());
}
processCameraProvider.unbind(useCases);
}

@Override
public void unbindAll(@NonNull Long identifier) {
ProcessCameraProvider processCameraProvider =
(ProcessCameraProvider) instanceManager.getInstance(identifier);
processCameraProvider.unbindAll();
}
}
Loading