diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md
index f2e845ff78b..de7c4d96618 100644
--- a/packages/camera/camera_android_camerax/CHANGELOG.md
+++ b/packages/camera/camera_android_camerax/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.5.0+14
+
+* Wraps classes needed to implement resolution configuration for video recording.
+
## 0.5.0+13
* Migrates `styleFrom` usage in examples off of deprecated `primary` and `onPrimary` parameters.
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 dbae2a46805..d2a9c277a37 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
@@ -103,6 +103,10 @@ public void setUp(
binaryMessenger, new ResolutionStrategyHostApiImpl(instanceManager));
GeneratedCameraXLibrary.AspectRatioStrategyHostApi.setup(
binaryMessenger, new AspectRatioStrategyHostApiImpl(instanceManager));
+ GeneratedCameraXLibrary.FallbackStrategyHostApi.setup(
+ binaryMessenger, new FallbackStrategyHostApiImpl(instanceManager));
+ GeneratedCameraXLibrary.QualitySelectorHostApi.setup(
+ binaryMessenger, new QualitySelectorHostApiImpl(instanceManager));
}
@Override
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FallbackStrategyHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FallbackStrategyHostApiImpl.java
new file mode 100644
index 00000000000..0309d5476a0
--- /dev/null
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FallbackStrategyHostApiImpl.java
@@ -0,0 +1,83 @@
+// 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.video.FallbackStrategy;
+import androidx.camera.video.Quality;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.FallbackStrategyHostApi;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.VideoQualityConstraint;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.VideoResolutionFallbackRule;
+
+/**
+ * Host API implementation for {@link FallbackStrategy}.
+ *
+ *
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 FallbackStrategyHostApiImpl implements FallbackStrategyHostApi {
+ private final InstanceManager instanceManager;
+
+ private final FallbackStrategyProxy proxy;
+
+ /** Proxy for constructors and static method of {@link FallbackStrategy}. */
+ @VisibleForTesting
+ public static class FallbackStrategyProxy {
+ /** Creates an instance of {@link FallbackStrategy}. */
+ public @NonNull FallbackStrategy create(
+ @NonNull VideoQualityConstraint videoQualityConstraint,
+ @NonNull VideoResolutionFallbackRule fallbackRule) {
+ Quality videoQuality =
+ QualitySelectorHostApiImpl.getQualityFromVideoQualityConstraint(videoQualityConstraint);
+
+ switch (fallbackRule) {
+ case HIGHER_QUALITY_OR_LOWER_THAN:
+ return FallbackStrategy.higherQualityOrLowerThan(videoQuality);
+ case HIGHER_QUALITY_THAN:
+ return FallbackStrategy.higherQualityThan(videoQuality);
+ case LOWER_QUALITY_OR_HIGHER_THAN:
+ return FallbackStrategy.lowerQualityOrHigherThan(videoQuality);
+ case LOWER_QUALITY_THAN:
+ return FallbackStrategy.lowerQualityThan(videoQuality);
+ }
+ throw new IllegalArgumentException(
+ "Specified fallback rule " + fallbackRule + " unrecognized.");
+ }
+ }
+
+ /**
+ * Constructs a {@link FallbackStrategyHostApiImpl}.
+ *
+ * @param instanceManager maintains instances stored to communicate with attached Dart objects
+ */
+ public FallbackStrategyHostApiImpl(@NonNull InstanceManager instanceManager) {
+ this(instanceManager, new FallbackStrategyProxy());
+ }
+
+ /**
+ * Constructs a {@link FallbackStrategyHostApiImpl}.
+ *
+ * @param instanceManager maintains instances stored to communicate with attached Dart objects
+ * @param proxy proxy for constructors and static method of {@link FallbackStrategy}
+ */
+ FallbackStrategyHostApiImpl(
+ @NonNull InstanceManager instanceManager, @NonNull FallbackStrategyProxy proxy) {
+ this.instanceManager = instanceManager;
+ this.proxy = proxy;
+ }
+
+ /**
+ * Creates a {@link FallbackStrategy} instance with the video quality and fallback rule specified.
+ */
+ @Override
+ public void create(
+ @NonNull Long identifier,
+ @NonNull VideoQualityConstraint videoQualityConstraint,
+ @NonNull VideoResolutionFallbackRule fallbackRule) {
+ instanceManager.addDartCreatedInstance(
+ proxy.create(videoQualityConstraint, fallbackRule), 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 680ecb80525..d7d378d158b 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
@@ -104,6 +104,43 @@ private LiveDataSupportedType(final int index) {
}
}
+ /**
+ * Video quality constraints that will be used by a QualitySelector to choose an appropriate video
+ * resolution.
+ *
+ *
These are pre-defined quality constants that are universally used for video.
+ *
+ *
See https://developer.android.com/reference/androidx/camera/video/Quality.
+ */
+ public enum VideoQualityConstraint {
+ SD(0),
+ HD(1),
+ FHD(2),
+ UHD(3),
+ LOWEST(4),
+ HIGHEST(5);
+
+ final int index;
+
+ private VideoQualityConstraint(final int index) {
+ this.index = index;
+ }
+ }
+
+ /** Fallback rules for selecting video resolution. */
+ public enum VideoResolutionFallbackRule {
+ HIGHER_QUALITY_OR_LOWER_THAN(0),
+ HIGHER_QUALITY_THAN(1),
+ LOWER_QUALITY_OR_HIGHER_THAN(2),
+ LOWER_QUALITY_THAN(3);
+
+ final int index;
+
+ private VideoResolutionFallbackRule(final int index) {
+ this.index = index;
+ }
+ }
+
/** Generated class from Pigeon that represents data sent in messages. */
public static final class ResolutionInfo {
private @NonNull Long width;
@@ -1556,7 +1593,11 @@ public void create(@NonNull Long identifierArg, @NonNull Reply callback) {
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
public interface RecorderHostApi {
- void create(@NonNull Long identifier, @Nullable Long aspectRatio, @Nullable Long bitRate);
+ void create(
+ @NonNull Long identifier,
+ @Nullable Long aspectRatio,
+ @Nullable Long bitRate,
+ @Nullable Long qualitySelectorId);
@NonNull
Long getAspectRatio(@NonNull Long identifier);
@@ -1587,11 +1628,13 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable RecorderHo
Number identifierArg = (Number) args.get(0);
Number aspectRatioArg = (Number) args.get(1);
Number bitRateArg = (Number) args.get(2);
+ Number qualitySelectorIdArg = (Number) args.get(3);
try {
api.create(
(identifierArg == null) ? null : identifierArg.longValue(),
(aspectRatioArg == null) ? null : aspectRatioArg.longValue(),
- (bitRateArg == null) ? null : bitRateArg.longValue());
+ (bitRateArg == null) ? null : bitRateArg.longValue(),
+ (qualitySelectorIdArg == null) ? null : qualitySelectorIdArg.longValue());
wrapped.add(0, null);
} catch (Throwable exception) {
ArrayList wrappedError = wrapError(exception);
@@ -2937,4 +2980,165 @@ public void create(
channelReply -> callback.reply(null));
}
}
+
+ private static class QualitySelectorHostApiCodec extends StandardMessageCodec {
+ public static final QualitySelectorHostApiCodec INSTANCE = new QualitySelectorHostApiCodec();
+
+ private QualitySelectorHostApiCodec() {}
+
+ @Override
+ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
+ switch (type) {
+ case (byte) 128:
+ return ResolutionInfo.fromList((ArrayList) readValue(buffer));
+ default:
+ return super.readValueOfType(type, buffer);
+ }
+ }
+
+ @Override
+ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
+ if (value instanceof ResolutionInfo) {
+ stream.write(128);
+ writeValue(stream, ((ResolutionInfo) value).toList());
+ } else {
+ super.writeValue(stream, value);
+ }
+ }
+ }
+
+ /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
+ public interface QualitySelectorHostApi {
+
+ void create(
+ @NonNull Long identifier,
+ @NonNull List videoQualityConstraintIndexList,
+ @Nullable Long fallbackStrategyId);
+
+ @NonNull
+ ResolutionInfo getResolution(
+ @NonNull Long cameraInfoId, @NonNull VideoQualityConstraint quality);
+
+ /** The codec used by QualitySelectorHostApi. */
+ static @NonNull MessageCodec getCodec() {
+ return QualitySelectorHostApiCodec.INSTANCE;
+ }
+ /**
+ * Sets up an instance of `QualitySelectorHostApi` to handle messages through the
+ * `binaryMessenger`.
+ */
+ static void setup(
+ @NonNull BinaryMessenger binaryMessenger, @Nullable QualitySelectorHostApi api) {
+ {
+ BasicMessageChannel channel =
+ new BasicMessageChannel<>(
+ binaryMessenger, "dev.flutter.pigeon.QualitySelectorHostApi.create", getCodec());
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ ArrayList wrapped = new ArrayList();
+ ArrayList args = (ArrayList) message;
+ Number identifierArg = (Number) args.get(0);
+ List videoQualityConstraintIndexListArg = (List) args.get(1);
+ Number fallbackStrategyIdArg = (Number) args.get(2);
+ try {
+ api.create(
+ (identifierArg == null) ? null : identifierArg.longValue(),
+ videoQualityConstraintIndexListArg,
+ (fallbackStrategyIdArg == null) ? null : fallbackStrategyIdArg.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.QualitySelectorHostApi.getResolution",
+ getCodec());
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ ArrayList wrapped = new ArrayList();
+ ArrayList args = (ArrayList) message;
+ Number cameraInfoIdArg = (Number) args.get(0);
+ VideoQualityConstraint qualityArg =
+ args.get(1) == null ? null : VideoQualityConstraint.values()[(int) args.get(1)];
+ try {
+ ResolutionInfo output =
+ api.getResolution(
+ (cameraInfoIdArg == null) ? null : cameraInfoIdArg.longValue(),
+ qualityArg);
+ 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 FallbackStrategyHostApi {
+
+ void create(
+ @NonNull Long identifier,
+ @NonNull VideoQualityConstraint quality,
+ @NonNull VideoResolutionFallbackRule fallbackRule);
+
+ /** The codec used by FallbackStrategyHostApi. */
+ static @NonNull MessageCodec getCodec() {
+ return new StandardMessageCodec();
+ }
+ /**
+ * Sets up an instance of `FallbackStrategyHostApi` to handle messages through the
+ * `binaryMessenger`.
+ */
+ static void setup(
+ @NonNull BinaryMessenger binaryMessenger, @Nullable FallbackStrategyHostApi api) {
+ {
+ BasicMessageChannel channel =
+ new BasicMessageChannel<>(
+ binaryMessenger, "dev.flutter.pigeon.FallbackStrategyHostApi.create", getCodec());
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ ArrayList wrapped = new ArrayList();
+ ArrayList args = (ArrayList) message;
+ Number identifierArg = (Number) args.get(0);
+ VideoQualityConstraint qualityArg =
+ args.get(1) == null ? null : VideoQualityConstraint.values()[(int) args.get(1)];
+ VideoResolutionFallbackRule fallbackRuleArg =
+ args.get(2) == null
+ ? null
+ : VideoResolutionFallbackRule.values()[(int) args.get(2)];
+ try {
+ api.create(
+ (identifierArg == null) ? null : identifierArg.longValue(),
+ qualityArg,
+ fallbackRuleArg);
+ wrapped.add(0, null);
+ } 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/QualitySelectorHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/QualitySelectorHostApiImpl.java
new file mode 100644
index 00000000000..c747c75962b
--- /dev/null
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/QualitySelectorHostApiImpl.java
@@ -0,0 +1,149 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.camerax;
+
+import android.util.Size;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.camera.video.FallbackStrategy;
+import androidx.camera.video.Quality;
+import androidx.camera.video.QualitySelector;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.QualitySelectorHostApi;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ResolutionInfo;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.VideoQualityConstraint;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Host API implementation for {@link QualitySelector}.
+ *
+ * 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 QualitySelectorHostApiImpl implements QualitySelectorHostApi {
+ private final InstanceManager instanceManager;
+
+ private final QualitySelectorProxy proxy;
+
+ /** Proxy for constructors and static method of {@link QualitySelector}. */
+ @VisibleForTesting
+ public static class QualitySelectorProxy {
+ /** Creates an instance of {@link QualitySelector}. */
+ public @NonNull QualitySelector create(
+ @NonNull List videoQualityConstraintIndexList,
+ @Nullable FallbackStrategy fallbackStrategy) {
+ // Convert each index of VideoQualityConstraint to Quality.
+ List qualityList = new ArrayList();
+ for (Long qualityIndex : videoQualityConstraintIndexList) {
+ qualityList.add(getQualityConstant(qualityIndex));
+ }
+
+ boolean fallbackStrategySpecified = fallbackStrategy != null;
+ if (qualityList.size() == 0) {
+ throw new IllegalArgumentException(
+ "List of at least one Quality must be supplied to create QualitySelector.");
+ } else if (qualityList.size() == 1) {
+ Quality quality = qualityList.get(0);
+ return fallbackStrategySpecified
+ ? QualitySelector.from(quality, fallbackStrategy)
+ : QualitySelector.from(quality);
+ }
+
+ return fallbackStrategySpecified
+ ? QualitySelector.fromOrderedList(qualityList, fallbackStrategy)
+ : QualitySelector.fromOrderedList(qualityList);
+ }
+
+ /** Converts from index of {@link VideoQualityConstraint} to {@link Quality}. */
+ private Quality getQualityConstant(@NonNull Long qualityIndex) {
+ VideoQualityConstraint quality = VideoQualityConstraint.values()[qualityIndex.intValue()];
+ return getQualityFromVideoQualityConstraint(quality);
+ }
+ }
+
+ /**
+ * Constructs a {@link QualitySelectorHostApiImpl}.
+ *
+ * @param instanceManager maintains instances stored to communicate with attached Dart objects
+ */
+ public QualitySelectorHostApiImpl(@NonNull InstanceManager instanceManager) {
+ this(instanceManager, new QualitySelectorProxy());
+ }
+
+ /**
+ * Constructs a {@link QualitySelectorHostApiImpl}.
+ *
+ * @param instanceManager maintains instances stored to communicate with attached Dart objects
+ * @param proxy proxy for constructors and static method of {@link QualitySelector}
+ */
+ QualitySelectorHostApiImpl(
+ @NonNull InstanceManager instanceManager, @NonNull QualitySelectorProxy proxy) {
+ this.instanceManager = instanceManager;
+ this.proxy = proxy;
+ }
+
+ /**
+ * Creates a {@link QualitySelector} instance with the quality list and {@link FallbackStrategy}
+ * with the identifier specified.
+ */
+ @Override
+ public void create(
+ @NonNull Long identifier,
+ @NonNull List videoQualityConstraintIndexList,
+ @Nullable Long fallbackStrategyIdentifier) {
+ instanceManager.addDartCreatedInstance(
+ proxy.create(
+ videoQualityConstraintIndexList,
+ fallbackStrategyIdentifier == null
+ ? null
+ : Objects.requireNonNull(instanceManager.getInstance(fallbackStrategyIdentifier))),
+ identifier);
+ }
+
+ /**
+ * Retrieves the corresponding resolution from the input quality for the camera represented by the
+ * {@link CameraInfo} represented by the identifier specified.
+ */
+ @Override
+ public @NonNull ResolutionInfo getResolution(
+ @NonNull Long cameraInfoIdentifier, @NonNull VideoQualityConstraint quality) {
+ final Size result =
+ QualitySelector.getResolution(
+ Objects.requireNonNull(instanceManager.getInstance(cameraInfoIdentifier)),
+ getQualityFromVideoQualityConstraint(quality));
+ return new ResolutionInfo.Builder()
+ .setWidth(Long.valueOf(result.getWidth()))
+ .setHeight(Long.valueOf(result.getHeight()))
+ .build();
+ }
+
+ /**
+ * Converts the specified {@link VideoQualityConstraint} to a {@link Quality} that is understood
+ * by CameraX.
+ */
+ public static @NonNull Quality getQualityFromVideoQualityConstraint(
+ @NonNull VideoQualityConstraint videoQualityConstraint) {
+ switch (videoQualityConstraint) {
+ case SD:
+ return Quality.SD;
+ case HD:
+ return Quality.HD;
+ case FHD:
+ return Quality.FHD;
+ case UHD:
+ return Quality.UHD;
+ case LOWEST:
+ return Quality.LOWEST;
+ case HIGHEST:
+ return Quality.HIGHEST;
+ }
+ throw new IllegalArgumentException(
+ "VideoQualityConstraint "
+ + videoQualityConstraint
+ + " is unhandled by QualitySelectorHostApiImpl.");
+ }
+}
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/RecorderHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/RecorderHostApiImpl.java
index 660c469c88b..8f7e8d293f4 100644
--- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/RecorderHostApiImpl.java
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/RecorderHostApiImpl.java
@@ -41,7 +41,11 @@ public RecorderHostApiImpl(
}
@Override
- public void create(@NonNull Long instanceId, @Nullable Long aspectRatio, @Nullable Long bitRate) {
+ public void create(
+ @NonNull Long instanceId,
+ @Nullable Long aspectRatio,
+ @Nullable Long bitRate,
+ @Nullable Long qualitySelector) {
Recorder.Builder recorderBuilder = cameraXProxy.createRecorderBuilder();
if (aspectRatio != null) {
recorderBuilder.setAspectRatio(aspectRatio.intValue());
@@ -49,6 +53,10 @@ public void create(@NonNull Long instanceId, @Nullable Long aspectRatio, @Nullab
if (bitRate != null) {
recorderBuilder.setTargetVideoEncodingBitRate(bitRate.intValue());
}
+ if (qualitySelector != null) {
+ recorderBuilder.setQualitySelector(
+ Objects.requireNonNull(instanceManager.getInstance(qualitySelector)));
+ }
Recorder recorder = recorderBuilder.setExecutor(ContextCompat.getMainExecutor(context)).build();
instanceManager.addDartCreatedInstance(recorder, instanceId);
}
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FallbackStrategyTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FallbackStrategyTest.java
new file mode 100644
index 00000000000..4a2eeef7f13
--- /dev/null
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FallbackStrategyTest.java
@@ -0,0 +1,121 @@
+// 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.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+
+import androidx.camera.video.FallbackStrategy;
+import androidx.camera.video.Quality;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.VideoQualityConstraint;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.VideoResolutionFallbackRule;
+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.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Answer;
+
+public class FallbackStrategyTest {
+
+ @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+ @Mock public FallbackStrategy mockFallbackStrategy;
+
+ InstanceManager instanceManager;
+
+ @Before
+ public void setUp() {
+ instanceManager = InstanceManager.create(identifier -> {});
+ }
+
+ @After
+ public void tearDown() {
+ instanceManager.stopFinalizationListener();
+ }
+
+ @Test
+ public void hostApiCreate_makesCallToCreateExpectedFallbackStrategy() {
+ final FallbackStrategyHostApiImpl hostApi = new FallbackStrategyHostApiImpl(instanceManager);
+ final long instanceIdentifier = 45;
+ final FallbackStrategy mockFallbackStrategy = mock(FallbackStrategy.class);
+
+ try (MockedStatic mockedFallbackStrategy =
+ mockStatic(FallbackStrategy.class)) {
+ for (VideoQualityConstraint videoQualityConstraint : VideoQualityConstraint.values()) {
+ for (VideoResolutionFallbackRule fallbackRule : VideoResolutionFallbackRule.values()) {
+ // Determine expected Quality based on videoQualityConstraint being tested.
+ Quality convertedQuality = null;
+ switch (videoQualityConstraint) {
+ case SD:
+ convertedQuality = Quality.SD;
+ break;
+ case HD:
+ convertedQuality = Quality.HD;
+ break;
+ case FHD:
+ convertedQuality = Quality.FHD;
+ break;
+ case UHD:
+ convertedQuality = Quality.UHD;
+ break;
+ case LOWEST:
+ convertedQuality = Quality.LOWEST;
+ break;
+ case HIGHEST:
+ convertedQuality = Quality.HIGHEST;
+ break;
+ default:
+ fail(
+ "The VideoQualityConstraint "
+ + videoQualityConstraint.toString()
+ + "is unhandled by this test.");
+ }
+ // Set Quality as final local variable to avoid error about using non-final (or effecitvely final) local variables in lambda expressions.
+ final Quality expectedQuality = convertedQuality;
+
+ // Mock calls to create FallbackStrategy according to fallbackRule being tested.
+ switch (fallbackRule) {
+ case HIGHER_QUALITY_OR_LOWER_THAN:
+ mockedFallbackStrategy
+ .when(() -> FallbackStrategy.higherQualityOrLowerThan(expectedQuality))
+ .thenAnswer((Answer) invocation -> mockFallbackStrategy);
+ break;
+ case HIGHER_QUALITY_THAN:
+ mockedFallbackStrategy
+ .when(() -> FallbackStrategy.higherQualityThan(expectedQuality))
+ .thenAnswer((Answer) invocation -> mockFallbackStrategy);
+ break;
+ case LOWER_QUALITY_OR_HIGHER_THAN:
+ mockedFallbackStrategy
+ .when(() -> FallbackStrategy.lowerQualityOrHigherThan(expectedQuality))
+ .thenAnswer((Answer) invocation -> mockFallbackStrategy);
+ break;
+ case LOWER_QUALITY_THAN:
+ mockedFallbackStrategy
+ .when(() -> FallbackStrategy.lowerQualityThan(expectedQuality))
+ .thenAnswer((Answer) invocation -> mockFallbackStrategy);
+ break;
+ default:
+ fail(
+ "The VideoResolutionFallbackRule "
+ + fallbackRule.toString()
+ + "is unhandled by this test.");
+ }
+ hostApi.create(instanceIdentifier, videoQualityConstraint, fallbackRule);
+ assertEquals(instanceManager.getInstance(instanceIdentifier), mockFallbackStrategy);
+
+ // Clear/reset FallbackStrategy mock and InstanceManager.
+ mockedFallbackStrategy.reset();
+ instanceManager.clear();
+ }
+ }
+ }
+ }
+}
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/QualitySelectorTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/QualitySelectorTest.java
new file mode 100644
index 00000000000..55195831f98
--- /dev/null
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/QualitySelectorTest.java
@@ -0,0 +1,155 @@
+// 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.mockStatic;
+
+import android.util.Size;
+import androidx.camera.core.CameraInfo;
+import androidx.camera.video.FallbackStrategy;
+import androidx.camera.video.Quality;
+import androidx.camera.video.QualitySelector;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ResolutionInfo;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.VideoQualityConstraint;
+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.MockedStatic;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Answer;
+
+public class QualitySelectorTest {
+
+ @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+ @Mock public QualitySelector mockQualitySelectorWithoutFallbackStrategy;
+ @Mock public QualitySelector mockQualitySelectorWithFallbackStrategy;
+
+ InstanceManager instanceManager;
+
+ @Before
+ public void setUp() {
+ instanceManager = InstanceManager.create(identifier -> {});
+ }
+
+ @After
+ public void tearDown() {
+ instanceManager.stopFinalizationListener();
+ }
+
+ @Test
+ public void hostApiCreate_createsExpectedQualitySelectorWhenOneQualitySpecified() {
+ final Long expectedVideoQualityConstraintIndex =
+ Long.valueOf(VideoQualityConstraint.UHD.ordinal());
+ final List videoQualityConstraintList =
+ Arrays.asList(expectedVideoQualityConstraintIndex);
+ final FallbackStrategy mockFallbackStrategy = mock(FallbackStrategy.class);
+ final long fallbackStrategyIdentifier = 9;
+ final QualitySelectorHostApiImpl hostApi = new QualitySelectorHostApiImpl(instanceManager);
+
+ instanceManager.addDartCreatedInstance(mockFallbackStrategy, fallbackStrategyIdentifier);
+
+ try (MockedStatic mockedQualitySelector = mockStatic(QualitySelector.class)) {
+ mockedQualitySelector
+ .when(() -> QualitySelector.from(Quality.UHD))
+ .thenAnswer(
+ (Answer) invocation -> mockQualitySelectorWithoutFallbackStrategy);
+ mockedQualitySelector
+ .when(() -> QualitySelector.from(Quality.UHD, mockFallbackStrategy))
+ .thenAnswer(
+ (Answer) invocation -> mockQualitySelectorWithFallbackStrategy);
+
+ // Test with no fallback strategy.
+ long instanceIdentifier = 0;
+ hostApi.create(instanceIdentifier, videoQualityConstraintList, null);
+
+ assertEquals(
+ instanceManager.getInstance(instanceIdentifier),
+ mockQualitySelectorWithoutFallbackStrategy);
+
+ // Test with fallback strategy.
+ instanceIdentifier = 1;
+ hostApi.create(instanceIdentifier, videoQualityConstraintList, fallbackStrategyIdentifier);
+
+ assertEquals(
+ instanceManager.getInstance(instanceIdentifier), mockQualitySelectorWithFallbackStrategy);
+ }
+ }
+
+ @Test
+ public void hostApiCreate_createsExpectedQualitySelectorWhenOrderedListOfQualitiesSpecified() {
+ final List expectedIndices =
+ Arrays.asList(
+ Long.valueOf(VideoQualityConstraint.UHD.ordinal()),
+ Long.valueOf(VideoQualityConstraint.HIGHEST.ordinal()));
+ final List videoQualityConstraintList =
+ Arrays.asList(VideoQualityConstraint.UHD, VideoQualityConstraint.HIGHEST);
+ final List expectedVideoQualityConstraintList =
+ Arrays.asList(Quality.UHD, Quality.HIGHEST);
+ final FallbackStrategy mockFallbackStrategy = mock(FallbackStrategy.class);
+ final long fallbackStrategyIdentifier = 9;
+ final QualitySelectorHostApiImpl hostApi = new QualitySelectorHostApiImpl(instanceManager);
+
+ instanceManager.addDartCreatedInstance(mockFallbackStrategy, fallbackStrategyIdentifier);
+
+ try (MockedStatic mockedQualitySelector = mockStatic(QualitySelector.class)) {
+ mockedQualitySelector
+ .when(() -> QualitySelector.fromOrderedList(expectedVideoQualityConstraintList))
+ .thenAnswer(
+ (Answer) invocation -> mockQualitySelectorWithoutFallbackStrategy);
+ mockedQualitySelector
+ .when(
+ () ->
+ QualitySelector.fromOrderedList(
+ expectedVideoQualityConstraintList, mockFallbackStrategy))
+ .thenAnswer(
+ (Answer) invocation -> mockQualitySelectorWithFallbackStrategy);
+
+ // Test with no fallback strategy.
+ long instanceIdentifier = 0;
+ hostApi.create(instanceIdentifier, expectedIndices, null);
+
+ assertEquals(
+ instanceManager.getInstance(instanceIdentifier),
+ mockQualitySelectorWithoutFallbackStrategy);
+
+ // Test with fallback strategy.
+ instanceIdentifier = 1;
+ hostApi.create(instanceIdentifier, expectedIndices, fallbackStrategyIdentifier);
+
+ assertEquals(
+ instanceManager.getInstance(instanceIdentifier), mockQualitySelectorWithFallbackStrategy);
+ }
+ }
+
+ @Test
+ public void getResolution_returnsExpectedResolutionInfo() {
+ final CameraInfo mockCameraInfo = mock(CameraInfo.class);
+ final long cameraInfoIdentifier = 6;
+ final VideoQualityConstraint videoQualityConstraint = VideoQualityConstraint.FHD;
+ final Size sizeResult = new Size(30, 40);
+ final QualitySelectorHostApiImpl hostApi = new QualitySelectorHostApiImpl(instanceManager);
+
+ instanceManager.addDartCreatedInstance(mockCameraInfo, cameraInfoIdentifier);
+
+ try (MockedStatic mockedQualitySelector = mockStatic(QualitySelector.class)) {
+ mockedQualitySelector
+ .when(() -> QualitySelector.getResolution(mockCameraInfo, Quality.FHD))
+ .thenAnswer((Answer) invocation -> sizeResult);
+
+ final ResolutionInfo result =
+ hostApi.getResolution(cameraInfoIdentifier, videoQualityConstraint);
+
+ assertEquals(result.getWidth(), Long.valueOf(sizeResult.getWidth()));
+ assertEquals(result.getHeight(), Long.valueOf(sizeResult.getHeight()));
+ }
+ }
+}
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/RecorderTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/RecorderTest.java
index 72355d260c5..a859e9d513e 100644
--- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/RecorderTest.java
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/RecorderTest.java
@@ -18,6 +18,7 @@
import android.content.Context;
import androidx.camera.video.FileOutputOptions;
import androidx.camera.video.PendingRecording;
+import androidx.camera.video.QualitySelector;
import androidx.camera.video.Recorder;
import androidx.test.core.app.ApplicationProvider;
import io.flutter.plugin.common.BinaryMessenger;
@@ -56,16 +57,18 @@ public void tearDown() {
}
@Test
- public void createTest() {
+ public void create_createsExpectedRecorderInstance() {
final int recorderId = 0;
final int aspectRatio = 1;
final int bitRate = 2;
+ final int qualitySelectorId = 3;
final RecorderHostApiImpl recorderHostApi =
new RecorderHostApiImpl(mockBinaryMessenger, testInstanceManager, context);
final CameraXProxy mockCameraXProxy = mock(CameraXProxy.class);
final Recorder.Builder mockRecorderBuilder = mock(Recorder.Builder.class);
+ final QualitySelector mockQualitySelector = mock(QualitySelector.class);
recorderHostApi.cameraXProxy = mockCameraXProxy;
when(mockCameraXProxy.createRecorderBuilder()).thenReturn(mockRecorderBuilder);
when(mockRecorderBuilder.setAspectRatio(aspectRatio)).thenReturn(mockRecorderBuilder);
@@ -73,12 +76,18 @@ public void createTest() {
.thenReturn(mockRecorderBuilder);
when(mockRecorderBuilder.setExecutor(any(Executor.class))).thenReturn(mockRecorderBuilder);
when(mockRecorderBuilder.build()).thenReturn(mockRecorder);
+ testInstanceManager.addDartCreatedInstance(
+ mockQualitySelector, Long.valueOf(qualitySelectorId));
recorderHostApi.create(
- Long.valueOf(recorderId), Long.valueOf(aspectRatio), Long.valueOf(bitRate));
+ Long.valueOf(recorderId),
+ Long.valueOf(aspectRatio),
+ Long.valueOf(bitRate),
+ Long.valueOf(qualitySelectorId));
verify(mockCameraXProxy).createRecorderBuilder();
verify(mockRecorderBuilder).setAspectRatio(aspectRatio);
verify(mockRecorderBuilder).setTargetVideoEncodingBitRate(bitRate);
+ verify(mockRecorderBuilder).setQualitySelector(mockQualitySelector);
verify(mockRecorderBuilder).build();
assertEquals(testInstanceManager.getInstance(Long.valueOf(recorderId)), mockRecorder);
testInstanceManager.remove(Long.valueOf(recorderId));
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 2d1dccf971c..b97d19360d8 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
@@ -43,6 +43,29 @@ enum LiveDataSupportedType {
zoomState,
}
+/// Video quality constraints that will be used by a QualitySelector to choose
+/// an appropriate video resolution.
+///
+/// These are pre-defined quality constants that are universally used for video.
+///
+/// See https://developer.android.com/reference/androidx/camera/video/Quality.
+enum VideoQualityConstraint {
+ SD,
+ HD,
+ FHD,
+ UHD,
+ lowest,
+ highest,
+}
+
+/// Fallback rules for selecting video resolution.
+enum VideoResolutionFallbackRule {
+ higherQualityOrLowerThan,
+ higherQualityThan,
+ lowerQualityOrHigherThan,
+ lowerQualityThan,
+}
+
class ResolutionInfo {
ResolutionInfo({
required this.width,
@@ -1183,14 +1206,17 @@ class RecorderHostApi {
static const MessageCodec codec = StandardMessageCodec();
- Future create(
- int arg_identifier, int? arg_aspectRatio, int? arg_bitRate) async {
+ Future create(int arg_identifier, int? arg_aspectRatio,
+ int? arg_bitRate, int? arg_qualitySelectorId) async {
final BasicMessageChannel channel = BasicMessageChannel(
'dev.flutter.pigeon.RecorderHostApi.create', codec,
binaryMessenger: _binaryMessenger);
- final List? replyList = await channel
- .send([arg_identifier, arg_aspectRatio, arg_bitRate])
- as List?;
+ final List? replyList = await channel.send([
+ arg_identifier,
+ arg_aspectRatio,
+ arg_bitRate,
+ arg_qualitySelectorId
+ ]) as List?;
if (replyList == null) {
throw PlatformException(
code: 'channel-error',
@@ -2444,3 +2470,130 @@ abstract class PlaneProxyFlutterApi {
}
}
}
+
+class _QualitySelectorHostApiCodec extends StandardMessageCodec {
+ const _QualitySelectorHostApiCodec();
+ @override
+ void writeValue(WriteBuffer buffer, Object? value) {
+ if (value is ResolutionInfo) {
+ buffer.putUint8(128);
+ writeValue(buffer, value.encode());
+ } else {
+ super.writeValue(buffer, value);
+ }
+ }
+
+ @override
+ Object? readValueOfType(int type, ReadBuffer buffer) {
+ switch (type) {
+ case 128:
+ return ResolutionInfo.decode(readValue(buffer)!);
+ default:
+ return super.readValueOfType(type, buffer);
+ }
+ }
+}
+
+class QualitySelectorHostApi {
+ /// Constructor for [QualitySelectorHostApi]. 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.
+ QualitySelectorHostApi({BinaryMessenger? binaryMessenger})
+ : _binaryMessenger = binaryMessenger;
+ final BinaryMessenger? _binaryMessenger;
+
+ static const MessageCodec codec = _QualitySelectorHostApiCodec();
+
+ Future create(
+ int arg_identifier,
+ List arg_videoQualityConstraintIndexList,
+ int? arg_fallbackStrategyId) async {
+ final BasicMessageChannel channel = BasicMessageChannel(
+ 'dev.flutter.pigeon.QualitySelectorHostApi.create', codec,
+ binaryMessenger: _binaryMessenger);
+ final List? replyList = await channel.send([
+ arg_identifier,
+ arg_videoQualityConstraintIndexList,
+ arg_fallbackStrategyId
+ ]) 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 getResolution(
+ int arg_cameraInfoId, VideoQualityConstraint arg_quality) async {
+ final BasicMessageChannel channel = BasicMessageChannel(
+ 'dev.flutter.pigeon.QualitySelectorHostApi.getResolution', codec,
+ binaryMessenger: _binaryMessenger);
+ final List? replyList = await channel
+ .send([arg_cameraInfoId, arg_quality.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 ResolutionInfo?)!;
+ }
+ }
+}
+
+class FallbackStrategyHostApi {
+ /// Constructor for [FallbackStrategyHostApi]. 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.
+ FallbackStrategyHostApi({BinaryMessenger? binaryMessenger})
+ : _binaryMessenger = binaryMessenger;
+ final BinaryMessenger? _binaryMessenger;
+
+ static const MessageCodec codec = StandardMessageCodec();
+
+ Future create(int arg_identifier, VideoQualityConstraint arg_quality,
+ VideoResolutionFallbackRule arg_fallbackRule) async {
+ final BasicMessageChannel channel = BasicMessageChannel(
+ 'dev.flutter.pigeon.FallbackStrategyHostApi.create', codec,
+ binaryMessenger: _binaryMessenger);
+ final List? replyList = await channel.send([
+ arg_identifier,
+ arg_quality.index,
+ arg_fallbackRule.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 {
+ return;
+ }
+ }
+}
diff --git a/packages/camera/camera_android_camerax/lib/src/fallback_strategy.dart b/packages/camera/camera_android_camerax/lib/src/fallback_strategy.dart
new file mode 100644
index 00000000000..ee098fafd14
--- /dev/null
+++ b/packages/camera/camera_android_camerax/lib/src/fallback_strategy.dart
@@ -0,0 +1,90 @@
+// 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';
+import 'package:meta/meta.dart' show immutable;
+
+import 'camerax_library.g.dart';
+import 'instance_manager.dart';
+import 'java_object.dart';
+
+/// Strategy that will be adopted when the device in use does not support all
+/// of the desired quality specified for a particular QualitySelector instance.
+///
+/// See https://developer.android.com/reference/androidx/camera/video/FallbackStrategy.
+@immutable
+class FallbackStrategy extends JavaObject {
+ /// Creates a [FallbackStrategy].
+ FallbackStrategy(
+ {BinaryMessenger? binaryMessenger,
+ InstanceManager? instanceManager,
+ required this.quality,
+ required this.fallbackRule})
+ : super.detached(
+ binaryMessenger: binaryMessenger,
+ instanceManager: instanceManager) {
+ _api = _FallbackStrategyHostApiImpl(
+ binaryMessenger: binaryMessenger, instanceManager: instanceManager);
+ _api.createFromInstance(this, quality, fallbackRule);
+ }
+
+ /// Constructs a [FallbackStrategy] that is not automatically attached to a native object.
+ FallbackStrategy.detached(
+ {super.binaryMessenger,
+ super.instanceManager,
+ required this.quality,
+ required this.fallbackRule})
+ : super.detached();
+
+ late final _FallbackStrategyHostApiImpl _api;
+
+ /// The input quality used to specify this fallback strategy relative to.
+ final VideoQualityConstraint quality;
+
+ /// The fallback rule that this strategy will follow.
+ final VideoResolutionFallbackRule fallbackRule;
+}
+
+/// Host API implementation of [FallbackStrategy].
+class _FallbackStrategyHostApiImpl extends FallbackStrategyHostApi {
+ /// Constructs a [FallbackStrategyHostApiImpl].
+ ///
+ /// 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].
+ _FallbackStrategyHostApiImpl(
+ {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 [FallbackStrategy] instance with the specified video [quality]
+ /// and [fallbackRule].
+ void createFromInstance(
+ FallbackStrategy instance,
+ VideoQualityConstraint quality,
+ VideoResolutionFallbackRule fallbackRule) {
+ final int identifier = instanceManager.addDartCreatedInstance(instance,
+ onCopy: (FallbackStrategy original) {
+ return FallbackStrategy.detached(
+ binaryMessenger: binaryMessenger,
+ instanceManager: instanceManager,
+ quality: original.quality,
+ fallbackRule: original.fallbackRule,
+ );
+ });
+ create(identifier, quality, fallbackRule);
+ }
+}
diff --git a/packages/camera/camera_android_camerax/lib/src/quality_selector.dart b/packages/camera/camera_android_camerax/lib/src/quality_selector.dart
new file mode 100644
index 00000000000..303d4aecaf5
--- /dev/null
+++ b/packages/camera/camera_android_camerax/lib/src/quality_selector.dart
@@ -0,0 +1,150 @@
+// 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';
+import 'package:meta/meta.dart' show immutable;
+
+import 'camera_info.dart';
+import 'camerax_library.g.dart';
+import 'fallback_strategy.dart';
+import 'instance_manager.dart';
+import 'java_object.dart';
+
+/// Quality setting used to configure components with quality setting
+/// requirements such as creating a Recorder.
+///
+/// See https://developer.android.com/reference/androidx/camera/video/QualitySelector.
+@immutable
+class QualitySelector extends JavaObject {
+ /// Creates a [QualitySelector] with the desired quality and fallback
+ /// strategy, if specified.
+ QualitySelector.from(
+ {BinaryMessenger? binaryMessenger,
+ InstanceManager? instanceManager,
+ required VideoQualityConstraint quality,
+ this.fallbackStrategy})
+ : qualityList = [quality],
+ super.detached(
+ binaryMessenger: binaryMessenger,
+ instanceManager: instanceManager) {
+ _api = _QualitySelectorHostApiImpl(
+ binaryMessenger: binaryMessenger, instanceManager: instanceManager);
+ _api.createFromInstance(this, qualityList, fallbackStrategy);
+ }
+
+ /// Creates a [QualitySelector] with ordered desired qualities and fallback
+ /// strategy, if specified.
+ ///
+ /// The final quality will be selected according to the order in which they are
+ /// specified.
+ QualitySelector.fromOrderedList(
+ {BinaryMessenger? binaryMessenger,
+ InstanceManager? instanceManager,
+ required this.qualityList,
+ this.fallbackStrategy})
+ : assert(qualityList.isNotEmpty,
+ 'Quality list specified must be non-empty.'),
+ super.detached(
+ binaryMessenger: binaryMessenger,
+ instanceManager: instanceManager) {
+ _api = _QualitySelectorHostApiImpl(
+ binaryMessenger: binaryMessenger, instanceManager: instanceManager);
+ _api.createFromInstance(this, qualityList, fallbackStrategy);
+ }
+
+ /// Creates a [QualitySelector] that is not automatically attached to a
+ /// native object.
+ QualitySelector.detached({
+ super.binaryMessenger,
+ super.instanceManager,
+ required this.qualityList,
+ this.fallbackStrategy,
+ }) : super.detached();
+
+ late final _QualitySelectorHostApiImpl _api;
+
+ /// Desired qualities for this selector instance.
+ final List qualityList;
+
+ /// Desired fallback strategy for this selector instance.
+ final FallbackStrategy? fallbackStrategy;
+
+ /// Retrieves the corresponding resolution from the input [quality] for the
+ /// camera represented by [cameraInfo].
+ static Future getResolution(
+ CameraInfo cameraInfo, VideoQualityConstraint quality,
+ {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) {
+ final _QualitySelectorHostApiImpl api = _QualitySelectorHostApiImpl(
+ binaryMessenger: binaryMessenger, instanceManager: instanceManager);
+ return api.getResolutionFromInstance(cameraInfo, quality);
+ }
+}
+
+/// Host API implementation of [QualitySelector].
+class _QualitySelectorHostApiImpl extends QualitySelectorHostApi {
+ /// Constructs a [QualitySelectorHostApiImpl].
+ ///
+ /// 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].
+ _QualitySelectorHostApiImpl(
+ {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 [QualitySelector] instance with the desired qualities and
+ /// fallback strategy specified.
+ void createFromInstance(
+ QualitySelector instance,
+ List qualityList,
+ FallbackStrategy? fallbackStrategy) {
+ final int identifier = instanceManager.addDartCreatedInstance(instance,
+ onCopy: (QualitySelector original) {
+ return QualitySelector.detached(
+ binaryMessenger: binaryMessenger,
+ instanceManager: instanceManager,
+ qualityList: original.qualityList,
+ fallbackStrategy: original.fallbackStrategy,
+ );
+ });
+ final List qualityIndices = qualityList
+ .map((VideoQualityConstraint quality) => quality.index)
+ .toList();
+
+ create(
+ identifier,
+ qualityIndices,
+ fallbackStrategy == null
+ ? null
+ : instanceManager.getIdentifier(fallbackStrategy));
+ }
+
+ /// Retrieves the corresponding resolution from the input [quality] for the
+ /// camera represented by [cameraInfo].
+ Future getResolutionFromInstance(
+ CameraInfo cameraInfo, VideoQualityConstraint quality) async {
+ final int? cameraInfoIdentifier = instanceManager.getIdentifier(cameraInfo);
+
+ if (cameraInfoIdentifier == null) {
+ throw ArgumentError(
+ 'The CameraInfo instance specified needs to be added to the InstanceManager instance in use.');
+ }
+
+ final ResolutionInfo resolution =
+ await getResolution(cameraInfoIdentifier, quality);
+ return resolution;
+ }
+}
diff --git a/packages/camera/camera_android_camerax/lib/src/recorder.dart b/packages/camera/camera_android_camerax/lib/src/recorder.dart
index 43f33e31734..13953621552 100644
--- a/packages/camera/camera_android_camerax/lib/src/recorder.dart
+++ b/packages/camera/camera_android_camerax/lib/src/recorder.dart
@@ -7,9 +7,11 @@ import 'package:meta/meta.dart' show immutable;
import 'android_camera_camerax_flutter_api_impls.dart';
import 'camerax_library.g.dart';
+import 'fallback_strategy.dart';
import 'instance_manager.dart';
import 'java_object.dart';
import 'pending_recording.dart';
+import 'quality_selector.dart';
/// A dart wrapping of the CameraX Recorder class.
///
@@ -21,14 +23,15 @@ class Recorder extends JavaObject {
{BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
this.aspectRatio,
- this.bitRate})
+ this.bitRate,
+ this.qualitySelector})
: super.detached(
binaryMessenger: binaryMessenger,
instanceManager: instanceManager) {
AndroidCameraXCameraFlutterApis.instance.ensureSetUp();
_api = RecorderHostApiImpl(
binaryMessenger: binaryMessenger, instanceManager: instanceManager);
- _api.createFromInstance(this, aspectRatio, bitRate);
+ _api.createFromInstance(this, aspectRatio, bitRate, qualitySelector);
}
/// Creates a [Recorder] that is not automatically attached to a native object
@@ -36,7 +39,8 @@ class Recorder extends JavaObject {
{BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
this.aspectRatio,
- this.bitRate})
+ this.bitRate,
+ this.qualitySelector})
: super.detached(
binaryMessenger: binaryMessenger,
instanceManager: instanceManager) {
@@ -45,14 +49,42 @@ class Recorder extends JavaObject {
AndroidCameraXCameraFlutterApis.instance.ensureSetUp();
}
+ /// Returns default [QualitySelector] for recordings.
+ ///
+ /// See https://developer.android.com/reference/androidx/camera/video/Recorder#DEFAULT_QUALITY_SELECTOR().
+ static QualitySelector getDefaultQualitySelector({
+ BinaryMessenger? binaryMessenger,
+ InstanceManager? instanceManager,
+ }) {
+ return QualitySelector.fromOrderedList(
+ binaryMessenger: binaryMessenger,
+ instanceManager: instanceManager,
+ qualityList: const [
+ VideoQualityConstraint.FHD,
+ VideoQualityConstraint.HD,
+ VideoQualityConstraint.SD
+ ],
+ fallbackStrategy: FallbackStrategy(
+ quality: VideoQualityConstraint.FHD,
+ fallbackRule: VideoResolutionFallbackRule.higherQualityOrLowerThan),
+ );
+ }
+
late final RecorderHostApiImpl _api;
- /// The video aspect ratio of this Recorder.
+ /// The video aspect ratio of this [Recorder].
final int? aspectRatio;
/// The intended video encoding bitrate for recording.
final int? bitRate;
+ /// The [QualitySelector] of this [Recorder] used to select the resolution of
+ /// the recording depending on the resoutions supported by the camera.
+ ///
+ /// Default selector is that returned by [getDefaultQualitySelector], and it
+ /// is compatible with setting the aspect ratio.
+ final QualitySelector? qualitySelector;
+
/// Prepare a recording that will be saved to a file.
Future prepareRecording(String path) {
return _api.prepareRecordingFromInstance(this, path);
@@ -77,7 +109,8 @@ class RecorderHostApiImpl extends RecorderHostApi {
late final InstanceManager instanceManager;
/// Creates a [Recorder] with the provided aspect ratio and bitrate if specified.
- void createFromInstance(Recorder instance, int? aspectRatio, int? bitRate) {
+ void createFromInstance(Recorder instance, int? aspectRatio, int? bitRate,
+ QualitySelector? qualitySelector) {
int? identifier = instanceManager.getIdentifier(instance);
identifier ??= instanceManager.addDartCreatedInstance(instance,
onCopy: (Recorder original) {
@@ -85,9 +118,16 @@ class RecorderHostApiImpl extends RecorderHostApi {
binaryMessenger: binaryMessenger,
instanceManager: instanceManager,
aspectRatio: aspectRatio,
- bitRate: bitRate);
+ bitRate: bitRate,
+ qualitySelector: qualitySelector);
});
- create(identifier, aspectRatio, bitRate);
+ create(
+ identifier,
+ aspectRatio,
+ bitRate,
+ qualitySelector == null
+ ? null
+ : instanceManager.getIdentifier(qualitySelector)!);
}
/// Prepares a [Recording] using this recorder. The output file will be saved
diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart
index a22d7f6e351..02ea78c049a 100644
--- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart
+++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart
@@ -96,6 +96,31 @@ class ExposureCompensationRange {
int maxCompensation;
}
+/// Video quality constraints that will be used by a QualitySelector to choose
+/// an appropriate video resolution.
+///
+/// These are pre-defined quality constants that are universally used for video.
+///
+/// See https://developer.android.com/reference/androidx/camera/video/Quality.
+enum VideoQualityConstraint {
+ SD, // 480p
+ HD, // 720p
+ FHD, // 1080p
+ UHD, // 2160p
+ lowest,
+ highest,
+}
+
+/// Fallback rules for selecting video resolution.
+///
+/// See https://developer.android.com/reference/androidx/camera/video/FallbackStrategy.
+enum VideoResolutionFallbackRule {
+ higherQualityOrLowerThan,
+ higherQualityThan,
+ lowerQualityOrHigherThan,
+ lowerQualityThan,
+}
+
@HostApi(dartHostTestHandler: 'TestInstanceManagerHostApi')
abstract class InstanceManagerHostApi {
/// Clear the native `InstanceManager`.
@@ -219,7 +244,8 @@ abstract class VideoCaptureFlutterApi {
@HostApi(dartHostTestHandler: 'TestRecorderHostApi')
abstract class RecorderHostApi {
- void create(int identifier, int? aspectRatio, int? bitRate);
+ void create(
+ int identifier, int? aspectRatio, int? bitRate, int? qualitySelectorId);
int getAspectRatio(int identifier);
@@ -372,3 +398,20 @@ abstract class ImageProxyFlutterApi {
abstract class PlaneProxyFlutterApi {
void create(int identifier, Uint8List buffer, int pixelStride, int rowStride);
}
+
+@HostApi(dartHostTestHandler: 'TestQualitySelectorHostApi')
+abstract class QualitySelectorHostApi {
+ // TODO(camsim99): Change qualityList to List when
+ // enums are supported for collection types.
+ void create(int identifier, List videoQualityConstraintIndexList,
+ int? fallbackStrategyId);
+
+ ResolutionInfo getResolution(
+ int cameraInfoId, VideoQualityConstraint quality);
+}
+
+@HostApi(dartHostTestHandler: 'TestFallbackStrategyHostApi')
+abstract class FallbackStrategyHostApi {
+ void create(int identifier, VideoQualityConstraint quality,
+ VideoResolutionFallbackRule fallbackRule);
+}
diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml
index 88e408d1df8..b233b0c18a4 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+13
+version: 0.5.0+14
environment:
sdk: ">=2.19.0 <4.0.0"
diff --git a/packages/camera/camera_android_camerax/test/fallback_strategy_test.dart b/packages/camera/camera_android_camerax/test/fallback_strategy_test.dart
new file mode 100644
index 00000000000..8c6960c082c
--- /dev/null
+++ b/packages/camera/camera_android_camerax/test/fallback_strategy_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/camerax_library.g.dart';
+import 'package:camera_android_camerax/src/fallback_strategy.dart';
+import 'package:camera_android_camerax/src/instance_manager.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+
+import 'fallback_strategy_test.mocks.dart';
+import 'test_camerax_library.g.dart';
+
+@GenerateMocks([TestFallbackStrategyHostApi, TestInstanceManagerHostApi])
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ group('FallbackStrategy', () {
+ tearDown(() {
+ TestFallbackStrategyHostApi.setup(null);
+ TestInstanceManagerHostApi.setup(null);
+ });
+
+ test('detached constructor does not call create on the Java side',
+ () async {
+ final MockTestFallbackStrategyHostApi mockApi =
+ MockTestFallbackStrategyHostApi();
+ TestFallbackStrategyHostApi.setup(mockApi);
+
+ final InstanceManager instanceManager = InstanceManager(
+ onWeakReferenceRemoved: (_) {},
+ );
+
+ FallbackStrategy.detached(
+ quality: VideoQualityConstraint.UHD,
+ fallbackRule: VideoResolutionFallbackRule.higherQualityThan,
+ instanceManager: instanceManager,
+ );
+
+ verifyNever(mockApi.create(
+ argThat(isA()),
+ argThat(isA()),
+ argThat(isA()),
+ ));
+ });
+
+ test('constructor calls create on the Java side', () {
+ final MockTestFallbackStrategyHostApi mockApi =
+ MockTestFallbackStrategyHostApi();
+ TestFallbackStrategyHostApi.setup(mockApi);
+ TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi());
+
+ final InstanceManager instanceManager = InstanceManager(
+ onWeakReferenceRemoved: (_) {},
+ );
+
+ const VideoQualityConstraint quality = VideoQualityConstraint.HD;
+
+ const VideoResolutionFallbackRule fallbackRule =
+ VideoResolutionFallbackRule.lowerQualityThan;
+
+ final FallbackStrategy instance = FallbackStrategy(
+ quality: quality,
+ fallbackRule: fallbackRule,
+ instanceManager: instanceManager,
+ );
+
+ verify(mockApi.create(
+ instanceManager.getIdentifier(instance),
+ quality,
+ fallbackRule,
+ ));
+ });
+ });
+}
diff --git a/packages/camera/camera_android_camerax/test/fallback_strategy_test.mocks.dart b/packages/camera/camera_android_camerax/test/fallback_strategy_test.mocks.dart
new file mode 100644
index 00000000000..02d185a7222
--- /dev/null
+++ b/packages/camera/camera_android_camerax/test/fallback_strategy_test.mocks.dart
@@ -0,0 +1,69 @@
+// Mocks generated by Mockito 5.4.1 from annotations
+// in camera_android_camerax/test/fallback_strategy_test.dart.
+// Do not manually edit this file.
+
+// @dart=2.19
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i3;
+import 'package:mockito/mockito.dart' as _i1;
+
+import 'test_camerax_library.g.dart' as _i2;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+/// A class which mocks [TestFallbackStrategyHostApi].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockTestFallbackStrategyHostApi extends _i1.Mock
+ implements _i2.TestFallbackStrategyHostApi {
+ MockTestFallbackStrategyHostApi() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ void create(
+ int? identifier,
+ _i3.VideoQualityConstraint? quality,
+ _i3.VideoResolutionFallbackRule? fallbackRule,
+ ) =>
+ super.noSuchMethod(
+ Invocation.method(
+ #create,
+ [
+ identifier,
+ quality,
+ fallbackRule,
+ ],
+ ),
+ returnValueForMissingStub: null,
+ );
+}
+
+/// A class which mocks [TestInstanceManagerHostApi].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockTestInstanceManagerHostApi extends _i1.Mock
+ implements _i2.TestInstanceManagerHostApi {
+ MockTestInstanceManagerHostApi() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ void clear() => super.noSuchMethod(
+ Invocation.method(
+ #clear,
+ [],
+ ),
+ returnValueForMissingStub: null,
+ );
+}
diff --git a/packages/camera/camera_android_camerax/test/quality_selector_test.dart b/packages/camera/camera_android_camerax/test/quality_selector_test.dart
new file mode 100644
index 00000000000..8c05aa7be43
--- /dev/null
+++ b/packages/camera/camera_android_camerax/test/quality_selector_test.dart
@@ -0,0 +1,180 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:camera_android_camerax/src/camera_info.dart';
+import 'package:camera_android_camerax/src/camerax_library.g.dart';
+import 'package:camera_android_camerax/src/fallback_strategy.dart';
+import 'package:camera_android_camerax/src/instance_manager.dart';
+import 'package:camera_android_camerax/src/quality_selector.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+
+import 'quality_selector_test.mocks.dart';
+import 'test_camerax_library.g.dart';
+
+@GenerateMocks([
+ CameraInfo,
+ FallbackStrategy,
+ TestQualitySelectorHostApi,
+ TestInstanceManagerHostApi
+])
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ group('QualitySelector', () {
+ tearDown(() {
+ TestQualitySelectorHostApi.setup(null);
+ TestInstanceManagerHostApi.setup(null);
+ });
+
+ test('detached constructor does not make call to create on the Java side',
+ () {
+ final MockTestQualitySelectorHostApi mockApi =
+ MockTestQualitySelectorHostApi();
+ TestQualitySelectorHostApi.setup(mockApi);
+ TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi());
+
+ final InstanceManager instanceManager = InstanceManager(
+ onWeakReferenceRemoved: (_) {},
+ );
+
+ QualitySelector.detached(
+ qualityList: const [VideoQualityConstraint.FHD],
+ fallbackStrategy: MockFallbackStrategy(),
+ instanceManager: instanceManager,
+ );
+
+ verifyNever(mockApi.create(
+ argThat(isA()), argThat(isA>()), argThat(isA())));
+ });
+
+ test('single quality constructor calls create on the Java side', () {
+ final MockTestQualitySelectorHostApi mockApi =
+ MockTestQualitySelectorHostApi();
+ TestQualitySelectorHostApi.setup(mockApi);
+ TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi());
+
+ final InstanceManager instanceManager = InstanceManager(
+ onWeakReferenceRemoved: (_) {},
+ );
+
+ const VideoQualityConstraint quality = VideoQualityConstraint.FHD;
+ final FallbackStrategy fallbackStrategy = MockFallbackStrategy();
+ const int fallbackStrategyIdentifier = 9;
+
+ instanceManager.addHostCreatedInstance(
+ fallbackStrategy,
+ fallbackStrategyIdentifier,
+ onCopy: (_) => MockFallbackStrategy(),
+ );
+
+ final QualitySelector instance = QualitySelector.from(
+ quality: quality,
+ fallbackStrategy: fallbackStrategy,
+ instanceManager: instanceManager,
+ );
+
+ verify(mockApi.create(
+ instanceManager.getIdentifier(instance),
+ [2],
+ fallbackStrategyIdentifier,
+ ));
+ });
+
+ test('quality list constructor calls create on the Java side', () {
+ final MockTestQualitySelectorHostApi mockApi =
+ MockTestQualitySelectorHostApi();
+ TestQualitySelectorHostApi.setup(mockApi);
+ TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi());
+
+ final InstanceManager instanceManager = InstanceManager(
+ onWeakReferenceRemoved: (_) {},
+ );
+
+ const List qualityList = [
+ VideoQualityConstraint.FHD,
+ VideoQualityConstraint.highest
+ ];
+
+ final FallbackStrategy fallbackStrategy = MockFallbackStrategy();
+
+ const int fallbackStrategyIdentifier = 9;
+ instanceManager.addHostCreatedInstance(
+ fallbackStrategy,
+ fallbackStrategyIdentifier,
+ onCopy: (_) => MockFallbackStrategy(),
+ );
+
+ final QualitySelector instance = QualitySelector.fromOrderedList(
+ qualityList: qualityList,
+ fallbackStrategy: fallbackStrategy,
+ instanceManager: instanceManager,
+ );
+
+ verify(mockApi.create(
+ instanceManager.getIdentifier(instance),
+ [2, 5],
+ fallbackStrategyIdentifier,
+ ));
+ });
+
+ test('getResolution returns expected resolution info', () async {
+ final MockTestQualitySelectorHostApi mockApi =
+ MockTestQualitySelectorHostApi();
+ TestQualitySelectorHostApi.setup(mockApi);
+
+ final InstanceManager instanceManager = InstanceManager(
+ onWeakReferenceRemoved: (_) {},
+ );
+
+ final QualitySelector instance = QualitySelector.detached(
+ instanceManager: instanceManager,
+ qualityList: const [VideoQualityConstraint.HD],
+ fallbackStrategy: MockFallbackStrategy(),
+ );
+ const int instanceIdentifier = 0;
+ instanceManager.addHostCreatedInstance(
+ instance,
+ instanceIdentifier,
+ onCopy: (QualitySelector original) => QualitySelector.detached(
+ qualityList: original.qualityList,
+ fallbackStrategy: original.fallbackStrategy,
+ instanceManager: instanceManager,
+ ),
+ );
+
+ final CameraInfo cameraInfo = MockCameraInfo();
+ const int cameraInfoIdentifier = 6;
+ instanceManager.addHostCreatedInstance(
+ cameraInfo,
+ cameraInfoIdentifier,
+ onCopy: (_) => MockCameraInfo(),
+ );
+
+ const VideoQualityConstraint quality = VideoQualityConstraint.FHD;
+ final ResolutionInfo expectedResult =
+ ResolutionInfo(width: 34, height: 23);
+
+ when(mockApi.getResolution(
+ cameraInfoIdentifier,
+ quality,
+ )).thenAnswer((_) {
+ return expectedResult;
+ });
+
+ final ResolutionInfo result = await QualitySelector.getResolution(
+ cameraInfo, quality,
+ instanceManager: instanceManager);
+
+ expect(result.width, expectedResult.width);
+ expect(result.height, expectedResult.height);
+
+ verify(mockApi.getResolution(
+ cameraInfoIdentifier,
+ quality,
+ ));
+ });
+ });
+}
diff --git a/packages/camera/camera_android_camerax/test/quality_selector_test.mocks.dart b/packages/camera/camera_android_camerax/test/quality_selector_test.mocks.dart
new file mode 100644
index 00000000000..ba08711ba73
--- /dev/null
+++ b/packages/camera/camera_android_camerax/test/quality_selector_test.mocks.dart
@@ -0,0 +1,218 @@
+// Mocks generated by Mockito 5.4.1 from annotations
+// in camera_android_camerax/test/quality_selector_test.dart.
+// Do not manually edit this file.
+
+// @dart=2.19
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i6;
+
+import 'package:camera_android_camerax/src/camera_info.dart' as _i5;
+import 'package:camera_android_camerax/src/camera_state.dart' as _i7;
+import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i4;
+import 'package:camera_android_camerax/src/exposure_state.dart' as _i3;
+import 'package:camera_android_camerax/src/fallback_strategy.dart' as _i9;
+import 'package:camera_android_camerax/src/live_data.dart' as _i2;
+import 'package:camera_android_camerax/src/zoom_state.dart' as _i8;
+import 'package:mockito/mockito.dart' as _i1;
+
+import 'test_camerax_library.g.dart' as _i10;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+class _FakeLiveData_0 extends _i1.SmartFake
+ implements _i2.LiveData {
+ _FakeLiveData_0(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakeExposureState_1 extends _i1.SmartFake implements _i3.ExposureState {
+ _FakeExposureState_1(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+class _FakeResolutionInfo_2 extends _i1.SmartFake
+ implements _i4.ResolutionInfo {
+ _FakeResolutionInfo_2(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+/// A class which mocks [CameraInfo].
+///
+/// See the documentation for Mockito's code generation for more information.
+// ignore: must_be_immutable
+class MockCameraInfo extends _i1.Mock implements _i5.CameraInfo {
+ MockCameraInfo() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i6.Future getSensorRotationDegrees() => (super.noSuchMethod(
+ Invocation.method(
+ #getSensorRotationDegrees,
+ [],
+ ),
+ returnValue: _i6.Future.value(0),
+ ) as _i6.Future);
+ @override
+ _i6.Future<_i2.LiveData<_i7.CameraState>> getCameraState() =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #getCameraState,
+ [],
+ ),
+ returnValue: _i6.Future<_i2.LiveData<_i7.CameraState>>.value(
+ _FakeLiveData_0<_i7.CameraState>(
+ this,
+ Invocation.method(
+ #getCameraState,
+ [],
+ ),
+ )),
+ ) as _i6.Future<_i2.LiveData<_i7.CameraState>>);
+ @override
+ _i6.Future<_i3.ExposureState> getExposureState() => (super.noSuchMethod(
+ Invocation.method(
+ #getExposureState,
+ [],
+ ),
+ returnValue: _i6.Future<_i3.ExposureState>.value(_FakeExposureState_1(
+ this,
+ Invocation.method(
+ #getExposureState,
+ [],
+ ),
+ )),
+ ) as _i6.Future<_i3.ExposureState>);
+ @override
+ _i6.Future<_i2.LiveData<_i8.ZoomState>> getZoomState() => (super.noSuchMethod(
+ Invocation.method(
+ #getZoomState,
+ [],
+ ),
+ returnValue: _i6.Future<_i2.LiveData<_i8.ZoomState>>.value(
+ _FakeLiveData_0<_i8.ZoomState>(
+ this,
+ Invocation.method(
+ #getZoomState,
+ [],
+ ),
+ )),
+ ) as _i6.Future<_i2.LiveData<_i8.ZoomState>>);
+}
+
+/// A class which mocks [FallbackStrategy].
+///
+/// See the documentation for Mockito's code generation for more information.
+// ignore: must_be_immutable
+class MockFallbackStrategy extends _i1.Mock implements _i9.FallbackStrategy {
+ MockFallbackStrategy() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ _i4.VideoQualityConstraint get quality => (super.noSuchMethod(
+ Invocation.getter(#quality),
+ returnValue: _i4.VideoQualityConstraint.SD,
+ ) as _i4.VideoQualityConstraint);
+ @override
+ _i4.VideoResolutionFallbackRule get fallbackRule => (super.noSuchMethod(
+ Invocation.getter(#fallbackRule),
+ returnValue: _i4.VideoResolutionFallbackRule.higherQualityOrLowerThan,
+ ) as _i4.VideoResolutionFallbackRule);
+}
+
+/// A class which mocks [TestQualitySelectorHostApi].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockTestQualitySelectorHostApi extends _i1.Mock
+ implements _i10.TestQualitySelectorHostApi {
+ MockTestQualitySelectorHostApi() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ void create(
+ int? identifier,
+ List? videoQualityConstraintIndexList,
+ int? fallbackStrategyId,
+ ) =>
+ super.noSuchMethod(
+ Invocation.method(
+ #create,
+ [
+ identifier,
+ videoQualityConstraintIndexList,
+ fallbackStrategyId,
+ ],
+ ),
+ returnValueForMissingStub: null,
+ );
+ @override
+ _i4.ResolutionInfo getResolution(
+ int? cameraInfoId,
+ _i4.VideoQualityConstraint? quality,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #getResolution,
+ [
+ cameraInfoId,
+ quality,
+ ],
+ ),
+ returnValue: _FakeResolutionInfo_2(
+ this,
+ Invocation.method(
+ #getResolution,
+ [
+ cameraInfoId,
+ quality,
+ ],
+ ),
+ ),
+ ) as _i4.ResolutionInfo);
+}
+
+/// A class which mocks [TestInstanceManagerHostApi].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockTestInstanceManagerHostApi extends _i1.Mock
+ implements _i10.TestInstanceManagerHostApi {
+ MockTestInstanceManagerHostApi() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ void clear() => super.noSuchMethod(
+ Invocation.method(
+ #clear,
+ [],
+ ),
+ returnValueForMissingStub: null,
+ );
+}
diff --git a/packages/camera/camera_android_camerax/test/recorder_test.dart b/packages/camera/camera_android_camerax/test/recorder_test.dart
index 6dc398bbdad..994c0f91b83 100644
--- a/packages/camera/camera_android_camerax/test/recorder_test.dart
+++ b/packages/camera/camera_android_camerax/test/recorder_test.dart
@@ -2,8 +2,10 @@
// 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/instance_manager.dart';
import 'package:camera_android_camerax/src/pending_recording.dart';
+import 'package:camera_android_camerax/src/quality_selector.dart';
import 'package:camera_android_camerax/src/recorder.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
@@ -12,8 +14,14 @@ import 'package:mockito/mockito.dart';
import 'recorder_test.mocks.dart';
import 'test_camerax_library.g.dart';
-@GenerateMocks(
- [TestRecorderHostApi, TestInstanceManagerHostApi, PendingRecording])
+@GenerateMocks([
+ QualitySelector,
+ TestInstanceManagerHostApi,
+ TestFallbackStrategyHostApi,
+ TestRecorderHostApi,
+ TestQualitySelectorHostApi,
+ PendingRecording
+])
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
@@ -33,8 +41,8 @@ void main() {
Recorder.detached(
instanceManager: instanceManager, aspectRatio: 0, bitRate: 0);
- verifyNever(mockApi.create(
- argThat(isA()), argThat(isA()), argThat(isA())));
+ verifyNever(mockApi.create(argThat(isA()), argThat(isA()),
+ argThat(isA()), argThat(isA())));
});
test('create does call create on the Java side', () async {
@@ -46,13 +54,52 @@ void main() {
const int aspectRatio = 1;
const int bitRate = 2;
+ final QualitySelector qualitySelector = MockQualitySelector();
+ const int qualitySelectorIdentifier = 33;
+
+ instanceManager.addHostCreatedInstance(
+ qualitySelector,
+ qualitySelectorIdentifier,
+ onCopy: (_) => MockQualitySelector(),
+ );
Recorder(
instanceManager: instanceManager,
aspectRatio: aspectRatio,
- bitRate: bitRate);
+ bitRate: bitRate,
+ qualitySelector: qualitySelector);
+
+ verify(mockApi.create(argThat(isA()), aspectRatio, bitRate,
+ qualitySelectorIdentifier));
+ });
+
+ test('getDefaultQualitySelector returns expected QualitySelector',
+ () async {
+ final MockTestQualitySelectorHostApi mockQualitySelectorApi =
+ MockTestQualitySelectorHostApi();
+ final MockTestFallbackStrategyHostApi mockFallbackStrategyApi =
+ MockTestFallbackStrategyHostApi();
+ TestQualitySelectorHostApi.setup(mockQualitySelectorApi);
+ TestFallbackStrategyHostApi.setup(mockFallbackStrategyApi);
- verify(mockApi.create(argThat(isA()), aspectRatio, bitRate));
+ final QualitySelector defaultQualitySelector =
+ Recorder.getDefaultQualitySelector();
+
+ expect(
+ defaultQualitySelector.qualityList,
+ equals(const [
+ VideoQualityConstraint.FHD,
+ VideoQualityConstraint.HD,
+ VideoQualityConstraint.SD
+ ]));
+ expect(defaultQualitySelector.fallbackStrategy!.quality,
+ equals(VideoQualityConstraint.FHD));
+ expect(defaultQualitySelector.fallbackStrategy!.fallbackRule,
+ equals(VideoResolutionFallbackRule.higherQualityOrLowerThan));
+
+ // Cleanup test Host APIs used only for this test.
+ TestQualitySelectorHostApi.setup(null);
+ TestFallbackStrategyHostApi.setup(null);
});
test('prepareRecording calls prepareRecording on Java side', () async {
@@ -84,7 +131,9 @@ void main() {
expect(pendingRecording, mockPendingRecording);
});
- test('flutterApiCreateTest', () {
+ test(
+ 'flutterApi create makes call to create Recorder instance with expected identifier',
+ () {
final InstanceManager instanceManager = InstanceManager(
onWeakReferenceRemoved: (_) {},
);
diff --git a/packages/camera/camera_android_camerax/test/recorder_test.mocks.dart b/packages/camera/camera_android_camerax/test/recorder_test.mocks.dart
index b563ba1bb78..c7850c70e53 100644
--- a/packages/camera/camera_android_camerax/test/recorder_test.mocks.dart
+++ b/packages/camera/camera_android_camerax/test/recorder_test.mocks.dart
@@ -5,13 +5,15 @@
// @dart=2.19
// ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'dart:async' as _i5;
+import 'dart:async' as _i7;
-import 'package:camera_android_camerax/src/pending_recording.dart' as _i4;
-import 'package:camera_android_camerax/src/recording.dart' as _i2;
+import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i2;
+import 'package:camera_android_camerax/src/pending_recording.dart' as _i6;
+import 'package:camera_android_camerax/src/quality_selector.dart' as _i4;
+import 'package:camera_android_camerax/src/recording.dart' as _i3;
import 'package:mockito/mockito.dart' as _i1;
-import 'test_camerax_library.g.dart' as _i3;
+import 'test_camerax_library.g.dart' as _i5;
// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
@@ -24,8 +26,9 @@ import 'test_camerax_library.g.dart' as _i3;
// ignore_for_file: camel_case_types
// ignore_for_file: subtype_of_sealed_class
-class _FakeRecording_0 extends _i1.SmartFake implements _i2.Recording {
- _FakeRecording_0(
+class _FakeResolutionInfo_0 extends _i1.SmartFake
+ implements _i2.ResolutionInfo {
+ _FakeResolutionInfo_0(
Object parent,
Invocation parentInvocation,
) : super(
@@ -34,11 +37,84 @@ class _FakeRecording_0 extends _i1.SmartFake implements _i2.Recording {
);
}
+class _FakeRecording_1 extends _i1.SmartFake implements _i3.Recording {
+ _FakeRecording_1(
+ Object parent,
+ Invocation parentInvocation,
+ ) : super(
+ parent,
+ parentInvocation,
+ );
+}
+
+/// A class which mocks [QualitySelector].
+///
+/// See the documentation for Mockito's code generation for more information.
+// ignore: must_be_immutable
+class MockQualitySelector extends _i1.Mock implements _i4.QualitySelector {
+ MockQualitySelector() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ List<_i2.VideoQualityConstraint> get qualityList => (super.noSuchMethod(
+ Invocation.getter(#qualityList),
+ returnValue: <_i2.VideoQualityConstraint>[],
+ ) as List<_i2.VideoQualityConstraint>);
+}
+
+/// A class which mocks [TestInstanceManagerHostApi].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockTestInstanceManagerHostApi extends _i1.Mock
+ implements _i5.TestInstanceManagerHostApi {
+ MockTestInstanceManagerHostApi() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ void clear() => super.noSuchMethod(
+ Invocation.method(
+ #clear,
+ [],
+ ),
+ returnValueForMissingStub: null,
+ );
+}
+
+/// A class which mocks [TestFallbackStrategyHostApi].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockTestFallbackStrategyHostApi extends _i1.Mock
+ implements _i5.TestFallbackStrategyHostApi {
+ MockTestFallbackStrategyHostApi() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ void create(
+ int? identifier,
+ _i2.VideoQualityConstraint? quality,
+ _i2.VideoResolutionFallbackRule? fallbackRule,
+ ) =>
+ super.noSuchMethod(
+ Invocation.method(
+ #create,
+ [
+ identifier,
+ quality,
+ fallbackRule,
+ ],
+ ),
+ returnValueForMissingStub: null,
+ );
+}
+
/// A class which mocks [TestRecorderHostApi].
///
/// See the documentation for Mockito's code generation for more information.
class MockTestRecorderHostApi extends _i1.Mock
- implements _i3.TestRecorderHostApi {
+ implements _i5.TestRecorderHostApi {
MockTestRecorderHostApi() {
_i1.throwOnMissingStub(this);
}
@@ -48,6 +124,7 @@ class MockTestRecorderHostApi extends _i1.Mock
int? identifier,
int? aspectRatio,
int? bitRate,
+ int? qualitySelectorId,
) =>
super.noSuchMethod(
Invocation.method(
@@ -56,6 +133,7 @@ class MockTestRecorderHostApi extends _i1.Mock
identifier,
aspectRatio,
bitRate,
+ qualitySelectorId,
],
),
returnValueForMissingStub: null,
@@ -93,46 +171,79 @@ class MockTestRecorderHostApi extends _i1.Mock
) as int);
}
-/// A class which mocks [TestInstanceManagerHostApi].
+/// A class which mocks [TestQualitySelectorHostApi].
///
/// See the documentation for Mockito's code generation for more information.
-class MockTestInstanceManagerHostApi extends _i1.Mock
- implements _i3.TestInstanceManagerHostApi {
- MockTestInstanceManagerHostApi() {
+class MockTestQualitySelectorHostApi extends _i1.Mock
+ implements _i5.TestQualitySelectorHostApi {
+ MockTestQualitySelectorHostApi() {
_i1.throwOnMissingStub(this);
}
@override
- void clear() => super.noSuchMethod(
+ void create(
+ int? identifier,
+ List? videoQualityConstraintIndexList,
+ int? fallbackStrategyId,
+ ) =>
+ super.noSuchMethod(
Invocation.method(
- #clear,
- [],
+ #create,
+ [
+ identifier,
+ videoQualityConstraintIndexList,
+ fallbackStrategyId,
+ ],
),
returnValueForMissingStub: null,
);
+ @override
+ _i2.ResolutionInfo getResolution(
+ int? cameraInfoId,
+ _i2.VideoQualityConstraint? quality,
+ ) =>
+ (super.noSuchMethod(
+ Invocation.method(
+ #getResolution,
+ [
+ cameraInfoId,
+ quality,
+ ],
+ ),
+ returnValue: _FakeResolutionInfo_0(
+ this,
+ Invocation.method(
+ #getResolution,
+ [
+ cameraInfoId,
+ quality,
+ ],
+ ),
+ ),
+ ) as _i2.ResolutionInfo);
}
/// A class which mocks [PendingRecording].
///
/// See the documentation for Mockito's code generation for more information.
// ignore: must_be_immutable
-class MockPendingRecording extends _i1.Mock implements _i4.PendingRecording {
+class MockPendingRecording extends _i1.Mock implements _i6.PendingRecording {
MockPendingRecording() {
_i1.throwOnMissingStub(this);
}
@override
- _i5.Future<_i2.Recording> start() => (super.noSuchMethod(
+ _i7.Future<_i3.Recording> start() => (super.noSuchMethod(
Invocation.method(
#start,
[],
),
- returnValue: _i5.Future<_i2.Recording>.value(_FakeRecording_0(
+ returnValue: _i7.Future<_i3.Recording>.value(_FakeRecording_1(
this,
Invocation.method(
#start,
[],
),
)),
- ) as _i5.Future<_i2.Recording>);
+ ) as _i7.Future<_i3.Recording>);
}
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 6a80cf0541f..d4c935e819c 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
@@ -780,7 +780,8 @@ abstract class TestRecorderHostApi {
TestDefaultBinaryMessengerBinding.instance;
static const MessageCodec codec = StandardMessageCodec();
- void create(int identifier, int? aspectRatio, int? bitRate);
+ void create(
+ int identifier, int? aspectRatio, int? bitRate, int? qualitySelectorId);
int getAspectRatio(int identifier);
@@ -809,7 +810,9 @@ abstract class TestRecorderHostApi {
'Argument for dev.flutter.pigeon.RecorderHostApi.create was null, expected non-null int.');
final int? arg_aspectRatio = (args[1] as int?);
final int? arg_bitRate = (args[2] as int?);
- api.create(arg_identifier!, arg_aspectRatio, arg_bitRate);
+ final int? arg_qualitySelectorId = (args[3] as int?);
+ api.create(arg_identifier!, arg_aspectRatio, arg_bitRate,
+ arg_qualitySelectorId);
return [];
});
}
@@ -1576,3 +1579,143 @@ abstract class TestImageProxyHostApi {
}
}
}
+
+class _TestQualitySelectorHostApiCodec extends StandardMessageCodec {
+ const _TestQualitySelectorHostApiCodec();
+ @override
+ void writeValue(WriteBuffer buffer, Object? value) {
+ if (value is ResolutionInfo) {
+ buffer.putUint8(128);
+ writeValue(buffer, value.encode());
+ } else {
+ super.writeValue(buffer, value);
+ }
+ }
+
+ @override
+ Object? readValueOfType(int type, ReadBuffer buffer) {
+ switch (type) {
+ case 128:
+ return ResolutionInfo.decode(readValue(buffer)!);
+ default:
+ return super.readValueOfType(type, buffer);
+ }
+ }
+}
+
+abstract class TestQualitySelectorHostApi {
+ static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding =>
+ TestDefaultBinaryMessengerBinding.instance;
+ static const MessageCodec codec = _TestQualitySelectorHostApiCodec();
+
+ void create(int identifier, List videoQualityConstraintIndexList,
+ int? fallbackStrategyId);
+
+ ResolutionInfo getResolution(
+ int cameraInfoId, VideoQualityConstraint quality);
+
+ static void setup(TestQualitySelectorHostApi? api,
+ {BinaryMessenger? binaryMessenger}) {
+ {
+ final BasicMessageChannel channel = BasicMessageChannel(
+ 'dev.flutter.pigeon.QualitySelectorHostApi.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.QualitySelectorHostApi.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.QualitySelectorHostApi.create was null, expected non-null int.');
+ final List? arg_videoQualityConstraintIndexList =
+ (args[1] as List?)?.cast();
+ assert(arg_videoQualityConstraintIndexList != null,
+ 'Argument for dev.flutter.pigeon.QualitySelectorHostApi.create was null, expected non-null List.');
+ final int? arg_fallbackStrategyId = (args[2] as int?);
+ api.create(arg_identifier!, arg_videoQualityConstraintIndexList!,
+ arg_fallbackStrategyId);
+ return [];
+ });
+ }
+ }
+ {
+ final BasicMessageChannel channel = BasicMessageChannel(
+ 'dev.flutter.pigeon.QualitySelectorHostApi.getResolution', 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.QualitySelectorHostApi.getResolution was null.');
+ final List args = (message as List?)!;
+ final int? arg_cameraInfoId = (args[0] as int?);
+ assert(arg_cameraInfoId != null,
+ 'Argument for dev.flutter.pigeon.QualitySelectorHostApi.getResolution was null, expected non-null int.');
+ final VideoQualityConstraint? arg_quality = args[1] == null
+ ? null
+ : VideoQualityConstraint.values[args[1] as int];
+ assert(arg_quality != null,
+ 'Argument for dev.flutter.pigeon.QualitySelectorHostApi.getResolution was null, expected non-null VideoQualityConstraint.');
+ final ResolutionInfo output =
+ api.getResolution(arg_cameraInfoId!, arg_quality!);
+ return [output];
+ });
+ }
+ }
+ }
+}
+
+abstract class TestFallbackStrategyHostApi {
+ static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding =>
+ TestDefaultBinaryMessengerBinding.instance;
+ static const MessageCodec codec = StandardMessageCodec();
+
+ void create(int identifier, VideoQualityConstraint quality,
+ VideoResolutionFallbackRule fallbackRule);
+
+ static void setup(TestFallbackStrategyHostApi? api,
+ {BinaryMessenger? binaryMessenger}) {
+ {
+ final BasicMessageChannel channel = BasicMessageChannel(
+ 'dev.flutter.pigeon.FallbackStrategyHostApi.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.FallbackStrategyHostApi.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.FallbackStrategyHostApi.create was null, expected non-null int.');
+ final VideoQualityConstraint? arg_quality = args[1] == null
+ ? null
+ : VideoQualityConstraint.values[args[1] as int];
+ assert(arg_quality != null,
+ 'Argument for dev.flutter.pigeon.FallbackStrategyHostApi.create was null, expected non-null VideoQualityConstraint.');
+ final VideoResolutionFallbackRule? arg_fallbackRule = args[2] == null
+ ? null
+ : VideoResolutionFallbackRule.values[args[2] as int];
+ assert(arg_fallbackRule != null,
+ 'Argument for dev.flutter.pigeon.FallbackStrategyHostApi.create was null, expected non-null VideoResolutionFallbackRule.');
+ api.create(arg_identifier!, arg_quality!, arg_fallbackRule!);
+ return [];
+ });
+ }
+ }
+ }
+}