diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 6db99566c86d..995e91706883 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.0+19 + +* Implements torch flash mode. + ## 0.5.0+18 * Implements `startVideoCapturing`. diff --git a/packages/camera/camera_android_camerax/README.md b/packages/camera/camera_android_camerax/README.md index 63c0bf5bfc2b..a9a3ddc305f2 100644 --- a/packages/camera/camera_android_camerax/README.md +++ b/packages/camera/camera_android_camerax/README.md @@ -35,10 +35,6 @@ and thus, the plugin will fall back to 480p if configured with a `lockCaptureOrientation` & `unLockCaptureOrientation` are unimplemented. -### Torch mode \[[Issue #120715][120715]\] - -Calling `setFlashMode` with mode `FlashMode.torch` currently does nothing. - ### Exposure mode, point, & offset configuration \[[Issue #120468][120468]\] `setExposureMode`, `setExposurePoint`, & `setExposureOffset` are unimplemented. 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 d2a9c277a37b..eeb3c02dd153 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 @@ -25,6 +25,7 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity private VideoCaptureHostApiImpl videoCaptureHostApiImpl; private ImageAnalysisHostApiImpl imageAnalysisHostApiImpl; private ImageCaptureHostApiImpl imageCaptureHostApiImpl; + private CameraControlHostApiImpl cameraControlHostApiImpl; public @Nullable SystemServicesHostApiImpl systemServicesHostApiImpl; @VisibleForTesting @@ -81,7 +82,8 @@ public void setUp( GeneratedCameraXLibrary.LiveDataHostApi.setup(binaryMessenger, liveDataHostApiImpl); GeneratedCameraXLibrary.ObserverHostApi.setup( binaryMessenger, new ObserverHostApiImpl(binaryMessenger, instanceManager)); - imageAnalysisHostApiImpl = new ImageAnalysisHostApiImpl(binaryMessenger, instanceManager); + imageAnalysisHostApiImpl = + new ImageAnalysisHostApiImpl(binaryMessenger, instanceManager, context); GeneratedCameraXLibrary.ImageAnalysisHostApi.setup(binaryMessenger, imageAnalysisHostApiImpl); GeneratedCameraXLibrary.AnalyzerHostApi.setup( binaryMessenger, new AnalyzerHostApiImpl(binaryMessenger, instanceManager)); @@ -107,6 +109,8 @@ public void setUp( binaryMessenger, new FallbackStrategyHostApiImpl(instanceManager)); GeneratedCameraXLibrary.QualitySelectorHostApi.setup( binaryMessenger, new QualitySelectorHostApiImpl(instanceManager)); + cameraControlHostApiImpl = new CameraControlHostApiImpl(instanceManager, context); + GeneratedCameraXLibrary.CameraControlHostApi.setup(binaryMessenger, cameraControlHostApiImpl); } @Override @@ -128,7 +132,6 @@ public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBi Activity activity = activityPluginBinding.getActivity(); setUp(pluginBinding.getBinaryMessenger(), activity, pluginBinding.getTextureRegistry()); - updateContext(activity); if (activity instanceof LifecycleOwner) { processCameraProviderHostApiImpl.setLifecycleOwner((LifecycleOwner) activity); @@ -183,5 +186,8 @@ public void updateContext(@NonNull Context context) { if (imageAnalysisHostApiImpl != null) { imageAnalysisHostApiImpl.setContext(context); } + if (cameraControlHostApiImpl != null) { + cameraControlHostApiImpl.setContext(context); + } } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlFlutterApiImpl.java new file mode 100644 index 000000000000..1b616180ec73 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlFlutterApiImpl.java @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camerax; + +import androidx.annotation.NonNull; +import androidx.camera.core.CameraControl; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraControlFlutterApi; + +public class CameraControlFlutterApiImpl extends CameraControlFlutterApi { + private final @NonNull InstanceManager instanceManager; + + public CameraControlFlutterApiImpl( + @NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) { + super(binaryMessenger); + this.instanceManager = instanceManager; + } + + /** + * Creates a {@link CameraControl} instance in Dart. {@code reply} is not used so it can be empty. + */ + void create(CameraControl cameraControl, Reply reply) { + if (!instanceManager.containsInstance(cameraControl)) { + create(instanceManager.addHostCreatedInstance(cameraControl), reply); + } + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlHostApiImpl.java new file mode 100644 index 000000000000..8241fdaad9a3 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlHostApiImpl.java @@ -0,0 +1,101 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.camerax; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.camera.core.CameraControl; +import androidx.core.content.ContextCompat; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraControlHostApi; +import java.util.Objects; + +/** + * Host API implementation for {@link CameraControl}. + * + *

