diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 68b090e7bfb..7c1a948dc08 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.5.0+28 +* Wraps CameraX classes needed to implement setting focus and exposure points and exposure offset. * Updates compileSdk version to 34. ## 0.5.0+27 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 6781e85212e..b0e0493cbe2 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 @@ -116,7 +116,8 @@ public void setUp( binaryMessenger, new FallbackStrategyHostApiImpl(instanceManager)); GeneratedCameraXLibrary.QualitySelectorHostApi.setup( binaryMessenger, new QualitySelectorHostApiImpl(instanceManager)); - cameraControlHostApiImpl = new CameraControlHostApiImpl(instanceManager, context); + cameraControlHostApiImpl = + new CameraControlHostApiImpl(binaryMessenger, instanceManager, context); GeneratedCameraXLibrary.CameraControlHostApi.setup(binaryMessenger, cameraControlHostApiImpl); } 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 index 2524d503af4..9d9778c6921 100644 --- 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 @@ -8,11 +8,15 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.camera.core.CameraControl; +import androidx.camera.core.FocusMeteringAction; +import androidx.camera.core.FocusMeteringResult; 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.plugin.common.BinaryMessenger; import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraControlHostApi; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.Result; import java.util.Objects; /** @@ -29,6 +33,8 @@ public class CameraControlHostApiImpl implements CameraControlHostApi { @VisibleForTesting public static class CameraControlProxy { Context context; + BinaryMessenger binaryMessenger; + InstanceManager instanceManager; /** Enables or disables the torch of the specified {@link CameraControl} instance. */ @NonNull @@ -82,6 +88,85 @@ public void onFailure(Throwable t) { }, ContextCompat.getMainExecutor(context)); } + + /** + * Starts a focus and metering action configured by the {@code FocusMeteringAction}. + * + *

