From bea50d39b1f974ab48cbe6be2c41b98c7015fc96 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Fri, 6 Jan 2023 12:10:00 -0800 Subject: [PATCH 01/17] Copy over code from proof of concept --- .../camerax/CameraAndroidCameraxPlugin.java | 26 ++-- .../plugins/camerax/CameraFlutterApiImpl.java | 22 +++ .../camerax/GeneratedCameraXLibrary.java | 145 ++++++++++++++++++ .../ProcessCameraProviderHostApiImpl.java | 76 ++++++++- .../lib/src/camera.dart | 52 +++++++ .../lib/src/camerax_library.pigeon.dart | 114 ++++++++++++++ .../lib/src/process_camera_provider.dart | 81 ++++++++++ .../lib/src/use_case.dart | 19 +++ .../pigeons/camerax_library.dart | 12 ++ .../test/test_camerax_library.pigeon.dart | 74 +++++++++ 10 files changed, 604 insertions(+), 17 deletions(-) create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraFlutterApiImpl.java create mode 100644 packages/camera/camera_android_camerax/lib/src/camera.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/use_case.dart diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java index b8fbaf539c32..6f9829d49926 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -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; @@ -15,7 +16,8 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware { private InstanceManager instanceManager; private FlutterPluginBinding pluginBinding; - private ProcessCameraProviderHostApiImpl processCameraProviderHostApi; + public ProcessCameraProviderHostApiImpl processCameraProviderHostApi; + public SystemServicesHostApiImpl systemServicesHostApi; /** * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. @@ -36,10 +38,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( @@ -49,10 +51,6 @@ void setUp(BinaryMessenger binaryMessenger, Context context) { @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { pluginBinding = flutterPluginBinding; - (new CameraAndroidCameraxPlugin()) - .setUp( - flutterPluginBinding.getBinaryMessenger(), - flutterPluginBinding.getApplicationContext()); } @Override @@ -66,7 +64,17 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { - updateContext(activityPluginBinding.getActivity()); + CameraAndroidCameraxPlugin plugin = new CameraAndroidCameraxPlugin(); + plugin.setUp( + pluginBinding.getBinaryMessenger(), + pluginBinding.getApplicationContext(), + pluginBinding.getTextureRegistry()); + plugin.updateContext(pluginBinding.getApplicationContext()); + plugin.processCameraProviderHostApi.setLifecycleOwner( + (LifecycleOwner) activityPluginBinding.getActivity()); + plugin.systemServicesHostApi.setActivity(activityPluginBinding.getActivity()); + plugin.systemServicesHostApi.setPermissionsRegistry( + activityPluginBinding::addRequestPermissionsResultListener); } @Override @@ -89,7 +97,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); } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraFlutterApiImpl.java new file mode 100644 index 000000000000..a03548399485 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraFlutterApiImpl.java @@ -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 reply) { + create(instanceManager.addHostCreatedInstance(camera), reply); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java index 041564c3bfcb..8c42a7911768 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java @@ -332,6 +332,16 @@ public interface ProcessCameraProviderHostApi { @NonNull List getAvailableCameraInfos(@NonNull Long identifier); + @NonNull + Long bindToLifecycle( + @NonNull Long identifier, + @NonNull Long cameraSelectorIdentifier, + @NonNull List useCaseIds); + + void unbind(@NonNull Long identifier, @NonNull List useCaseIds); + + void unbindAll(@NonNull Long identifier); + /** The codec used by ProcessCameraProviderHostApi. */ static MessageCodec getCodec() { return ProcessCameraProviderHostApiCodec.INSTANCE; @@ -405,6 +415,107 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) 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 useCaseIdsArg = (List) 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 channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + List useCaseIdsArg = (List) 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 channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) 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); + } + } } } @@ -445,6 +556,40 @@ public void create(@NonNull Long identifierArg, Reply 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 { + void reply(T reply); + } + + static MessageCodec getCodec() { + return CameraFlutterApiCodec.INSTANCE; + } + + public void create(@NonNull Long identifierArg, Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraFlutterApi.create", getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); + } + } + private static Map wrapError(Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java index 19c5eb5b3f70..168442576cbf 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java @@ -6,9 +6,14 @@ 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.Preview; +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; @@ -20,6 +25,7 @@ public class ProcessCameraProviderHostApiImpl implements ProcessCameraProviderHo private final InstanceManager instanceManager; private Context context; + private LifecycleOwner lifecycleOwner; public ProcessCameraProviderHostApiImpl( BinaryMessenger binaryMessenger, InstanceManager instanceManager, Context context) { @@ -28,6 +34,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. @@ -40,8 +50,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 result) { @@ -54,11 +64,9 @@ public void getInstance(GeneratedCameraXLibrary.Result 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); @@ -67,7 +75,7 @@ public void getInstance(GeneratedCameraXLibrary.Result result) { ContextCompat.getMainExecutor(context)); } - /** Returns cameras available to the ProcessCameraProvider. */ + /** Returns cameras available to the {@code ProcessCameraProvider}. */ @Override public List getAvailableCameraInfos(@NonNull Long identifier) { ProcessCameraProvider processCameraProvider = @@ -84,4 +92,56 @@ public List getAvailableCameraInfos(@NonNull Long identifier) { } return availableCamerasIds; } + + /** + * Binds specified {@code UseCase}s to the lifecycle of the {@code LifecycleOwner} that + * corresponds to this instance. + */ + @Override + public Long bindToLifecycle( + @NonNull Long identifier, + @NonNull Long cameraSelectorIdentifier, + @NonNull List useCaseIds) { + ProcessCameraProvider processCameraProvider = + (ProcessCameraProvider) instanceManager.getInstance(identifier); + 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()); + } + + if (lifecycleOwner != null) { + Camera camera = + processCameraProvider.bindToLifecycle( + (LifecycleOwner) lifecycleOwner, cameraSelector, useCases); + + final CameraFlutterApiImpl camraFlutterApi = + new CameraFlutterApiImpl(binaryMessenger, instanceManager); + camraFlutterApi.create(camera, result -> {}); + + return instanceManager.getIdentifierForStrongReference(camera); + } else { + // TODO(camsim99): Throw error here? + return null; + } + } + + @Override + public void unbind(@NonNull Long identifier, @NonNull List useCaseIds) { + ProcessCameraProvider processCameraProvider = + (ProcessCameraProvider) instanceManager.getInstance(identifier); + UseCase[] useCases = new UseCase[useCaseIds.size()]; + for (int i = 0; i < useCaseIds.size(); i++) { + useCases[i] = (Preview) 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(); + } } diff --git a/packages/camera/camera_android_camerax/lib/src/camera.dart b/packages/camera/camera_android_camerax/lib/src/camera.dart new file mode 100644 index 000000000000..8dd7e6697ae8 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/camera.dart @@ -0,0 +1,52 @@ +// 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. + +import 'package:flutter/services.dart' show BinaryMessenger; + +import 'android_camera_camerax_flutter_api_impls.dart'; +import 'camerax_library.pigeon.dart'; +import 'instance_manager.dart'; +import 'java_object.dart'; + +class Camera extends JavaObject { + /// Constructs a [Camera] that is not automatically attached to a native object. + Camera.detached( + {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager) { + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + } +} + +/// Flutter API implementation of [Camera]. +class CameraFlutterApiImpl implements CameraFlutterApi { + /// Constructs a [CameraSelectorFlutterApiImpl]. + CameraFlutterApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void create(int identifier) { + instanceManager.addHostCreatedInstance( + Camera.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager), + identifier, + onCopy: (Camera original) { + return Camera.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + }, + ); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart index c0b052378def..636a375145b9 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart @@ -338,6 +338,89 @@ class ProcessCameraProviderHostApi { return (replyMap['result'] as List?)!.cast(); } } + + Future bindToLifecycle(int arg_identifier, + int arg_cameraSelectorIdentifier, List arg_useCaseIds) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send([ + arg_identifier, + arg_cameraSelectorIdentifier, + arg_useCaseIds + ]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as int?)!; + } + } + + Future unbind(int arg_identifier, List arg_useCaseIds) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier, arg_useCaseIds]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future unbindAll(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } } class _ProcessCameraProviderFlutterApiCodec extends StandardMessageCodec { @@ -372,3 +455,34 @@ abstract class ProcessCameraProviderFlutterApi { } } } + +class _CameraFlutterApiCodec extends StandardMessageCodec { + const _CameraFlutterApiCodec(); +} + +abstract class CameraFlutterApi { + static const MessageCodec codec = _CameraFlutterApiCodec(); + + void create(int identifier); + static void setup(CameraFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraFlutterApi.create was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.CameraFlutterApi.create was null, expected non-null int.'); + api.create(arg_identifier!); + return; + }); + } + } + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart index 5a67fa7e4dc3..5c31e89dc038 100644 --- a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart +++ b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart @@ -5,10 +5,13 @@ import 'package:flutter/services.dart'; import 'android_camera_camerax_flutter_api_impls.dart'; +import 'camera.dart'; import 'camera_info.dart'; +import 'camera_selector.dart'; import 'camerax_library.pigeon.dart'; import 'instance_manager.dart'; import 'java_object.dart'; +import 'use_case.dart'; /// Provides an object to manage the camera. /// @@ -42,6 +45,25 @@ class ProcessCameraProvider extends JavaObject { Future> getAvailableCameraInfos() { return _api.getAvailableCameraInfosFromInstances(this); } + + /// Binds the specified [UseCase]s to the lifecycle of the camera that it + /// returns. + Future bindToLifecycle( + CameraSelector cameraSelector, List useCases) { + return _api.bindToLifecycleFromInstances(this, cameraSelector, useCases); + } + + /// Unbinds specified [UseCase]s from the lifecycle of the camera that this + /// instance tracks. + void unbind(List useCases) { + _api.unbindFromInstances(this, useCases); + } + + /// Unbinds all previously bound [UseCase]s from the lifecycle of the camera + /// that this tracks. + void unbindAll() { + _api.unbindAllFromInstances(this); + } } /// Host API implementation of [ProcessCameraProvider]. @@ -85,6 +107,65 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { instanceManager.getInstanceWithWeakReference(id!)! as CameraInfo) .toList(); } + + /// Binds the specified [UseCase]s to the lifecycle of the camera which + /// the provided [ProcessCameraProvider] instance tracks. + /// + /// The instance of the camera whose lifecycle the [UseCase]s are bound to + /// is returned. + Future bindToLifecycleFromInstances( + ProcessCameraProvider instance, + CameraSelector cameraSelector, + List useCases, + ) async { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (ProcessCameraProvider original) { + return ProcessCameraProvider.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + }); + final List useCaseIds = (useCases.map( + (UseCase useCase) => instanceManager.getIdentifier(useCase)!)).toList(); + + final int cameraIdentifier = await bindToLifecycle( + identifier, + instanceManager.getIdentifier(cameraSelector)!, + useCaseIds, + ); + return instanceManager.getInstanceWithWeakReference(cameraIdentifier)! + as Camera; + } + + /// Unbinds specified [UseCase]s from the lifecycle of the camera which the + /// provided [ProcessCameraProvider] instance tracks. + void unbindFromInstances( + ProcessCameraProvider instance, + List useCases, + ) { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (ProcessCameraProvider original) { + return ProcessCameraProvider.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + }); + final List useCaseIds = (useCases.map( + (UseCase useCase) => instanceManager.getIdentifier(useCase)!)).toList(); + + unbind(identifier, useCaseIds); + } + + /// Unbinds all previously bound [UseCase]s from the lifecycle of the camera + /// which the provided [ProcessCameraProvider] instance tracks. + void unbindAllFromInstances(ProcessCameraProvider instance) { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (ProcessCameraProvider original) { + return ProcessCameraProvider.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + }); + + unbindAll(identifier); + } } /// Flutter API Implementation of [ProcessCameraProvider]. diff --git a/packages/camera/camera_android_camerax/lib/src/use_case.dart b/packages/camera/camera_android_camerax/lib/src/use_case.dart new file mode 100644 index 000000000000..8bf5aad1e910 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/use_case.dart @@ -0,0 +1,19 @@ +// 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. + +import 'package:flutter/services.dart' show BinaryMessenger; + +import 'instance_manager.dart'; +import 'java_object.dart'; + +/// An object representing the different functionalitites of the camera. +/// +/// See https://developer.android.com/reference/androidx/camera/core/UseCase. +class UseCase extends JavaObject { + /// Creates a detached [UseCase]. + UseCase.detached( + {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : super.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); +} diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index 4d7d96910246..edd2059e162b 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -64,9 +64,21 @@ abstract class ProcessCameraProviderHostApi { int getInstance(); List getAvailableCameraInfos(int identifier); + + int bindToLifecycle( + int identifier, int cameraSelectorIdentifier, List useCaseIds); + + void unbind(int identifier, List useCaseIds); + + void unbindAll(int identifier); } @FlutterApi() abstract class ProcessCameraProviderFlutterApi { void create(int identifier); } + +@FlutterApi() +abstract class CameraFlutterApi { + void create(int identifier); +} diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart index 2196b73d7fdb..c6afe067a3b6 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart @@ -146,6 +146,10 @@ abstract class TestProcessCameraProviderHostApi { Future getInstance(); List getAvailableCameraInfos(int identifier); + int bindToLifecycle( + int identifier, int cameraSelectorIdentifier, List useCaseIds); + void unbind(int identifier, List useCaseIds); + void unbindAll(int identifier); static void setup(TestProcessCameraProviderHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -183,5 +187,75 @@ abstract class TestProcessCameraProviderHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null, expected non-null int.'); + final int? arg_cameraSelectorIdentifier = (args[1] as int?); + assert(arg_cameraSelectorIdentifier != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null, expected non-null int.'); + final List? arg_useCaseIds = + (args[2] as List?)?.cast(); + assert(arg_useCaseIds != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.bindToLifecycle was null, expected non-null List.'); + final int output = api.bindToLifecycle( + arg_identifier!, arg_cameraSelectorIdentifier!, arg_useCaseIds!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind was null, expected non-null int.'); + final List? arg_useCaseIds = + (args[1] as List?)?.cast(); + assert(arg_useCaseIds != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbind was null, expected non-null List.'); + api.unbind(arg_identifier!, arg_useCaseIds!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.unbindAll was null, expected non-null int.'); + api.unbindAll(arg_identifier!); + return {}; + }); + } + } } } From 154eed6b1afb302041930dd1118b0b20d37c6b07 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Fri, 6 Jan 2023 13:28:18 -0800 Subject: [PATCH 02/17] Add dart tests --- ...roid_camera_camerax_flutter_api_impls.dart | 7 ++ .../test/camera_info_test.mocks.dart | 10 +- .../test/camera_selector_test.mocks.dart | 36 ++++-- .../test/camera_test.dart | 29 +++++ .../test/process_camera_provider_test.dart | 104 ++++++++++++++++++ .../process_camera_provider_test.mocks.dart | 60 +++++++++- 6 files changed, 230 insertions(+), 16 deletions(-) create mode 100644 packages/camera/camera_android_camerax/test/camera_test.dart diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart index 9c6564a06c08..620831bfe54d 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'camera.dart'; import 'camera_info.dart'; import 'camera_selector.dart'; import 'camerax_library.pigeon.dart'; @@ -13,6 +14,7 @@ class AndroidCameraXCameraFlutterApis { /// Creates a [AndroidCameraXCameraFlutterApis]. AndroidCameraXCameraFlutterApis({ JavaObjectFlutterApiImpl? javaObjectFlutterApi, + CameraFlutterApiImpl? cameraFlutterApi, CameraInfoFlutterApiImpl? cameraInfoFlutterApi, CameraSelectorFlutterApiImpl? cameraSelectorFlutterApi, ProcessCameraProviderFlutterApiImpl? processCameraProviderFlutterApi, @@ -25,6 +27,7 @@ class AndroidCameraXCameraFlutterApis { cameraSelectorFlutterApi ?? CameraSelectorFlutterApiImpl(); this.processCameraProviderFlutterApi = processCameraProviderFlutterApi ?? ProcessCameraProviderFlutterApiImpl(); + this.cameraFlutterApi = cameraFlutterApi ?? CameraFlutterApiImpl(); } static bool _haveBeenSetUp = false; @@ -48,6 +51,9 @@ class AndroidCameraXCameraFlutterApis { late final ProcessCameraProviderFlutterApiImpl processCameraProviderFlutterApi; + /// Flutter Api for [Camera]. + late final CameraFlutterApiImpl cameraFlutterApi; + /// Ensures all the Flutter APIs have been setup to receive calls from native code. void ensureSetUp() { if (!_haveBeenSetUp) { @@ -55,6 +61,7 @@ class AndroidCameraXCameraFlutterApis { CameraInfoFlutterApi.setup(cameraInfoFlutterApi); CameraSelectorFlutterApi.setup(cameraSelectorFlutterApi); ProcessCameraProviderFlutterApi.setup(processCameraProviderFlutterApi); + CameraFlutterApi.setup(cameraFlutterApi); _haveBeenSetUp = true; } } diff --git a/packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart index e1f1e3ca9e9b..63ec03c30083 100644 --- a/packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.3.0 from annotations +// Mocks generated by Mockito 5.3.2 from annotations // in camera_android_camerax/test/camera_info_test.dart. // Do not manually edit this file. @@ -29,6 +29,10 @@ class MockTestCameraInfoHostApi extends _i1.Mock @override int getSensorRotationDegrees(int? identifier) => (super.noSuchMethod( - Invocation.method(#getSensorRotationDegrees, [identifier]), - returnValue: 0) as int); + Invocation.method( + #getSensorRotationDegrees, + [identifier], + ), + returnValue: 0, + ) as int); } diff --git a/packages/camera/camera_android_camerax/test/camera_selector_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera_selector_test.mocks.dart index 456db1eaf822..bb08c82514a5 100644 --- a/packages/camera/camera_android_camerax/test/camera_selector_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/camera_selector_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.3.0 from annotations +// Mocks generated by Mockito 5.3.2 from annotations // in camera_android_camerax/test/camera_selector_test.dart. // Do not manually edit this file. @@ -28,11 +28,33 @@ class MockTestCameraSelectorHostApi extends _i1.Mock } @override - void create(int? identifier, int? lensFacing) => - super.noSuchMethod(Invocation.method(#create, [identifier, lensFacing]), - returnValueForMissingStub: null); + void create( + int? identifier, + int? lensFacing, + ) => + super.noSuchMethod( + Invocation.method( + #create, + [ + identifier, + lensFacing, + ], + ), + returnValueForMissingStub: null, + ); @override - List filter(int? identifier, List? cameraInfoIds) => (super - .noSuchMethod(Invocation.method(#filter, [identifier, cameraInfoIds]), - returnValue: []) as List); + List filter( + int? identifier, + List? cameraInfoIds, + ) => + (super.noSuchMethod( + Invocation.method( + #filter, + [ + identifier, + cameraInfoIds, + ], + ), + returnValue: [], + ) as List); } diff --git a/packages/camera/camera_android_camerax/test/camera_test.dart b/packages/camera/camera_android_camerax/test/camera_test.dart new file mode 100644 index 000000000000..91b57a8f8cfe --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_test.dart @@ -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. + +import 'package:camera_android_camerax/src/camera.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('Camera', () { + tearDown(() => TestProcessCameraProviderHostApi.setup(null)); + + test('flutterApiCreateTest', () { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final CameraFlutterApiImpl flutterApi = + CameraFlutterApiImpl( + instanceManager: instanceManager, + ); + + flutterApi.create(0); + + expect(instanceManager.getInstanceWithWeakReference(0), + isA()); + }); + }); +} \ No newline at end of file diff --git a/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart b/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart index 65e7d00ddaea..d0745c503cad 100644 --- a/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart +++ b/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart @@ -3,8 +3,10 @@ // found in the LICENSE file. import 'package:camera_android_camerax/src/camera_info.dart'; +import 'package:camera_android_camerax/src/camera.dart'; import 'package:camera_android_camerax/src/instance_manager.dart'; import 'package:camera_android_camerax/src/process_camera_provider.dart'; +import 'package:camera_android_camerax/src/use_case.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -78,6 +80,108 @@ void main() { verify(mockApi.getAvailableCameraInfos(0)); }); + test('bindToLifecycleTest', () async { + final MockTestProcessCameraProviderHostApi mockApi = + MockTestProcessCameraProviderHostApi(); + TestProcessCameraProviderHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final ProcessCameraProvider processCameraProvider = + ProcessCameraProvider.detached( + instanceManager: instanceManager, + ); + final CameraSelector fakeCameraSelector = + CameraSelector.detached(instanceManager: instanceManager); + final UseCase fakeUseCase = UseCase.detached(instanceManager: instanceManager); + final Camera camera = Camera.detached(instanceManager: instanceManager); + + instanceManager.addHostCreatedInstance( + processCameraProvider, + 0, + onCopy: (_) => ProcessCameraProvider.detached(), + ); + instanceManager.addHostCreatedInstance( + fakeCameraSelector, + 1, + onCopy: (_) => CameraSelector.detached(), + ); + instanceManager.addHostCreatedInstance( + fakeUseCase, + 2, + onCopy: (_) => UseCase.detached(), + ); + instanceManager.addHostCreatedInstance( + camera, + 3, + onCopy: (_) => Camera.detached(), + ); + + when(mockApi.bindToLifecycle(1, [2])).thenReturn(3); + expect(await processCameraProvider.bindToLifecycle(fakeCameraSelector, [fakeUseCase]), + equals(fakeCamera)); + verify(mockApi.bindToLifecycle(1, [2])); + }); + + test('unbindTest', () async { + final MockTestProcessCameraProviderHostApi mockApi = + MockTestProcessCameraProviderHostApi(); + TestProcessCameraProviderHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final ProcessCameraProvider processCameraProvider = + ProcessCameraProvider.detached( + instanceManager: instanceManager, + ); + final UseCase fakeUseCase = UseCase.detached(instanceManager: instanceManager); + + instanceManager.addHostCreatedInstance( + processCameraProvider, + 0, + onCopy: (_) => ProcessCameraProvider.detached(), + ); + instanceManager.addHostCreatedInstance( + fakeUseCase, + 1, + onCopy: (_) => UseCase.detached(), + ); + + await processCameraProvider.unbind([fakeUseCase]); + verify(mockApi.unbind([1])); + }); + + test('unbindAllTest', () async { + final MockTestProcessCameraProviderHostApi mockApi = + MockTestProcessCameraProviderHostApi(); + TestProcessCameraProviderHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final ProcessCameraProvider processCameraProvider = + ProcessCameraProvider.detached( + instanceManager: instanceManager, + ); + final UseCase fakeUseCase = UseCase.detached(instanceManager: instanceManager); + + instanceManager.addHostCreatedInstance( + processCameraProvider, + 0, + onCopy: (_) => ProcessCameraProvider.detached(), + ); + instanceManager.addHostCreatedInstance( + fakeUseCase, + 1, + onCopy: (_) => UseCase.detached(), + ); + + await processCameraProvider.unbind([fakeUseCase]); + verify(mockApi.unbind([1])); + }); + test('flutterApiCreateTest', () { final InstanceManager instanceManager = InstanceManager( onWeakReferenceRemoved: (_) {}, diff --git a/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart b/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart index 9fcfe690c062..7b0ca76dd1a0 100644 --- a/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.3.0 from annotations +// Mocks generated by Mockito 5.3.2 from annotations // in camera_android_camerax/test/process_camera_provider_test.dart. // Do not manually edit this file. @@ -30,11 +30,59 @@ class MockTestProcessCameraProviderHostApi extends _i1.Mock } @override - _i3.Future getInstance() => - (super.noSuchMethod(Invocation.method(#getInstance, []), - returnValue: _i3.Future.value(0)) as _i3.Future); + _i3.Future getInstance() => (super.noSuchMethod( + Invocation.method( + #getInstance, + [], + ), + returnValue: _i3.Future.value(0), + ) as _i3.Future); @override List getAvailableCameraInfos(int? identifier) => (super.noSuchMethod( - Invocation.method(#getAvailableCameraInfos, [identifier]), - returnValue: []) as List); + Invocation.method( + #getAvailableCameraInfos, + [identifier], + ), + returnValue: [], + ) as List); + @override + int bindToLifecycle( + int? identifier, + int? cameraSelectorIdentifier, + List? useCaseIds, + ) => + (super.noSuchMethod( + Invocation.method( + #bindToLifecycle, + [ + identifier, + cameraSelectorIdentifier, + useCaseIds, + ], + ), + returnValue: 0, + ) as int); + @override + void unbind( + int? identifier, + List? useCaseIds, + ) => + super.noSuchMethod( + Invocation.method( + #unbind, + [ + identifier, + useCaseIds, + ], + ), + returnValueForMissingStub: null, + ); + @override + void unbindAll(int? identifier) => super.noSuchMethod( + Invocation.method( + #unbindAll, + [identifier], + ), + returnValueForMissingStub: null, + ); } From 7c318632386bbe67f4ac88b49ac18dad91ca4375 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 9 Jan 2023 13:20:14 -0800 Subject: [PATCH 03/17] Fix dart tests --- .../camerax/CameraAndroidCameraxPlugin.java | 9 +---- .../test/camera_test.dart | 11 +++---- .../test/process_camera_provider_test.dart | 33 +++++++++++-------- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java index 6f9829d49926..f13bb294b6cc 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -17,7 +17,6 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity private InstanceManager instanceManager; private FlutterPluginBinding pluginBinding; public ProcessCameraProviderHostApiImpl processCameraProviderHostApi; - public SystemServicesHostApiImpl systemServicesHostApi; /** * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. @@ -65,16 +64,10 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { CameraAndroidCameraxPlugin plugin = new CameraAndroidCameraxPlugin(); - plugin.setUp( - pluginBinding.getBinaryMessenger(), - pluginBinding.getApplicationContext(), - pluginBinding.getTextureRegistry()); + plugin.setUp(pluginBinding.getBinaryMessenger(), pluginBinding.getApplicationContext()); plugin.updateContext(pluginBinding.getApplicationContext()); plugin.processCameraProviderHostApi.setLifecycleOwner( (LifecycleOwner) activityPluginBinding.getActivity()); - plugin.systemServicesHostApi.setActivity(activityPluginBinding.getActivity()); - plugin.systemServicesHostApi.setPermissionsRegistry( - activityPluginBinding::addRequestPermissionsResultListener); } @Override diff --git a/packages/camera/camera_android_camerax/test/camera_test.dart b/packages/camera/camera_android_camerax/test/camera_test.dart index 91b57a8f8cfe..c2948282dcf1 100644 --- a/packages/camera/camera_android_camerax/test/camera_test.dart +++ b/packages/camera/camera_android_camerax/test/camera_test.dart @@ -3,27 +3,24 @@ // found in the LICENSE file. import 'package:camera_android_camerax/src/camera.dart'; +import 'package:camera_android_camerax/src/instance_manager.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('Camera', () { - tearDown(() => TestProcessCameraProviderHostApi.setup(null)); - test('flutterApiCreateTest', () { final InstanceManager instanceManager = InstanceManager( onWeakReferenceRemoved: (_) {}, ); - final CameraFlutterApiImpl flutterApi = - CameraFlutterApiImpl( + final CameraFlutterApiImpl flutterApi = CameraFlutterApiImpl( instanceManager: instanceManager, ); flutterApi.create(0); - expect(instanceManager.getInstanceWithWeakReference(0), - isA()); + expect(instanceManager.getInstanceWithWeakReference(0), isA()); }); }); -} \ No newline at end of file +} diff --git a/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart b/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart index d0745c503cad..66f3413e0dbf 100644 --- a/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart +++ b/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:camera_android_camerax/src/camera_info.dart'; +import 'package:camera_android_camerax/src/camera_selector.dart'; import 'package:camera_android_camerax/src/camera.dart'; import 'package:camera_android_camerax/src/instance_manager.dart'; import 'package:camera_android_camerax/src/process_camera_provider.dart'; @@ -93,9 +94,11 @@ void main() { instanceManager: instanceManager, ); final CameraSelector fakeCameraSelector = - CameraSelector.detached(instanceManager: instanceManager); - final UseCase fakeUseCase = UseCase.detached(instanceManager: instanceManager); - final Camera camera = Camera.detached(instanceManager: instanceManager); + CameraSelector.detached(instanceManager: instanceManager); + final UseCase fakeUseCase = + UseCase.detached(instanceManager: instanceManager); + final Camera fakeCamera = + Camera.detached(instanceManager: instanceManager); instanceManager.addHostCreatedInstance( processCameraProvider, @@ -113,15 +116,17 @@ void main() { onCopy: (_) => UseCase.detached(), ); instanceManager.addHostCreatedInstance( - camera, + fakeCamera, 3, onCopy: (_) => Camera.detached(), ); - when(mockApi.bindToLifecycle(1, [2])).thenReturn(3); - expect(await processCameraProvider.bindToLifecycle(fakeCameraSelector, [fakeUseCase]), + when(mockApi.bindToLifecycle(0, 1, [2])).thenReturn(3); + expect( + await processCameraProvider + .bindToLifecycle(fakeCameraSelector, [fakeUseCase]), equals(fakeCamera)); - verify(mockApi.bindToLifecycle(1, [2])); + verify(mockApi.bindToLifecycle(0, 1, [2])); }); test('unbindTest', () async { @@ -136,7 +141,8 @@ void main() { ProcessCameraProvider.detached( instanceManager: instanceManager, ); - final UseCase fakeUseCase = UseCase.detached(instanceManager: instanceManager); + final UseCase fakeUseCase = + UseCase.detached(instanceManager: instanceManager); instanceManager.addHostCreatedInstance( processCameraProvider, @@ -149,8 +155,8 @@ void main() { onCopy: (_) => UseCase.detached(), ); - await processCameraProvider.unbind([fakeUseCase]); - verify(mockApi.unbind([1])); + processCameraProvider.unbind([fakeUseCase]); + verify(mockApi.unbind(0, [1])); }); test('unbindAllTest', () async { @@ -165,7 +171,8 @@ void main() { ProcessCameraProvider.detached( instanceManager: instanceManager, ); - final UseCase fakeUseCase = UseCase.detached(instanceManager: instanceManager); + final UseCase fakeUseCase = + UseCase.detached(instanceManager: instanceManager); instanceManager.addHostCreatedInstance( processCameraProvider, @@ -178,8 +185,8 @@ void main() { onCopy: (_) => UseCase.detached(), ); - await processCameraProvider.unbind([fakeUseCase]); - verify(mockApi.unbind([1])); + processCameraProvider.unbind([fakeUseCase]); + verify(mockApi.unbind(0, [1])); }); test('flutterApiCreateTest', () { From 8855f0096367cafc89bc27c33678c332ef6cf757 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 9 Jan 2023 14:15:56 -0800 Subject: [PATCH 04/17] Add java tests --- .../ProcessCameraProviderHostApiImpl.java | 25 ++++----- .../flutter/plugins/camerax/CameraTest.java | 49 ++++++++++++++++ .../camerax/ProcessCameraProviderTest.java | 56 +++++++++++++++++++ 3 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java index 168442576cbf..4e6bce6a7df6 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java @@ -9,7 +9,6 @@ import androidx.camera.core.Camera; import androidx.camera.core.CameraInfo; import androidx.camera.core.CameraSelector; -import androidx.camera.core.Preview; import androidx.camera.core.UseCase; import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.core.content.ContextCompat; @@ -95,7 +94,8 @@ public List getAvailableCameraInfos(@NonNull Long identifier) { /** * Binds specified {@code UseCase}s to the lifecycle of the {@code LifecycleOwner} that - * corresponds to this instance. + * corresponds to this instance and returns the instance of the {@code Camera} whose lifecycle + * that {@code LifecycleOwner} reflects. */ @Override public Long bindToLifecycle( @@ -111,20 +111,15 @@ public Long bindToLifecycle( useCases[i] = (UseCase) instanceManager.getInstance(((Number) useCaseIds.get(i)).longValue()); } - if (lifecycleOwner != null) { - Camera camera = - processCameraProvider.bindToLifecycle( - (LifecycleOwner) lifecycleOwner, cameraSelector, useCases); + Camera camera = + processCameraProvider.bindToLifecycle( + (LifecycleOwner) lifecycleOwner, cameraSelector, useCases); - final CameraFlutterApiImpl camraFlutterApi = - new CameraFlutterApiImpl(binaryMessenger, instanceManager); - camraFlutterApi.create(camera, result -> {}); + final CameraFlutterApiImpl camraFlutterApi = + new CameraFlutterApiImpl(binaryMessenger, instanceManager); + camraFlutterApi.create(camera, result -> {}); - return instanceManager.getIdentifierForStrongReference(camera); - } else { - // TODO(camsim99): Throw error here? - return null; - } + return instanceManager.getIdentifierForStrongReference(camera); } @Override @@ -133,7 +128,7 @@ public void unbind(@NonNull Long identifier, @NonNull List useCaseIds) { (ProcessCameraProvider) instanceManager.getInstance(identifier); UseCase[] useCases = new UseCase[useCaseIds.size()]; for (int i = 0; i < useCaseIds.size(); i++) { - useCases[i] = (Preview) instanceManager.getInstance(((Number) useCaseIds.get(i)).longValue()); + useCases[i] = (UseCase) instanceManager.getInstance(((Number) useCaseIds.get(i)).longValue()); } processCameraProvider.unbind(useCases); } diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java new file mode 100644 index 000000000000..c74de625a8e4 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java @@ -0,0 +1,49 @@ +// 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 static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import androidx.camera.core.Camera; +import io.flutter.plugin.common.BinaryMessenger; +import java.util.Objects; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class CameraTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public BinaryMessenger mockBinaryMessenger; + @Mock public Camera camera; + + InstanceManager testInstanceManager; + + @Before + public void setUp() { + testInstanceManager = InstanceManager.open(identifier -> {}); + } + + @Test + public void flutterApiCreateTest() { + final CameraFlutterApiImpl spyFlutterApi = + spy(new CameraFlutterApiImpl(mockBinaryMessenger, testInstanceManager)); + + spyFlutterApi.create(camera, reply -> {}); + + final long identifier = + Objects.requireNonNull(testInstanceManager.getIdentifierForStrongReference(camera)); + verify(spyFlutterApi).create(eq(identifier), any()); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java index 5008e4ef34b0..47b4ed6ad26d 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java @@ -13,8 +13,12 @@ import static org.mockito.Mockito.when; import android.content.Context; +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.lifecycle.LifecycleOwner; import androidx.test.core.app.ApplicationProvider; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -99,6 +103,58 @@ public void getAvailableCameraInfosTest() { verify(processCameraProvider).getAvailableCameraInfos(); } + @Test + public void bindToLifecycleTest() { + final ProcessCameraProviderHostApiImpl processCameraProviderHostApi = + new ProcessCameraProviderHostApiImpl(mockBinaryMessenger, testInstanceManager, context); + final Camera mockCamera = mock(Camera.class); + final CameraSelector mockCameraSelector = mock(CameraSelector.class); + final UseCase mockUseCase = mock(UseCase.class); + UseCase[] mockUseCases = new UseCase[] {mockUseCase}; + + LifecycleOwner mockLifecycleOwner = mock(LifecycleOwner.class); + processCameraProviderHostApi.setLifecycleOwner(mockLifecycleOwner); + + testInstanceManager.addDartCreatedInstance(processCameraProvider, 0); + testInstanceManager.addDartCreatedInstance(mockCameraSelector, 1); + testInstanceManager.addDartCreatedInstance(mockUseCase, 2); + testInstanceManager.addDartCreatedInstance(mockCamera, 3); + + when(processCameraProvider.bindToLifecycle( + mockLifecycleOwner, mockCameraSelector, mockUseCases)) + .thenReturn(mockCamera); + + assertEquals( + processCameraProviderHostApi.bindToLifecycle(0L, 1L, Arrays.asList(2L)), Long.valueOf(3)); + verify(processCameraProvider) + .bindToLifecycle(mockLifecycleOwner, mockCameraSelector, mockUseCases); + } + + @Test + public void unbindTest() { + final ProcessCameraProviderHostApiImpl processCameraProviderHostApi = + new ProcessCameraProviderHostApiImpl(mockBinaryMessenger, testInstanceManager, context); + final UseCase mockUseCase = mock(UseCase.class); + UseCase[] mockUseCases = new UseCase[] {mockUseCase}; + + testInstanceManager.addDartCreatedInstance(processCameraProvider, 0); + testInstanceManager.addDartCreatedInstance(mockUseCase, 1); + + processCameraProviderHostApi.unbind(0L, Arrays.asList(1L)); + verify(processCameraProvider).unbind(mockUseCases); + } + + @Test + public void unbindAllTest() { + final ProcessCameraProviderHostApiImpl processCameraProviderHostApi = + new ProcessCameraProviderHostApiImpl(mockBinaryMessenger, testInstanceManager, context); + + testInstanceManager.addDartCreatedInstance(processCameraProvider, 0); + + processCameraProviderHostApi.unbindAll(0L); + verify(processCameraProvider).unbindAll(); + } + @Test public void flutterApiCreateTest() { final ProcessCameraProviderFlutterApiImpl spyFlutterApi = From 147c4f6c63d975e8943bc7d28b10c473336bce97 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 9 Jan 2023 14:25:52 -0800 Subject: [PATCH 05/17] Add me as owner and changelog change --- CODEOWNERS | 1 + packages/camera/camera_android_camerax/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index c93c45c9c6b1..9436aba60f1c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index ce2fb9046c69..cbbbaf6d61a0 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -4,3 +4,4 @@ * Adds CameraInfo class and removes unnecessary code from plugin. * Adds CameraSelector class. * Adds ProcessCameraProvider class. +* Adds Camera and UseCase classes, along with methods for binding UseCases to a lifecycle with the ProcessCameraProvider. From 53a352195816b0fbf9932c83963e26a7e6ebf925 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 9 Jan 2023 14:50:44 -0800 Subject: [PATCH 06/17] Fix analyzer --- .../camera/camera_android_camerax/lib/src/camera.dart | 11 ++++++----- .../lib/src/process_camera_provider.dart | 10 ++++++---- .../camera_android_camerax/lib/src/use_case.dart | 9 ++------- .../test/process_camera_provider_test.dart | 2 +- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/camera/camera_android_camerax/lib/src/camera.dart b/packages/camera/camera_android_camerax/lib/src/camera.dart index 8dd7e6697ae8..0a3820cd0248 100644 --- a/packages/camera/camera_android_camerax/lib/src/camera.dart +++ b/packages/camera/camera_android_camerax/lib/src/camera.dart @@ -9,13 +9,14 @@ import 'camerax_library.pigeon.dart'; import 'instance_manager.dart'; import 'java_object.dart'; +/// The interface used to control the flow of data of use cases, control the +/// camera, and publich the state of the camera. +/// +/// See https://developer.android.com/reference/androidx/camera/core/Camera. class Camera extends JavaObject { /// Constructs a [Camera] that is not automatically attached to a native object. - Camera.detached( - {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) - : super.detached( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager) { + Camera.detached({super.binaryMessenger, super.instanceManager}) + : super.detached() { AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); } } diff --git a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart index 5c31e89dc038..65bea38681c4 100644 --- a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart +++ b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart @@ -124,8 +124,9 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { return ProcessCameraProvider.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager); }); - final List useCaseIds = (useCases.map( - (UseCase useCase) => instanceManager.getIdentifier(useCase)!)).toList(); + final List useCaseIds = useCases + .map((UseCase useCase) => instanceManager.getIdentifier(useCase)!) + .toList(); final int cameraIdentifier = await bindToLifecycle( identifier, @@ -148,8 +149,9 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { return ProcessCameraProvider.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager); }); - final List useCaseIds = (useCases.map( - (UseCase useCase) => instanceManager.getIdentifier(useCase)!)).toList(); + final List useCaseIds = useCases + .map((UseCase useCase) => instanceManager.getIdentifier(useCase)!) + .toList(); unbind(identifier, useCaseIds); } diff --git a/packages/camera/camera_android_camerax/lib/src/use_case.dart b/packages/camera/camera_android_camerax/lib/src/use_case.dart index 8bf5aad1e910..f8910d9c5347 100644 --- a/packages/camera/camera_android_camerax/lib/src/use_case.dart +++ b/packages/camera/camera_android_camerax/lib/src/use_case.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/services.dart' show BinaryMessenger; - -import 'instance_manager.dart'; import 'java_object.dart'; /// An object representing the different functionalitites of the camera. @@ -12,8 +9,6 @@ import 'java_object.dart'; /// See https://developer.android.com/reference/androidx/camera/core/UseCase. class UseCase extends JavaObject { /// Creates a detached [UseCase]. - UseCase.detached( - {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) - : super.detached( - binaryMessenger: binaryMessenger, instanceManager: instanceManager); + UseCase.detached({super.binaryMessenger, super.instanceManager}) + : super.detached(); } diff --git a/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart b/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart index 66f3413e0dbf..c4f56f677a58 100644 --- a/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart +++ b/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:camera_android_camerax/src/camera.dart'; import 'package:camera_android_camerax/src/camera_info.dart'; import 'package:camera_android_camerax/src/camera_selector.dart'; -import 'package:camera_android_camerax/src/camera.dart'; import 'package:camera_android_camerax/src/instance_manager.dart'; import 'package:camera_android_camerax/src/process_camera_provider.dart'; import 'package:camera_android_camerax/src/use_case.dart'; From 66265ef3a0d290d2dd935329a1a1693978a96ee2 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 9 Jan 2023 15:12:16 -0800 Subject: [PATCH 07/17] Add instance manager fix --- .../camera/camera_android_camerax/lib/src/instance_manager.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/lib/src/instance_manager.dart b/packages/camera/camera_android_camerax/lib/src/instance_manager.dart index dd48610c8b56..4e2b615618be 100644 --- a/packages/camera/camera_android_camerax/lib/src/instance_manager.dart +++ b/packages/camera/camera_android_camerax/lib/src/instance_manager.dart @@ -161,7 +161,6 @@ class InstanceManager { int identifier, { required T Function(T original) onCopy, }) { - assert(!containsIdentifier(identifier)); assert(getIdentifier(instance) == null); assert(identifier >= 0); _addInstanceWithIdentifier(instance, identifier, onCopy: onCopy); From af382402b95993974e270fa6f629021232448fb8 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 9 Jan 2023 15:18:55 -0800 Subject: [PATCH 08/17] Update comment --- .../camera/camera_android_camerax/lib/src/instance_manager.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/lib/src/instance_manager.dart b/packages/camera/camera_android_camerax/lib/src/instance_manager.dart index 4e2b615618be..0eddd64cbc13 100644 --- a/packages/camera/camera_android_camerax/lib/src/instance_manager.dart +++ b/packages/camera/camera_android_camerax/lib/src/instance_manager.dart @@ -152,7 +152,7 @@ class InstanceManager { /// In other words, the host platform wants to add a new instance that /// represents an object on the host platform. Stored with [identifier]. /// - /// Throws assertion error if the instance or its identifier has already been + /// Throws assertion error if the instance has already been /// added. /// /// Returns unique identifier of the [instance] added. From 55c82d3317fc91214090c178ec0634a0181cbfcd Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 11 Jan 2023 15:58:50 -0800 Subject: [PATCH 09/17] Undo instance manager changes --- .../camera_android_camerax/lib/src/instance_manager.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/lib/src/instance_manager.dart b/packages/camera/camera_android_camerax/lib/src/instance_manager.dart index 0eddd64cbc13..dd48610c8b56 100644 --- a/packages/camera/camera_android_camerax/lib/src/instance_manager.dart +++ b/packages/camera/camera_android_camerax/lib/src/instance_manager.dart @@ -152,7 +152,7 @@ class InstanceManager { /// In other words, the host platform wants to add a new instance that /// represents an object on the host platform. Stored with [identifier]. /// - /// Throws assertion error if the instance has already been + /// Throws assertion error if the instance or its identifier has already been /// added. /// /// Returns unique identifier of the [instance] added. @@ -161,6 +161,7 @@ class InstanceManager { int identifier, { required T Function(T original) onCopy, }) { + assert(!containsIdentifier(identifier)); assert(getIdentifier(instance) == null); assert(identifier >= 0); _addInstanceWithIdentifier(instance, identifier, onCopy: onCopy); From fa70664037a50572a37e0f5141c05a8c06486285 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 11 Jan 2023 17:49:43 -0800 Subject: [PATCH 10/17] Formatting --- .../camera_android_camerax/CHANGELOG.md | 2 +- .../lib/src/process_camera_provider.dart | 32 ++++++------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 6e5211841c68..389fc31e26e7 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -4,5 +4,5 @@ * Adds CameraInfo class and removes unnecessary code from plugin. * Adds CameraSelector class. * Adds ProcessCameraProvider class. -* Adds Camera and UseCase classes, along with methods for binding UseCases to a lifecycle with the ProcessCameraProvider. * 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. diff --git a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart index 65bea38681c4..40e80f5a8520 100644 --- a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart +++ b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart @@ -91,16 +91,20 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { as ProcessCameraProvider; } - /// Retrives the list of CameraInfos corresponding to the available cameras. - Future> getAvailableCameraInfosFromInstances( - ProcessCameraProvider instance) async { + int getProcessCameraProviderIdentifier(ProcessCameraProvider instance) { int? identifier = instanceManager.getIdentifier(instance); identifier ??= instanceManager.addDartCreatedInstance(instance, onCopy: (ProcessCameraProvider original) { return ProcessCameraProvider.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager); }); + return identifier; + } + /// Retrives the list of CameraInfos corresponding to the available cameras. + Future> getAvailableCameraInfosFromInstances( + ProcessCameraProvider instance) async { + int identifier = getProcessCameraProviderIdentifier(instance); final List cameraInfos = await getAvailableCameraInfos(identifier); return cameraInfos .map((int? id) => @@ -118,12 +122,7 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { CameraSelector cameraSelector, List useCases, ) async { - int? identifier = instanceManager.getIdentifier(instance); - identifier ??= instanceManager.addDartCreatedInstance(instance, - onCopy: (ProcessCameraProvider original) { - return ProcessCameraProvider.detached( - binaryMessenger: binaryMessenger, instanceManager: instanceManager); - }); + int identifier = getProcessCameraProviderIdentifier(instance); final List useCaseIds = useCases .map((UseCase useCase) => instanceManager.getIdentifier(useCase)!) .toList(); @@ -143,12 +142,7 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { ProcessCameraProvider instance, List useCases, ) { - int? identifier = instanceManager.getIdentifier(instance); - identifier ??= instanceManager.addDartCreatedInstance(instance, - onCopy: (ProcessCameraProvider original) { - return ProcessCameraProvider.detached( - binaryMessenger: binaryMessenger, instanceManager: instanceManager); - }); + int identifier = getProcessCameraProviderIdentifier(instance); final List useCaseIds = useCases .map((UseCase useCase) => instanceManager.getIdentifier(useCase)!) .toList(); @@ -159,13 +153,7 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { /// Unbinds all previously bound [UseCase]s from the lifecycle of the camera /// which the provided [ProcessCameraProvider] instance tracks. void unbindAllFromInstances(ProcessCameraProvider instance) { - int? identifier = instanceManager.getIdentifier(instance); - identifier ??= instanceManager.addDartCreatedInstance(instance, - onCopy: (ProcessCameraProvider original) { - return ProcessCameraProvider.detached( - binaryMessenger: binaryMessenger, instanceManager: instanceManager); - }); - + int identifier = getProcessCameraProviderIdentifier(instance); unbindAll(identifier); } } From b4f890c3cfa2fce320a12acea885d53302fdbb14 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 12 Jan 2023 09:01:54 -0800 Subject: [PATCH 11/17] Fix analyze --- .../lib/src/process_camera_provider.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart index 40e80f5a8520..96d4094ebcf9 100644 --- a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart +++ b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart @@ -91,6 +91,8 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { as ProcessCameraProvider; } + /// Gets identifier that the [instanceManager] has set for + /// the [ProcessCameraProvider] instance. int getProcessCameraProviderIdentifier(ProcessCameraProvider instance) { int? identifier = instanceManager.getIdentifier(instance); identifier ??= instanceManager.addDartCreatedInstance(instance, @@ -104,7 +106,7 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { /// Retrives the list of CameraInfos corresponding to the available cameras. Future> getAvailableCameraInfosFromInstances( ProcessCameraProvider instance) async { - int identifier = getProcessCameraProviderIdentifier(instance); + final int identifier = getProcessCameraProviderIdentifier(instance); final List cameraInfos = await getAvailableCameraInfos(identifier); return cameraInfos .map((int? id) => @@ -122,7 +124,7 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { CameraSelector cameraSelector, List useCases, ) async { - int identifier = getProcessCameraProviderIdentifier(instance); + final int identifier = getProcessCameraProviderIdentifier(instance); final List useCaseIds = useCases .map((UseCase useCase) => instanceManager.getIdentifier(useCase)!) .toList(); @@ -142,7 +144,7 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { ProcessCameraProvider instance, List useCases, ) { - int identifier = getProcessCameraProviderIdentifier(instance); + final int identifier = getProcessCameraProviderIdentifier(instance); final List useCaseIds = useCases .map((UseCase useCase) => instanceManager.getIdentifier(useCase)!) .toList(); @@ -153,7 +155,7 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { /// Unbinds all previously bound [UseCase]s from the lifecycle of the camera /// which the provided [ProcessCameraProvider] instance tracks. void unbindAllFromInstances(ProcessCameraProvider instance) { - int identifier = getProcessCameraProviderIdentifier(instance); + final int identifier = getProcessCameraProviderIdentifier(instance); unbindAll(identifier); } } From 452b5c0a402ff43d3a16c805b1c91f016c5de5fe Mon Sep 17 00:00:00 2001 From: camsim99 Date: Fri, 20 Jan 2023 13:57:52 -0800 Subject: [PATCH 12/17] Address review --- .../camerax/CameraAndroidCameraxPlugin.java | 7 +++--- .../camerax/CameraInfoHostApiImpl.java | 4 +++- .../camerax/CameraSelectorHostApiImpl.java | 7 ++++-- .../ProcessCameraProviderHostApiImpl.java | 22 +++++++++++++------ .../flutter/plugins/camerax/CameraTest.java | 8 ++++--- .../lib/src/process_camera_provider.dart | 8 +++---- 6 files changed, 34 insertions(+), 22 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java index f13bb294b6cc..86575244ff4e 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -63,10 +63,9 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { - CameraAndroidCameraxPlugin plugin = new CameraAndroidCameraxPlugin(); - plugin.setUp(pluginBinding.getBinaryMessenger(), pluginBinding.getApplicationContext()); - plugin.updateContext(pluginBinding.getApplicationContext()); - plugin.processCameraProviderHostApi.setLifecycleOwner( + this.setUp(pluginBinding.getBinaryMessenger(), pluginBinding.getApplicationContext()); + this.updateContext(pluginBinding.getApplicationContext()); + this.processCameraProviderHostApi.setLifecycleOwner( (LifecycleOwner) activityPluginBinding.getActivity()); } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java index 7daba0d38d6a..d960b7fff70a 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java @@ -7,6 +7,7 @@ import androidx.annotation.NonNull; import androidx.camera.core.CameraInfo; import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraInfoHostApi; +import java.util.Objects; public class CameraInfoHostApiImpl implements CameraInfoHostApi { private final InstanceManager instanceManager; @@ -17,7 +18,8 @@ public CameraInfoHostApiImpl(InstanceManager instanceManager) { @Override public Long getSensorRotationDegrees(@NonNull Long identifier) { - CameraInfo cameraInfo = (CameraInfo) instanceManager.getInstance(identifier); + CameraInfo cameraInfo = + (CameraInfo) Objects.requireNonNull(instanceManager.getInstance(identifier)); return Long.valueOf(cameraInfo.getSensorRotationDegrees()); } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorHostApiImpl.java index 9c559a72e63c..87c69dea9e1c 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorHostApiImpl.java @@ -12,6 +12,7 @@ import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraSelectorHostApi; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class CameraSelectorHostApiImpl implements CameraSelectorHostApi { private final BinaryMessenger binaryMessenger; @@ -41,13 +42,15 @@ public void create(@NonNull Long identifier, Long lensFacing) { @Override public List filter(@NonNull Long identifier, @NonNull List cameraInfoIds) { - CameraSelector cameraSelector = (CameraSelector) instanceManager.getInstance(identifier); + CameraSelector cameraSelector = + (CameraSelector) Objects.requireNonNull(instanceManager.getInstance(identifier)); List cameraInfosForFilter = new ArrayList(); for (Number cameraInfoAsNumber : cameraInfoIds) { Long cameraInfoId = cameraInfoAsNumber.longValue(); - CameraInfo cameraInfo = (CameraInfo) instanceManager.getInstance(cameraInfoId); + CameraInfo cameraInfo = + (CameraInfo) Objects.requireNonNull(instanceManager.getInstance(cameraInfoId)); cameraInfosForFilter.add(cameraInfo); } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java index 4e6bce6a7df6..c03eefc9b71b 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java @@ -18,6 +18,7 @@ import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderHostApi; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class ProcessCameraProviderHostApiImpl implements ProcessCameraProviderHostApi { private final BinaryMessenger binaryMessenger; @@ -78,7 +79,7 @@ public void getInstance(GeneratedCameraXLibrary.Result result) { @Override public List getAvailableCameraInfos(@NonNull Long identifier) { ProcessCameraProvider processCameraProvider = - (ProcessCameraProvider) instanceManager.getInstance(identifier); + (ProcessCameraProvider) Objects.requireNonNull(instanceManager.getInstance(identifier)); List availableCameras = processCameraProvider.getAvailableCameraInfos(); List availableCamerasIds = new ArrayList(); @@ -103,12 +104,16 @@ public Long bindToLifecycle( @NonNull Long cameraSelectorIdentifier, @NonNull List useCaseIds) { ProcessCameraProvider processCameraProvider = - (ProcessCameraProvider) instanceManager.getInstance(identifier); + (ProcessCameraProvider) Objects.requireNonNull(instanceManager.getInstance(identifier)); CameraSelector cameraSelector = - (CameraSelector) instanceManager.getInstance(cameraSelectorIdentifier); + (CameraSelector) + Objects.requireNonNull(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()); + useCases[i] = + (UseCase) + Objects.requireNonNull( + instanceManager.getInstance(((Number) useCaseIds.get(i)).longValue())); } Camera camera = @@ -125,10 +130,13 @@ public Long bindToLifecycle( @Override public void unbind(@NonNull Long identifier, @NonNull List useCaseIds) { ProcessCameraProvider processCameraProvider = - (ProcessCameraProvider) instanceManager.getInstance(identifier); + (ProcessCameraProvider) Objects.requireNonNull(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()); + useCases[i] = + (UseCase) + Objects.requireNonNull( + instanceManager.getInstance(((Number) useCaseIds.get(i)).longValue())); } processCameraProvider.unbind(useCases); } @@ -136,7 +144,7 @@ public void unbind(@NonNull Long identifier, @NonNull List useCaseIds) { @Override public void unbindAll(@NonNull Long identifier) { ProcessCameraProvider processCameraProvider = - (ProcessCameraProvider) instanceManager.getInstance(identifier); + (ProcessCameraProvider) Objects.requireNonNull(instanceManager.getInstance(identifier)); processCameraProvider.unbindAll(); } } diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java index c74de625a8e4..8c7a093711f6 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java @@ -15,13 +15,10 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import org.robolectric.RobolectricTestRunner; -@RunWith(RobolectricTestRunner.class) public class CameraTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @@ -35,6 +32,11 @@ public void setUp() { testInstanceManager = InstanceManager.open(identifier -> {}); } + @After + public void tearDown() { + testInstanceManager.close(); + } + @Test public void flutterApiCreateTest() { final CameraFlutterApiImpl spyFlutterApi = diff --git a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart index 96d4094ebcf9..cade71f90897 100644 --- a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart +++ b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart @@ -95,11 +95,9 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { /// the [ProcessCameraProvider] instance. int getProcessCameraProviderIdentifier(ProcessCameraProvider instance) { int? identifier = instanceManager.getIdentifier(instance); - identifier ??= instanceManager.addDartCreatedInstance(instance, - onCopy: (ProcessCameraProvider original) { - return ProcessCameraProvider.detached( - binaryMessenger: binaryMessenger, instanceManager: instanceManager); - }); + + assert(identifier != null, + 'No ProcessCameraProvider has the identifer of that requested.'); return identifier; } From 7dae91425b1afbb2b803187f5553d38f2b612033 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 23 Jan 2023 13:11:25 -0800 Subject: [PATCH 13/17] Fix analyze --- .../lib/src/process_camera_provider.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart index cade71f90897..5d5715af18d9 100644 --- a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart +++ b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart @@ -94,11 +94,11 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { /// Gets identifier that the [instanceManager] has set for /// the [ProcessCameraProvider] instance. int getProcessCameraProviderIdentifier(ProcessCameraProvider instance) { - int? identifier = instanceManager.getIdentifier(instance); + final int? identifier = instanceManager.getIdentifier(instance); assert(identifier != null, 'No ProcessCameraProvider has the identifer of that requested.'); - return identifier; + return identifier!; } /// Retrives the list of CameraInfos corresponding to the available cameras. From d08702a229eee3973eab3911a70849b5f3e37c94 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 23 Jan 2023 13:18:51 -0800 Subject: [PATCH 14/17] Add import --- .../src/test/java/io/flutter/plugins/camerax/CameraTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java index 8c7a093711f6..e2135b3945b0 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java @@ -12,6 +12,7 @@ import androidx.camera.core.Camera; import io.flutter.plugin.common.BinaryMessenger; import java.util.Objects; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; From 33f26126c2502b460b6e3dc515c5301d290a20bd Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 23 Jan 2023 14:43:23 -0800 Subject: [PATCH 15/17] Fix assertion error --- .../camera_android_camerax/lib/src/process_camera_provider.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart index 5d5715af18d9..30c8162654ad 100644 --- a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart +++ b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart @@ -97,7 +97,7 @@ class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { final int? identifier = instanceManager.getIdentifier(instance); assert(identifier != null, - 'No ProcessCameraProvider has the identifer of that requested.'); + 'No ProcessCameraProvider has the identifer of that which was requested.'); return identifier!; } From b7b4b98f14dde1330f5974f5ffa51bf2e9365884 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 25 Jan 2023 14:08:24 -0800 Subject: [PATCH 16/17] Remove unecessary this keywrod --- .../flutter/plugins/camerax/CameraAndroidCameraxPlugin.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java index 86575244ff4e..7ee7263f7779 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -63,9 +63,9 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { - this.setUp(pluginBinding.getBinaryMessenger(), pluginBinding.getApplicationContext()); - this.updateContext(pluginBinding.getApplicationContext()); - this.processCameraProviderHostApi.setLifecycleOwner( + setUp(pluginBinding.getBinaryMessenger(), pluginBinding.getApplicationContext()); + updateContext(pluginBinding.getApplicationContext()); + processCameraProviderHostApi.setLifecycleOwner( (LifecycleOwner) activityPluginBinding.getActivity()); } From fcad911e2d48933221fc5cdea45620ddd9509026 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Wed, 25 Jan 2023 14:08:57 -0800 Subject: [PATCH 17/17] Update packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java Co-authored-by: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> --- .../plugins/camerax/ProcessCameraProviderHostApiImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java index c03eefc9b71b..f82f18f054c9 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java @@ -120,9 +120,9 @@ public Long bindToLifecycle( processCameraProvider.bindToLifecycle( (LifecycleOwner) lifecycleOwner, cameraSelector, useCases); - final CameraFlutterApiImpl camraFlutterApi = + final CameraFlutterApiImpl cameraFlutterApi = new CameraFlutterApiImpl(binaryMessenger, instanceManager); - camraFlutterApi.create(camera, result -> {}); + cameraFlutterApi.create(camera, result -> {}); return instanceManager.getIdentifierForStrongReference(camera); }