This class handles instantiating and adding native object instances that are attached to a + * Dart instance or handle method calls on the associated native class or an instance of the class. + */ +public class CameraControlHostApiImpl implements CameraControlHostApi { + private final InstanceManager instanceManager; + private final CameraControlProxy proxy; + + /** Proxy for constructors and static method of {@link CameraControl}. */ + @VisibleForTesting + public static class CameraControlProxy { + Context context; + + /** Enables or disables the torch of the specified {@link CameraControl} instance. */ + @NonNull + public void enableTorch( + @NonNull CameraControl cameraControl, + @NonNull Boolean torch, + @NonNull GeneratedCameraXLibrary.Result result) { + ListenableFuture enableTorchFuture = cameraControl.enableTorch(torch); + + Futures.addCallback( + enableTorchFuture, + new FutureCallback() { + public void onSuccess(Void voidResult) { + result.success(null); + } + + public void onFailure(Throwable t) { + result.error(t); + } + }, + ContextCompat.getMainExecutor(context)); + } + } + + /** + * Constructs an {@link CameraControlHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with attached Dart objects + */ + public CameraControlHostApiImpl( + @NonNull InstanceManager instanceManager, @NonNull Context context) { + this(instanceManager, new CameraControlProxy(), context); + } + + /** + * Constructs an {@link CameraControlHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with attached Dart objects + * @param proxy proxy for constructors and static method of {@link CameraControl} + * @param context {@link Context} used to retrieve {@code Executor} used to enable torch mode + */ + @VisibleForTesting + CameraControlHostApiImpl( + @NonNull InstanceManager instanceManager, + @NonNull CameraControlProxy proxy, + @NonNull Context context) { + this.instanceManager = instanceManager; + this.proxy = proxy; + proxy.context = context; + } + + /** + * Sets the context that the {@code ProcessCameraProvider} will use to enable/disable torch mode. + * + *

If using the camera plugin in an add-to-app context, ensure that a new instance of the + * {@code CameraControl} is fetched via {@code #enableTorch} anytime the context changes. + */ + public void setContext(@NonNull Context context) { + this.proxy.context = context; + } + + @Override + public void enableTorch( + @NonNull Long identifier, + @NonNull Boolean torch, + @NonNull GeneratedCameraXLibrary.Result result) { + proxy.enableTorch( + Objects.requireNonNull(instanceManager.getInstance(identifier)), torch, result); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraHostApiImpl.java index 98f5f74b959d..603cc62edf83 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraHostApiImpl.java @@ -6,6 +6,7 @@ import androidx.annotation.NonNull; import androidx.camera.core.Camera; +import androidx.camera.core.CameraControl; import androidx.camera.core.CameraInfo; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraHostApi; @@ -28,14 +29,33 @@ public CameraHostApiImpl( @Override @NonNull public Long getCameraInfo(@NonNull Long identifier) { - Camera camera = (Camera) Objects.requireNonNull(instanceManager.getInstance(identifier)); + Camera camera = getCameraInstance(identifier); CameraInfo cameraInfo = camera.getCameraInfo(); - if (!instanceManager.containsInstance(cameraInfo)) { - CameraInfoFlutterApiImpl cameraInfoFlutterApiImpl = - new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager); - cameraInfoFlutterApiImpl.create(cameraInfo, reply -> {}); - } + CameraInfoFlutterApiImpl cameraInfoFlutterApiImpl = + new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager); + cameraInfoFlutterApiImpl.create(cameraInfo, reply -> {}); return instanceManager.getIdentifierForStrongReference(cameraInfo); } + + /** + * Retrieves the {@link CameraControl} instance that provides access to asynchronous operations + * like zoom and focus & metering on the {@link Camera} instance with the specified identifier. + */ + @Override + @NonNull + public Long getCameraControl(@NonNull Long identifier) { + Camera camera = getCameraInstance(identifier); + CameraControl cameraControl = camera.getCameraControl(); + + CameraControlFlutterApiImpl cameraControlFlutterApiImpl = + new CameraControlFlutterApiImpl(binaryMessenger, instanceManager); + cameraControlFlutterApiImpl.create(cameraControl, reply -> {}); + return instanceManager.getIdentifierForStrongReference(cameraControl); + } + + /** Retrieives the {@link Camera} instance associated with the specified {@code identifier}. */ + private Camera getCameraInstance(@NonNull Long identifier) { + return (Camera) Objects.requireNonNull(instanceManager.getInstance(identifier)); + } } 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 fe9a935989fe..224ba11e0385 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 @@ -1136,6 +1136,9 @@ public interface CameraHostApi { @NonNull Long getCameraInfo(@NonNull Long identifier); + @NonNull + Long getCameraControl(@NonNull Long identifier); + /** The codec used by CameraHostApi. */ static @NonNull MessageCodec getCodec() { return new StandardMessageCodec(); @@ -1166,6 +1169,31 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable CameraHost channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraHostApi.getCameraControl", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + try { + Long output = + api.getCameraControl( + (identifierArg == null) ? null : identifierArg.longValue()); + wrapped.add(0, output); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } } } /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ @@ -3203,4 +3231,82 @@ static void setup( } } } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface CameraControlHostApi { + + void enableTorch( + @NonNull Long identifier, @NonNull Boolean torch, @NonNull Result result); + + /** The codec used by CameraControlHostApi. */ + static @NonNull MessageCodec getCodec() { + return new StandardMessageCodec(); + } + /** + * Sets up an instance of `CameraControlHostApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup( + @NonNull BinaryMessenger binaryMessenger, @Nullable CameraControlHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraControlHostApi.enableTorch", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + Boolean torchArg = (Boolean) args.get(1); + Result resultCallback = + new Result() { + public void success(Void result) { + wrapped.add(0, null); + reply.reply(wrapped); + } + + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; + + api.enableTorch( + (identifierArg == null) ? null : identifierArg.longValue(), + torchArg, + resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ + public static class CameraControlFlutterApi { + private final @NonNull BinaryMessenger binaryMessenger; + + public CameraControlFlutterApi(@NonNull BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + /** Public interface for sending reply. */ + @SuppressWarnings("UnknownNullness") + public interface Reply { + void reply(T reply); + } + /** The codec used by CameraControlFlutterApi. */ + static @NonNull MessageCodec getCodec() { + return new StandardMessageCodec(); + } + + public void create(@NonNull Long identifierArg, @NonNull Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraControlFlutterApi.create", getCodec()); + channel.send( + new ArrayList(Collections.singletonList(identifierArg)), + channelReply -> callback.reply(null)); + } + } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ImageAnalysisHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ImageAnalysisHostApiImpl.java index 58491477ae15..00bceb76a7e5 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ImageAnalysisHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ImageAnalysisHostApiImpl.java @@ -24,9 +24,12 @@ public class ImageAnalysisHostApiImpl implements ImageAnalysisHostApi { @VisibleForTesting @NonNull public CameraXProxy cameraXProxy = new CameraXProxy(); public ImageAnalysisHostApiImpl( - @NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) { + @NonNull BinaryMessenger binaryMessenger, + @NonNull InstanceManager instanceManager, + @NonNull Context context) { this.binaryMessenger = binaryMessenger; this.instanceManager = instanceManager; + this.context = context; } /** diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraControlTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraControlTest.java new file mode 100644 index 000000000000..5e17f6441367 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraControlTest.java @@ -0,0 +1,111 @@ +// 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.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import androidx.camera.core.CameraControl; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +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; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class CameraControlTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public BinaryMessenger mockBinaryMessenger; + @Mock public CameraControl cameraControl; + + InstanceManager testInstanceManager; + + @Before + public void setUp() { + testInstanceManager = InstanceManager.create(identifier -> {}); + } + + @After + public void tearDown() { + testInstanceManager.stopFinalizationListener(); + } + + @Test + public void enableTorch_turnsTorchModeOnAndOffAsExpected() { + try (MockedStatic mockedFutures = Mockito.mockStatic(Futures.class)) { + final CameraControlHostApiImpl cameraControlHostApiImpl = + new CameraControlHostApiImpl(testInstanceManager, mock(Context.class)); + final Long cameraControlIdentifier = 88L; + final boolean enableTorch = true; + + @SuppressWarnings("unchecked") + final ListenableFuture enableTorchFuture = mock(ListenableFuture.class); + + testInstanceManager.addDartCreatedInstance(cameraControl, cameraControlIdentifier); + + when(cameraControl.enableTorch(true)).thenReturn(enableTorchFuture); + + @SuppressWarnings("unchecked") + final ArgumentCaptor> futureCallbackCaptor = + ArgumentCaptor.forClass(FutureCallback.class); + + // Test turning on torch mode. + @SuppressWarnings("unchecked") + final GeneratedCameraXLibrary.Result successfulMockResult = + mock(GeneratedCameraXLibrary.Result.class); + cameraControlHostApiImpl.enableTorch( + cameraControlIdentifier, enableTorch, successfulMockResult); + mockedFutures.verify( + () -> Futures.addCallback(eq(enableTorchFuture), futureCallbackCaptor.capture(), any())); + mockedFutures.clearInvocations(); + + FutureCallback successfulEnableTorchCallback = futureCallbackCaptor.getValue(); + + successfulEnableTorchCallback.onSuccess(mock(Void.class)); + verify(successfulMockResult).success(null); + + // Test turning off torch mode. + @SuppressWarnings("unchecked") + final GeneratedCameraXLibrary.Result failedMockResult = + mock(GeneratedCameraXLibrary.Result.class); + final Throwable testThrowable = new Throwable(); + cameraControlHostApiImpl.enableTorch(cameraControlIdentifier, enableTorch, failedMockResult); + mockedFutures.verify( + () -> Futures.addCallback(eq(enableTorchFuture), futureCallbackCaptor.capture(), any())); + + FutureCallback failedEnableTorchCallback = futureCallbackCaptor.getValue(); + + failedEnableTorchCallback.onFailure(testThrowable); + verify(failedMockResult).error(testThrowable); + } + } + + @Test + public void flutterApiCreate_makesCallToCreateInstanceOnDartSide() { + final CameraControlFlutterApiImpl spyFlutterApi = + spy(new CameraControlFlutterApiImpl(mockBinaryMessenger, testInstanceManager)); + + spyFlutterApi.create(cameraControl, reply -> {}); + + final long cameraControlIdentifier = + Objects.requireNonNull(testInstanceManager.getIdentifierForStrongReference(cameraControl)); + verify(spyFlutterApi).create(eq(cameraControlIdentifier), any()); + } +} 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 fb6b0b3cf353..590529c93346 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 @@ -13,6 +13,7 @@ import static org.mockito.Mockito.when; import androidx.camera.core.Camera; +import androidx.camera.core.CameraControl; import androidx.camera.core.CameraInfo; import io.flutter.plugin.common.BinaryMessenger; import java.util.Objects; @@ -59,6 +60,23 @@ public void getCameraInfo_retrievesExpectedCameraInfoInstance() { verify(camera).getCameraInfo(); } + @Test + public void getCameraControl_retrievesExpectedCameraControlInstance() { + final CameraHostApiImpl cameraHostApiImpl = + new CameraHostApiImpl(mockBinaryMessenger, testInstanceManager); + final CameraControl mockCameraControl = mock(CameraControl.class); + final Long cameraIdentifier = 43L; + final Long mockCameraControlIdentifier = 79L; + + testInstanceManager.addDartCreatedInstance(camera, cameraIdentifier); + testInstanceManager.addDartCreatedInstance(mockCameraControl, mockCameraControlIdentifier); + + when(camera.getCameraControl()).thenReturn(mockCameraControl); + + assertEquals(cameraHostApiImpl.getCameraControl(cameraIdentifier), mockCameraControlIdentifier); + verify(camera).getCameraControl(); + } + @Test public void flutterApiCreate_makesCallToCreateInstanceOnDartSide() { final CameraFlutterApiImpl spyFlutterApi = diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageAnalysisTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageAnalysisTest.java index b9a6d299ae30..08a9f80b6373 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageAnalysisTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageAnalysisTest.java @@ -50,7 +50,7 @@ public void tearDown() { @Test public void hostApiCreate_createsExpectedImageAnalysisInstanceWithExpectedIdentifier() { final ImageAnalysisHostApiImpl hostApi = - new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager); + new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager, context); final CameraXProxy mockCameraXProxy = mock(CameraXProxy.class); final ImageAnalysis.Builder mockImageAnalysisBuilder = mock(ImageAnalysis.Builder.class); final ResolutionSelector mockResolutionSelector = mock(ResolutionSelector.class); @@ -72,8 +72,7 @@ public void hostApiCreate_createsExpectedImageAnalysisInstanceWithExpectedIdenti @Test public void setAnalyzer_makesCallToSetAnalyzerOnExpectedImageAnalysisInstance() { final ImageAnalysisHostApiImpl hostApi = - new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager); - hostApi.setContext(context); + new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager, context); final ImageAnalysis.Analyzer mockAnalyzer = mock(ImageAnalysis.Analyzer.class); final long analyzerIdentifier = 10; @@ -90,7 +89,7 @@ public void setAnalyzer_makesCallToSetAnalyzerOnExpectedImageAnalysisInstance() @Test public void clearAnalyzer_makesCallToClearAnalyzerOnExpectedImageAnalysisInstance() { final ImageAnalysisHostApiImpl hostApi = - new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager); + new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager, context); final long instanceIdentifier = 22; instanceManager.addDartCreatedInstance(mockImageAnalysis, instanceIdentifier); diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart index 5dbfffad1b36..ec6fb512eeb3 100644 --- a/packages/camera/camera_android_camerax/example/lib/main.dart +++ b/packages/camera/camera_android_camerax/example/lib/main.dart @@ -274,7 +274,7 @@ class _CameraExampleHomeState extends State IconButton( icon: const Icon(Icons.flash_on), color: Colors.blue, - onPressed: () {}, // TODO(camsim99): Add functionality back here. + onPressed: controller != null ? onFlashModeButtonPressed : null, ), // The exposure and focus mode are currently not supported on the web. ...!kIsWeb @@ -326,28 +326,36 @@ class _CameraExampleHomeState extends State color: controller?.value.flashMode == FlashMode.off ? Colors.orange : Colors.blue, - onPressed: () {}, // TODO(camsim99): Add functionality back here. + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.off) + : null, ), IconButton( icon: const Icon(Icons.flash_auto), color: controller?.value.flashMode == FlashMode.auto ? Colors.orange : Colors.blue, - onPressed: () {}, // TODO(camsim99): Add functionality back here. + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.auto) + : null, ), IconButton( icon: const Icon(Icons.flash_on), color: controller?.value.flashMode == FlashMode.always ? Colors.orange : Colors.blue, - onPressed: () {}, // TODO(camsim99): Add functionality back here. + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.always) + : null, ), IconButton( icon: const Icon(Icons.highlight), color: controller?.value.flashMode == FlashMode.torch ? Colors.orange : Colors.blue, - onPressed: () {}, // TODO(camsim99): Add functionality back here. + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.torch) + : null, ), ], ), diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index c915961208db..8b06bf61a916 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -11,6 +11,7 @@ import 'package:stream_transform/stream_transform.dart'; import 'analyzer.dart'; import 'camera.dart'; +import 'camera_control.dart'; import 'camera_info.dart'; import 'camera_selector.dart'; import 'camera_state.dart'; @@ -111,6 +112,10 @@ class AndroidCameraCameraX extends CameraPlatform { /// The flash mode currently configured for [imageCapture]. int? _currentFlashMode; + /// Whether or not torch flash mode has been enabled for the [camera]. + @visibleForTesting + bool torchEnabled = false; + /// The [ImageAnalysis] instance that can be configured to analyze individual /// frames. ImageAnalysis? imageAnalysis; @@ -483,14 +488,37 @@ class AndroidCameraCameraX extends CameraPlatform { Future takePicture(int cameraId) async { if (_currentFlashMode != null) { await imageCapture!.setFlashMode(_currentFlashMode!); + } else if (torchEnabled) { + // Ensure any previously set flash modes are unset when torch mode has + // been enabled. + await imageCapture!.setFlashMode(ImageCapture.flashModeOff); } final String picturePath = await imageCapture!.takePicture(); return XFile(picturePath); } /// Sets the flash mode for the selected camera. + /// + /// When the [FlashMode.torch] is enabled, any previously set [FlashMode] with + /// this method will be disabled, just as with any other [FlashMode]; while + /// this is not default native Android behavior as defined by the CameraX API, + /// this behavior is compliant with the plugin platform interface. + /// + /// This method combines the notion of setting the flash mode of the + /// [imageCapture] UseCase and enabling the camera torch, as described + /// by https://developer.android.com/reference/androidx/camera/core/ImageCapture + /// and https://developer.android.com/reference/androidx/camera/core/CameraControl#enableTorch(boolean), + /// respectively. @override Future setFlashMode(int cameraId, FlashMode mode) async { + CameraControl? cameraControl; + // Turn off torch mode if it is enabled and not being redundantly set. + if (mode != FlashMode.torch && torchEnabled) { + cameraControl = await camera!.getCameraControl(); + await cameraControl.enableTorch(false); + torchEnabled = false; + } + switch (mode) { case FlashMode.off: _currentFlashMode = ImageCapture.flashModeOff; @@ -502,7 +530,14 @@ class AndroidCameraCameraX extends CameraPlatform { _currentFlashMode = ImageCapture.flashModeOn; break; case FlashMode.torch: - // TODO(camsim99): Implement torch mode when CameraControl is wrapped. + _currentFlashMode = null; + if (torchEnabled) { + // Torch mode enabled already. + return; + } + cameraControl = await camera!.getCameraControl(); + await cameraControl.enableTorch(true); + torchEnabled = true; break; } } 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 b268323ccfe2..b59e0df30da1 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 @@ -4,6 +4,7 @@ import 'analyzer.dart'; import 'camera.dart'; +import 'camera_control.dart'; import 'camera_info.dart'; import 'camera_selector.dart'; import 'camera_state.dart'; @@ -26,27 +27,27 @@ import 'zoom_state.dart'; /// Handles initialization of Flutter APIs for the Android CameraX library. class AndroidCameraXCameraFlutterApis { /// Creates a [AndroidCameraXCameraFlutterApis]. - AndroidCameraXCameraFlutterApis({ - JavaObjectFlutterApiImpl? javaObjectFlutterApiImpl, - CameraFlutterApiImpl? cameraFlutterApiImpl, - CameraInfoFlutterApiImpl? cameraInfoFlutterApiImpl, - CameraSelectorFlutterApiImpl? cameraSelectorFlutterApiImpl, - ProcessCameraProviderFlutterApiImpl? processCameraProviderFlutterApiImpl, - SystemServicesFlutterApiImpl? systemServicesFlutterApiImpl, - CameraStateErrorFlutterApiImpl? cameraStateErrorFlutterApiImpl, - CameraStateFlutterApiImpl? cameraStateFlutterApiImpl, - PendingRecordingFlutterApiImpl? pendingRecordingFlutterApiImpl, - RecordingFlutterApiImpl? recordingFlutterApiImpl, - RecorderFlutterApiImpl? recorderFlutterApiImpl, - VideoCaptureFlutterApiImpl? videoCaptureFlutterApiImpl, - ExposureStateFlutterApiImpl? exposureStateFlutterApiImpl, - ZoomStateFlutterApiImpl? zoomStateFlutterApiImpl, - LiveDataFlutterApiImpl? liveDataFlutterApiImpl, - ObserverFlutterApiImpl? observerFlutterApiImpl, - ImageProxyFlutterApiImpl? imageProxyFlutterApiImpl, - PlaneProxyFlutterApiImpl? planeProxyFlutterApiImpl, - AnalyzerFlutterApiImpl? analyzerFlutterApiImpl, - }) { + AndroidCameraXCameraFlutterApis( + {JavaObjectFlutterApiImpl? javaObjectFlutterApiImpl, + CameraFlutterApiImpl? cameraFlutterApiImpl, + CameraInfoFlutterApiImpl? cameraInfoFlutterApiImpl, + CameraSelectorFlutterApiImpl? cameraSelectorFlutterApiImpl, + ProcessCameraProviderFlutterApiImpl? processCameraProviderFlutterApiImpl, + SystemServicesFlutterApiImpl? systemServicesFlutterApiImpl, + CameraStateErrorFlutterApiImpl? cameraStateErrorFlutterApiImpl, + CameraStateFlutterApiImpl? cameraStateFlutterApiImpl, + PendingRecordingFlutterApiImpl? pendingRecordingFlutterApiImpl, + RecordingFlutterApiImpl? recordingFlutterApiImpl, + RecorderFlutterApiImpl? recorderFlutterApiImpl, + VideoCaptureFlutterApiImpl? videoCaptureFlutterApiImpl, + ExposureStateFlutterApiImpl? exposureStateFlutterApiImpl, + ZoomStateFlutterApiImpl? zoomStateFlutterApiImpl, + LiveDataFlutterApiImpl? liveDataFlutterApiImpl, + ObserverFlutterApiImpl? observerFlutterApiImpl, + ImageProxyFlutterApiImpl? imageProxyFlutterApiImpl, + PlaneProxyFlutterApiImpl? planeProxyFlutterApiImpl, + AnalyzerFlutterApiImpl? analyzerFlutterApiImpl, + CameraControlFlutterApiImpl? cameraControlFlutterApiImpl}) { this.javaObjectFlutterApiImpl = javaObjectFlutterApiImpl ?? JavaObjectFlutterApiImpl(); this.cameraInfoFlutterApiImpl = @@ -85,6 +86,8 @@ class AndroidCameraXCameraFlutterApis { imageProxyFlutterApiImpl ?? ImageProxyFlutterApiImpl(); this.planeProxyFlutterApiImpl = planeProxyFlutterApiImpl ?? PlaneProxyFlutterApiImpl(); + this.cameraControlFlutterApiImpl = + cameraControlFlutterApiImpl ?? CameraControlFlutterApiImpl(); } static bool _haveBeenSetUp = false; @@ -153,6 +156,9 @@ class AndroidCameraXCameraFlutterApis { /// Flutter Api implementation for [PlaneProxy]. late final PlaneProxyFlutterApiImpl planeProxyFlutterApiImpl; + /// Flutter Api implementation for [CameraControl]. + late final CameraControlFlutterApiImpl cameraControlFlutterApiImpl; + /// Ensures all the Flutter APIs have been setup to receive calls from native code. void ensureSetUp() { if (!_haveBeenSetUp) { @@ -176,6 +182,7 @@ class AndroidCameraXCameraFlutterApis { PlaneProxyFlutterApi.setup(planeProxyFlutterApiImpl); LiveDataFlutterApi.setup(liveDataFlutterApiImpl); ObserverFlutterApi.setup(observerFlutterApiImpl); + CameraControlFlutterApi.setup(cameraControlFlutterApiImpl); _haveBeenSetUp = true; } } diff --git a/packages/camera/camera_android_camerax/lib/src/camera.dart b/packages/camera/camera_android_camerax/lib/src/camera.dart index 8bc1dbf75f87..1001450353be 100644 --- a/packages/camera/camera_android_camerax/lib/src/camera.dart +++ b/packages/camera/camera_android_camerax/lib/src/camera.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart' show BinaryMessenger; import 'package:meta/meta.dart' show immutable; import 'android_camera_camerax_flutter_api_impls.dart'; +import 'camera_control.dart'; import 'camera_info.dart'; import 'camerax_library.g.dart'; import 'instance_manager.dart'; @@ -23,27 +24,33 @@ class Camera extends JavaObject { : super.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager) { - _api = CameraHostApiImpl( + _api = _CameraHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager); AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); } - late final CameraHostApiImpl _api; + late final _CameraHostApiImpl _api; - /// Retrieve the [CameraInfo] instance that contains information about this + /// Retrieves the [CameraInfo] instance that contains information about this /// instance. Future getCameraInfo() async { return _api.getCameraInfoFromInstance(this); } + + /// Retrieves the [CameraControl] instance that provides asynchronous + /// operations like zoom and focus & metering for this instance. + Future getCameraControl() async { + return _api.getCameraControlFromInstance(this); + } } /// Host API implementation of [Camera]. -class CameraHostApiImpl extends CameraHostApi { - /// Constructs a [CameraHostApiImpl]. +class _CameraHostApiImpl extends CameraHostApi { + /// Constructs a [_CameraHostApiImpl]. /// /// An [instanceManager] is typically passed when a copy of an instance /// contained by an [InstanceManager] is being created. - CameraHostApiImpl({this.binaryMessenger, InstanceManager? instanceManager}) + _CameraHostApiImpl({this.binaryMessenger, InstanceManager? instanceManager}) : super(binaryMessenger: binaryMessenger) { this.instanceManager = instanceManager ?? JavaObject.globalInstanceManager; } @@ -57,7 +64,7 @@ class CameraHostApiImpl extends CameraHostApi { /// Maintains instances stored to communicate with native language objects. late final InstanceManager instanceManager; - /// Gets the [CameraInfo] associated with the specified instance of [Camera]. + /// Gets the [CameraInfo] associated with the specified [Camera] instance. Future getCameraInfoFromInstance(Camera instance) async { final int identifier = instanceManager.getIdentifier(instance)!; final int cameraInfoId = await getCameraInfo(identifier); @@ -65,6 +72,15 @@ class CameraHostApiImpl extends CameraHostApi { return instanceManager .getInstanceWithWeakReference(cameraInfoId)!; } + + /// Gets the [CameraControl] associated with the specified [Camera] instance. + Future getCameraControlFromInstance(Camera instance) async { + final int identifier = instanceManager.getIdentifier(instance)!; + final int cameraControlId = await getCameraControl(identifier); + + return instanceManager + .getInstanceWithWeakReference(cameraControlId)!; + } } /// Flutter API implementation of [Camera]. diff --git a/packages/camera/camera_android_camerax/lib/src/camera_control.dart b/packages/camera/camera_android_camerax/lib/src/camera_control.dart new file mode 100644 index 000000000000..b30195ce9286 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/camera_control.dart @@ -0,0 +1,109 @@ +// 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, PlatformException; +import 'package:meta/meta.dart' show immutable; + +import 'android_camera_camerax_flutter_api_impls.dart'; +import 'camerax_library.g.dart'; +import 'instance_manager.dart'; +import 'java_object.dart'; +import 'system_services.dart'; + +/// The interface that provides asynchronous operations like zoom and focus & +/// metering, which affects output of all [UseCase]s currently bound to the +/// corresponding [Camera] instance. +/// +/// See https://developer.android.com/reference/androidx/camera/core/CameraControl. +@immutable +class CameraControl extends JavaObject { + /// Constructs a [CameraControl] that is not automatically attached to a native object. + CameraControl.detached( + {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager) { + _api = _CameraControlHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + } + + late final _CameraControlHostApiImpl _api; + + /// Enables or disables the torch of related [Camera] instance. + Future enableTorch(bool torch) async { + return _api.enableTorchFromInstance(this, torch); + } +} + +/// Host API implementation of [CameraControl]. +class _CameraControlHostApiImpl extends CameraControlHostApi { + /// Constructs a [_CameraControlHostApiImpl]. + /// + /// An [instanceManager] is typically passed when a copy of an instance + /// contained by an [InstanceManager] is being created. + _CameraControlHostApiImpl( + {this.binaryMessenger, InstanceManager? instanceManager}) + : super(binaryMessenger: binaryMessenger) { + this.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. + late final InstanceManager instanceManager; + + /// Enables or disables the torch for the specified [CameraControl] instance. + Future enableTorchFromInstance( + CameraControl instance, bool torch) async { + final int identifier = instanceManager.getIdentifier(instance)!; + try { + await enableTorch(identifier, torch); + } on PlatformException catch (e) { + SystemServices.cameraErrorStreamController + .add(e.message ?? 'The camera was unable to change torch modes.'); + } + } +} + +/// Flutter API implementation of [CameraControl]. +class CameraControlFlutterApiImpl extends CameraControlFlutterApi { + /// Constructs a [CameraControlFlutterApiImpl]. + /// + /// If [binaryMessenger] is null, the default [BinaryMessenger] will be used, + /// which routes to the host platform. + /// + /// An [instanceManager] is typically passed when a copy of an instance + /// contained by an [InstanceManager] is being created. If left null, it + /// will default to the global instance defined in [JavaObject]. + CameraControlFlutterApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _binaryMessenger = binaryMessenger, + _instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Receives binary data across the Flutter platform barrier. + final BinaryMessenger? _binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager _instanceManager; + + @override + void create(int identifier) { + _instanceManager.addHostCreatedInstance( + CameraControl.detached( + binaryMessenger: _binaryMessenger, instanceManager: _instanceManager), + identifier, + onCopy: (CameraControl original) { + return CameraControl.detached( + binaryMessenger: _binaryMessenger, + instanceManager: _instanceManager); + }, + ); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart index dda3a81442dc..4fc4d1573ef2 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart @@ -780,6 +780,33 @@ class CameraHostApi { return (replyList[0] as int?)!; } } + + Future getCameraControl(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraHostApi.getCameraControl', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_identifier]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as int?)!; + } + } } abstract class CameraFlutterApi { @@ -2626,3 +2653,65 @@ class FallbackStrategyHostApi { } } } + +class CameraControlHostApi { + /// Constructor for [CameraControlHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + CameraControlHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = StandardMessageCodec(); + + Future enableTorch(int arg_identifier, bool arg_torch) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraControlHostApi.enableTorch', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel + .send([arg_identifier, arg_torch]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } +} + +abstract class CameraControlFlutterApi { + static const MessageCodec codec = StandardMessageCodec(); + + void create(int identifier); + + static void setup(CameraControlFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraControlFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraControlFlutterApi.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.CameraControlFlutterApi.create was null, expected non-null int.'); + api.create(arg_identifier!); + return; + }); + } + } + } +} diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index 9ebf34b73bea..2c5b9c2a77c9 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -197,6 +197,8 @@ abstract class ProcessCameraProviderFlutterApi { @HostApi(dartHostTestHandler: 'TestCameraHostApi') abstract class CameraHostApi { int getCameraInfo(int identifier); + + int getCameraControl(int identifier); } @FlutterApi() @@ -417,3 +419,14 @@ abstract class FallbackStrategyHostApi { void create(int identifier, VideoQuality quality, VideoResolutionFallbackRule fallbackRule); } + +@HostApi(dartHostTestHandler: 'TestCameraControlHostApi') +abstract class CameraControlHostApi { + @async + void enableTorch(int identifier, bool torch); +} + +@FlutterApi() +abstract class CameraControlFlutterApi { + void create(int identifier); +} diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 75b3c0390019..238d94c8c969 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.5.0+18 +version: 0.5.0+19 environment: sdk: ">=2.19.0 <4.0.0" diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart index b90aabdd28dd..10c8e5b87239 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart @@ -8,6 +8,7 @@ import 'package:async/async.dart'; import 'package:camera_android_camerax/camera_android_camerax.dart'; import 'package:camera_android_camerax/src/analyzer.dart'; import 'package:camera_android_camerax/src/camera.dart'; +import 'package:camera_android_camerax/src/camera_control.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_state.dart'; @@ -46,6 +47,7 @@ import 'test_camerax_library.g.dart'; @GenerateNiceMocks(>[ MockSpec(), MockSpec(), + MockSpec(), MockSpec(), MockSpec(), MockSpec(), @@ -1025,12 +1027,35 @@ void main() { expect(imageFile.path, equals(testPicturePath)); }); - test('setFlashMode configures ImageCapture with expected flash mode', + test('takePicture turns non-torch flash mode off when torch mode enabled', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 77; + final MockCameraControl mockCameraControl = MockCameraControl(); + + camera.imageCapture = MockImageCapture(); + camera.camera = MockCamera(); + + when(camera.camera!.getCameraControl()) + .thenAnswer((_) async => mockCameraControl); + + await camera.setFlashMode(cameraId, FlashMode.torch); + await camera.takePicture(cameraId); + verify(camera.imageCapture!.setFlashMode(ImageCapture.flashModeOff)); + }); + + test( + 'setFlashMode configures ImageCapture with expected non-torch flash mode', () async { final AndroidCameraCameraX camera = AndroidCameraCameraX(); const int cameraId = 22; + final MockCameraControl mockCameraControl = MockCameraControl(); camera.imageCapture = MockImageCapture(); + camera.camera = MockCamera(); + + when(camera.camera!.getCameraControl()) + .thenAnswer((_) async => mockCameraControl); for (final FlashMode flashMode in FlashMode.values) { await camera.setFlashMode(cameraId, flashMode); @@ -1047,19 +1072,68 @@ void main() { expectedFlashMode = ImageCapture.flashModeOn; break; case FlashMode.torch: - // TODO(camsim99): Test torch mode when implemented. + expectedFlashMode = null; break; } if (expectedFlashMode == null) { + // Torch mode enabled and won't be used for configuring image capture. continue; } + verifyNever(mockCameraControl.enableTorch(true)); + expect(camera.torchEnabled, isFalse); await camera.takePicture(cameraId); verify(camera.imageCapture!.setFlashMode(expectedFlashMode)); } }); + test('setFlashMode turns on torch mode as expected', () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 44; + final MockCameraControl mockCameraControl = MockCameraControl(); + + camera.camera = MockCamera(); + + when(camera.camera!.getCameraControl()) + .thenAnswer((_) async => mockCameraControl); + + await camera.setFlashMode(cameraId, FlashMode.torch); + + verify(mockCameraControl.enableTorch(true)); + expect(camera.torchEnabled, isTrue); + }); + + test('setFlashMode turns off torch mode when non-torch flash modes set', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 33; + final MockCameraControl mockCameraControl = MockCameraControl(); + + camera.camera = MockCamera(); + + when(camera.camera!.getCameraControl()) + .thenAnswer((_) async => mockCameraControl); + + for (final FlashMode flashMode in FlashMode.values) { + camera.torchEnabled = true; + await camera.setFlashMode(cameraId, flashMode); + + switch (flashMode) { + case FlashMode.off: + case FlashMode.auto: + case FlashMode.always: + verify(mockCameraControl.enableTorch(false)); + expect(camera.torchEnabled, isFalse); + break; + case FlashMode.torch: + verifyNever(mockCameraControl.enableTorch(true)); + expect(camera.torchEnabled, true); + break; + } + } + }); + test('getMinExposureOffset returns expected exposure offset', () async { final AndroidCameraCameraX camera = AndroidCameraCameraX(); final MockCameraInfo mockCameraInfo = MockCameraInfo(); diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart index 1d3d6363bad1..f275ea7021a1 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart @@ -5,39 +5,40 @@ // @dart=2.19 // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i14; -import 'dart:typed_data' as _i23; +import 'dart:async' as _i15; +import 'dart:typed_data' as _i24; -import 'package:camera_android_camerax/src/analyzer.dart' as _i19; -import 'package:camera_android_camerax/src/camera.dart' as _i7; +import 'package:camera_android_camerax/src/analyzer.dart' as _i20; +import 'package:camera_android_camerax/src/camera.dart' as _i8; +import 'package:camera_android_camerax/src/camera_control.dart' as _i3; import 'package:camera_android_camerax/src/camera_info.dart' as _i2; -import 'package:camera_android_camerax/src/camera_selector.dart' as _i17; -import 'package:camera_android_camerax/src/camera_state.dart' as _i15; -import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i6; -import 'package:camera_android_camerax/src/exposure_state.dart' as _i4; -import 'package:camera_android_camerax/src/image_analysis.dart' as _i18; -import 'package:camera_android_camerax/src/image_capture.dart' as _i20; -import 'package:camera_android_camerax/src/image_proxy.dart' as _i21; -import 'package:camera_android_camerax/src/live_data.dart' as _i3; -import 'package:camera_android_camerax/src/observer.dart' as _i29; -import 'package:camera_android_camerax/src/pending_recording.dart' as _i8; -import 'package:camera_android_camerax/src/plane_proxy.dart' as _i22; -import 'package:camera_android_camerax/src/preview.dart' as _i24; +import 'package:camera_android_camerax/src/camera_selector.dart' as _i18; +import 'package:camera_android_camerax/src/camera_state.dart' as _i16; +import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i7; +import 'package:camera_android_camerax/src/exposure_state.dart' as _i5; +import 'package:camera_android_camerax/src/image_analysis.dart' as _i19; +import 'package:camera_android_camerax/src/image_capture.dart' as _i21; +import 'package:camera_android_camerax/src/image_proxy.dart' as _i22; +import 'package:camera_android_camerax/src/live_data.dart' as _i4; +import 'package:camera_android_camerax/src/observer.dart' as _i30; +import 'package:camera_android_camerax/src/pending_recording.dart' as _i9; +import 'package:camera_android_camerax/src/plane_proxy.dart' as _i23; +import 'package:camera_android_camerax/src/preview.dart' as _i25; import 'package:camera_android_camerax/src/process_camera_provider.dart' - as _i25; -import 'package:camera_android_camerax/src/recorder.dart' as _i10; -import 'package:camera_android_camerax/src/recording.dart' as _i9; -import 'package:camera_android_camerax/src/use_case.dart' as _i26; -import 'package:camera_android_camerax/src/video_capture.dart' as _i27; -import 'package:camera_android_camerax/src/zoom_state.dart' as _i16; + as _i26; +import 'package:camera_android_camerax/src/recorder.dart' as _i11; +import 'package:camera_android_camerax/src/recording.dart' as _i10; +import 'package:camera_android_camerax/src/use_case.dart' as _i27; +import 'package:camera_android_camerax/src/video_capture.dart' as _i28; +import 'package:camera_android_camerax/src/zoom_state.dart' as _i17; import 'package:camera_platform_interface/camera_platform_interface.dart' - as _i5; -import 'package:flutter/foundation.dart' as _i13; -import 'package:flutter/services.dart' as _i12; -import 'package:flutter/widgets.dart' as _i11; + as _i6; +import 'package:flutter/foundation.dart' as _i14; +import 'package:flutter/services.dart' as _i13; +import 'package:flutter/widgets.dart' as _i12; import 'package:mockito/mockito.dart' as _i1; -import 'test_camerax_library.g.dart' as _i28; +import 'test_camerax_library.g.dart' as _i29; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -60,9 +61,8 @@ class _FakeCameraInfo_0 extends _i1.SmartFake implements _i2.CameraInfo { ); } -class _FakeLiveData_1 extends _i1.SmartFake - implements _i3.LiveData { - _FakeLiveData_1( +class _FakeCameraControl_1 extends _i1.SmartFake implements _i3.CameraControl { + _FakeCameraControl_1( Object parent, Invocation parentInvocation, ) : super( @@ -71,8 +71,9 @@ class _FakeLiveData_1 extends _i1.SmartFake ); } -class _FakeExposureState_2 extends _i1.SmartFake implements _i4.ExposureState { - _FakeExposureState_2( +class _FakeLiveData_2 extends _i1.SmartFake + implements _i4.LiveData { + _FakeLiveData_2( Object parent, Invocation parentInvocation, ) : super( @@ -81,9 +82,8 @@ class _FakeExposureState_2 extends _i1.SmartFake implements _i4.ExposureState { ); } -class _FakeCameraImageFormat_3 extends _i1.SmartFake - implements _i5.CameraImageFormat { - _FakeCameraImageFormat_3( +class _FakeExposureState_3 extends _i1.SmartFake implements _i5.ExposureState { + _FakeExposureState_3( Object parent, Invocation parentInvocation, ) : super( @@ -92,9 +92,9 @@ class _FakeCameraImageFormat_3 extends _i1.SmartFake ); } -class _FakeExposureCompensationRange_4 extends _i1.SmartFake - implements _i6.ExposureCompensationRange { - _FakeExposureCompensationRange_4( +class _FakeCameraImageFormat_4 extends _i1.SmartFake + implements _i6.CameraImageFormat { + _FakeCameraImageFormat_4( Object parent, Invocation parentInvocation, ) : super( @@ -103,9 +103,9 @@ class _FakeExposureCompensationRange_4 extends _i1.SmartFake ); } -class _FakeResolutionInfo_5 extends _i1.SmartFake - implements _i6.ResolutionInfo { - _FakeResolutionInfo_5( +class _FakeExposureCompensationRange_5 extends _i1.SmartFake + implements _i7.ExposureCompensationRange { + _FakeExposureCompensationRange_5( Object parent, Invocation parentInvocation, ) : super( @@ -114,8 +114,9 @@ class _FakeResolutionInfo_5 extends _i1.SmartFake ); } -class _FakeCamera_6 extends _i1.SmartFake implements _i7.Camera { - _FakeCamera_6( +class _FakeResolutionInfo_6 extends _i1.SmartFake + implements _i7.ResolutionInfo { + _FakeResolutionInfo_6( Object parent, Invocation parentInvocation, ) : super( @@ -124,9 +125,8 @@ class _FakeCamera_6 extends _i1.SmartFake implements _i7.Camera { ); } -class _FakePendingRecording_7 extends _i1.SmartFake - implements _i8.PendingRecording { - _FakePendingRecording_7( +class _FakeCamera_7 extends _i1.SmartFake implements _i8.Camera { + _FakeCamera_7( Object parent, Invocation parentInvocation, ) : super( @@ -135,8 +135,9 @@ class _FakePendingRecording_7 extends _i1.SmartFake ); } -class _FakeRecording_8 extends _i1.SmartFake implements _i9.Recording { - _FakeRecording_8( +class _FakePendingRecording_8 extends _i1.SmartFake + implements _i9.PendingRecording { + _FakePendingRecording_8( Object parent, Invocation parentInvocation, ) : super( @@ -145,8 +146,8 @@ class _FakeRecording_8 extends _i1.SmartFake implements _i9.Recording { ); } -class _FakeRecorder_9 extends _i1.SmartFake implements _i10.Recorder { - _FakeRecorder_9( +class _FakeRecording_9 extends _i1.SmartFake implements _i10.Recording { + _FakeRecording_9( Object parent, Invocation parentInvocation, ) : super( @@ -155,8 +156,18 @@ class _FakeRecorder_9 extends _i1.SmartFake implements _i10.Recorder { ); } -class _FakeWidget_10 extends _i1.SmartFake implements _i11.Widget { - _FakeWidget_10( +class _FakeRecorder_10 extends _i1.SmartFake implements _i11.Recorder { + _FakeRecorder_10( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeWidget_11 extends _i1.SmartFake implements _i12.Widget { + _FakeWidget_11( Object parent, Invocation parentInvocation, ) : super( @@ -166,13 +177,13 @@ class _FakeWidget_10 extends _i1.SmartFake implements _i11.Widget { @override String toString( - {_i12.DiagnosticLevel? minLevel = _i12.DiagnosticLevel.info}) => + {_i13.DiagnosticLevel? minLevel = _i13.DiagnosticLevel.info}) => super.toString(); } -class _FakeInheritedWidget_11 extends _i1.SmartFake - implements _i11.InheritedWidget { - _FakeInheritedWidget_11( +class _FakeInheritedWidget_12 extends _i1.SmartFake + implements _i12.InheritedWidget { + _FakeInheritedWidget_12( Object parent, Invocation parentInvocation, ) : super( @@ -182,13 +193,13 @@ class _FakeInheritedWidget_11 extends _i1.SmartFake @override String toString( - {_i12.DiagnosticLevel? minLevel = _i12.DiagnosticLevel.info}) => + {_i13.DiagnosticLevel? minLevel = _i13.DiagnosticLevel.info}) => super.toString(); } -class _FakeDiagnosticsNode_12 extends _i1.SmartFake - implements _i13.DiagnosticsNode { - _FakeDiagnosticsNode_12( +class _FakeDiagnosticsNode_13 extends _i1.SmartFake + implements _i14.DiagnosticsNode { + _FakeDiagnosticsNode_13( Object parent, Invocation parentInvocation, ) : super( @@ -198,8 +209,8 @@ class _FakeDiagnosticsNode_12 extends _i1.SmartFake @override String toString({ - _i13.TextTreeConfiguration? parentConfiguration, - _i12.DiagnosticLevel? minLevel = _i12.DiagnosticLevel.info, + _i14.TextTreeConfiguration? parentConfiguration, + _i13.DiagnosticLevel? minLevel = _i13.DiagnosticLevel.info, }) => super.toString(); } @@ -208,14 +219,14 @@ class _FakeDiagnosticsNode_12 extends _i1.SmartFake /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockCamera extends _i1.Mock implements _i7.Camera { +class MockCamera extends _i1.Mock implements _i8.Camera { @override - _i14.Future<_i2.CameraInfo> getCameraInfo() => (super.noSuchMethod( + _i15.Future<_i2.CameraInfo> getCameraInfo() => (super.noSuchMethod( Invocation.method( #getCameraInfo, [], ), - returnValue: _i14.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0( + returnValue: _i15.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0( this, Invocation.method( #getCameraInfo, @@ -223,14 +234,36 @@ class MockCamera extends _i1.Mock implements _i7.Camera { ), )), returnValueForMissingStub: - _i14.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0( + _i15.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0( this, Invocation.method( #getCameraInfo, [], ), )), - ) as _i14.Future<_i2.CameraInfo>); + ) as _i15.Future<_i2.CameraInfo>); + @override + _i15.Future<_i3.CameraControl> getCameraControl() => (super.noSuchMethod( + Invocation.method( + #getCameraControl, + [], + ), + returnValue: _i15.Future<_i3.CameraControl>.value(_FakeCameraControl_1( + this, + Invocation.method( + #getCameraControl, + [], + ), + )), + returnValueForMissingStub: + _i15.Future<_i3.CameraControl>.value(_FakeCameraControl_1( + this, + Invocation.method( + #getCameraControl, + [], + ), + )), + ) as _i15.Future<_i3.CameraControl>); } /// A class which mocks [CameraInfo]. @@ -239,23 +272,23 @@ class MockCamera extends _i1.Mock implements _i7.Camera { // ignore: must_be_immutable class MockCameraInfo extends _i1.Mock implements _i2.CameraInfo { @override - _i14.Future getSensorRotationDegrees() => (super.noSuchMethod( + _i15.Future getSensorRotationDegrees() => (super.noSuchMethod( Invocation.method( #getSensorRotationDegrees, [], ), - returnValue: _i14.Future.value(0), - returnValueForMissingStub: _i14.Future.value(0), - ) as _i14.Future); + returnValue: _i15.Future.value(0), + returnValueForMissingStub: _i15.Future.value(0), + ) as _i15.Future); @override - _i14.Future<_i3.LiveData<_i15.CameraState>> getCameraState() => + _i15.Future<_i4.LiveData<_i16.CameraState>> getCameraState() => (super.noSuchMethod( Invocation.method( #getCameraState, [], ), - returnValue: _i14.Future<_i3.LiveData<_i15.CameraState>>.value( - _FakeLiveData_1<_i15.CameraState>( + returnValue: _i15.Future<_i4.LiveData<_i16.CameraState>>.value( + _FakeLiveData_2<_i16.CameraState>( this, Invocation.method( #getCameraState, @@ -263,22 +296,22 @@ class MockCameraInfo extends _i1.Mock implements _i2.CameraInfo { ), )), returnValueForMissingStub: - _i14.Future<_i3.LiveData<_i15.CameraState>>.value( - _FakeLiveData_1<_i15.CameraState>( + _i15.Future<_i4.LiveData<_i16.CameraState>>.value( + _FakeLiveData_2<_i16.CameraState>( this, Invocation.method( #getCameraState, [], ), )), - ) as _i14.Future<_i3.LiveData<_i15.CameraState>>); + ) as _i15.Future<_i4.LiveData<_i16.CameraState>>); @override - _i14.Future<_i4.ExposureState> getExposureState() => (super.noSuchMethod( + _i15.Future<_i5.ExposureState> getExposureState() => (super.noSuchMethod( Invocation.method( #getExposureState, [], ), - returnValue: _i14.Future<_i4.ExposureState>.value(_FakeExposureState_2( + returnValue: _i15.Future<_i5.ExposureState>.value(_FakeExposureState_3( this, Invocation.method( #getExposureState, @@ -286,23 +319,23 @@ class MockCameraInfo extends _i1.Mock implements _i2.CameraInfo { ), )), returnValueForMissingStub: - _i14.Future<_i4.ExposureState>.value(_FakeExposureState_2( + _i15.Future<_i5.ExposureState>.value(_FakeExposureState_3( this, Invocation.method( #getExposureState, [], ), )), - ) as _i14.Future<_i4.ExposureState>); + ) as _i15.Future<_i5.ExposureState>); @override - _i14.Future<_i3.LiveData<_i16.ZoomState>> getZoomState() => + _i15.Future<_i4.LiveData<_i17.ZoomState>> getZoomState() => (super.noSuchMethod( Invocation.method( #getZoomState, [], ), - returnValue: _i14.Future<_i3.LiveData<_i16.ZoomState>>.value( - _FakeLiveData_1<_i16.ZoomState>( + returnValue: _i15.Future<_i4.LiveData<_i17.ZoomState>>.value( + _FakeLiveData_2<_i17.ZoomState>( this, Invocation.method( #getZoomState, @@ -310,34 +343,50 @@ class MockCameraInfo extends _i1.Mock implements _i2.CameraInfo { ), )), returnValueForMissingStub: - _i14.Future<_i3.LiveData<_i16.ZoomState>>.value( - _FakeLiveData_1<_i16.ZoomState>( + _i15.Future<_i4.LiveData<_i17.ZoomState>>.value( + _FakeLiveData_2<_i17.ZoomState>( this, Invocation.method( #getZoomState, [], ), )), - ) as _i14.Future<_i3.LiveData<_i16.ZoomState>>); + ) as _i15.Future<_i4.LiveData<_i17.ZoomState>>); +} + +/// A class which mocks [CameraControl]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockCameraControl extends _i1.Mock implements _i3.CameraControl { + @override + _i15.Future enableTorch(bool? torch) => (super.noSuchMethod( + Invocation.method( + #enableTorch, + [torch], + ), + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); } /// A class which mocks [CameraImageData]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockCameraImageData extends _i1.Mock implements _i5.CameraImageData { +class MockCameraImageData extends _i1.Mock implements _i6.CameraImageData { @override - _i5.CameraImageFormat get format => (super.noSuchMethod( + _i6.CameraImageFormat get format => (super.noSuchMethod( Invocation.getter(#format), - returnValue: _FakeCameraImageFormat_3( + returnValue: _FakeCameraImageFormat_4( this, Invocation.getter(#format), ), - returnValueForMissingStub: _FakeCameraImageFormat_3( + returnValueForMissingStub: _FakeCameraImageFormat_4( this, Invocation.getter(#format), ), - ) as _i5.CameraImageFormat); + ) as _i6.CameraImageFormat); @override int get height => (super.noSuchMethod( Invocation.getter(#height), @@ -351,50 +400,50 @@ class MockCameraImageData extends _i1.Mock implements _i5.CameraImageData { returnValueForMissingStub: 0, ) as int); @override - List<_i5.CameraImagePlane> get planes => (super.noSuchMethod( + List<_i6.CameraImagePlane> get planes => (super.noSuchMethod( Invocation.getter(#planes), - returnValue: <_i5.CameraImagePlane>[], - returnValueForMissingStub: <_i5.CameraImagePlane>[], - ) as List<_i5.CameraImagePlane>); + returnValue: <_i6.CameraImagePlane>[], + returnValueForMissingStub: <_i6.CameraImagePlane>[], + ) as List<_i6.CameraImagePlane>); } /// A class which mocks [CameraSelector]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockCameraSelector extends _i1.Mock implements _i17.CameraSelector { +class MockCameraSelector extends _i1.Mock implements _i18.CameraSelector { @override - _i14.Future> filter(List<_i2.CameraInfo>? cameraInfos) => + _i15.Future> filter(List<_i2.CameraInfo>? cameraInfos) => (super.noSuchMethod( Invocation.method( #filter, [cameraInfos], ), returnValue: - _i14.Future>.value(<_i2.CameraInfo>[]), + _i15.Future>.value(<_i2.CameraInfo>[]), returnValueForMissingStub: - _i14.Future>.value(<_i2.CameraInfo>[]), - ) as _i14.Future>); + _i15.Future>.value(<_i2.CameraInfo>[]), + ) as _i15.Future>); } /// A class which mocks [ExposureState]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockExposureState extends _i1.Mock implements _i4.ExposureState { +class MockExposureState extends _i1.Mock implements _i5.ExposureState { @override - _i6.ExposureCompensationRange get exposureCompensationRange => + _i7.ExposureCompensationRange get exposureCompensationRange => (super.noSuchMethod( Invocation.getter(#exposureCompensationRange), - returnValue: _FakeExposureCompensationRange_4( + returnValue: _FakeExposureCompensationRange_5( this, Invocation.getter(#exposureCompensationRange), ), - returnValueForMissingStub: _FakeExposureCompensationRange_4( + returnValueForMissingStub: _FakeExposureCompensationRange_5( this, Invocation.getter(#exposureCompensationRange), ), - ) as _i6.ExposureCompensationRange); + ) as _i7.ExposureCompensationRange); @override double get exposureCompensationStep => (super.noSuchMethod( Invocation.getter(#exposureCompensationStep), @@ -407,57 +456,57 @@ class MockExposureState extends _i1.Mock implements _i4.ExposureState { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockImageAnalysis extends _i1.Mock implements _i18.ImageAnalysis { +class MockImageAnalysis extends _i1.Mock implements _i19.ImageAnalysis { @override - _i14.Future setAnalyzer(_i19.Analyzer? analyzer) => (super.noSuchMethod( + _i15.Future setAnalyzer(_i20.Analyzer? analyzer) => (super.noSuchMethod( Invocation.method( #setAnalyzer, [analyzer], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); @override - _i14.Future clearAnalyzer() => (super.noSuchMethod( + _i15.Future clearAnalyzer() => (super.noSuchMethod( Invocation.method( #clearAnalyzer, [], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); } /// A class which mocks [ImageCapture]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockImageCapture extends _i1.Mock implements _i20.ImageCapture { +class MockImageCapture extends _i1.Mock implements _i21.ImageCapture { @override - _i14.Future setFlashMode(int? newFlashMode) => (super.noSuchMethod( + _i15.Future setFlashMode(int? newFlashMode) => (super.noSuchMethod( Invocation.method( #setFlashMode, [newFlashMode], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); @override - _i14.Future takePicture() => (super.noSuchMethod( + _i15.Future takePicture() => (super.noSuchMethod( Invocation.method( #takePicture, [], ), - returnValue: _i14.Future.value(''), - returnValueForMissingStub: _i14.Future.value(''), - ) as _i14.Future); + returnValue: _i15.Future.value(''), + returnValueForMissingStub: _i15.Future.value(''), + ) as _i15.Future); } /// A class which mocks [ImageProxy]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockImageProxy extends _i1.Mock implements _i21.ImageProxy { +class MockImageProxy extends _i1.Mock implements _i22.ImageProxy { @override int get format => (super.noSuchMethod( Invocation.getter(#format), @@ -477,38 +526,38 @@ class MockImageProxy extends _i1.Mock implements _i21.ImageProxy { returnValueForMissingStub: 0, ) as int); @override - _i14.Future> getPlanes() => (super.noSuchMethod( + _i15.Future> getPlanes() => (super.noSuchMethod( Invocation.method( #getPlanes, [], ), returnValue: - _i14.Future>.value(<_i22.PlaneProxy>[]), + _i15.Future>.value(<_i23.PlaneProxy>[]), returnValueForMissingStub: - _i14.Future>.value(<_i22.PlaneProxy>[]), - ) as _i14.Future>); + _i15.Future>.value(<_i23.PlaneProxy>[]), + ) as _i15.Future>); @override - _i14.Future close() => (super.noSuchMethod( + _i15.Future close() => (super.noSuchMethod( Invocation.method( #close, [], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); } /// A class which mocks [PlaneProxy]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockPlaneProxy extends _i1.Mock implements _i22.PlaneProxy { +class MockPlaneProxy extends _i1.Mock implements _i23.PlaneProxy { @override - _i23.Uint8List get buffer => (super.noSuchMethod( + _i24.Uint8List get buffer => (super.noSuchMethod( Invocation.getter(#buffer), - returnValue: _i23.Uint8List(0), - returnValueForMissingStub: _i23.Uint8List(0), - ) as _i23.Uint8List); + returnValue: _i24.Uint8List(0), + returnValueForMissingStub: _i24.Uint8List(0), + ) as _i24.Uint8List); @override int get pixelStride => (super.noSuchMethod( Invocation.getter(#pixelStride), @@ -527,16 +576,16 @@ class MockPlaneProxy extends _i1.Mock implements _i22.PlaneProxy { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockPreview extends _i1.Mock implements _i24.Preview { +class MockPreview extends _i1.Mock implements _i25.Preview { @override - _i14.Future setSurfaceProvider() => (super.noSuchMethod( + _i15.Future setSurfaceProvider() => (super.noSuchMethod( Invocation.method( #setSurfaceProvider, [], ), - returnValue: _i14.Future.value(0), - returnValueForMissingStub: _i14.Future.value(0), - ) as _i14.Future); + returnValue: _i15.Future.value(0), + returnValueForMissingStub: _i15.Future.value(0), + ) as _i15.Future); @override void releaseFlutterSurfaceTexture() => super.noSuchMethod( Invocation.method( @@ -546,13 +595,13 @@ class MockPreview extends _i1.Mock implements _i24.Preview { returnValueForMissingStub: null, ); @override - _i14.Future<_i6.ResolutionInfo> getResolutionInfo() => (super.noSuchMethod( + _i15.Future<_i7.ResolutionInfo> getResolutionInfo() => (super.noSuchMethod( Invocation.method( #getResolutionInfo, [], ), returnValue: - _i14.Future<_i6.ResolutionInfo>.value(_FakeResolutionInfo_5( + _i15.Future<_i7.ResolutionInfo>.value(_FakeResolutionInfo_6( this, Invocation.method( #getResolutionInfo, @@ -560,14 +609,14 @@ class MockPreview extends _i1.Mock implements _i24.Preview { ), )), returnValueForMissingStub: - _i14.Future<_i6.ResolutionInfo>.value(_FakeResolutionInfo_5( + _i15.Future<_i7.ResolutionInfo>.value(_FakeResolutionInfo_6( this, Invocation.method( #getResolutionInfo, [], ), )), - ) as _i14.Future<_i6.ResolutionInfo>); + ) as _i15.Future<_i7.ResolutionInfo>); } /// A class which mocks [ProcessCameraProvider]. @@ -575,23 +624,23 @@ class MockPreview extends _i1.Mock implements _i24.Preview { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockProcessCameraProvider extends _i1.Mock - implements _i25.ProcessCameraProvider { + implements _i26.ProcessCameraProvider { @override - _i14.Future> getAvailableCameraInfos() => + _i15.Future> getAvailableCameraInfos() => (super.noSuchMethod( Invocation.method( #getAvailableCameraInfos, [], ), returnValue: - _i14.Future>.value(<_i2.CameraInfo>[]), + _i15.Future>.value(<_i2.CameraInfo>[]), returnValueForMissingStub: - _i14.Future>.value(<_i2.CameraInfo>[]), - ) as _i14.Future>); + _i15.Future>.value(<_i2.CameraInfo>[]), + ) as _i15.Future>); @override - _i14.Future<_i7.Camera> bindToLifecycle( - _i17.CameraSelector? cameraSelector, - List<_i26.UseCase>? useCases, + _i15.Future<_i8.Camera> bindToLifecycle( + _i18.CameraSelector? cameraSelector, + List<_i27.UseCase>? useCases, ) => (super.noSuchMethod( Invocation.method( @@ -601,7 +650,7 @@ class MockProcessCameraProvider extends _i1.Mock useCases, ], ), - returnValue: _i14.Future<_i7.Camera>.value(_FakeCamera_6( + returnValue: _i15.Future<_i8.Camera>.value(_FakeCamera_7( this, Invocation.method( #bindToLifecycle, @@ -611,7 +660,7 @@ class MockProcessCameraProvider extends _i1.Mock ], ), )), - returnValueForMissingStub: _i14.Future<_i7.Camera>.value(_FakeCamera_6( + returnValueForMissingStub: _i15.Future<_i8.Camera>.value(_FakeCamera_7( this, Invocation.method( #bindToLifecycle, @@ -621,18 +670,18 @@ class MockProcessCameraProvider extends _i1.Mock ], ), )), - ) as _i14.Future<_i7.Camera>); + ) as _i15.Future<_i8.Camera>); @override - _i14.Future isBound(_i26.UseCase? useCase) => (super.noSuchMethod( + _i15.Future isBound(_i27.UseCase? useCase) => (super.noSuchMethod( Invocation.method( #isBound, [useCase], ), - returnValue: _i14.Future.value(false), - returnValueForMissingStub: _i14.Future.value(false), - ) as _i14.Future); + returnValue: _i15.Future.value(false), + returnValueForMissingStub: _i15.Future.value(false), + ) as _i15.Future); @override - void unbind(List<_i26.UseCase>? useCases) => super.noSuchMethod( + void unbind(List<_i27.UseCase>? useCases) => super.noSuchMethod( Invocation.method( #unbind, [useCases], @@ -653,16 +702,16 @@ class MockProcessCameraProvider extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockRecorder extends _i1.Mock implements _i10.Recorder { +class MockRecorder extends _i1.Mock implements _i11.Recorder { @override - _i14.Future<_i8.PendingRecording> prepareRecording(String? path) => + _i15.Future<_i9.PendingRecording> prepareRecording(String? path) => (super.noSuchMethod( Invocation.method( #prepareRecording, [path], ), returnValue: - _i14.Future<_i8.PendingRecording>.value(_FakePendingRecording_7( + _i15.Future<_i9.PendingRecording>.value(_FakePendingRecording_8( this, Invocation.method( #prepareRecording, @@ -670,28 +719,28 @@ class MockRecorder extends _i1.Mock implements _i10.Recorder { ), )), returnValueForMissingStub: - _i14.Future<_i8.PendingRecording>.value(_FakePendingRecording_7( + _i15.Future<_i9.PendingRecording>.value(_FakePendingRecording_8( this, Invocation.method( #prepareRecording, [path], ), )), - ) as _i14.Future<_i8.PendingRecording>); + ) as _i15.Future<_i9.PendingRecording>); } /// A class which mocks [PendingRecording]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockPendingRecording extends _i1.Mock implements _i8.PendingRecording { +class MockPendingRecording extends _i1.Mock implements _i9.PendingRecording { @override - _i14.Future<_i9.Recording> start() => (super.noSuchMethod( + _i15.Future<_i10.Recording> start() => (super.noSuchMethod( Invocation.method( #start, [], ), - returnValue: _i14.Future<_i9.Recording>.value(_FakeRecording_8( + returnValue: _i15.Future<_i10.Recording>.value(_FakeRecording_9( this, Invocation.method( #start, @@ -699,71 +748,71 @@ class MockPendingRecording extends _i1.Mock implements _i8.PendingRecording { ), )), returnValueForMissingStub: - _i14.Future<_i9.Recording>.value(_FakeRecording_8( + _i15.Future<_i10.Recording>.value(_FakeRecording_9( this, Invocation.method( #start, [], ), )), - ) as _i14.Future<_i9.Recording>); + ) as _i15.Future<_i10.Recording>); } /// A class which mocks [Recording]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockRecording extends _i1.Mock implements _i9.Recording { +class MockRecording extends _i1.Mock implements _i10.Recording { @override - _i14.Future close() => (super.noSuchMethod( + _i15.Future close() => (super.noSuchMethod( Invocation.method( #close, [], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); @override - _i14.Future pause() => (super.noSuchMethod( + _i15.Future pause() => (super.noSuchMethod( Invocation.method( #pause, [], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); @override - _i14.Future resume() => (super.noSuchMethod( + _i15.Future resume() => (super.noSuchMethod( Invocation.method( #resume, [], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); @override - _i14.Future stop() => (super.noSuchMethod( + _i15.Future stop() => (super.noSuchMethod( Invocation.method( #stop, [], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); } /// A class which mocks [VideoCapture]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockVideoCapture extends _i1.Mock implements _i27.VideoCapture { +class MockVideoCapture extends _i1.Mock implements _i28.VideoCapture { @override - _i14.Future<_i10.Recorder> getOutput() => (super.noSuchMethod( + _i15.Future<_i11.Recorder> getOutput() => (super.noSuchMethod( Invocation.method( #getOutput, [], ), - returnValue: _i14.Future<_i10.Recorder>.value(_FakeRecorder_9( + returnValue: _i15.Future<_i11.Recorder>.value(_FakeRecorder_10( this, Invocation.method( #getOutput, @@ -771,32 +820,32 @@ class MockVideoCapture extends _i1.Mock implements _i27.VideoCapture { ), )), returnValueForMissingStub: - _i14.Future<_i10.Recorder>.value(_FakeRecorder_9( + _i15.Future<_i11.Recorder>.value(_FakeRecorder_10( this, Invocation.method( #getOutput, [], ), )), - ) as _i14.Future<_i10.Recorder>); + ) as _i15.Future<_i11.Recorder>); } /// A class which mocks [BuildContext]. /// /// See the documentation for Mockito's code generation for more information. -class MockBuildContext extends _i1.Mock implements _i11.BuildContext { +class MockBuildContext extends _i1.Mock implements _i12.BuildContext { @override - _i11.Widget get widget => (super.noSuchMethod( + _i12.Widget get widget => (super.noSuchMethod( Invocation.getter(#widget), - returnValue: _FakeWidget_10( + returnValue: _FakeWidget_11( this, Invocation.getter(#widget), ), - returnValueForMissingStub: _FakeWidget_10( + returnValueForMissingStub: _FakeWidget_11( this, Invocation.getter(#widget), ), - ) as _i11.Widget); + ) as _i12.Widget); @override bool get mounted => (super.noSuchMethod( Invocation.getter(#mounted), @@ -810,8 +859,8 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { returnValueForMissingStub: false, ) as bool); @override - _i11.InheritedWidget dependOnInheritedElement( - _i11.InheritedElement? ancestor, { + _i12.InheritedWidget dependOnInheritedElement( + _i12.InheritedElement? ancestor, { Object? aspect, }) => (super.noSuchMethod( @@ -820,7 +869,7 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { [ancestor], {#aspect: aspect}, ), - returnValue: _FakeInheritedWidget_11( + returnValue: _FakeInheritedWidget_12( this, Invocation.method( #dependOnInheritedElement, @@ -828,7 +877,7 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { {#aspect: aspect}, ), ), - returnValueForMissingStub: _FakeInheritedWidget_11( + returnValueForMissingStub: _FakeInheritedWidget_12( this, Invocation.method( #dependOnInheritedElement, @@ -836,9 +885,9 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { {#aspect: aspect}, ), ), - ) as _i11.InheritedWidget); + ) as _i12.InheritedWidget); @override - void visitAncestorElements(_i11.ConditionalElementVisitor? visitor) => + void visitAncestorElements(_i12.ConditionalElementVisitor? visitor) => super.noSuchMethod( Invocation.method( #visitAncestorElements, @@ -847,7 +896,7 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { returnValueForMissingStub: null, ); @override - void visitChildElements(_i11.ElementVisitor? visitor) => super.noSuchMethod( + void visitChildElements(_i12.ElementVisitor? visitor) => super.noSuchMethod( Invocation.method( #visitChildElements, [visitor], @@ -855,7 +904,7 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { returnValueForMissingStub: null, ); @override - void dispatchNotification(_i11.Notification? notification) => + void dispatchNotification(_i12.Notification? notification) => super.noSuchMethod( Invocation.method( #dispatchNotification, @@ -864,9 +913,9 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { returnValueForMissingStub: null, ); @override - _i13.DiagnosticsNode describeElement( + _i14.DiagnosticsNode describeElement( String? name, { - _i13.DiagnosticsTreeStyle? style = _i13.DiagnosticsTreeStyle.errorProperty, + _i14.DiagnosticsTreeStyle? style = _i14.DiagnosticsTreeStyle.errorProperty, }) => (super.noSuchMethod( Invocation.method( @@ -874,7 +923,7 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { [name], {#style: style}, ), - returnValue: _FakeDiagnosticsNode_12( + returnValue: _FakeDiagnosticsNode_13( this, Invocation.method( #describeElement, @@ -882,7 +931,7 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { {#style: style}, ), ), - returnValueForMissingStub: _FakeDiagnosticsNode_12( + returnValueForMissingStub: _FakeDiagnosticsNode_13( this, Invocation.method( #describeElement, @@ -890,11 +939,11 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { {#style: style}, ), ), - ) as _i13.DiagnosticsNode); + ) as _i14.DiagnosticsNode); @override - _i13.DiagnosticsNode describeWidget( + _i14.DiagnosticsNode describeWidget( String? name, { - _i13.DiagnosticsTreeStyle? style = _i13.DiagnosticsTreeStyle.errorProperty, + _i14.DiagnosticsTreeStyle? style = _i14.DiagnosticsTreeStyle.errorProperty, }) => (super.noSuchMethod( Invocation.method( @@ -902,7 +951,7 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { [name], {#style: style}, ), - returnValue: _FakeDiagnosticsNode_12( + returnValue: _FakeDiagnosticsNode_13( this, Invocation.method( #describeWidget, @@ -910,7 +959,7 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { {#style: style}, ), ), - returnValueForMissingStub: _FakeDiagnosticsNode_12( + returnValueForMissingStub: _FakeDiagnosticsNode_13( this, Invocation.method( #describeWidget, @@ -918,9 +967,9 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { {#style: style}, ), ), - ) as _i13.DiagnosticsNode); + ) as _i14.DiagnosticsNode); @override - List<_i13.DiagnosticsNode> describeMissingAncestor( + List<_i14.DiagnosticsNode> describeMissingAncestor( {required Type? expectedAncestorType}) => (super.noSuchMethod( Invocation.method( @@ -928,38 +977,38 @@ class MockBuildContext extends _i1.Mock implements _i11.BuildContext { [], {#expectedAncestorType: expectedAncestorType}, ), - returnValue: <_i13.DiagnosticsNode>[], - returnValueForMissingStub: <_i13.DiagnosticsNode>[], - ) as List<_i13.DiagnosticsNode>); + returnValue: <_i14.DiagnosticsNode>[], + returnValueForMissingStub: <_i14.DiagnosticsNode>[], + ) as List<_i14.DiagnosticsNode>); @override - _i13.DiagnosticsNode describeOwnershipChain(String? name) => + _i14.DiagnosticsNode describeOwnershipChain(String? name) => (super.noSuchMethod( Invocation.method( #describeOwnershipChain, [name], ), - returnValue: _FakeDiagnosticsNode_12( + returnValue: _FakeDiagnosticsNode_13( this, Invocation.method( #describeOwnershipChain, [name], ), ), - returnValueForMissingStub: _FakeDiagnosticsNode_12( + returnValueForMissingStub: _FakeDiagnosticsNode_13( this, Invocation.method( #describeOwnershipChain, [name], ), ), - ) as _i13.DiagnosticsNode); + ) as _i14.DiagnosticsNode); } /// A class which mocks [TestInstanceManagerHostApi]. /// /// See the documentation for Mockito's code generation for more information. class MockTestInstanceManagerHostApi extends _i1.Mock - implements _i28.TestInstanceManagerHostApi { + implements _i29.TestInstanceManagerHostApi { @override void clear() => super.noSuchMethod( Invocation.method( @@ -974,19 +1023,19 @@ class MockTestInstanceManagerHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestSystemServicesHostApi extends _i1.Mock - implements _i28.TestSystemServicesHostApi { + implements _i29.TestSystemServicesHostApi { @override - _i14.Future<_i6.CameraPermissionsErrorData?> requestCameraPermissions( + _i15.Future<_i7.CameraPermissionsErrorData?> requestCameraPermissions( bool? enableAudio) => (super.noSuchMethod( Invocation.method( #requestCameraPermissions, [enableAudio], ), - returnValue: _i14.Future<_i6.CameraPermissionsErrorData?>.value(), + returnValue: _i15.Future<_i7.CameraPermissionsErrorData?>.value(), returnValueForMissingStub: - _i14.Future<_i6.CameraPermissionsErrorData?>.value(), - ) as _i14.Future<_i6.CameraPermissionsErrorData?>); + _i15.Future<_i7.CameraPermissionsErrorData?>.value(), + ) as _i15.Future<_i7.CameraPermissionsErrorData?>); @override void startListeningForDeviceOrientationChange( bool? isFrontFacing, @@ -1032,7 +1081,7 @@ class MockTestSystemServicesHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockZoomState extends _i1.Mock implements _i16.ZoomState { +class MockZoomState extends _i1.Mock implements _i17.ZoomState { @override double get minZoomRatio => (super.noSuchMethod( Invocation.getter(#minZoomRatio), @@ -1052,30 +1101,30 @@ class MockZoomState extends _i1.Mock implements _i16.ZoomState { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockLiveCameraState extends _i1.Mock - implements _i3.LiveData<_i15.CameraState> { + implements _i4.LiveData<_i16.CameraState> { MockLiveCameraState() { _i1.throwOnMissingStub(this); } @override - _i14.Future observe(_i29.Observer<_i15.CameraState>? observer) => + _i15.Future observe(_i30.Observer<_i16.CameraState>? observer) => (super.noSuchMethod( Invocation.method( #observe, [observer], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); @override - _i14.Future removeObservers() => (super.noSuchMethod( + _i15.Future removeObservers() => (super.noSuchMethod( Invocation.method( #removeObservers, [], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); } /// A class which mocks [LiveData]. @@ -1083,28 +1132,28 @@ class MockLiveCameraState extends _i1.Mock /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockLiveZoomState extends _i1.Mock - implements _i3.LiveData<_i16.ZoomState> { + implements _i4.LiveData<_i17.ZoomState> { MockLiveZoomState() { _i1.throwOnMissingStub(this); } @override - _i14.Future observe(_i29.Observer<_i16.ZoomState>? observer) => + _i15.Future observe(_i30.Observer<_i17.ZoomState>? observer) => (super.noSuchMethod( Invocation.method( #observe, [observer], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); @override - _i14.Future removeObservers() => (super.noSuchMethod( + _i15.Future removeObservers() => (super.noSuchMethod( Invocation.method( #removeObservers, [], ), - returnValue: _i14.Future.value(), - returnValueForMissingStub: _i14.Future.value(), - ) as _i14.Future); + returnValue: _i15.Future.value(), + returnValueForMissingStub: _i15.Future.value(), + ) as _i15.Future); } diff --git a/packages/camera/camera_android_camerax/test/camera_control_test.dart b/packages/camera/camera_android_camerax/test/camera_control_test.dart new file mode 100644 index 000000000000..99acc94b4bc0 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_control_test.dart @@ -0,0 +1,67 @@ +// 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_control.dart'; +import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'camera_control_test.mocks.dart'; +import 'test_camerax_library.g.dart'; + +@GenerateMocks([TestCameraControlHostApi, TestInstanceManagerHostApi]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + // Mocks the call to clear the native InstanceManager. + TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi()); + + group('CameraControl', () { + tearDown(() => TestCameraHostApi.setup(null)); + + test('enableTorch makes call on Java side to enable torch', () async { + final MockTestCameraControlHostApi mockApi = + MockTestCameraControlHostApi(); + TestCameraControlHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final CameraControl cameraControl = CameraControl.detached( + instanceManager: instanceManager, + ); + const int cameraControlIdentifier = 22; + + instanceManager.addHostCreatedInstance( + cameraControl, + cameraControlIdentifier, + onCopy: (_) => CameraControl.detached(instanceManager: instanceManager), + ); + + const bool enableTorch = true; + await cameraControl.enableTorch(enableTorch); + + verify(mockApi.enableTorch(cameraControlIdentifier, enableTorch)); + }); + + test('flutterApiCreate makes call to add instance to instance manager', () { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final CameraControlFlutterApiImpl flutterApi = + CameraControlFlutterApiImpl( + instanceManager: instanceManager, + ); + const int cameraControlIdentifier = 67; + + flutterApi.create(cameraControlIdentifier); + + expect( + instanceManager.getInstanceWithWeakReference(cameraControlIdentifier), + isA()); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart new file mode 100644 index 000000000000..0f43f0c4348f --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart @@ -0,0 +1,69 @@ +// Mocks generated by Mockito 5.4.1 from annotations +// in camera_android_camerax/test/camera_control_test.dart. +// Do not manually edit this file. + +// @dart=2.19 + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; + +import 'package:mockito/mockito.dart' as _i1; + +import 'test_camerax_library.g.dart' as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +/// A class which mocks [TestCameraControlHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestCameraControlHostApi extends _i1.Mock + implements _i2.TestCameraControlHostApi { + MockTestCameraControlHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future enableTorch( + int? identifier, + bool? torch, + ) => + (super.noSuchMethod( + Invocation.method( + #enableTorch, + [ + identifier, + torch, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); +} + +/// A class which mocks [TestInstanceManagerHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestInstanceManagerHostApi extends _i1.Mock + implements _i2.TestInstanceManagerHostApi { + MockTestInstanceManagerHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void clear() => super.noSuchMethod( + Invocation.method( + #clear, + [], + ), + returnValueForMissingStub: null, + ); +} diff --git a/packages/camera/camera_android_camerax/test/camera_test.dart b/packages/camera/camera_android_camerax/test/camera_test.dart index 05f5fe7dd029..22d9dabd40d3 100644 --- a/packages/camera/camera_android_camerax/test/camera_test.dart +++ b/packages/camera/camera_android_camerax/test/camera_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:camera_android_camerax/src/camera.dart'; +import 'package:camera_android_camerax/src/camera_control.dart'; import 'package:camera_android_camerax/src/camera_info.dart'; import 'package:camera_android_camerax/src/instance_manager.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -54,6 +55,39 @@ void main() { verify(mockApi.getCameraInfo(cameraIdentifier)); }); + test('getCameraControl makes call to retrieve expected CameraControl', + () async { + final MockTestCameraHostApi mockApi = MockTestCameraHostApi(); + TestCameraHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final Camera camera = Camera.detached( + instanceManager: instanceManager, + ); + const int cameraIdentifier = 42; + final CameraControl cameraControl = CameraControl.detached(); + const int cameraControlIdentifier = 8; + instanceManager.addHostCreatedInstance( + camera, + cameraIdentifier, + onCopy: (_) => Camera.detached(instanceManager: instanceManager), + ); + instanceManager.addHostCreatedInstance( + cameraControl, + cameraControlIdentifier, + onCopy: (_) => CameraControl.detached(instanceManager: instanceManager), + ); + + when(mockApi.getCameraControl(cameraIdentifier)) + .thenAnswer((_) => cameraControlIdentifier); + + expect(await camera.getCameraControl(), equals(cameraControl)); + verify(mockApi.getCameraControl(cameraIdentifier)); + }); + test('flutterApiCreate makes call to add instance to instance manager', () { final InstanceManager instanceManager = InstanceManager( onWeakReferenceRemoved: (_) {}, diff --git a/packages/camera/camera_android_camerax/test/camera_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera_test.mocks.dart index 47a15aedf993..7bae8650bd0c 100644 --- a/packages/camera/camera_android_camerax/test/camera_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/camera_test.mocks.dart @@ -36,6 +36,14 @@ class MockTestCameraHostApi extends _i1.Mock implements _i2.TestCameraHostApi { ), returnValue: 0, ) as int); + @override + int getCameraControl(int? identifier) => (super.noSuchMethod( + Invocation.method( + #getCameraControl, + [identifier], + ), + returnValue: 0, + ) as int); } /// A class which mocks [TestInstanceManagerHostApi]. diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart index c649a735215d..88e44ae61195 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart @@ -426,6 +426,8 @@ abstract class TestCameraHostApi { int getCameraInfo(int identifier); + int getCameraControl(int identifier); + static void setup(TestCameraHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -450,6 +452,28 @@ abstract class TestCameraHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraHostApi.getCameraControl', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraHostApi.getCameraControl 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.CameraHostApi.getCameraControl was null, expected non-null int.'); + final int output = api.getCameraControl(arg_identifier!); + return [output]; + }); + } + } } } @@ -1721,3 +1745,40 @@ abstract class TestFallbackStrategyHostApi { } } } + +abstract class TestCameraControlHostApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = StandardMessageCodec(); + + Future enableTorch(int identifier, bool torch); + + static void setup(TestCameraControlHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraControlHostApi.enableTorch', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraControlHostApi.enableTorch 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.CameraControlHostApi.enableTorch was null, expected non-null int.'); + final bool? arg_torch = (args[1] as bool?); + assert(arg_torch != null, + 'Argument for dev.flutter.pigeon.CameraControlHostApi.enableTorch was null, expected non-null bool.'); + await api.enableTorch(arg_identifier!, arg_torch!); + return []; + }); + } + } + } +}