Will trigger an auto focus action and enable auto focus/auto exposure/auto white balance + * metering regions. + */ + public void startFocusAndMetering( + @NonNull CameraControl cameraControl, + @NonNull FocusMeteringAction focusMeteringAction, + @NonNull GeneratedCameraXLibrary.Result result) { + ListenableFuture focusMeteringResultFuture = + cameraControl.startFocusAndMetering(focusMeteringAction); + + Futures.addCallback( + focusMeteringResultFuture, + new FutureCallback() { + public void onSuccess(FocusMeteringResult focusMeteringResult) { + final FocusMeteringResultFlutterApiImpl flutterApi = + new FocusMeteringResultFlutterApiImpl(binaryMessenger, instanceManager); + flutterApi.create(focusMeteringResult, reply -> {}); + result.success(instanceManager.getIdentifierForStrongReference(focusMeteringResult)); + } + + public void onFailure(Throwable t) { + result.error(t); + } + }, + ContextCompat.getMainExecutor(context)); + } + + /** + * Cancels current {@code FocusMeteringAction} and clears auto focus/auto exposure/auto white + * balance regions. + */ + public void cancelFocusAndMetering( + @NonNull CameraControl cameraControl, @NonNull Result result) { + ListenableFuture cancelFocusAndMeteringFuture = cameraControl.cancelFocusAndMetering(); + + Futures.addCallback( + cancelFocusAndMeteringFuture, + new FutureCallback() { + public void onSuccess(Void voidResult) { + result.success(null); + } + + public void onFailure(Throwable t) { + result.error(t); + } + }, + ContextCompat.getMainExecutor(context)); + } + + /** + * Sets the exposure compensation index for the specified {@link CameraControl} instance and + * returns the new target exposure value. + * + *

The exposure compensation value set on the camera must be within the range of {@code + * ExposureState#getExposureCompensationRange()} for the current {@code ExposureState} for the + * call to succeed. + */ + public void setExposureCompensationIndex( + @NonNull CameraControl cameraControl, @NonNull Long index, @NonNull Result result) { + ListenableFuture setExposureCompensationIndexFuture = + cameraControl.setExposureCompensationIndex(index.intValue()); + + Futures.addCallback( + setExposureCompensationIndexFuture, + new FutureCallback() { + public void onSuccess(Integer integerResult) { + result.success(integerResult.longValue()); + } + + public void onFailure(Throwable t) { + result.error(t); + } + }, + ContextCompat.getMainExecutor(context)); + } } /** @@ -90,8 +175,10 @@ public void onFailure(Throwable t) { * @param instanceManager maintains instances stored to communicate with attached Dart objects */ public CameraControlHostApiImpl( - @NonNull InstanceManager instanceManager, @NonNull Context context) { - this(instanceManager, new CameraControlProxy(), context); + @NonNull BinaryMessenger binaryMessenger, + @NonNull InstanceManager instanceManager, + @NonNull Context context) { + this(binaryMessenger, instanceManager, new CameraControlProxy(), context); } /** @@ -103,12 +190,16 @@ public CameraControlHostApiImpl( */ @VisibleForTesting CameraControlHostApiImpl( + @NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager, @NonNull CameraControlProxy proxy, @NonNull Context context) { this.instanceManager = instanceManager; this.proxy = proxy; proxy.context = context; + // proxy.startFocusAndMetering needs to access these to create a FocusMeteringResult when it becomes available: + proxy.instanceManager = instanceManager; + proxy.binaryMessenger = binaryMessenger; } /** @@ -127,8 +218,7 @@ public void enableTorch( @NonNull Long identifier, @NonNull Boolean torch, @NonNull GeneratedCameraXLibrary.Result result) { - proxy.enableTorch( - Objects.requireNonNull(instanceManager.getInstance(identifier)), torch, result); + proxy.enableTorch(getCameraControlInstance(identifier), torch, result); } @Override @@ -136,7 +226,30 @@ public void setZoomRatio( @NonNull Long identifier, @NonNull Double ratio, @NonNull GeneratedCameraXLibrary.Result result) { - proxy.setZoomRatio( - Objects.requireNonNull(instanceManager.getInstance(identifier)), ratio, result); + proxy.setZoomRatio(getCameraControlInstance(identifier), ratio, result); + } + + @Override + public void startFocusAndMetering( + @NonNull Long identifier, @NonNull Long focusMeteringActionId, @NonNull Result result) { + proxy.startFocusAndMetering( + getCameraControlInstance(identifier), + Objects.requireNonNull(instanceManager.getInstance(focusMeteringActionId)), + result); + } + + @Override + public void cancelFocusAndMetering(@NonNull Long identifier, @NonNull Result result) { + proxy.cancelFocusAndMetering(getCameraControlInstance(identifier), result); + } + + @Override + public void setExposureCompensationIndex( + @NonNull Long identifier, @NonNull Long index, @NonNull Result result) { + proxy.setExposureCompensationIndex(getCameraControlInstance(identifier), index, result); + } + + private CameraControl getCameraControlInstance(@NonNull Long identifier) { + return Objects.requireNonNull(instanceManager.getInstance(identifier)); } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringActionHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringActionHostApiImpl.java new file mode 100644 index 00000000000..8c6222ff7b6 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringActionHostApiImpl.java @@ -0,0 +1,115 @@ +// 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.annotation.VisibleForTesting; +import androidx.camera.core.FocusMeteringAction; +import androidx.camera.core.MeteringPoint; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FocusMeteringActionHostApi; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.MeteringPointInfo; +import java.util.ArrayList; +import java.util.List; + +/** + * Host API implementation for {@link FocusMeteringAction}. + * + *

This class may handle 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 FocusMeteringActionHostApiImpl implements FocusMeteringActionHostApi { + private final InstanceManager instanceManager; + + private final FocusMeteringActionProxy proxy; + + /** Proxy for constructors and static method of {@link FocusMeteringAction}. */ + @VisibleForTesting + public static class FocusMeteringActionProxy { + /** Creates an instance of {@link FocusMeteringAction}. */ + public @NonNull FocusMeteringAction create( + @NonNull List meteringPoints, @NonNull List meteringPointModes) { + if (meteringPoints.size() >= 1 && meteringPoints.size() != meteringPointModes.size()) { + throw new IllegalArgumentException( + "One metering point must be specified and the number of specified metering points must match the number of specified metering point modes."); + } + + FocusMeteringAction.Builder focusMeteringActionBuilder; + + // Create builder to potentially add more MeteringPoints to. + MeteringPoint firstMeteringPoint = meteringPoints.get(0); + Integer firstMeteringPointMode = meteringPointModes.get(0); + if (firstMeteringPointMode == null) { + focusMeteringActionBuilder = getFocusMeteringActionBuilder(firstMeteringPoint); + } else { + focusMeteringActionBuilder = + getFocusMeteringActionBuilder(firstMeteringPoint, firstMeteringPointMode); + } + + // Add any additional metering points in order as specified by input lists. + for (int i = 1; i < meteringPoints.size(); i++) { + MeteringPoint meteringPoint = meteringPoints.get(i); + Integer meteringMode = meteringPointModes.get(i); + + if (meteringMode == null) { + focusMeteringActionBuilder.addPoint(meteringPoint); + } else { + focusMeteringActionBuilder.addPoint(meteringPoint, meteringMode); + } + } + + return focusMeteringActionBuilder.build(); + } + + @VisibleForTesting + @NonNull + public FocusMeteringAction.Builder getFocusMeteringActionBuilder( + @NonNull MeteringPoint meteringPoint) { + return new FocusMeteringAction.Builder(meteringPoint); + } + + @VisibleForTesting + @NonNull + public FocusMeteringAction.Builder getFocusMeteringActionBuilder( + @NonNull MeteringPoint meteringPoint, int meteringMode) { + return new FocusMeteringAction.Builder(meteringPoint, meteringMode); + } + } + + /** + * Constructs a {@link FocusMeteringActionHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with attached Dart objects + */ + public FocusMeteringActionHostApiImpl(@NonNull InstanceManager instanceManager) { + this(instanceManager, new FocusMeteringActionProxy()); + } + + /** + * Constructs a {@link FocusMeteringActionHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with attached Dart objects + * @param proxy proxy for constructors and static method of {@link FocusMeteringAction} + */ + FocusMeteringActionHostApiImpl( + @NonNull InstanceManager instanceManager, @NonNull FocusMeteringActionProxy proxy) { + this.instanceManager = instanceManager; + this.proxy = proxy; + } + + @Override + public void create( + @NonNull Long identifier, @NonNull List meteringPointInfos) { + final List meteringPoints = new ArrayList(); + final List meteringPointModes = new ArrayList(); + for (MeteringPointInfo meteringPointInfo : meteringPointInfos) { + meteringPoints.add(instanceManager.getInstance(meteringPointInfo.getMeteringPointId())); + Long meteringPointMode = meteringPointInfo.getMeteringMode(); + meteringPointModes.add(meteringPointMode == null ? null : meteringPointMode.intValue()); + } + + instanceManager.addDartCreatedInstance( + proxy.create(meteringPoints, meteringPointModes), identifier); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringResultFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringResultFlutterApiImpl.java new file mode 100644 index 00000000000..3c0ed4c4482 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringResultFlutterApiImpl.java @@ -0,0 +1,55 @@ +// 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.annotation.VisibleForTesting; +import androidx.camera.core.FocusMeteringResult; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FocusMeteringResultFlutterApi; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FocusMeteringResultFlutterApi.Reply; + +/** + * Flutter API implementation for {@link FocusMeteringResult}. + * + *

This class may handle adding native instances that are attached to a Dart instance or passing + * arguments of callbacks methods to a Dart instance. + */ +public class FocusMeteringResultFlutterApiImpl { + private final BinaryMessenger binaryMessenger; + private final InstanceManager instanceManager; + private FocusMeteringResultFlutterApi focusMeteringResultFlutterApi; + + /** + * Constructs a {@link FocusMeteringResultFlutterApiImpl}. + * + * @param binaryMessenger used to communicate with Dart over asynchronous messages + * @param instanceManager maintains instances stored to communicate with attached Dart objects + */ + public FocusMeteringResultFlutterApiImpl( + @NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) { + this.binaryMessenger = binaryMessenger; + this.instanceManager = instanceManager; + focusMeteringResultFlutterApi = new FocusMeteringResultFlutterApi(binaryMessenger); + } + + /** + * Stores the {@link FocusMeteringResult} instance and notifies Dart to create and store a new + * {@link FocusMeteringResult} instance that is attached to this one. If {@code instance} has + * already been added, this method does nothing. + */ + public void create(@NonNull FocusMeteringResult instance, @NonNull Reply callback) { + if (!instanceManager.containsInstance(instance)) { + focusMeteringResultFlutterApi.create( + instanceManager.addHostCreatedInstance(instance), callback); + } + } + + /** Sets the Flutter API used to send messages to Dart. */ + @VisibleForTesting + void setApi(@NonNull FocusMeteringResultFlutterApi api) { + this.focusMeteringResultFlutterApi = api; + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringResultHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringResultHostApiImpl.java new file mode 100644 index 00000000000..2c6c33e0c26 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringResultHostApiImpl.java @@ -0,0 +1,65 @@ +// 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.annotation.VisibleForTesting; +import androidx.camera.core.FocusMeteringResult; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FocusMeteringResultHostApi; + +/** + * Host API implementation for {@link FocusMeteringResult}. + * + *

This class may handle 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 FocusMeteringResultHostApiImpl implements FocusMeteringResultHostApi { + private final InstanceManager instanceManager; + + private final FocusMeteringResultProxy proxy; + + /** Proxy for constructors and static method of {@link FocusMeteringResult}. */ + @VisibleForTesting + public static class FocusMeteringResultProxy { + + /** + * Returns whether or not auto focus was successful. + * + *

If the current camera does not support auto focus, it will return true. If auto focus is + * not requested, it will return false. + */ + @NonNull + public Boolean isFocusSuccessful(@NonNull FocusMeteringResult focusMeteringResult) { + return focusMeteringResult.isFocusSuccessful(); + } + } + + /** + * Constructs a {@link FocusMeteringResultHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with attached Dart objects + */ + public FocusMeteringResultHostApiImpl(@NonNull InstanceManager instanceManager) { + this(instanceManager, new FocusMeteringResultProxy()); + } + + /** + * Constructs a {@link FocusMeteringResultHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with attached Dart objects + * @param proxy proxy for constructors and static method of {@link FocusMeteringResult} + */ + FocusMeteringResultHostApiImpl( + @NonNull InstanceManager instanceManager, @NonNull FocusMeteringResultProxy proxy) { + this.instanceManager = instanceManager; + this.proxy = proxy; + } + + @Override + @NonNull + public Boolean isFocusSuccessful(@NonNull Long identifier) { + return proxy.isFocusSuccessful(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 e8da1975336..0797d7bee8f 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 @@ -529,6 +529,90 @@ ArrayList toList() { } } + /** + * Convenience class for building [FocusMeteringAction] with multiple metering points. + * + *

Generated class from Pigeon that represents data sent in messages. + */ + public static final class MeteringPointInfo { + /** Instance manager ID corresponding to [MeteringPoint] that relates to this info. */ + private @NonNull Long meteringPointId; + + public @NonNull Long getMeteringPointId() { + return meteringPointId; + } + + public void setMeteringPointId(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"meteringPointId\" is null."); + } + this.meteringPointId = setterArg; + } + + /** Metering mode represented by one of the [FocusMeteringAction] constants. */ + private @Nullable Long meteringMode; + + public @Nullable Long getMeteringMode() { + return meteringMode; + } + + public void setMeteringMode(@Nullable Long setterArg) { + this.meteringMode = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + MeteringPointInfo() {} + + public static final class Builder { + + private @Nullable Long meteringPointId; + + public @NonNull Builder setMeteringPointId(@NonNull Long setterArg) { + this.meteringPointId = setterArg; + return this; + } + + private @Nullable Long meteringMode; + + public @NonNull Builder setMeteringMode(@Nullable Long setterArg) { + this.meteringMode = setterArg; + return this; + } + + public @NonNull MeteringPointInfo build() { + MeteringPointInfo pigeonReturn = new MeteringPointInfo(); + pigeonReturn.setMeteringPointId(meteringPointId); + pigeonReturn.setMeteringMode(meteringMode); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(2); + toListResult.add(meteringPointId); + toListResult.add(meteringMode); + return toListResult; + } + + static @NonNull MeteringPointInfo fromList(@NonNull ArrayList list) { + MeteringPointInfo pigeonResult = new MeteringPointInfo(); + Object meteringPointId = list.get(0); + pigeonResult.setMeteringPointId( + (meteringPointId == null) + ? null + : ((meteringPointId instanceof Integer) + ? (Integer) meteringPointId + : (Long) meteringPointId)); + Object meteringMode = list.get(1); + pigeonResult.setMeteringMode( + (meteringMode == null) + ? null + : ((meteringMode instanceof Integer) ? (Integer) meteringMode : (Long) meteringMode)); + return pigeonResult; + } + } + public interface Result { @SuppressWarnings("UnknownNullness") void success(T result); @@ -3427,6 +3511,16 @@ void enableTorch( void setZoomRatio( @NonNull Long identifier, @NonNull Double ratio, @NonNull Result result); + void startFocusAndMetering( + @NonNull Long identifier, + @NonNull Long focusMeteringActionId, + @NonNull Result result); + + void cancelFocusAndMetering(@NonNull Long identifier, @NonNull Result result); + + void setExposureCompensationIndex( + @NonNull Long identifier, @NonNull Long index, @NonNull Result result); + /** The codec used by CameraControlHostApi. */ static @NonNull MessageCodec getCodec() { return new StandardMessageCodec(); @@ -3505,6 +3599,110 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.CameraControlHostApi.startFocusAndMetering", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + Number focusMeteringActionIdArg = (Number) args.get(1); + Result resultCallback = + new Result() { + public void success(Long result) { + wrapped.add(0, result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; + + api.startFocusAndMetering( + (identifierArg == null) ? null : identifierArg.longValue(), + (focusMeteringActionIdArg == null) + ? null + : focusMeteringActionIdArg.longValue(), + resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.CameraControlHostApi.cancelFocusAndMetering", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + 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.cancelFocusAndMetering( + (identifierArg == null) ? null : identifierArg.longValue(), resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.CameraControlHostApi.setExposureCompensationIndex", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + Number indexArg = (Number) args.get(1); + Result resultCallback = + new Result() { + public void success(Long result) { + wrapped.add(0, result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; + + api.setExposureCompensationIndex( + (identifierArg == null) ? null : identifierArg.longValue(), + (indexArg == null) ? null : indexArg.longValue(), + resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } } } /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ @@ -3534,4 +3732,340 @@ public void create(@NonNull Long identifierArg, @NonNull Reply callback) { channelReply -> callback.reply(null)); } } + + private static class FocusMeteringActionHostApiCodec extends StandardMessageCodec { + public static final FocusMeteringActionHostApiCodec INSTANCE = + new FocusMeteringActionHostApiCodec(); + + private FocusMeteringActionHostApiCodec() {} + + @Override + protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { + switch (type) { + case (byte) 128: + return MeteringPointInfo.fromList((ArrayList) readValue(buffer)); + default: + return super.readValueOfType(type, buffer); + } + } + + @Override + protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { + if (value instanceof MeteringPointInfo) { + stream.write(128); + writeValue(stream, ((MeteringPointInfo) value).toList()); + } else { + super.writeValue(stream, value); + } + } + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface FocusMeteringActionHostApi { + + void create(@NonNull Long identifier, @NonNull List meteringPointInfos); + + /** The codec used by FocusMeteringActionHostApi. */ + static @NonNull MessageCodec getCodec() { + return FocusMeteringActionHostApiCodec.INSTANCE; + } + /** + * Sets up an instance of `FocusMeteringActionHostApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup( + @NonNull BinaryMessenger binaryMessenger, @Nullable FocusMeteringActionHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.FocusMeteringActionHostApi.create", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + List meteringPointInfosArg = + (List) args.get(1); + try { + api.create( + (identifierArg == null) ? null : identifierArg.longValue(), + meteringPointInfosArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface FocusMeteringResultHostApi { + + @NonNull + Boolean isFocusSuccessful(@NonNull Long identifier); + + /** The codec used by FocusMeteringResultHostApi. */ + static @NonNull MessageCodec getCodec() { + return new StandardMessageCodec(); + } + /** + * Sets up an instance of `FocusMeteringResultHostApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup( + @NonNull BinaryMessenger binaryMessenger, @Nullable FocusMeteringResultHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.FocusMeteringResultHostApi.isFocusSuccessful", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + try { + Boolean output = + api.isFocusSuccessful( + (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. */ + public static class FocusMeteringResultFlutterApi { + private final @NonNull BinaryMessenger binaryMessenger; + + public FocusMeteringResultFlutterApi(@NonNull BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + /** Public interface for sending reply. */ + @SuppressWarnings("UnknownNullness") + public interface Reply { + void reply(T reply); + } + /** The codec used by FocusMeteringResultFlutterApi. */ + static @NonNull MessageCodec getCodec() { + return new StandardMessageCodec(); + } + + public void create(@NonNull Long identifierArg, @NonNull Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.FocusMeteringResultFlutterApi.create", + getCodec()); + channel.send( + new ArrayList(Collections.singletonList(identifierArg)), + channelReply -> callback.reply(null)); + } + } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface MeteringPointHostApi { + + void create( + @NonNull Long identifier, @NonNull Double x, @NonNull Double y, @Nullable Double size); + + @NonNull + Double getDefaultPointSize(); + + /** The codec used by MeteringPointHostApi. */ + static @NonNull MessageCodec getCodec() { + return new StandardMessageCodec(); + } + /** + * Sets up an instance of `MeteringPointHostApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup( + @NonNull BinaryMessenger binaryMessenger, @Nullable MeteringPointHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.MeteringPointHostApi.create", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + Double xArg = (Double) args.get(1); + Double yArg = (Double) args.get(2); + Double sizeArg = (Double) args.get(3); + try { + api.create( + (identifierArg == null) ? null : identifierArg.longValue(), + xArg, + yArg, + sizeArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.MeteringPointHostApi.getDefaultPointSize", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + Double output = api.getDefaultPointSize(); + wrapped.add(0, output); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface DisplayOrientedMeteringPointFactoryHostApi { + + void create( + @NonNull Long identifier, + @NonNull Long cameraInfoId, + @NonNull Long width, + @NonNull Long height); + + @NonNull + Long createPoint(@NonNull Long x, @NonNull Long y, @Nullable Long size); + + @NonNull + Long getDefaultPointSize(); + + /** The codec used by DisplayOrientedMeteringPointFactoryHostApi. */ + static @NonNull MessageCodec getCodec() { + return new StandardMessageCodec(); + } + /** + * Sets up an instance of `DisplayOrientedMeteringPointFactoryHostApi` to handle messages + * through the `binaryMessenger`. + */ + static void setup( + @NonNull BinaryMessenger binaryMessenger, + @Nullable DisplayOrientedMeteringPointFactoryHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.create", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + Number cameraInfoIdArg = (Number) args.get(1); + Number widthArg = (Number) args.get(2); + Number heightArg = (Number) args.get(3); + try { + api.create( + (identifierArg == null) ? null : identifierArg.longValue(), + (cameraInfoIdArg == null) ? null : cameraInfoIdArg.longValue(), + (widthArg == null) ? null : widthArg.longValue(), + (heightArg == null) ? null : heightArg.longValue()); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.createPoint", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Number xArg = (Number) args.get(0); + Number yArg = (Number) args.get(1); + Number sizeArg = (Number) args.get(2); + try { + Long output = + api.createPoint( + (xArg == null) ? null : xArg.longValue(), + (yArg == null) ? null : yArg.longValue(), + (sizeArg == null) ? null : sizeArg.longValue()); + wrapped.add(0, output); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.getDefaultPointSize", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + Long output = api.getDefaultPointSize(); + wrapped.add(0, output); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/MeteringPointHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/MeteringPointHostApiImpl.java new file mode 100644 index 00000000000..343fbe731ac --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/MeteringPointHostApiImpl.java @@ -0,0 +1,96 @@ +// 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.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.camera.core.MeteringPoint; +import androidx.camera.core.MeteringPointFactory; +import androidx.camera.core.SurfaceOrientedMeteringPointFactory; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.MeteringPointHostApi; + +/** + * Host API implementation for {@link MeteringPoint}. + * + *

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 MeteringPointHostApiImpl implements MeteringPointHostApi { + private final InstanceManager instanceManager; + private final MeteringPointProxy proxy; + + /** Proxy for constructors and static method of {@link MeteringPoint}. */ + @VisibleForTesting + public static class MeteringPointProxy { + + /** + * Creates a surface oriented {@link MeteringPoint} with the specified x, y, and size. + * + *

A {@link SurfaceOrientedMeteringPointFactory} is used to construct the {@link + * MeteringPoint} because underlying the camera preview that this plugin uses is a Flutter + * texture that is backed by a {@link Surface} created by the Flutter Android embedding. + */ + @NonNull + public MeteringPoint create(@NonNull Double x, @NonNull Double y, @Nullable Double size) { + SurfaceOrientedMeteringPointFactory factory = getSurfaceOrientedMeteringPointFactory(1f, 1f); + if (size == null) { + return factory.createPoint(x.floatValue(), y.floatValue()); + } else { + return factory.createPoint(x.floatValue(), y.floatValue(), size.floatValue()); + } + } + + @VisibleForTesting + @NonNull + public SurfaceOrientedMeteringPointFactory getSurfaceOrientedMeteringPointFactory( + float width, float height) { + return new SurfaceOrientedMeteringPointFactory(width, height); + } + + /** + * Returns the default point size of the {@link MeteringPoint} width and height, which is a + * normalized percentage of the sensor width/height. + */ + @NonNull + public float getDefaultPointSize() { + return MeteringPointFactory.getDefaultPointSize(); + } + } + /** + * Constructs a {@link MeteringPointHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with attached Dart objects + */ + public MeteringPointHostApiImpl(@NonNull InstanceManager instanceManager) { + this(instanceManager, new MeteringPointProxy()); + } + + /** + * Constructs a {@link MeteringPointHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with attached Dart objects + * @param proxy proxy for constructors and static method of {@link MeteringPoint} + */ + @VisibleForTesting + MeteringPointHostApiImpl( + @NonNull InstanceManager instanceManager, @NonNull MeteringPointProxy proxy) { + this.instanceManager = instanceManager; + this.proxy = proxy; + } + + @Override + public void create( + @NonNull Long identifier, @NonNull Double x, @NonNull Double y, @Nullable Double size) { + MeteringPoint meteringPoint = proxy.create(x, y, size); + instanceManager.addDartCreatedInstance(meteringPoint, identifier); + } + + @Override + @NonNull + public Double getDefaultPointSize() { + return (double) proxy.getDefaultPointSize(); + } +} 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 index a01e3db8238..42ace7a51fb 100644 --- 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 @@ -13,6 +13,8 @@ import android.content.Context; import androidx.camera.core.CameraControl; +import androidx.camera.core.FocusMeteringAction; +import androidx.camera.core.FocusMeteringResult; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -51,7 +53,8 @@ public void tearDown() { public void enableTorch_turnsTorchModeOnAndOffAsExpected() { try (MockedStatic mockedFutures = Mockito.mockStatic(Futures.class)) { final CameraControlHostApiImpl cameraControlHostApiImpl = - new CameraControlHostApiImpl(testInstanceManager, mock(Context.class)); + new CameraControlHostApiImpl( + mockBinaryMessenger, testInstanceManager, mock(Context.class)); final Long cameraControlIdentifier = 88L; final boolean enableTorch = true; @@ -66,7 +69,7 @@ public void enableTorch_turnsTorchModeOnAndOffAsExpected() { final ArgumentCaptor> futureCallbackCaptor = ArgumentCaptor.forClass(FutureCallback.class); - // Test turning on torch mode. + // Test successful behavior. @SuppressWarnings("unchecked") final GeneratedCameraXLibrary.Result successfulMockResult = mock(GeneratedCameraXLibrary.Result.class); @@ -81,7 +84,7 @@ public void enableTorch_turnsTorchModeOnAndOffAsExpected() { successfulEnableTorchCallback.onSuccess(mock(Void.class)); verify(successfulMockResult).success(null); - // Test turning off torch mode. + // Test failed behavior. @SuppressWarnings("unchecked") final GeneratedCameraXLibrary.Result failedMockResult = mock(GeneratedCameraXLibrary.Result.class); @@ -101,7 +104,8 @@ public void enableTorch_turnsTorchModeOnAndOffAsExpected() { public void setZoomRatio_setsZoomAsExpected() { try (MockedStatic mockedFutures = Mockito.mockStatic(Futures.class)) { final CameraControlHostApiImpl cameraControlHostApiImpl = - new CameraControlHostApiImpl(testInstanceManager, mock(Context.class)); + new CameraControlHostApiImpl( + mockBinaryMessenger, testInstanceManager, mock(Context.class)); final Long cameraControlIdentifier = 33L; final Double zoomRatio = 0.2D; @@ -148,6 +152,183 @@ public void setZoomRatio_setsZoomAsExpected() { } } + @Test + public void startFocusAndMetering_startsFocusAndMeteringAsExpected() { + try (MockedStatic mockedFutures = Mockito.mockStatic(Futures.class)) { + final CameraControlHostApiImpl cameraControlHostApiImpl = + new CameraControlHostApiImpl( + mockBinaryMessenger, testInstanceManager, mock(Context.class)); + final Long cameraControlIdentifier = 90L; + final FocusMeteringAction mockAction = mock(FocusMeteringAction.class); + final Long mockActionId = 44L; + final FocusMeteringResult mockResult = mock(FocusMeteringResult.class); + final Long mockResultId = 33L; + + @SuppressWarnings("unchecked") + final ListenableFuture startFocusAndMeteringFuture = + mock(ListenableFuture.class); + + testInstanceManager.addDartCreatedInstance(cameraControl, cameraControlIdentifier); + testInstanceManager.addDartCreatedInstance(mockResult, mockResultId); + testInstanceManager.addDartCreatedInstance(mockAction, mockActionId); + + when(cameraControl.startFocusAndMetering(mockAction)).thenReturn(startFocusAndMeteringFuture); + + @SuppressWarnings("unchecked") + final ArgumentCaptor> futureCallbackCaptor = + ArgumentCaptor.forClass(FutureCallback.class); + + // Test successful behavior. + @SuppressWarnings("unchecked") + final GeneratedCameraXLibrary.Result successfulMockResult = + mock(GeneratedCameraXLibrary.Result.class); + cameraControlHostApiImpl.startFocusAndMetering( + cameraControlIdentifier, mockActionId, successfulMockResult); + mockedFutures.verify( + () -> + Futures.addCallback( + eq(startFocusAndMeteringFuture), futureCallbackCaptor.capture(), any())); + mockedFutures.clearInvocations(); + + FutureCallback successfulCallback = futureCallbackCaptor.getValue(); + + successfulCallback.onSuccess(mockResult); + verify(successfulMockResult).success(mockResultId); + + // Test failed behavior. + @SuppressWarnings("unchecked") + final GeneratedCameraXLibrary.Result failedMockResult = + mock(GeneratedCameraXLibrary.Result.class); + final Throwable testThrowable = new Throwable(); + cameraControlHostApiImpl.startFocusAndMetering( + cameraControlIdentifier, mockActionId, failedMockResult); + mockedFutures.verify( + () -> + Futures.addCallback( + eq(startFocusAndMeteringFuture), futureCallbackCaptor.capture(), any())); + mockedFutures.clearInvocations(); + + FutureCallback failedCallback = futureCallbackCaptor.getValue(); + + failedCallback.onFailure(testThrowable); + verify(failedMockResult).error(testThrowable); + } + } + + @Test + public void cancelFocusAndMetering_cancelsFocusAndMeteringAsExpected() { + try (MockedStatic mockedFutures = Mockito.mockStatic(Futures.class)) { + final CameraControlHostApiImpl cameraControlHostApiImpl = + new CameraControlHostApiImpl( + mockBinaryMessenger, testInstanceManager, mock(Context.class)); + final Long cameraControlIdentifier = 8L; + + @SuppressWarnings("unchecked") + final ListenableFuture cancelFocusAndMeteringFuture = mock(ListenableFuture.class); + + testInstanceManager.addDartCreatedInstance(cameraControl, cameraControlIdentifier); + + when(cameraControl.cancelFocusAndMetering()).thenReturn(cancelFocusAndMeteringFuture); + + @SuppressWarnings("unchecked") + final ArgumentCaptor> futureCallbackCaptor = + ArgumentCaptor.forClass(FutureCallback.class); + + // Test successful behavior. + @SuppressWarnings("unchecked") + final GeneratedCameraXLibrary.Result successfulMockResult = + mock(GeneratedCameraXLibrary.Result.class); + cameraControlHostApiImpl.cancelFocusAndMetering( + cameraControlIdentifier, successfulMockResult); + mockedFutures.verify( + () -> + Futures.addCallback( + eq(cancelFocusAndMeteringFuture), futureCallbackCaptor.capture(), any())); + mockedFutures.clearInvocations(); + + FutureCallback successfulCallback = futureCallbackCaptor.getValue(); + + successfulCallback.onSuccess(mock(Void.class)); + verify(successfulMockResult).success(null); + + // Test failed behavior. + @SuppressWarnings("unchecked") + final GeneratedCameraXLibrary.Result failedMockResult = + mock(GeneratedCameraXLibrary.Result.class); + final Throwable testThrowable = new Throwable(); + cameraControlHostApiImpl.cancelFocusAndMetering(cameraControlIdentifier, failedMockResult); + mockedFutures.verify( + () -> + Futures.addCallback( + eq(cancelFocusAndMeteringFuture), futureCallbackCaptor.capture(), any())); + + FutureCallback failedCallback = futureCallbackCaptor.getValue(); + + failedCallback.onFailure(testThrowable); + verify(failedMockResult).error(testThrowable); + } + } + + @Test + public void setExposureCompensationIndex_setsExposureCompensationIndexAsExpected() { + try (MockedStatic mockedFutures = Mockito.mockStatic(Futures.class)) { + final CameraControlHostApiImpl cameraControlHostApiImpl = + new CameraControlHostApiImpl( + mockBinaryMessenger, testInstanceManager, mock(Context.class)); + final Long cameraControlIdentifier = 53L; + final Long index = 2L; + + @SuppressWarnings("unchecked") + final ListenableFuture setExposureCompensationIndexFuture = + mock(ListenableFuture.class); + + testInstanceManager.addDartCreatedInstance(cameraControl, cameraControlIdentifier); + + when(cameraControl.setExposureCompensationIndex(index.intValue())) + .thenReturn(setExposureCompensationIndexFuture); + + @SuppressWarnings("unchecked") + final ArgumentCaptor> futureCallbackCaptor = + ArgumentCaptor.forClass(FutureCallback.class); + + // Test successful behavior. + @SuppressWarnings("unchecked") + final GeneratedCameraXLibrary.Result successfulMockResult = + mock(GeneratedCameraXLibrary.Result.class); + cameraControlHostApiImpl.setExposureCompensationIndex( + cameraControlIdentifier, index, successfulMockResult); + mockedFutures.verify( + () -> + Futures.addCallback( + eq(setExposureCompensationIndexFuture), futureCallbackCaptor.capture(), any())); + mockedFutures.clearInvocations(); + + FutureCallback successfulCallback = futureCallbackCaptor.getValue(); + final Integer fakeResult = 4; + + successfulCallback.onSuccess(fakeResult); + verify(successfulMockResult).success(fakeResult.longValue()); + + // Test failed behavior. + @SuppressWarnings("unchecked") + final GeneratedCameraXLibrary.Result failedMockResult = + mock(GeneratedCameraXLibrary.Result.class); + final Throwable testThrowable = new Throwable(); + cameraControlHostApiImpl.setExposureCompensationIndex( + cameraControlIdentifier, index, failedMockResult); + mockedFutures.verify( + () -> + Futures.addCallback( + eq(setExposureCompensationIndexFuture), futureCallbackCaptor.capture(), any())); + mockedFutures.clearInvocations(); + + FutureCallback failedCallback = futureCallbackCaptor.getValue(); + + failedCallback.onFailure(testThrowable); + verify(failedMockResult).error(testThrowable); + } + } + @Test public void flutterApiCreate_makesCallToCreateInstanceOnDartSide() { final CameraControlFlutterApiImpl spyFlutterApi = diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FocusMeteringActionTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FocusMeteringActionTest.java new file mode 100644 index 00000000000..64db8054161 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FocusMeteringActionTest.java @@ -0,0 +1,152 @@ +// 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.junit.Assert.assertEquals; +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 androidx.camera.core.FocusMeteringAction; +import androidx.camera.core.MeteringPoint; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.MeteringPointInfo; +import java.util.Arrays; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class FocusMeteringActionTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public BinaryMessenger mockBinaryMessenger; + @Mock public FocusMeteringAction focusMeteringAction; + + InstanceManager testInstanceManager; + + @Before + public void setUp() { + testInstanceManager = InstanceManager.create(identifier -> {}); + } + + @After + public void tearDown() { + testInstanceManager.stopFinalizationListener(); + } + + @Test + public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatHasMode() { + FocusMeteringActionHostApiImpl.FocusMeteringActionProxy proxySpy = + spy(new FocusMeteringActionHostApiImpl.FocusMeteringActionProxy()); + FocusMeteringActionHostApiImpl hostApi = + new FocusMeteringActionHostApiImpl(testInstanceManager, proxySpy); + final Long focusMeteringActionIdentifier = 43L; + + FocusMeteringAction.Builder mockFocusMeteringActionBuilder = + mock(FocusMeteringAction.Builder.class); + final MeteringPoint mockMeteringPoint1 = mock(MeteringPoint.class); + final MeteringPoint mockMeteringPoint2 = mock(MeteringPoint.class); + final MeteringPoint mockMeteringPoint3 = mock(MeteringPoint.class); + final Long mockMeteringPoint1Id = 47L; + final Long mockMeteringPoint2Id = 56L; + final Long mockMeteringPoint3Id = 99L; + final Integer mockMeteringPoint1Mode = FocusMeteringAction.FLAG_AE; + final Integer mockMeteringPoint2Mode = FocusMeteringAction.FLAG_AF; + + MeteringPointInfo fakeMeteringPointInfo1 = + new MeteringPointInfo.Builder() + .setMeteringPointId(mockMeteringPoint1Id) + .setMeteringMode(mockMeteringPoint1Mode.longValue()) + .build(); + MeteringPointInfo fakeMeteringPointInfo2 = + new MeteringPointInfo.Builder() + .setMeteringPointId(mockMeteringPoint2Id) + .setMeteringMode(mockMeteringPoint2Mode.longValue()) + .build(); + MeteringPointInfo fakeMeteringPointInfo3 = + new MeteringPointInfo.Builder() + .setMeteringPointId(mockMeteringPoint3Id) + .setMeteringMode(null) + .build(); + + testInstanceManager.addDartCreatedInstance(mockMeteringPoint1, mockMeteringPoint1Id); + testInstanceManager.addDartCreatedInstance(mockMeteringPoint2, mockMeteringPoint2Id); + testInstanceManager.addDartCreatedInstance(mockMeteringPoint3, mockMeteringPoint3Id); + + when(proxySpy.getFocusMeteringActionBuilder( + mockMeteringPoint1, mockMeteringPoint1Mode.intValue())) + .thenReturn(mockFocusMeteringActionBuilder); + when(mockFocusMeteringActionBuilder.build()).thenReturn(focusMeteringAction); + + List mockMeteringPointInfos = + Arrays.asList(fakeMeteringPointInfo1, fakeMeteringPointInfo2, fakeMeteringPointInfo3); + + hostApi.create(focusMeteringActionIdentifier, mockMeteringPointInfos); + + verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint2, mockMeteringPoint2Mode); + verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint3); + assertEquals( + testInstanceManager.getInstance(focusMeteringActionIdentifier), focusMeteringAction); + } + + @Test + public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatDoesNotHaveMode() { + FocusMeteringActionHostApiImpl.FocusMeteringActionProxy proxySpy = + spy(new FocusMeteringActionHostApiImpl.FocusMeteringActionProxy()); + FocusMeteringActionHostApiImpl hostApi = + new FocusMeteringActionHostApiImpl(testInstanceManager, proxySpy); + final Long focusMeteringActionIdentifier = 43L; + + FocusMeteringAction.Builder mockFocusMeteringActionBuilder = + mock(FocusMeteringAction.Builder.class); + final MeteringPoint mockMeteringPoint1 = mock(MeteringPoint.class); + final MeteringPoint mockMeteringPoint2 = mock(MeteringPoint.class); + final MeteringPoint mockMeteringPoint3 = mock(MeteringPoint.class); + final Long mockMeteringPoint1Id = 47L; + final Long mockMeteringPoint2Id = 56L; + final Long mockMeteringPoint3Id = 99L; + final Integer mockMeteringPoint2Mode = FocusMeteringAction.FLAG_AF; + + MeteringPointInfo fakeMeteringPointInfo1 = + new MeteringPointInfo.Builder() + .setMeteringPointId(mockMeteringPoint1Id) + .setMeteringMode(null) + .build(); + MeteringPointInfo fakeMeteringPointInfo2 = + new MeteringPointInfo.Builder() + .setMeteringPointId(mockMeteringPoint2Id) + .setMeteringMode(mockMeteringPoint2Mode.longValue()) + .build(); + MeteringPointInfo fakeMeteringPointInfo3 = + new MeteringPointInfo.Builder() + .setMeteringPointId(mockMeteringPoint3Id) + .setMeteringMode(null) + .build(); + + testInstanceManager.addDartCreatedInstance(mockMeteringPoint1, mockMeteringPoint1Id); + testInstanceManager.addDartCreatedInstance(mockMeteringPoint2, mockMeteringPoint2Id); + testInstanceManager.addDartCreatedInstance(mockMeteringPoint3, mockMeteringPoint3Id); + + when(proxySpy.getFocusMeteringActionBuilder(mockMeteringPoint1)) + .thenReturn(mockFocusMeteringActionBuilder); + when(mockFocusMeteringActionBuilder.build()).thenReturn(focusMeteringAction); + + List mockMeteringPointInfos = + Arrays.asList(fakeMeteringPointInfo1, fakeMeteringPointInfo2, fakeMeteringPointInfo3); + + hostApi.create(focusMeteringActionIdentifier, mockMeteringPointInfos); + + verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint2, mockMeteringPoint2Mode); + verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint3); + assertEquals( + testInstanceManager.getInstance(focusMeteringActionIdentifier), focusMeteringAction); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FocusMeteringResultTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FocusMeteringResultTest.java new file mode 100644 index 00000000000..dae86557b45 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FocusMeteringResultTest.java @@ -0,0 +1,73 @@ +// 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.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import androidx.camera.core.FocusMeteringResult; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FocusMeteringResultFlutterApi; +import java.util.Objects; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class FocusMeteringResultTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public BinaryMessenger mockBinaryMessenger; + @Mock public FocusMeteringResult focusMeteringResult; + @Mock public FocusMeteringResultFlutterApi mockFlutterApi; + + InstanceManager testInstanceManager; + + @Before + public void setUp() { + testInstanceManager = InstanceManager.create(identifier -> {}); + } + + @After + public void tearDown() { + testInstanceManager.stopFinalizationListener(); + } + + @Test + public void isFocusSuccessful_returnsExpectedResult() { + final FocusMeteringResultHostApiImpl focusMeteringResultHostApiImpl = + new FocusMeteringResultHostApiImpl(testInstanceManager); + final Long focusMeteringResultIdentifier = 98L; + final boolean result = true; + + testInstanceManager.addDartCreatedInstance(focusMeteringResult, focusMeteringResultIdentifier); + + when(focusMeteringResult.isFocusSuccessful()).thenReturn(result); + + assertTrue(focusMeteringResultHostApiImpl.isFocusSuccessful(focusMeteringResultIdentifier)); + verify(focusMeteringResult).isFocusSuccessful(); + } + + @Test + public void flutterApiCreate_makesCallToCreateInstanceOnDartSide() { + final FocusMeteringResultFlutterApiImpl flutterApi = + new FocusMeteringResultFlutterApiImpl(mockBinaryMessenger, testInstanceManager); + + flutterApi.setApi(mockFlutterApi); + + flutterApi.create(focusMeteringResult, reply -> {}); + final long focusMeteringResultIdentifier = + Objects.requireNonNull( + testInstanceManager.getIdentifierForStrongReference(focusMeteringResult)); + + verify(mockFlutterApi).create(eq(focusMeteringResultIdentifier), any()); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/MeteringPointTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/MeteringPointTest.java new file mode 100644 index 00000000000..f245eb53124 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/MeteringPointTest.java @@ -0,0 +1,113 @@ +// 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.junit.Assert.assertEquals; +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 androidx.camera.core.MeteringPoint; +import androidx.camera.core.MeteringPointFactory; +import androidx.camera.core.SurfaceOrientedMeteringPointFactory; +import io.flutter.plugin.common.BinaryMessenger; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.stubbing.Answer; + +public class MeteringPointTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public BinaryMessenger mockBinaryMessenger; + @Mock public MeteringPoint meteringPoint; + + InstanceManager testInstanceManager; + + @Before + public void setUp() { + testInstanceManager = InstanceManager.create(identifier -> {}); + } + + @After + public void tearDown() { + testInstanceManager.stopFinalizationListener(); + } + + @Test + public void hostApiCreate_createsExpectedMeteringPointWithSizeSpecified() { + MeteringPointHostApiImpl.MeteringPointProxy proxySpy = + spy(new MeteringPointHostApiImpl.MeteringPointProxy()); + MeteringPointHostApiImpl hostApi = new MeteringPointHostApiImpl(testInstanceManager, proxySpy); + final Long meteringPointIdentifier = 78L; + final Float x = 0.3f; + final Float y = 0.2f; + final Float size = 6f; + final Float surfaceWidth = 1f; + final Float surfaceHeight = 1f; + SurfaceOrientedMeteringPointFactory mockSurfaceOrientedMeteringPointFactory = + mock(SurfaceOrientedMeteringPointFactory.class); + + when(proxySpy.getSurfaceOrientedMeteringPointFactory(surfaceWidth, surfaceHeight)) + .thenReturn(mockSurfaceOrientedMeteringPointFactory); + when(mockSurfaceOrientedMeteringPointFactory.createPoint(x, y, size)).thenReturn(meteringPoint); + + hostApi.create(meteringPointIdentifier, x.doubleValue(), y.doubleValue(), size.doubleValue()); + + verify(mockSurfaceOrientedMeteringPointFactory).createPoint(x, y, size); + assertEquals(testInstanceManager.getInstance(meteringPointIdentifier), meteringPoint); + } + + @Test + public void hostApiCreate_createsExpectedMeteringPointWithoutSizeSpecified() { + MeteringPointHostApiImpl.MeteringPointProxy proxySpy = + spy(new MeteringPointHostApiImpl.MeteringPointProxy()); + MeteringPointHostApiImpl hostApi = new MeteringPointHostApiImpl(testInstanceManager, proxySpy); + final Long meteringPointIdentifier = 78L; + final Float x = 0.3f; + final Float y = 0.2f; + final Float surfaceWidth = 1f; + final Float surfaceHeight = 1f; + SurfaceOrientedMeteringPointFactory mockSurfaceOrientedMeteringPointFactory = + mock(SurfaceOrientedMeteringPointFactory.class); + + when(proxySpy.getSurfaceOrientedMeteringPointFactory(surfaceWidth, surfaceHeight)) + .thenReturn(mockSurfaceOrientedMeteringPointFactory); + when(mockSurfaceOrientedMeteringPointFactory.createPoint(x, y)).thenReturn(meteringPoint); + + hostApi.create(meteringPointIdentifier, x.doubleValue(), y.doubleValue(), null); + + verify(mockSurfaceOrientedMeteringPointFactory).createPoint(x, y); + assertEquals(testInstanceManager.getInstance(meteringPointIdentifier), meteringPoint); + } + + @Test + public void getDefaultPointSize_returnsExpectedSize() { + try (MockedStatic mockedMeteringPointFactory = + Mockito.mockStatic(MeteringPointFactory.class)) { + final MeteringPointHostApiImpl meteringPointHostApiImpl = + new MeteringPointHostApiImpl(testInstanceManager); + final Long meteringPointIdentifier = 93L; + final Long index = 2L; + final Double defaultPointSize = 4D; + + testInstanceManager.addDartCreatedInstance(meteringPoint, meteringPointIdentifier); + + mockedMeteringPointFactory + .when(() -> MeteringPointFactory.getDefaultPointSize()) + .thenAnswer((Answer) invocation -> defaultPointSize.floatValue()); + + assertEquals(meteringPointHostApiImpl.getDefaultPointSize(), defaultPointSize); + mockedMeteringPointFactory.verify(() -> MeteringPointFactory.getDefaultPointSize()); + } + } +} 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 24f159a1eab..db7ec4b32d4 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 @@ -12,6 +12,7 @@ import 'camera_state_error.dart'; import 'camerax_library.g.dart'; import 'device_orientation_manager.dart'; import 'exposure_state.dart'; +import 'focus_metering_result.dart'; import 'image_proxy.dart'; import 'java_object.dart'; import 'live_data.dart'; @@ -50,7 +51,8 @@ class AndroidCameraXCameraFlutterApis { ImageProxyFlutterApiImpl? imageProxyFlutterApiImpl, PlaneProxyFlutterApiImpl? planeProxyFlutterApiImpl, AnalyzerFlutterApiImpl? analyzerFlutterApiImpl, - CameraControlFlutterApiImpl? cameraControlFlutterApiImpl}) { + CameraControlFlutterApiImpl? cameraControlFlutterApiImpl, + FocusMeteringResultFlutterApiImpl? focusMeteringResultFlutterApiImpl}) { this.javaObjectFlutterApiImpl = javaObjectFlutterApiImpl ?? JavaObjectFlutterApiImpl(); this.cameraInfoFlutterApiImpl = @@ -94,6 +96,9 @@ class AndroidCameraXCameraFlutterApis { planeProxyFlutterApiImpl ?? PlaneProxyFlutterApiImpl(); this.cameraControlFlutterApiImpl = cameraControlFlutterApiImpl ?? CameraControlFlutterApiImpl(); + this.focusMeteringResultFlutterApiImpl = + focusMeteringResultFlutterApiImpl ?? + FocusMeteringResultFlutterApiImpl(); } static bool _haveBeenSetUp = false; @@ -169,6 +174,10 @@ class AndroidCameraXCameraFlutterApis { /// Flutter Api implementation for [CameraControl]. late final CameraControlFlutterApiImpl cameraControlFlutterApiImpl; + /// Flutter Api implementation for [FocusMeteringResult]. + late final FocusMeteringResultFlutterApiImpl + focusMeteringResultFlutterApiImpl; + /// Ensures all the Flutter APIs have been setup to receive calls from native code. void ensureSetUp() { if (!_haveBeenSetUp) { @@ -195,6 +204,7 @@ class AndroidCameraXCameraFlutterApis { LiveDataFlutterApi.setup(liveDataFlutterApiImpl); ObserverFlutterApi.setup(observerFlutterApiImpl); CameraControlFlutterApi.setup(cameraControlFlutterApiImpl); + FocusMeteringResultFlutterApi.setup(focusMeteringResultFlutterApiImpl); _haveBeenSetUp = true; } } diff --git a/packages/camera/camera_android_camerax/lib/src/camera_control.dart b/packages/camera/camera_android_camerax/lib/src/camera_control.dart index 57b84772be3..9c50ffa161f 100644 --- a/packages/camera/camera_android_camerax/lib/src/camera_control.dart +++ b/packages/camera/camera_android_camerax/lib/src/camera_control.dart @@ -7,6 +7,8 @@ import 'package:meta/meta.dart' show immutable; import 'android_camera_camerax_flutter_api_impls.dart'; import 'camerax_library.g.dart'; +import 'focus_metering_action.dart'; +import 'focus_metering_result.dart'; import 'instance_manager.dart'; import 'java_object.dart'; import 'system_services.dart'; @@ -48,6 +50,34 @@ class CameraControl extends JavaObject { Future setZoomRatio(double ratio) async { return _api.setZoomRatioFromInstance(this, ratio); } + + /// Starts a focus and metering action configured by the [FocusMeteringAction]. + /// + /// Will trigger an auto focus action and enable auto focus/auto exposure/ + /// auto white balance metering regions. + /// + /// Returns null if focus and metering could not be started. + Future startFocusAndMetering( + FocusMeteringAction action) { + return _api.startFocusAndMeteringFromInstance(this, action); + } + + /// Cancels current [FocusMeteringAction] and clears auto focus/auto exposure/ + /// auto white balance regions. + Future cancelFocusAndMetering() => + _api.cancelFocusAndMeteringFromInstance(this); + + /// Sets the exposure compensation value for related [Camera] and returns the + /// new target exposure value. + /// + /// The exposure compensation value set on the camera must be within the range + /// of the current [ExposureState]'s `exposureCompensationRange` for the call + /// to succeed. + /// + /// Returns null if the exposure compensation index failed to be set. + Future setExposureCompensationIndex(int index) async { + return _api.setExposureCompensationIndexFromInstance(this, index); + } } /// Host API implementation of [CameraControl]. @@ -94,6 +124,47 @@ class _CameraControlHostApiImpl extends CameraControlHostApi { 'Zoom ratio was unable to be set. If ratio was not out of range, newer value may have been set; otherwise, the camera may be closed.'); } } + + /// Starts a focus and metering action configured by the [FocusMeteringAction] + /// for the specified [CameraControl] instance. + Future startFocusAndMeteringFromInstance( + CameraControl instance, FocusMeteringAction action) async { + final int cameraControlIdentifier = + instanceManager.getIdentifier(instance)!; + final int actionIdentifier = instanceManager.getIdentifier(action)!; + try { + final int focusMeteringResultId = await startFocusAndMetering( + cameraControlIdentifier, actionIdentifier); + return instanceManager.getInstanceWithWeakReference( + focusMeteringResultId); + } on PlatformException catch (e) { + SystemServices.cameraErrorStreamController + .add(e.message ?? 'Starting focus and metering failed.'); + return Future.value(); + } + } + + /// Cancels current [FocusMeteringAction] and clears AF/AE/AWB regions for the + /// specified [CameraControl] instance. + Future cancelFocusAndMeteringFromInstance( + CameraControl instance) async { + final int identifier = instanceManager.getIdentifier(instance)!; + await cancelFocusAndMetering(identifier); + } + + /// Sets exposure compensation index for specified [CameraControl] instance + /// and returns the new target exposure value. + Future setExposureCompensationIndexFromInstance( + CameraControl instance, int index) async { + final int identifier = instanceManager.getIdentifier(instance)!; + try { + return setExposureCompensationIndex(identifier, index); + } on PlatformException catch (e) { + SystemServices.cameraErrorStreamController.add(e.message ?? + 'Setting the camera exposure compensation index failed.'); + return Future.value(); + } + } } /// Flutter API implementation of [CameraControl]. 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 f1528bbeec8..e57dc8222ac 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 @@ -210,6 +210,37 @@ class VideoQualityData { } } +/// Convenience class for building [FocusMeteringAction] with multiple metering +/// points. +class MeteringPointInfo { + MeteringPointInfo({ + required this.meteringPointId, + this.meteringMode, + }); + + /// Instance manager ID corresponding to [MeteringPoint] that relates to this + /// info. + int meteringPointId; + + /// Metering mode represented by one of the [FocusMeteringAction] constants. + int? meteringMode; + + Object encode() { + return [ + meteringPointId, + meteringMode, + ]; + } + + static MeteringPointInfo decode(Object result) { + result as List; + return MeteringPointInfo( + meteringPointId: result[0]! as int, + meteringMode: result[1] as int?, + ); + } +} + class InstanceManagerHostApi { /// Constructor for [InstanceManagerHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default @@ -2847,6 +2878,86 @@ class CameraControlHostApi { return; } } + + Future startFocusAndMetering( + int arg_identifier, int arg_focusMeteringActionId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraControlHostApi.startFocusAndMetering', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_identifier, arg_focusMeteringActionId]) + 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?)!; + } + } + + Future cancelFocusAndMetering(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraControlHostApi.cancelFocusAndMetering', 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 { + return; + } + } + + Future setExposureCompensationIndex( + int arg_identifier, int arg_index) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraControlHostApi.setExposureCompensationIndex', + codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel + .send([arg_identifier, arg_index]) 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 CameraControlFlutterApi { @@ -2877,3 +2988,281 @@ abstract class CameraControlFlutterApi { } } } + +class _FocusMeteringActionHostApiCodec extends StandardMessageCodec { + const _FocusMeteringActionHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is MeteringPointInfo) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return MeteringPointInfo.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class FocusMeteringActionHostApi { + /// Constructor for [FocusMeteringActionHostApi]. 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. + FocusMeteringActionHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _FocusMeteringActionHostApiCodec(); + + Future create(int arg_identifier, + List arg_meteringPointInfos) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FocusMeteringActionHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_identifier, arg_meteringPointInfos]) + 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; + } + } +} + +class FocusMeteringResultHostApi { + /// Constructor for [FocusMeteringResultHostApi]. 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. + FocusMeteringResultHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = StandardMessageCodec(); + + Future isFocusSuccessful(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FocusMeteringResultHostApi.isFocusSuccessful', + 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 bool?)!; + } + } +} + +abstract class FocusMeteringResultFlutterApi { + static const MessageCodec codec = StandardMessageCodec(); + + void create(int identifier); + + static void setup(FocusMeteringResultFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FocusMeteringResultFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.FocusMeteringResultFlutterApi.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.FocusMeteringResultFlutterApi.create was null, expected non-null int.'); + api.create(arg_identifier!); + return; + }); + } + } + } +} + +class MeteringPointHostApi { + /// Constructor for [MeteringPointHostApi]. 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. + MeteringPointHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = StandardMessageCodec(); + + Future create( + int arg_identifier, double arg_x, double arg_y, double? arg_size) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.MeteringPointHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_identifier, arg_x, arg_y, arg_size]) + 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; + } + } + + Future getDefaultPointSize() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.MeteringPointHostApi.getDefaultPointSize', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel.send(null) 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 double?)!; + } + } +} + +class DisplayOrientedMeteringPointFactoryHostApi { + /// Constructor for [DisplayOrientedMeteringPointFactoryHostApi]. 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. + DisplayOrientedMeteringPointFactoryHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = StandardMessageCodec(); + + Future create(int arg_identifier, int arg_cameraInfoId, int arg_width, + int arg_height) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.create', + codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel.send( + [arg_identifier, arg_cameraInfoId, arg_width, arg_height]) + 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; + } + } + + Future createPoint(int arg_x, int arg_y, int? arg_size) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.createPoint', + codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_x, arg_y, arg_size]) 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?)!; + } + } + + Future getDefaultPointSize() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.getDefaultPointSize', + codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel.send(null) 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?)!; + } + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/focus_metering_action.dart b/packages/camera/camera_android_camerax/lib/src/focus_metering_action.dart new file mode 100644 index 00000000000..6d9ebd97f01 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/focus_metering_action.dart @@ -0,0 +1,116 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart' show BinaryMessenger; +import 'package:meta/meta.dart' show immutable; + +import 'camerax_library.g.dart'; +import 'instance_manager.dart'; +import 'java_object.dart'; +import 'metering_point.dart'; + +/// A configuration used to trigger a focus and/or metering action. +/// +/// See https://developer.android.com/reference/androidx/camera/core/FocusMeteringAction. +@immutable +class FocusMeteringAction extends JavaObject { + /// Creates a [FocusMeteringAction]. + FocusMeteringAction({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + required List<(MeteringPoint meteringPoint, int? meteringMode)> + meteringPointInfos, + }) : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _api = _FocusMeteringActionHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + _api.createFromInstance(this, meteringPointInfos); + } + + /// Creates a [FocusMeteringAction] that is not automatically attached to a + /// native object. + FocusMeteringAction.detached({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _api = _FocusMeteringActionHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + } + + late final _FocusMeteringActionHostApiImpl _api; + + /// Flag for metering mode that indicates the auto focus region is enabled. + /// + /// An autofocus scan is also triggered when [flagAf] is assigned. + /// + /// See https://developer.android.com/reference/androidx/camera/core/FocusMeteringAction#FLAG_AF(). + static const int flagAf = 1; + + /// Flag for metering mode that indicates the auto exposure region is enabled. + /// + /// See https://developer.android.com/reference/androidx/camera/core/FocusMeteringAction#FLAG_AE(). + static const int flagAe = 2; + + /// Flag for metering mode that indicates the auto white balance region is + /// enabled. + /// + /// See https://developer.android.com/reference/androidx/camera/core/FocusMeteringAction#FLAG_AWB(). + static const int flagAwb = 4; +} + +/// Host API implementation of [FocusMeteringAction]. +class _FocusMeteringActionHostApiImpl extends FocusMeteringActionHostApi { + /// Constructs a [_FocusMeteringActionHostApiImpl]. + /// + /// 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]. + _FocusMeteringActionHostApiImpl( + {this.binaryMessenger, InstanceManager? instanceManager}) { + 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; + + /// Creates a [FocusMeteringAction] instance with the specified list of + /// [MeteringPoint]s and their modes in order of descending priority. + void createFromInstance( + FocusMeteringAction instance, + List<(MeteringPoint meteringPoint, int? meteringMode)> + meteringPointInfos) { + final int identifier = instanceManager.addDartCreatedInstance(instance, + onCopy: (FocusMeteringAction original) { + return FocusMeteringAction.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + }); + + final List meteringPointInfosWithIds = + []; + for (final ( + MeteringPoint meteringPoint, + int? meteringMode + ) meteringPointInfo in meteringPointInfos) { + meteringPointInfosWithIds.add(MeteringPointInfo( + meteringPointId: instanceManager.getIdentifier(meteringPointInfo.$1)!, + meteringMode: meteringPointInfo.$2)); + } + + create(identifier, meteringPointInfosWithIds); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/focus_metering_result.dart b/packages/camera/camera_android_camerax/lib/src/focus_metering_result.dart new file mode 100644 index 00000000000..3a1afbd5fa5 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/focus_metering_result.dart @@ -0,0 +1,108 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart' show BinaryMessenger; +import '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'; + +/// The result of [CameraControl.startFocusAndMetering]. +/// +/// See https://developer.android.com/reference/kotlin/androidx/camera/core/FocusMeteringResult. +@immutable +class FocusMeteringResult extends JavaObject { + /// Creates a [FocusMeteringResult] that is not automatically attached to a + /// native object. + FocusMeteringResult.detached({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _api = _FocusMeteringResultHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + } + + late final _FocusMeteringResultHostApiImpl _api; + + /// Returns whether or not auto focus is successful. + /// + /// If the current camera does not support auto focus, it will return true. If + /// auto focus is not requested, it will return false. + Future isFocusSuccessful() => _api.isFocusSuccessfulFromInstance(this); +} + +/// Host API implementation of [FocusMeteringResult]. +class _FocusMeteringResultHostApiImpl extends FocusMeteringResultHostApi { + /// Constructs a [FocusMeteringActionHostApiImpl]. + /// + /// 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]. + _FocusMeteringResultHostApiImpl( + {this.binaryMessenger, InstanceManager? instanceManager}) { + 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; + + /// Returns whether or not the [instance] indicates that auto focus was + /// successful. + Future isFocusSuccessfulFromInstance(FocusMeteringResult instance) { + final int identifier = instanceManager.getIdentifier(instance)!; + return isFocusSuccessful(identifier); + } +} + +/// Flutter API implementation of [FocusMeteringResult]. +class FocusMeteringResultFlutterApiImpl extends FocusMeteringResultFlutterApi { + /// Constructs a [FocusMeteringResultFlutterApiImpl]. + /// + /// 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]. + FocusMeteringResultFlutterApiImpl({ + 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( + FocusMeteringResult.detached( + binaryMessenger: _binaryMessenger, instanceManager: _instanceManager), + identifier, + onCopy: (FocusMeteringResult original) { + return FocusMeteringResult.detached( + binaryMessenger: _binaryMessenger, + instanceManager: _instanceManager); + }, + ); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/metering_point.dart b/packages/camera/camera_android_camerax/lib/src/metering_point.dart new file mode 100644 index 00000000000..d699993a893 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/metering_point.dart @@ -0,0 +1,118 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart' show BinaryMessenger; +import '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'; + +/// Representation for a region which can be converted to sensor coordinate +/// system for focus and metering purpose. +/// +/// See https://developer.android.com/reference/androidx/camera/core/MeteringPoint. +@immutable +class MeteringPoint extends JavaObject { + /// Creates a [MeteringPoint]. + MeteringPoint({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + required this.x, + required this.y, + this.size, + }) : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _api = _MeteringPointHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + _api.createFromInstance(this, x, y, size); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + } + + /// Creates a [MeteringPoint] that is not automatically attached to a + /// native object. + MeteringPoint.detached({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + required this.x, + required this.y, + this.size, + }) : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _api = _MeteringPointHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + } + + late final _MeteringPointHostApiImpl _api; + + /// X coordinate. + final double x; + + /// Y coordinate. + final double y; + + /// The size of the [MeteringPoint] width and height (ranging from 0 to 1), + /// which is a normalized percentage of the sensor width/height (or crop + /// region width/height if crop region is set). + final double? size; + + /// The default size of the [MeteringPoint] width and height (ranging from 0 + /// to 1) which is a (normalized) percentage of the sensor width/height (or + /// crop region width/height if crop region is set). + static Future getDefaultPointSize( + {BinaryMessenger? binaryMessenger}) { + final MeteringPointHostApi hostApi = + MeteringPointHostApi(binaryMessenger: binaryMessenger); + return hostApi.getDefaultPointSize(); + } +} + +/// Host API implementation of [MeteringPoint]. +class _MeteringPointHostApiImpl extends MeteringPointHostApi { + /// Constructs a [_MeteringPointHostApiImpl]. + /// + /// 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]. + _MeteringPointHostApiImpl( + {this.binaryMessenger, InstanceManager? instanceManager}) { + 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; + + /// Creates a [MeteringPoint] instance with the specified [x] and [y] + /// coordinates as well as [size] if non-null. + Future createFromInstance( + MeteringPoint instance, double x, double y, double? size) { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (MeteringPoint original) { + return MeteringPoint.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + x: original.x, + y: original.y, + size: original.size); + }); + + return create(identifier, x, y, size); + } +} diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index 94285d148df..7fbc7e1abaf 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -126,6 +126,23 @@ enum VideoResolutionFallbackRule { lowerQualityThan, } +/// Convenience class for building [FocusMeteringAction]s with multiple metering +/// points. +class MeteringPointInfo { + MeteringPointInfo({ + required this.meteringPointId, + required this.meteringMode, + }); + + /// InstanceManager ID for a [MeteringPoint]. + int meteringPointId; + + /// The metering mode of the [MeteringPoint] whose ID is [meteringPointId]. + /// + /// Metering mode should be one of the [FocusMeteringAction] constants. + int? meteringMode; +} + @HostApi(dartHostTestHandler: 'TestInstanceManagerHostApi') abstract class InstanceManagerHostApi { /// Clear the native `InstanceManager`. @@ -444,9 +461,40 @@ abstract class CameraControlHostApi { @async void setZoomRatio(int identifier, double ratio); + + @async + int startFocusAndMetering(int identifier, int focusMeteringActionId); + + @async + void cancelFocusAndMetering(int identifier); + + @async + int setExposureCompensationIndex(int identifier, int index); } @FlutterApi() abstract class CameraControlFlutterApi { void create(int identifier); } + +@HostApi(dartHostTestHandler: 'TestFocusMeteringActionHostApi') +abstract class FocusMeteringActionHostApi { + void create(int identifier, List meteringPointInfos); +} + +@HostApi(dartHostTestHandler: 'TestFocusMeteringResultHostApi') +abstract class FocusMeteringResultHostApi { + bool isFocusSuccessful(int identifier); +} + +@FlutterApi() +abstract class FocusMeteringResultFlutterApi { + void create(int identifier); +} + +@HostApi(dartHostTestHandler: 'TestMeteringPointHostApi') +abstract class MeteringPointHostApi { + void create(int identifier, double x, double y, double? size); + + double getDefaultPointSize(); +} diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 8b8825eed1f..5ae0942e7d9 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+27 +version: 0.5.0+28 environment: sdk: ">=3.0.0 <4.0.0" 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 174fde92675..4c90e940e71 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 @@ -4,34 +4,36 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i16; -import 'dart:typed_data' as _i27; +import 'dart:typed_data' as _i29; import 'package:camera_android_camerax/src/analyzer.dart' as _i15; import 'package:camera_android_camerax/src/camera.dart' as _i9; 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 _i20; +import 'package:camera_android_camerax/src/camera_selector.dart' as _i22; import 'package:camera_android_camerax/src/camera_state.dart' as _i18; 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/fallback_strategy.dart' as _i21; -import 'package:camera_android_camerax/src/image_analysis.dart' as _i22; -import 'package:camera_android_camerax/src/image_capture.dart' as _i23; +import 'package:camera_android_camerax/src/fallback_strategy.dart' as _i23; +import 'package:camera_android_camerax/src/focus_metering_action.dart' as _i21; +import 'package:camera_android_camerax/src/focus_metering_result.dart' as _i20; +import 'package:camera_android_camerax/src/image_analysis.dart' as _i24; +import 'package:camera_android_camerax/src/image_capture.dart' as _i25; import 'package:camera_android_camerax/src/image_proxy.dart' as _i17; import 'package:camera_android_camerax/src/live_data.dart' as _i4; -import 'package:camera_android_camerax/src/observer.dart' as _i26; +import 'package:camera_android_camerax/src/observer.dart' as _i28; import 'package:camera_android_camerax/src/pending_recording.dart' as _i10; -import 'package:camera_android_camerax/src/plane_proxy.dart' as _i25; -import 'package:camera_android_camerax/src/preview.dart' as _i28; +import 'package:camera_android_camerax/src/plane_proxy.dart' as _i27; +import 'package:camera_android_camerax/src/preview.dart' as _i30; import 'package:camera_android_camerax/src/process_camera_provider.dart' - as _i29; -import 'package:camera_android_camerax/src/quality_selector.dart' as _i31; + as _i31; +import 'package:camera_android_camerax/src/quality_selector.dart' as _i33; import 'package:camera_android_camerax/src/recorder.dart' as _i11; import 'package:camera_android_camerax/src/recording.dart' as _i8; -import 'package:camera_android_camerax/src/resolution_selector.dart' as _i32; -import 'package:camera_android_camerax/src/resolution_strategy.dart' as _i33; -import 'package:camera_android_camerax/src/use_case.dart' as _i30; -import 'package:camera_android_camerax/src/video_capture.dart' as _i34; +import 'package:camera_android_camerax/src/resolution_selector.dart' as _i34; +import 'package:camera_android_camerax/src/resolution_strategy.dart' as _i35; +import 'package:camera_android_camerax/src/use_case.dart' as _i32; +import 'package:camera_android_camerax/src/video_capture.dart' as _i36; import 'package:camera_android_camerax/src/zoom_state.dart' as _i19; import 'package:camera_platform_interface/camera_platform_interface.dart' as _i6; @@ -39,9 +41,9 @@ 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 'package:mockito/src/dummies.dart' as _i24; +import 'package:mockito/src/dummies.dart' as _i26; -import 'test_camerax_library.g.dart' as _i35; +import 'test_camerax_library.g.dart' as _i37; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -402,6 +404,40 @@ class MockCameraControl extends _i1.Mock implements _i3.CameraControl { returnValue: _i16.Future.value(), returnValueForMissingStub: _i16.Future.value(), ) as _i16.Future); + + @override + _i16.Future<_i20.FocusMeteringResult?> startFocusAndMetering( + _i21.FocusMeteringAction? action) => + (super.noSuchMethod( + Invocation.method( + #startFocusAndMetering, + [action], + ), + returnValue: _i16.Future<_i20.FocusMeteringResult?>.value(), + returnValueForMissingStub: + _i16.Future<_i20.FocusMeteringResult?>.value(), + ) as _i16.Future<_i20.FocusMeteringResult?>); + + @override + _i16.Future cancelFocusAndMetering() => (super.noSuchMethod( + Invocation.method( + #cancelFocusAndMetering, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + + @override + _i16.Future setExposureCompensationIndex(int? index) => + (super.noSuchMethod( + Invocation.method( + #setExposureCompensationIndex, + [index], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); } /// A class which mocks [CameraImageData]. @@ -448,7 +484,7 @@ class MockCameraImageData extends _i1.Mock implements _i6.CameraImageData { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockCameraSelector extends _i1.Mock implements _i20.CameraSelector { +class MockCameraSelector extends _i1.Mock implements _i22.CameraSelector { @override _i16.Future> filter(List<_i2.CameraInfo>? cameraInfos) => (super.noSuchMethod( @@ -494,7 +530,7 @@ class MockExposureState extends _i1.Mock implements _i5.ExposureState { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockFallbackStrategy extends _i1.Mock implements _i21.FallbackStrategy { +class MockFallbackStrategy extends _i1.Mock implements _i23.FallbackStrategy { @override _i7.VideoQuality get quality => (super.noSuchMethod( Invocation.getter(#quality), @@ -515,7 +551,7 @@ class MockFallbackStrategy extends _i1.Mock implements _i21.FallbackStrategy { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockImageAnalysis extends _i1.Mock implements _i22.ImageAnalysis { +class MockImageAnalysis extends _i1.Mock implements _i24.ImageAnalysis { @override _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( @@ -551,7 +587,7 @@ class MockImageAnalysis extends _i1.Mock implements _i22.ImageAnalysis { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockImageCapture extends _i1.Mock implements _i23.ImageCapture { +class MockImageCapture extends _i1.Mock implements _i25.ImageCapture { @override _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( @@ -578,7 +614,7 @@ class MockImageCapture extends _i1.Mock implements _i23.ImageCapture { #takePicture, [], ), - returnValue: _i16.Future.value(_i24.dummyValue( + returnValue: _i16.Future.value(_i26.dummyValue( this, Invocation.method( #takePicture, @@ -586,7 +622,7 @@ class MockImageCapture extends _i1.Mock implements _i23.ImageCapture { ), )), returnValueForMissingStub: - _i16.Future.value(_i24.dummyValue( + _i16.Future.value(_i26.dummyValue( this, Invocation.method( #takePicture, @@ -623,16 +659,16 @@ class MockImageProxy extends _i1.Mock implements _i17.ImageProxy { ) as int); @override - _i16.Future> getPlanes() => (super.noSuchMethod( + _i16.Future> getPlanes() => (super.noSuchMethod( Invocation.method( #getPlanes, [], ), returnValue: - _i16.Future>.value(<_i25.PlaneProxy>[]), + _i16.Future>.value(<_i27.PlaneProxy>[]), returnValueForMissingStub: - _i16.Future>.value(<_i25.PlaneProxy>[]), - ) as _i16.Future>); + _i16.Future>.value(<_i27.PlaneProxy>[]), + ) as _i16.Future>); @override _i16.Future close() => (super.noSuchMethod( @@ -649,7 +685,7 @@ class MockImageProxy extends _i1.Mock implements _i17.ImageProxy { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockObserver extends _i1.Mock implements _i26.Observer<_i18.CameraState> { +class MockObserver extends _i1.Mock implements _i28.Observer<_i18.CameraState> { @override void Function(Object) get onChanged => (super.noSuchMethod( Invocation.getter(#onChanged), @@ -700,13 +736,13 @@ class MockPendingRecording extends _i1.Mock implements _i10.PendingRecording { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockPlaneProxy extends _i1.Mock implements _i25.PlaneProxy { +class MockPlaneProxy extends _i1.Mock implements _i27.PlaneProxy { @override - _i27.Uint8List get buffer => (super.noSuchMethod( + _i29.Uint8List get buffer => (super.noSuchMethod( Invocation.getter(#buffer), - returnValue: _i27.Uint8List(0), - returnValueForMissingStub: _i27.Uint8List(0), - ) as _i27.Uint8List); + returnValue: _i29.Uint8List(0), + returnValueForMissingStub: _i29.Uint8List(0), + ) as _i29.Uint8List); @override int get pixelStride => (super.noSuchMethod( @@ -727,7 +763,7 @@ class MockPlaneProxy extends _i1.Mock implements _i25.PlaneProxy { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockPreview extends _i1.Mock implements _i28.Preview { +class MockPreview extends _i1.Mock implements _i30.Preview { @override _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( @@ -787,7 +823,7 @@ class MockPreview extends _i1.Mock implements _i28.Preview { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockProcessCameraProvider extends _i1.Mock - implements _i29.ProcessCameraProvider { + implements _i31.ProcessCameraProvider { @override _i16.Future> getAvailableCameraInfos() => (super.noSuchMethod( @@ -803,8 +839,8 @@ class MockProcessCameraProvider extends _i1.Mock @override _i16.Future<_i9.Camera> bindToLifecycle( - _i20.CameraSelector? cameraSelector, - List<_i30.UseCase>? useCases, + _i22.CameraSelector? cameraSelector, + List<_i32.UseCase>? useCases, ) => (super.noSuchMethod( Invocation.method( @@ -837,7 +873,7 @@ class MockProcessCameraProvider extends _i1.Mock ) as _i16.Future<_i9.Camera>); @override - _i16.Future isBound(_i30.UseCase? useCase) => (super.noSuchMethod( + _i16.Future isBound(_i32.UseCase? useCase) => (super.noSuchMethod( Invocation.method( #isBound, [useCase], @@ -847,7 +883,7 @@ class MockProcessCameraProvider extends _i1.Mock ) as _i16.Future); @override - void unbind(List<_i30.UseCase>? useCases) => super.noSuchMethod( + void unbind(List<_i32.UseCase>? useCases) => super.noSuchMethod( Invocation.method( #unbind, [useCases], @@ -869,7 +905,7 @@ class MockProcessCameraProvider extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockQualitySelector extends _i1.Mock implements _i31.QualitySelector { +class MockQualitySelector extends _i1.Mock implements _i33.QualitySelector { @override List<_i7.VideoQualityData> get qualityList => (super.noSuchMethod( Invocation.getter(#qualityList), @@ -914,14 +950,14 @@ class MockRecorder extends _i1.Mock implements _i11.Recorder { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockResolutionSelector extends _i1.Mock - implements _i32.ResolutionSelector {} + implements _i34.ResolutionSelector {} /// A class which mocks [ResolutionStrategy]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockResolutionStrategy extends _i1.Mock - implements _i33.ResolutionStrategy {} + implements _i35.ResolutionStrategy {} /// A class which mocks [Recording]. /// @@ -973,7 +1009,7 @@ class MockRecording extends _i1.Mock implements _i8.Recording { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockVideoCapture extends _i1.Mock implements _i34.VideoCapture { +class MockVideoCapture extends _i1.Mock implements _i36.VideoCapture { @override _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( @@ -1196,7 +1232,7 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { /// /// See the documentation for Mockito's code generation for more information. class MockTestInstanceManagerHostApi extends _i1.Mock - implements _i35.TestInstanceManagerHostApi { + implements _i37.TestInstanceManagerHostApi { @override void clear() => super.noSuchMethod( Invocation.method( @@ -1211,7 +1247,7 @@ class MockTestInstanceManagerHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestSystemServicesHostApi extends _i1.Mock - implements _i35.TestSystemServicesHostApi { + implements _i37.TestSystemServicesHostApi { @override _i16.Future<_i7.CameraPermissionsErrorData?> requestCameraPermissions( bool? enableAudio) => @@ -1238,7 +1274,7 @@ class MockTestSystemServicesHostApi extends _i1.Mock suffix, ], ), - returnValue: _i24.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.method( #getTempFilePath, @@ -1248,7 +1284,7 @@ class MockTestSystemServicesHostApi extends _i1.Mock ], ), ), - returnValueForMissingStub: _i24.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.method( #getTempFilePath, @@ -1292,7 +1328,7 @@ class MockLiveCameraState extends _i1.Mock } @override - _i16.Future observe(_i26.Observer<_i18.CameraState>? observer) => + _i16.Future observe(_i28.Observer<_i18.CameraState>? observer) => (super.noSuchMethod( Invocation.method( #observe, @@ -1324,7 +1360,7 @@ class MockLiveZoomState extends _i1.Mock } @override - _i16.Future observe(_i26.Observer<_i19.ZoomState>? observer) => + _i16.Future observe(_i28.Observer<_i19.ZoomState>? observer) => (super.noSuchMethod( Invocation.method( #observe, diff --git a/packages/camera/camera_android_camerax/test/camera_control_test.dart b/packages/camera/camera_android_camerax/test/camera_control_test.dart index 77a9e0c0327..63c7a7d9aa9 100644 --- a/packages/camera/camera_android_camerax/test/camera_control_test.dart +++ b/packages/camera/camera_android_camerax/test/camera_control_test.dart @@ -3,6 +3,8 @@ // found in the LICENSE file. import 'package:camera_android_camerax/src/camera_control.dart'; +import 'package:camera_android_camerax/src/focus_metering_action.dart'; +import 'package:camera_android_camerax/src/focus_metering_result.dart'; import 'package:camera_android_camerax/src/instance_manager.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; @@ -73,6 +75,113 @@ void main() { verify(mockApi.setZoomRatio(cameraControlIdentifier, zoom)); }); + test( + 'startFocusAndMetering makes call on Java side to start focus and metering and returns expected result', + () async { + final MockTestCameraControlHostApi mockApi = + MockTestCameraControlHostApi(); + TestCameraControlHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final CameraControl cameraControl = CameraControl.detached( + instanceManager: instanceManager, + ); + const int cameraControlIdentifier = 75; + final FocusMeteringAction action = + FocusMeteringAction.detached(instanceManager: instanceManager); + const int actionId = 5; + final FocusMeteringResult result = + FocusMeteringResult.detached(instanceManager: instanceManager); + const int resultId = 2; + + instanceManager.addHostCreatedInstance( + cameraControl, + cameraControlIdentifier, + onCopy: (_) => CameraControl.detached(instanceManager: instanceManager), + ); + instanceManager.addHostCreatedInstance( + action, + actionId, + onCopy: (_) => + FocusMeteringAction.detached(instanceManager: instanceManager), + ); + instanceManager.addHostCreatedInstance( + result, + resultId, + onCopy: (_) => + FocusMeteringResult.detached(instanceManager: instanceManager), + ); + + when(mockApi.startFocusAndMetering(cameraControlIdentifier, actionId)) + .thenAnswer((_) => Future.value(resultId)); + + expect(await cameraControl.startFocusAndMetering(action), equals(result)); + verify(mockApi.startFocusAndMetering(cameraControlIdentifier, actionId)); + }); + + test( + 'cancelFocusAndMetering makes call on Java side to cancel focus and metering', + () async { + final MockTestCameraControlHostApi mockApi = + MockTestCameraControlHostApi(); + TestCameraControlHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final CameraControl cameraControl = CameraControl.detached( + instanceManager: instanceManager, + ); + const int cameraControlIdentifier = 45; + + instanceManager.addHostCreatedInstance( + cameraControl, + cameraControlIdentifier, + onCopy: (_) => CameraControl.detached(instanceManager: instanceManager), + ); + + await cameraControl.cancelFocusAndMetering(); + + verify(mockApi.cancelFocusAndMetering(cameraControlIdentifier)); + }); + + test( + 'setExposureCompensationIndex makes call on Java side to set index and returns expected target exposure value', + () async { + final MockTestCameraControlHostApi mockApi = + MockTestCameraControlHostApi(); + TestCameraControlHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final CameraControl cameraControl = CameraControl.detached( + instanceManager: instanceManager, + ); + const int cameraControlIdentifier = 40; + + instanceManager.addHostCreatedInstance( + cameraControl, + cameraControlIdentifier, + onCopy: (_) => CameraControl.detached(instanceManager: instanceManager), + ); + + const int index = 3; + const int fakeTargetExposureValue = 2; + when(mockApi.setExposureCompensationIndex(cameraControlIdentifier, index)) + .thenAnswer((_) => Future.value(fakeTargetExposureValue)); + + expect(await cameraControl.setExposureCompensationIndex(index), + equals(fakeTargetExposureValue)); + verify( + mockApi.setExposureCompensationIndex(cameraControlIdentifier, index)); + }); + 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_control_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart index b06d2721f89..a11525af986 100644 --- a/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart @@ -64,6 +64,49 @@ class MockTestCameraControlHostApi extends _i1.Mock returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); + + @override + _i3.Future startFocusAndMetering( + int? identifier, + int? focusMeteringActionId, + ) => + (super.noSuchMethod( + Invocation.method( + #startFocusAndMetering, + [ + identifier, + focusMeteringActionId, + ], + ), + returnValue: _i3.Future.value(0), + ) as _i3.Future); + + @override + _i3.Future cancelFocusAndMetering(int? identifier) => + (super.noSuchMethod( + Invocation.method( + #cancelFocusAndMetering, + [identifier], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future setExposureCompensationIndex( + int? identifier, + int? index, + ) => + (super.noSuchMethod( + Invocation.method( + #setExposureCompensationIndex, + [ + identifier, + index, + ], + ), + returnValue: _i3.Future.value(0), + ) as _i3.Future); } /// A class which mocks [TestInstanceManagerHostApi]. diff --git a/packages/camera/camera_android_camerax/test/focus_metering_action_test.dart b/packages/camera/camera_android_camerax/test/focus_metering_action_test.dart new file mode 100644 index 00000000000..657fb1aa3da --- /dev/null +++ b/packages/camera/camera_android_camerax/test/focus_metering_action_test.dart @@ -0,0 +1,100 @@ +// 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/camerax_library.g.dart'; +import 'package:camera_android_camerax/src/focus_metering_action.dart'; +import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:camera_android_camerax/src/metering_point.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'focus_metering_action_test.mocks.dart'; +import 'test_camerax_library.g.dart'; + +@GenerateMocks([ + MeteringPoint, + TestFocusMeteringActionHostApi, + TestInstanceManagerHostApi +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + // Mocks the call to clear the native InstanceManager. + TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi()); + + group('FocusMeteringAction', () { + tearDown(() => TestCameraHostApi.setup(null)); + + test('detached create does not call create on the Java side', () { + final MockTestFocusMeteringActionHostApi mockApi = + MockTestFocusMeteringActionHostApi(); + TestFocusMeteringActionHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + FocusMeteringAction.detached( + instanceManager: instanceManager, + ); + + verifyNever( + mockApi.create(argThat(isA()), argThat(isA>()))); + }); + test('create calls create on the Java side', () { + final MockTestFocusMeteringActionHostApi mockApi = + MockTestFocusMeteringActionHostApi(); + TestFocusMeteringActionHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final MeteringPoint mockMeteringPoint1 = MockMeteringPoint(); + const int mockMeteringPoint1Mode = FocusMeteringAction.flagAe; + const int mockMeteringPoint1Id = 7; + final MeteringPoint mockMeteringPoint2 = MockMeteringPoint(); + const int mockMeteringPoint2Mode = FocusMeteringAction.flagAwb; + const int mockMeteringPoint2Id = 17; + final List<(MeteringPoint meteringPoint, int? meteringMode)> + meteringPointInfos = + <(MeteringPoint meteringPoint, int? meteringMode)>[ + (mockMeteringPoint1, mockMeteringPoint1Mode), + (mockMeteringPoint2, mockMeteringPoint2Mode) + ]; + + instanceManager + .addHostCreatedInstance(mockMeteringPoint1, mockMeteringPoint1Id, + onCopy: (MeteringPoint original) { + return MockMeteringPoint(); + }); + instanceManager + .addHostCreatedInstance(mockMeteringPoint2, mockMeteringPoint2Id, + onCopy: (MeteringPoint original) { + return MockMeteringPoint(); + }); + + final FocusMeteringAction instance = FocusMeteringAction( + meteringPointInfos: meteringPointInfos, + instanceManager: instanceManager, + ); + + final VerificationResult verificationResult = verify(mockApi.create( + argThat(equals(instanceManager.getIdentifier(instance))), + captureAny)); + final List captureMeteringPointInfos = + verificationResult.captured.single as List; + expect(captureMeteringPointInfos.length, equals(2)); + expect(captureMeteringPointInfos[0]!.meteringPointId, + equals(mockMeteringPoint1Id)); + expect( + captureMeteringPointInfos[0]!.meteringMode, mockMeteringPoint1Mode); + expect(captureMeteringPointInfos[1]!.meteringPointId, + equals(mockMeteringPoint2Id)); + expect( + captureMeteringPointInfos[1]!.meteringMode, mockMeteringPoint2Mode); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/focus_metering_action_test.mocks.dart b/packages/camera/camera_android_camerax/test/focus_metering_action_test.mocks.dart new file mode 100644 index 00000000000..717215ca228 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/focus_metering_action_test.mocks.dart @@ -0,0 +1,90 @@ +// Mocks generated by Mockito 5.4.3 from annotations +// in camera_android_camerax/test/focus_metering_action_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i4; +import 'package:camera_android_camerax/src/metering_point.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; + +import 'test_camerax_library.g.dart' as _i3; + +// 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: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// 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 [MeteringPoint]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockMeteringPoint extends _i1.Mock implements _i2.MeteringPoint { + MockMeteringPoint() { + _i1.throwOnMissingStub(this); + } + + @override + double get x => (super.noSuchMethod( + Invocation.getter(#x), + returnValue: 0.0, + ) as double); + + @override + double get y => (super.noSuchMethod( + Invocation.getter(#y), + returnValue: 0.0, + ) as double); +} + +/// A class which mocks [TestFocusMeteringActionHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestFocusMeteringActionHostApi extends _i1.Mock + implements _i3.TestFocusMeteringActionHostApi { + MockTestFocusMeteringActionHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create( + int? identifier, + List<_i4.MeteringPointInfo?>? meteringPointInfos, + ) => + super.noSuchMethod( + Invocation.method( + #create, + [ + identifier, + meteringPointInfos, + ], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [TestInstanceManagerHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestInstanceManagerHostApi extends _i1.Mock + implements _i3.TestInstanceManagerHostApi { + MockTestInstanceManagerHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void clear() => super.noSuchMethod( + Invocation.method( + #clear, + [], + ), + returnValueForMissingStub: null, + ); +} diff --git a/packages/camera/camera_android_camerax/test/focus_metering_result_test.dart b/packages/camera/camera_android_camerax/test/focus_metering_result_test.dart new file mode 100644 index 00000000000..591a1c2d32b --- /dev/null +++ b/packages/camera/camera_android_camerax/test/focus_metering_result_test.dart @@ -0,0 +1,76 @@ +// 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/focus_metering_result.dart'; +import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:camera_android_camerax/src/metering_point.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'focus_metering_result_test.mocks.dart'; +import 'test_camerax_library.g.dart'; + +@GenerateMocks([ + MeteringPoint, + TestFocusMeteringResultHostApi, + TestInstanceManagerHostApi +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + // Mocks the call to clear the native InstanceManager. + TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi()); + + group('FocusMeteringResult', () { + tearDown(() => TestCameraHostApi.setup(null)); + + test('isFocusSuccessful returns expected result', () async { + final MockTestFocusMeteringResultHostApi mockApi = + MockTestFocusMeteringResultHostApi(); + TestFocusMeteringResultHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final FocusMeteringResult focusMeteringResult = + FocusMeteringResult.detached( + instanceManager: instanceManager, + ); + const int focusMeteringResultIdentifier = 5; + + instanceManager.addHostCreatedInstance( + focusMeteringResult, + focusMeteringResultIdentifier, + onCopy: (_) => + FocusMeteringResult.detached(instanceManager: instanceManager), + ); + + when(mockApi.isFocusSuccessful(focusMeteringResultIdentifier)) + .thenAnswer((_) => false); + + expect(await focusMeteringResult.isFocusSuccessful(), isFalse); + verify(mockApi.isFocusSuccessful(focusMeteringResultIdentifier)); + }); + + test('flutterApiCreate makes call to add instance to instance manager', () { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final FocusMeteringResultFlutterApiImpl flutterApi = + FocusMeteringResultFlutterApiImpl( + instanceManager: instanceManager, + ); + const int focusMeteringResultIdentifier = 37; + + flutterApi.create(focusMeteringResultIdentifier); + + expect( + instanceManager + .getInstanceWithWeakReference(focusMeteringResultIdentifier), + isA()); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/focus_metering_result_test.mocks.dart b/packages/camera/camera_android_camerax/test/focus_metering_result_test.mocks.dart new file mode 100644 index 00000000000..d35cdc15efb --- /dev/null +++ b/packages/camera/camera_android_camerax/test/focus_metering_result_test.mocks.dart @@ -0,0 +1,82 @@ +// Mocks generated by Mockito 5.4.3 from annotations +// in camera_android_camerax/test/focus_metering_result_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:camera_android_camerax/src/metering_point.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; + +import 'test_camerax_library.g.dart' as _i3; + +// 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: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// 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 [MeteringPoint]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockMeteringPoint extends _i1.Mock implements _i2.MeteringPoint { + MockMeteringPoint() { + _i1.throwOnMissingStub(this); + } + + @override + double get x => (super.noSuchMethod( + Invocation.getter(#x), + returnValue: 0.0, + ) as double); + + @override + double get y => (super.noSuchMethod( + Invocation.getter(#y), + returnValue: 0.0, + ) as double); +} + +/// A class which mocks [TestFocusMeteringResultHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestFocusMeteringResultHostApi extends _i1.Mock + implements _i3.TestFocusMeteringResultHostApi { + MockTestFocusMeteringResultHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + bool isFocusSuccessful(int? identifier) => (super.noSuchMethod( + Invocation.method( + #isFocusSuccessful, + [identifier], + ), + returnValue: false, + ) as bool); +} + +/// A class which mocks [TestInstanceManagerHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestInstanceManagerHostApi extends _i1.Mock + implements _i3.TestInstanceManagerHostApi { + MockTestInstanceManagerHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void clear() => super.noSuchMethod( + Invocation.method( + #clear, + [], + ), + returnValueForMissingStub: null, + ); +} diff --git a/packages/camera/camera_android_camerax/test/metering_point_test.dart b/packages/camera/camera_android_camerax/test/metering_point_test.dart new file mode 100644 index 00000000000..ba3daae96b1 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/metering_point_test.dart @@ -0,0 +1,78 @@ +// 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/instance_manager.dart'; +import 'package:camera_android_camerax/src/metering_point.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'metering_point_test.mocks.dart'; +import 'test_camerax_library.g.dart'; + +@GenerateMocks([TestInstanceManagerHostApi, TestMeteringPointHostApi]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + // Mocks the call to clear the native InstanceManager. + TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi()); + + group('MeteringPoint', () { + tearDown(() => TestMeteringPointHostApi.setup(null)); + + test('detached create does not call create on the Java side', () async { + final MockTestMeteringPointHostApi mockApi = + MockTestMeteringPointHostApi(); + TestMeteringPointHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + MeteringPoint.detached( + x: 0, + y: 0.3, + size: 4, + instanceManager: instanceManager, + ); + + verifyNever(mockApi.create(argThat(isA()), argThat(isA()), + argThat(isA()), argThat(isA()))); + }); + + test('create calls create on the Java side', () async { + final MockTestMeteringPointHostApi mockApi = + MockTestMeteringPointHostApi(); + TestMeteringPointHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + const double x = 0.5; + const double y = 0.6; + const double size = 3; + MeteringPoint( + x: x, + y: y, + size: size, + instanceManager: instanceManager, + ); + + verify(mockApi.create(argThat(isA()), x, y, size)); + }); + + test('getDefaultPointSize returns expected size', () async { + final MockTestMeteringPointHostApi mockApi = + MockTestMeteringPointHostApi(); + TestMeteringPointHostApi.setup(mockApi); + + const double defaultPointSize = 6; + when(mockApi.getDefaultPointSize()).thenAnswer((_) => defaultPointSize); + + expect( + await MeteringPoint.getDefaultPointSize(), equals(defaultPointSize)); + verify(mockApi.getDefaultPointSize()); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/metering_point_test.mocks.dart b/packages/camera/camera_android_camerax/test/metering_point_test.mocks.dart new file mode 100644 index 00000000000..ba199f66c63 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/metering_point_test.mocks.dart @@ -0,0 +1,79 @@ +// Mocks generated by Mockito 5.4.3 from annotations +// in camera_android_camerax/test/metering_point_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +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: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// 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 [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, + ); +} + +/// A class which mocks [TestMeteringPointHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestMeteringPointHostApi extends _i1.Mock + implements _i2.TestMeteringPointHostApi { + MockTestMeteringPointHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create( + int? identifier, + double? x, + double? y, + double? size, + ) => + super.noSuchMethod( + Invocation.method( + #create, + [ + identifier, + x, + y, + size, + ], + ), + returnValueForMissingStub: null, + ); + + @override + double getDefaultPointSize() => (super.noSuchMethod( + Invocation.method( + #getDefaultPointSize, + [], + ), + returnValue: 0.0, + ) as double); +} 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 35d9d333c0c..abb6fad2987 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 @@ -1898,6 +1898,12 @@ abstract class TestCameraControlHostApi { Future setZoomRatio(int identifier, double ratio); + Future startFocusAndMetering(int identifier, int focusMeteringActionId); + + Future cancelFocusAndMetering(int identifier); + + Future setExposureCompensationIndex(int identifier, int index); + static void setup(TestCameraControlHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1950,5 +1956,333 @@ abstract class TestCameraControlHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraControlHostApi.startFocusAndMetering', + 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.startFocusAndMetering 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.startFocusAndMetering was null, expected non-null int.'); + final int? arg_focusMeteringActionId = (args[1] as int?); + assert(arg_focusMeteringActionId != null, + 'Argument for dev.flutter.pigeon.CameraControlHostApi.startFocusAndMetering was null, expected non-null int.'); + final int output = await api.startFocusAndMetering( + arg_identifier!, arg_focusMeteringActionId!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraControlHostApi.cancelFocusAndMetering', + 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.cancelFocusAndMetering 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.cancelFocusAndMetering was null, expected non-null int.'); + await api.cancelFocusAndMetering(arg_identifier!); + return []; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraControlHostApi.setExposureCompensationIndex', + 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.setExposureCompensationIndex 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.setExposureCompensationIndex was null, expected non-null int.'); + final int? arg_index = (args[1] as int?); + assert(arg_index != null, + 'Argument for dev.flutter.pigeon.CameraControlHostApi.setExposureCompensationIndex was null, expected non-null int.'); + final int output = await api.setExposureCompensationIndex( + arg_identifier!, arg_index!); + return [output]; + }); + } + } + } +} + +class _TestFocusMeteringActionHostApiCodec extends StandardMessageCodec { + const _TestFocusMeteringActionHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is MeteringPointInfo) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return MeteringPointInfo.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestFocusMeteringActionHostApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = + _TestFocusMeteringActionHostApiCodec(); + + void create(int identifier, List meteringPointInfos); + + static void setup(TestFocusMeteringActionHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FocusMeteringActionHostApi.create', 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.FocusMeteringActionHostApi.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.FocusMeteringActionHostApi.create was null, expected non-null int.'); + final List? arg_meteringPointInfos = + (args[1] as List?)?.cast(); + assert(arg_meteringPointInfos != null, + 'Argument for dev.flutter.pigeon.FocusMeteringActionHostApi.create was null, expected non-null List.'); + api.create(arg_identifier!, arg_meteringPointInfos!); + return []; + }); + } + } + } +} + +abstract class TestFocusMeteringResultHostApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = StandardMessageCodec(); + + bool isFocusSuccessful(int identifier); + + static void setup(TestFocusMeteringResultHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FocusMeteringResultHostApi.isFocusSuccessful', + 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.FocusMeteringResultHostApi.isFocusSuccessful 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.FocusMeteringResultHostApi.isFocusSuccessful was null, expected non-null int.'); + final bool output = api.isFocusSuccessful(arg_identifier!); + return [output]; + }); + } + } + } +} + +abstract class TestMeteringPointHostApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = StandardMessageCodec(); + + void create(int identifier, double x, double y, double? size); + + double getDefaultPointSize(); + + static void setup(TestMeteringPointHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.MeteringPointHostApi.create', 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.MeteringPointHostApi.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.MeteringPointHostApi.create was null, expected non-null int.'); + final double? arg_x = (args[1] as double?); + assert(arg_x != null, + 'Argument for dev.flutter.pigeon.MeteringPointHostApi.create was null, expected non-null double.'); + final double? arg_y = (args[2] as double?); + assert(arg_y != null, + 'Argument for dev.flutter.pigeon.MeteringPointHostApi.create was null, expected non-null double.'); + final double? arg_size = (args[3] as double?); + api.create(arg_identifier!, arg_x!, arg_y!, arg_size); + return []; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.MeteringPointHostApi.getDefaultPointSize', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + // ignore message + final double output = api.getDefaultPointSize(); + return [output]; + }); + } + } + } +} + +abstract class TestDisplayOrientedMeteringPointFactoryHostApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = StandardMessageCodec(); + + void create(int identifier, int cameraInfoId, int width, int height); + + int createPoint(int x, int y, int? size); + + int getDefaultPointSize(); + + static void setup(TestDisplayOrientedMeteringPointFactoryHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.create', + 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.DisplayOrientedMeteringPointFactoryHostApi.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.DisplayOrientedMeteringPointFactoryHostApi.create was null, expected non-null int.'); + final int? arg_cameraInfoId = (args[1] as int?); + assert(arg_cameraInfoId != null, + 'Argument for dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.create was null, expected non-null int.'); + final int? arg_width = (args[2] as int?); + assert(arg_width != null, + 'Argument for dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.create was null, expected non-null int.'); + final int? arg_height = (args[3] as int?); + assert(arg_height != null, + 'Argument for dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.create was null, expected non-null int.'); + api.create( + arg_identifier!, arg_cameraInfoId!, arg_width!, arg_height!); + return []; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.createPoint', + 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.DisplayOrientedMeteringPointFactoryHostApi.createPoint was null.'); + final List args = (message as List?)!; + final int? arg_x = (args[0] as int?); + assert(arg_x != null, + 'Argument for dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.createPoint was null, expected non-null int.'); + final int? arg_y = (args[1] as int?); + assert(arg_y != null, + 'Argument for dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.createPoint was null, expected non-null int.'); + final int? arg_size = (args[2] as int?); + final int output = api.createPoint(arg_x!, arg_y!, arg_size); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.DisplayOrientedMeteringPointFactoryHostApi.getDefaultPointSize', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + // ignore message + final int output = api.getDefaultPointSize(); + return [output]; + }); + } + } } }