diff --git a/packages/image_picker/image_picker/example/test/readme_excerpts_test.dart b/packages/image_picker/image_picker/example/test/readme_excerpts_test.dart index 2231d4738e52..df90236ce2a8 100644 --- a/packages/image_picker/image_picker/example/test/readme_excerpts_test.dart +++ b/packages/image_picker/image_picker/example/test/readme_excerpts_test.dart @@ -65,6 +65,7 @@ class FakeImagePicker extends ImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) async { return XFile(source == ImageSource.camera ? 'cameraVideo' : 'galleryVideo'); } diff --git a/packages/image_picker/image_picker/lib/image_picker.dart b/packages/image_picker/image_picker/lib/image_picker.dart index 9e96d5cd73b5..61215f9d08ba 100755 --- a/packages/image_picker/image_picker/lib/image_picker.dart +++ b/packages/image_picker/image_picker/lib/image_picker.dart @@ -14,6 +14,7 @@ export 'package:image_picker_platform_interface/image_picker_platform_interface. LostDataResponse, PickedFile, RetrieveType, + VideoQuality, XFile, kTypeImage, kTypeVideo; @@ -277,6 +278,8 @@ class ImagePicker { /// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device. /// Defaults to [CameraDevice.rear]. /// + /// The [quality] argument specifies the video quality for recording/picking. Defaults to [VideoQuality.high]. + /// /// In Android, the MainActivity can be destroyed for various reasons. If that happens, the result will be lost /// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data. /// @@ -289,11 +292,13 @@ class ImagePicker { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) { return platform.getVideo( source: source, preferredCameraDevice: preferredCameraDevice, maxDuration: maxDuration, + quality: quality, ); } diff --git a/packages/image_picker/image_picker/test/image_picker_test.dart b/packages/image_picker/image_picker/test/image_picker_test.dart index 6230481169f3..6988c98cdda9 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.dart @@ -358,6 +358,7 @@ void main() { source: anyNamed('source'), preferredCameraDevice: anyNamed('preferredCameraDevice'), maxDuration: anyNamed('maxDuration'), + quality: anyNamed('quality'), ), ).thenAnswer((Invocation _) async => null); }); @@ -390,6 +391,49 @@ void main() { ]); }); + test('video quality defaults to high', () async { + final picker = ImagePicker(); + await picker.pickVideo(source: ImageSource.camera); + + verify( + mockPlatform.getVideo( + source: ImageSource.camera, + quality: VideoQuality.high, + ), + ); + }); + + test('passes the video quality argument correctly', () async { + final picker = ImagePicker(); + await picker.pickVideo( + source: ImageSource.camera, + quality: VideoQuality.low, + ); + await picker.pickVideo( + source: ImageSource.camera, + quality: VideoQuality.medium, + ); + await picker.pickVideo( + source: ImageSource.camera, + quality: VideoQuality.high, + ); + + verifyInOrder([ + mockPlatform.getVideo( + source: ImageSource.camera, + quality: VideoQuality.low, + ), + mockPlatform.getVideo( + source: ImageSource.camera, + quality: VideoQuality.medium, + ), + mockPlatform.getVideo( + source: ImageSource.camera, + quality: VideoQuality.high, + ), + ]); + }); + test('handles a null video file response gracefully', () async { final picker = ImagePicker(); @@ -401,7 +445,12 @@ void main() { final picker = ImagePicker(); await picker.pickVideo(source: ImageSource.camera); - verify(mockPlatform.getVideo(source: ImageSource.camera)); + verify( + mockPlatform.getVideo( + source: ImageSource.camera, + quality: VideoQuality.high, + ), + ); }); test('camera position can set to front', () async { diff --git a/packages/image_picker/image_picker/test/image_picker_test.mocks.dart b/packages/image_picker/image_picker/test/image_picker_test.mocks.dart index 6aaca18fcac5..aec683cafc9f 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.mocks.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.mocks.dart @@ -24,6 +24,7 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +// ignore_for_file: invalid_use_of_internal_member class _FakeLostData_0 extends _i1.SmartFake implements _i2.LostData { _FakeLostData_0(Object parent, Invocation parentInvocation) @@ -156,12 +157,14 @@ class MockImagePickerPlatform extends _i1.Mock required _i2.ImageSource? source, _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear, Duration? maxDuration, + _i2.VideoQuality? quality = _i2.VideoQuality.high, }) => (super.noSuchMethod( Invocation.method(#getVideo, [], { #source: source, #preferredCameraDevice: preferredCameraDevice, #maxDuration: maxDuration, + #quality: quality, }), returnValue: _i4.Future<_i5.XFile?>.value(), ) diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java index 043546de2b3e..e9836296d5ef 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java @@ -389,6 +389,10 @@ private void launchTakeVideoWithCameraIntent() { int maxSeconds = localVideoOptions.getMaxDurationSeconds().intValue(); intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, maxSeconds); } + if (localVideoOptions != null && localVideoOptions.getVideoQuality() != null) { + int videoQuality = localVideoOptions.getVideoQuality().intValue(); + intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, videoQuality); + } if (cameraDevice == CameraDevice.FRONT) { useFrontCamera(intent); } diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java index bf582fa4d35b..df2ff02011d6 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// Autogenerated from Pigeon (v26.1.7), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.imagepicker; @@ -21,7 +21,11 @@ import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; /** Generated class from Pigeon. */ @@ -37,7 +41,8 @@ public static class FlutterError extends RuntimeException { /** The error details. Must be a datatype supported by the api codec. */ public final Object details; - public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) { + public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) + { super(message); this.code = code; this.details = details; @@ -56,7 +61,7 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { errorList.add(exception.toString()); errorList.add(exception.getClass().getSimpleName()); errorList.add( - "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); } return errorList; } @@ -141,16 +146,10 @@ public void setLimit(@Nullable Long setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } GeneralOptions that = (GeneralOptions) o; - return allowMultiple.equals(that.allowMultiple) - && usePhotoPicker.equals(that.usePhotoPicker) - && Objects.equals(limit, that.limit); + return allowMultiple.equals(that.allowMultiple) && usePhotoPicker.equals(that.usePhotoPicker) && Objects.equals(limit, that.limit); } @Override @@ -217,7 +216,7 @@ ArrayList toList() { /** * Options for image selection and output. * - *

Generated class from Pigeon that represents data sent in messages. + * Generated class from Pigeon that represents data sent in messages. */ public static final class ImageSelectionOptions { /** If set, the max width that the image should be resized to fit in. */ @@ -245,7 +244,7 @@ public void setMaxHeight(@Nullable Double setterArg) { /** * The quality of the output image, from 0-100. * - *

100 indicates original quality. + * 100 indicates original quality. */ private @NonNull Long quality; @@ -265,16 +264,10 @@ public void setQuality(@NonNull Long setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } ImageSelectionOptions that = (ImageSelectionOptions) o; - return Objects.equals(maxWidth, that.maxWidth) - && Objects.equals(maxHeight, that.maxHeight) - && quality.equals(that.quality); + return Objects.equals(maxWidth, that.maxWidth) && Objects.equals(maxHeight, that.maxHeight) && quality.equals(that.quality); } @Override @@ -358,12 +351,8 @@ public void setImageSelectionOptions(@NonNull ImageSelectionOptions setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } MediaSelectionOptions that = (MediaSelectionOptions) o; return imageSelectionOptions.equals(that.imageSelectionOptions); } @@ -408,7 +397,7 @@ ArrayList toList() { /** * Options for image selection and output. * - *

Generated class from Pigeon that represents data sent in messages. + * Generated class from Pigeon that represents data sent in messages. */ public static final class VideoSelectionOptions { /** The maximum desired length for the video, in seconds. */ @@ -422,21 +411,33 @@ public void setMaxDurationSeconds(@Nullable Long setterArg) { this.maxDurationSeconds = setterArg; } + /** + * The video quality setting for Android. + * 0 = low quality, 1 = high quality + * Note: Android only supports two quality levels, while iOS supports three. + * Medium quality is mapped to high quality (1) on Android. + */ + private @Nullable Long videoQuality; + + public @Nullable Long getVideoQuality() { + return videoQuality; + } + + public void setVideoQuality(@Nullable Long setterArg) { + this.videoQuality = setterArg; + } + @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } VideoSelectionOptions that = (VideoSelectionOptions) o; - return Objects.equals(maxDurationSeconds, that.maxDurationSeconds); + return Objects.equals(maxDurationSeconds, that.maxDurationSeconds) && Objects.equals(videoQuality, that.videoQuality); } @Override public int hashCode() { - return Objects.hash(maxDurationSeconds); + return Objects.hash(maxDurationSeconds, videoQuality); } public static final class Builder { @@ -449,17 +450,27 @@ public static final class Builder { return this; } + private @Nullable Long videoQuality; + + @CanIgnoreReturnValue + public @NonNull Builder setVideoQuality(@Nullable Long setterArg) { + this.videoQuality = setterArg; + return this; + } + public @NonNull VideoSelectionOptions build() { VideoSelectionOptions pigeonReturn = new VideoSelectionOptions(); pigeonReturn.setMaxDurationSeconds(maxDurationSeconds); + pigeonReturn.setVideoQuality(videoQuality); return pigeonReturn; } } @NonNull ArrayList toList() { - ArrayList toListResult = new ArrayList<>(1); + ArrayList toListResult = new ArrayList<>(2); toListResult.add(maxDurationSeconds); + toListResult.add(videoQuality); return toListResult; } @@ -467,6 +478,8 @@ ArrayList toList() { VideoSelectionOptions pigeonResult = new VideoSelectionOptions(); Object maxDurationSeconds = pigeonVar_list.get(0); pigeonResult.setMaxDurationSeconds((Long) maxDurationSeconds); + Object videoQuality = pigeonVar_list.get(1); + pigeonResult.setVideoQuality((Long) videoQuality); return pigeonResult; } } @@ -474,7 +487,7 @@ ArrayList toList() { /** * Specification for the source of an image or video selection. * - *

Generated class from Pigeon that represents data sent in messages. + * Generated class from Pigeon that represents data sent in messages. */ public static final class SourceSpecification { private @NonNull SourceType type; @@ -505,12 +518,8 @@ public void setCamera(@Nullable SourceCamera setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } SourceSpecification that = (SourceSpecification) o; return type.equals(that.type) && Objects.equals(camera, that.camera); } @@ -567,9 +576,9 @@ ArrayList toList() { /** * An error that occurred during lost result retrieval. * - *

The data here maps to the `PlatformException` that will be created from it. + * The data here maps to the `PlatformException` that will be created from it. * - *

Generated class from Pigeon that represents data sent in messages. + * Generated class from Pigeon that represents data sent in messages. */ public static final class CacheRetrievalError { private @NonNull String code; @@ -600,12 +609,8 @@ public void setMessage(@Nullable String setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } CacheRetrievalError that = (CacheRetrievalError) o; return code.equals(that.code) && Objects.equals(message, that.message); } @@ -662,7 +667,7 @@ ArrayList toList() { /** * The result of retrieving cached results from a previous run. * - *

Generated class from Pigeon that represents data sent in messages. + * Generated class from Pigeon that represents data sent in messages. */ public static final class CacheRetrievalResult { /** The type of the retrieved data. */ @@ -709,16 +714,10 @@ public void setPaths(@NonNull List setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } CacheRetrievalResult that = (CacheRetrievalResult) o; - return type.equals(that.type) - && Objects.equals(error, that.error) - && paths.equals(that.paths); + return type.equals(that.type) && Objects.equals(error, that.error) && paths.equals(that.paths); } @Override @@ -790,21 +789,18 @@ private PigeonCodec() {} @Override protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { - case (byte) 129: - { - Object value = readValue(buffer); - return value == null ? null : SourceCamera.values()[((Long) value).intValue()]; - } - case (byte) 130: - { - Object value = readValue(buffer); - return value == null ? null : SourceType.values()[((Long) value).intValue()]; - } - case (byte) 131: - { - Object value = readValue(buffer); - return value == null ? null : CacheRetrievalType.values()[((Long) value).intValue()]; - } + case (byte) 129: { + Object value = readValue(buffer); + return value == null ? null : SourceCamera.values()[((Long) value).intValue()]; + } + case (byte) 130: { + Object value = readValue(buffer); + return value == null ? null : SourceType.values()[((Long) value).intValue()]; + } + case (byte) 131: { + Object value = readValue(buffer); + return value == null ? null : CacheRetrievalType.values()[((Long) value).intValue()]; + } case (byte) 132: return GeneralOptions.fromList((ArrayList) readValue(buffer)); case (byte) 133: @@ -862,6 +858,7 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { } } + /** Asynchronous error handling return type for non-nullable API method returns. */ public interface Result { /** Success case callback method for handling returns. */ @@ -889,49 +886,30 @@ public interface VoidResult { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface ImagePickerApi { /** Selects images and returns their paths. */ - void pickImages( - @NonNull SourceSpecification source, - @NonNull ImageSelectionOptions options, - @NonNull GeneralOptions generalOptions, - @NonNull Result> result); + void pickImages(@NonNull SourceSpecification source, @NonNull ImageSelectionOptions options, @NonNull GeneralOptions generalOptions, @NonNull Result> result); /** Selects video and returns their paths. */ - void pickVideos( - @NonNull SourceSpecification source, - @NonNull VideoSelectionOptions options, - @NonNull GeneralOptions generalOptions, - @NonNull Result> result); + void pickVideos(@NonNull SourceSpecification source, @NonNull VideoSelectionOptions options, @NonNull GeneralOptions generalOptions, @NonNull Result> result); /** Selects images and videos and returns their paths. */ - void pickMedia( - @NonNull MediaSelectionOptions mediaSelectionOptions, - @NonNull GeneralOptions generalOptions, - @NonNull Result> result); + void pickMedia(@NonNull MediaSelectionOptions mediaSelectionOptions, @NonNull GeneralOptions generalOptions, @NonNull Result> result); /** Returns results from a previous app session, if any. */ - @Nullable + @Nullable CacheRetrievalResult retrieveLostResults(); /** The codec used by ImagePickerApi. */ static @NonNull MessageCodec getCodec() { return PigeonCodec.INSTANCE; } - /** Sets up an instance of `ImagePickerApi` to handle messages through the `binaryMessenger`. */ + /**Sets up an instance of `ImagePickerApi` to handle messages through the `binaryMessenger`. */ static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable ImagePickerApi api) { setUp(binaryMessenger, "", api); } - - static void setUp( - @NonNull BinaryMessenger binaryMessenger, - @NonNull String messageChannelSuffix, - @Nullable ImagePickerApi api) { + static void setUp(@NonNull BinaryMessenger binaryMessenger, @NonNull String messageChannelSuffix, @Nullable ImagePickerApi api) { messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages" - + messageChannelSuffix, - getCodec(), - taskQueue); + binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages" + messageChannelSuffix, getCodec(), taskQueue); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -962,11 +940,7 @@ public void error(Throwable error) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos" - + messageChannelSuffix, - getCodec(), - taskQueue); + binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos" + messageChannelSuffix, getCodec(), taskQueue); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -997,17 +971,13 @@ public void error(Throwable error) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; - MediaSelectionOptions mediaSelectionOptionsArg = - (MediaSelectionOptions) args.get(0); + MediaSelectionOptions mediaSelectionOptionsArg = (MediaSelectionOptions) args.get(0); GeneralOptions generalOptionsArg = (GeneralOptions) args.get(1); Result> resultCallback = new Result>() { @@ -1031,11 +1001,7 @@ public void error(Throwable error) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults" - + messageChannelSuffix, - getCodec(), - taskQueue); + binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults" + messageChannelSuffix, getCodec(), taskQueue); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -1043,7 +1009,8 @@ public void error(Throwable error) { try { CacheRetrievalResult output = api.retrieveLostResults(); wrapped.add(0, output); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); diff --git a/packages/image_picker/image_picker_android/lib/image_picker_android.dart b/packages/image_picker/image_picker_android/lib/image_picker_android.dart index fd7080f536b5..08a3978db3c4 100644 --- a/packages/image_picker/image_picker_android/lib/image_picker_android.dart +++ b/packages/image_picker/image_picker_android/lib/image_picker_android.dart @@ -161,10 +161,14 @@ class ImagePickerAndroid extends ImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) async { final List paths = await _hostApi.pickVideos( _buildSourceSpec(source, preferredCameraDevice), - VideoSelectionOptions(maxDurationSeconds: maxDuration?.inSeconds), + VideoSelectionOptions( + maxDurationSeconds: maxDuration?.inSeconds, + videoQuality: _convertVideoQualityToAndroid(quality), + ), GeneralOptions( allowMultiple: false, usePhotoPicker: useAndroidPhotoPicker, @@ -256,11 +260,13 @@ class ImagePickerAndroid extends ImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) async { final String? path = await _getVideoPath( source: source, maxDuration: maxDuration, preferredCameraDevice: preferredCameraDevice, + quality: quality, ); return path != null ? XFile(path) : null; } @@ -436,6 +442,27 @@ class ImagePickerAndroid extends ImagePickerPlatform { return SourceCamera.rear; } + /// Converts a [VideoQuality] to Android's EXTRA_VIDEO_QUALITY value. + /// + /// Android only supports two quality levels (0=low, 1=high), while iOS supports + /// three (low, medium, high). Medium quality is mapped to high quality on Android. + int _convertVideoQualityToAndroid(VideoQuality quality) { + switch (quality) { + case VideoQuality.low: + return 0; // Low quality + case VideoQuality.medium: + case VideoQuality.high: + return 1; // High quality (medium is mapped to high) + } + // The enum comes from a different package, which could get a new value at + // any time, so provide a fallback that ensures this won't break when used + // with a version that contains new values. This is deliberately outside + // the switch rather than a `default` so that the linter will flag the + // switch as needing an update. + // ignore: dead_code + return 1; // Default to high quality + } + RetrieveType _retrieveTypeForCacheType(CacheRetrievalType type) { switch (type) { case CacheRetrievalType.image: diff --git a/packages/image_picker/image_picker_android/lib/src/messages.g.dart b/packages/image_picker/image_picker_android/lib/src/messages.g.dart index 89aea6de03bd..32179acaf2cb 100644 --- a/packages/image_picker/image_picker_android/lib/src/messages.g.dart +++ b/packages/image_picker/image_picker_android/lib/src/messages.g.dart @@ -1,9 +1,9 @@ // Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// Autogenerated from Pigeon (v26.1.7), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -17,30 +17,35 @@ PlatformException _createConnectionError(String channelName) { message: 'Unable to establish connection on channel: "$channelName".', ); } - bool _deepEquals(Object? a, Object? b) { if (a is List && b is List) { return a.length == b.length && - a.indexed.every( - ((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]), - ); + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && - a.entries.every( - (MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key]), - ); + return a.length == b.length && a.entries.every((MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key])); } return a == b; } -enum SourceCamera { rear, front } -enum SourceType { camera, gallery } +enum SourceCamera { + rear, + front, +} + +enum SourceType { + camera, + gallery, +} -enum CacheRetrievalType { image, video } +enum CacheRetrievalType { + image, + video, +} class GeneralOptions { GeneralOptions({ @@ -56,12 +61,15 @@ class GeneralOptions { int? limit; List _toList() { - return [allowMultiple, usePhotoPicker, limit]; + return [ + allowMultiple, + usePhotoPicker, + limit, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static GeneralOptions decode(Object result) { result as List; @@ -86,12 +94,17 @@ class GeneralOptions { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } /// Options for image selection and output. class ImageSelectionOptions { - ImageSelectionOptions({this.maxWidth, this.maxHeight, required this.quality}); + ImageSelectionOptions({ + this.maxWidth, + this.maxHeight, + required this.quality, + }); /// If set, the max width that the image should be resized to fit in. double? maxWidth; @@ -105,12 +118,15 @@ class ImageSelectionOptions { int quality; List _toList() { - return [maxWidth, maxHeight, quality]; + return [ + maxWidth, + maxHeight, + quality, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static ImageSelectionOptions decode(Object result) { result as List; @@ -135,21 +151,25 @@ class ImageSelectionOptions { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } class MediaSelectionOptions { - MediaSelectionOptions({required this.imageSelectionOptions}); + MediaSelectionOptions({ + required this.imageSelectionOptions, + }); ImageSelectionOptions imageSelectionOptions; List _toList() { - return [imageSelectionOptions]; + return [ + imageSelectionOptions, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static MediaSelectionOptions decode(Object result) { result as List; @@ -172,27 +192,42 @@ class MediaSelectionOptions { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } /// Options for image selection and output. class VideoSelectionOptions { - VideoSelectionOptions({this.maxDurationSeconds}); + VideoSelectionOptions({ + this.maxDurationSeconds, + this.videoQuality, + }); /// The maximum desired length for the video, in seconds. int? maxDurationSeconds; + /// The video quality setting for Android. + /// 0 = low quality, 1 = high quality + /// Note: Android only supports two quality levels, while iOS supports three. + /// Medium quality is mapped to high quality (1) on Android. + int? videoQuality; + List _toList() { - return [maxDurationSeconds]; + return [ + maxDurationSeconds, + videoQuality, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static VideoSelectionOptions decode(Object result) { result as List; - return VideoSelectionOptions(maxDurationSeconds: result[0] as int?); + return VideoSelectionOptions( + maxDurationSeconds: result[0] as int?, + videoQuality: result[1] as int?, + ); } @override @@ -209,24 +244,30 @@ class VideoSelectionOptions { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } /// Specification for the source of an image or video selection. class SourceSpecification { - SourceSpecification({required this.type, this.camera}); + SourceSpecification({ + required this.type, + this.camera, + }); SourceType type; SourceCamera? camera; List _toList() { - return [type, camera]; + return [ + type, + camera, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static SourceSpecification decode(Object result) { result as List; @@ -250,26 +291,32 @@ class SourceSpecification { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } /// An error that occurred during lost result retrieval. /// /// The data here maps to the `PlatformException` that will be created from it. class CacheRetrievalError { - CacheRetrievalError({required this.code, this.message}); + CacheRetrievalError({ + required this.code, + this.message, + }); String code; String? message; List _toList() { - return [code, message]; + return [ + code, + message, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static CacheRetrievalError decode(Object result) { result as List; @@ -293,7 +340,8 @@ class CacheRetrievalError { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } /// The result of retrieving cached results from a previous run. @@ -314,12 +362,15 @@ class CacheRetrievalResult { List paths; List _toList() { - return [type, error, paths]; + return [ + type, + error, + paths, + ]; } Object encode() { - return _toList(); - } + return _toList(); } static CacheRetrievalResult decode(Object result) { result as List; @@ -344,9 +395,11 @@ class CacheRetrievalResult { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()); + int get hashCode => Object.hashAll(_toList()) +; } + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -354,34 +407,34 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is SourceCamera) { + } else if (value is SourceCamera) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is SourceType) { + } else if (value is SourceType) { buffer.putUint8(130); writeValue(buffer, value.index); - } else if (value is CacheRetrievalType) { + } else if (value is CacheRetrievalType) { buffer.putUint8(131); writeValue(buffer, value.index); - } else if (value is GeneralOptions) { + } else if (value is GeneralOptions) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is ImageSelectionOptions) { + } else if (value is ImageSelectionOptions) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is MediaSelectionOptions) { + } else if (value is MediaSelectionOptions) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is VideoSelectionOptions) { + } else if (value is VideoSelectionOptions) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is SourceSpecification) { + } else if (value is SourceSpecification) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is CacheRetrievalError) { + } else if (value is CacheRetrievalError) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is CacheRetrievalResult) { + } else if (value is CacheRetrievalResult) { buffer.putUint8(138); writeValue(buffer, value.encode()); } else { @@ -392,28 +445,28 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: - final int? value = readValue(buffer) as int?; + case 129: + final value = readValue(buffer) as int?; return value == null ? null : SourceCamera.values[value]; - case 130: - final int? value = readValue(buffer) as int?; + case 130: + final value = readValue(buffer) as int?; return value == null ? null : SourceType.values[value]; - case 131: - final int? value = readValue(buffer) as int?; + case 131: + final value = readValue(buffer) as int?; return value == null ? null : CacheRetrievalType.values[value]; - case 132: + case 132: return GeneralOptions.decode(readValue(buffer)!); - case 133: + case 133: return ImageSelectionOptions.decode(readValue(buffer)!); - case 134: + case 134: return MediaSelectionOptions.decode(readValue(buffer)!); - case 135: + case 135: return VideoSelectionOptions.decode(readValue(buffer)!); - case 136: + case 136: return SourceSpecification.decode(readValue(buffer)!); - case 137: + case 137: return CacheRetrievalError.decode(readValue(buffer)!); - case 138: + case 138: return CacheRetrievalResult.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -425,13 +478,9 @@ class ImagePickerApi { /// Constructor for [ImagePickerApi]. 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. - ImagePickerApi({ - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty - ? '.$messageChannelSuffix' - : ''; + ImagePickerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -439,24 +488,15 @@ class ImagePickerApi { final String pigeonVar_messageChannelSuffix; /// Selects images and returns their paths. - Future> pickImages( - SourceSpecification source, - ImageSelectionOptions options, - GeneralOptions generalOptions, - ) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [source, options, generalOptions], + Future> pickImages(SourceSpecification source, ImageSelectionOptions options, GeneralOptions generalOptions) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final Future pigeonVar_sendFuture = pigeonVar_channel.send([source, options, generalOptions]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -476,24 +516,15 @@ class ImagePickerApi { } /// Selects video and returns their paths. - Future> pickVideos( - SourceSpecification source, - VideoSelectionOptions options, - GeneralOptions generalOptions, - ) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [source, options, generalOptions], + Future> pickVideos(SourceSpecification source, VideoSelectionOptions options, GeneralOptions generalOptions) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final Future pigeonVar_sendFuture = pigeonVar_channel.send([source, options, generalOptions]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -513,23 +544,15 @@ class ImagePickerApi { } /// Selects images and videos and returns their paths. - Future> pickMedia( - MediaSelectionOptions mediaSelectionOptions, - GeneralOptions generalOptions, - ) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [mediaSelectionOptions, generalOptions], + Future> pickMedia(MediaSelectionOptions mediaSelectionOptions, GeneralOptions generalOptions) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final Future pigeonVar_sendFuture = pigeonVar_channel.send([mediaSelectionOptions, generalOptions]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -550,17 +573,14 @@ class ImagePickerApi { /// Returns results from a previous app session, if any. Future retrieveLostResults() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { diff --git a/packages/image_picker/image_picker_android/pigeons/messages.dart b/packages/image_picker/image_picker_android/pigeons/messages.dart index 940e4650eab9..10bb256ddf9e 100644 --- a/packages/image_picker/image_picker_android/pigeons/messages.dart +++ b/packages/image_picker/image_picker_android/pigeons/messages.dart @@ -44,10 +44,16 @@ class MediaSelectionOptions { /// Options for image selection and output. class VideoSelectionOptions { - VideoSelectionOptions({this.maxDurationSeconds}); + VideoSelectionOptions({this.maxDurationSeconds, this.videoQuality}); /// The maximum desired length for the video, in seconds. int? maxDurationSeconds; + + /// The video quality setting for Android. + /// 0 = low quality, 1 = high quality + /// Note: Android only supports two quality levels, while iOS supports three. + /// Medium quality is mapped to high quality (1) on Android. + int? videoQuality; } // Corresponds to `CameraDevice` from the platform interface package. diff --git a/packages/image_picker/image_picker_android/test/image_picker_android_test.dart b/packages/image_picker/image_picker_android/test/image_picker_android_test.dart index 891764af5d36..d1a2549db13e 100644 --- a/packages/image_picker/image_picker_android/test/image_picker_android_test.dart +++ b/packages/image_picker/image_picker_android/test/image_picker_android_test.dart @@ -563,6 +563,32 @@ void main() { expect(api.passedVideoOptions?.maxDurationSeconds, 60); }); + test('video quality defaults to high', () async { + await picker.getVideo(source: ImageSource.gallery); + + expect(api.passedVideoOptions?.videoQuality, 1); + }); + + test('passes the video quality argument correctly', () async { + await picker.getVideo( + source: ImageSource.gallery, + quality: VideoQuality.low, + ); + expect(api.passedVideoOptions?.videoQuality, 0); + + await picker.getVideo( + source: ImageSource.gallery, + quality: VideoQuality.medium, + ); + expect(api.passedVideoOptions?.videoQuality, 1); + + await picker.getVideo( + source: ImageSource.gallery, + quality: VideoQuality.high, + ); + expect(api.passedVideoOptions?.videoQuality, 1); + }); + test('handles a null video path response gracefully', () async { api.returnValue = null; diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 117c68774e20..79bb803dd9c4 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -106,6 +106,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) async { final String? capture = computeCaptureAttribute( source, diff --git a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m index 82991980d0c7..9748d0376d08 100644 --- a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m @@ -96,6 +96,23 @@ - (UIImagePickerControllerCameraDevice)cameraDeviceForSource:(FLTSourceSpecifica } } +/// Returns the UIImagePickerControllerQualityType to use given [videoQualityBoxed]. +/// +/// @param videoQualityBoxed The video quality from Dart. nil defaults to High. +- (UIImagePickerControllerQualityType)videoQualityForQuality:(FLTApiVideoQualityBox *)videoQualityBoxed { + if (videoQualityBoxed == nil) { + return UIImagePickerControllerQualityTypeHigh; + } + switch (videoQualityBoxed.value) { + case FLTApiVideoQualityLow: + return UIImagePickerControllerQualityTypeLow; + case FLTApiVideoQualityMedium: + return UIImagePickerControllerQualityTypeMedium; + case FLTApiVideoQualityHigh: + return UIImagePickerControllerQualityTypeHigh; + } +} + - (void)launchPHPickerWithContext:(nonnull FLTImagePickerMethodCallContext *)context API_AVAILABLE(ios(14)) { PHPickerConfiguration *config = @@ -131,7 +148,10 @@ - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source } if (context.includeVideo) { [mediaTypes addObject:(NSString *)kUTTypeMovie]; - imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; + UIImagePickerControllerQualityType quality = context.videoQuality != nil + ? (UIImagePickerControllerQualityType)context.videoQuality.integerValue + : UIImagePickerControllerQualityTypeHigh; + imagePickerController.videoQuality = quality; } imagePickerController.mediaTypes = mediaTypes; if (context.maxDuration != 0.0) { @@ -250,6 +270,7 @@ - (void)pickMediaWithMediaSelectionOptions:(nonnull FLTMediaSelectionOptions *)m - (void)pickVideoWithSource:(nonnull FLTSourceSpecification *)source maxDuration:(nullable NSNumber *)maxDurationSeconds + videoQuality:(nullable FLTApiVideoQualityBox *)videoQualityBoxed completion: (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { [self cancelInProgressCall]; @@ -265,6 +286,7 @@ - (void)pickVideoWithSource:(nonnull FLTSourceSpecification *)source context.includeVideo = YES; context.maxItemCount = 1; context.maxDuration = maxDurationSeconds.doubleValue; + context.videoQuality = @([self videoQualityForQuality:videoQualityBoxed]); if (source.type == FLTSourceTypeGallery) { // Capture is not possible with PHPicker if (@available(iOS 14, *)) { diff --git a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/FLTImagePickerPlugin_Test.h b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/FLTImagePickerPlugin_Test.h index 5525827e2d63..a6a856ad3bc4 100644 --- a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/FLTImagePickerPlugin_Test.h +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/FLTImagePickerPlugin_Test.h @@ -42,6 +42,9 @@ typedef void (^FlutterResultAdapter)(NSArray *_Nullable, FlutterErro /// Maximum duration for videos. 0 indicates no maximum. @property(nonatomic, assign) NSTimeInterval maxDuration; +/// Video quality for recording/picking. nil indicates default (high) quality. +@property(nonatomic, strong, nullable) NSNumber *videoQuality; + /// Whether the picker should include images in the list. @property(nonatomic, assign) BOOL includeImages; diff --git a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/messages.g.h b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/messages.g.h index a880e336c02b..29b90d69d387 100644 --- a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/messages.g.h +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/messages.g.h @@ -1,10 +1,10 @@ // Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// Autogenerated from Pigeon (v26.1.7), do not edit directly. // See also: https://pub.dev/packages/pigeon -#import +@import Foundation; @protocol FlutterBinaryMessenger; @protocol FlutterMessageCodec; @@ -35,35 +35,49 @@ typedef NS_ENUM(NSUInteger, FLTSourceType) { - (instancetype)initWithValue:(FLTSourceType)value; @end +typedef NS_ENUM(NSUInteger, FLTApiVideoQuality) { + FLTApiVideoQualityLow = 0, + FLTApiVideoQualityMedium = 1, + FLTApiVideoQualityHigh = 2, +}; + +/// Wrapper for FLTApiVideoQuality to allow for nullability. +@interface FLTApiVideoQualityBox : NSObject +@property(nonatomic, assign) FLTApiVideoQuality value; +- (instancetype)initWithValue:(FLTApiVideoQuality)value; +@end + @class FLTMaxSize; @class FLTMediaSelectionOptions; @class FLTSourceSpecification; @interface FLTMaxSize : NSObject -+ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height; -@property(nonatomic, strong, nullable) NSNumber *width; -@property(nonatomic, strong, nullable) NSNumber *height; ++ (instancetype)makeWithWidth:(nullable NSNumber *)width + height:(nullable NSNumber *)height; +@property(nonatomic, strong, nullable) NSNumber * width; +@property(nonatomic, strong, nullable) NSNumber * height; @end @interface FLTMediaSelectionOptions : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithMaxSize:(FLTMaxSize *)maxSize - imageQuality:(nullable NSNumber *)imageQuality - requestFullMetadata:(BOOL)requestFullMetadata - allowMultiple:(BOOL)allowMultiple - limit:(nullable NSNumber *)limit; -@property(nonatomic, strong) FLTMaxSize *maxSize; -@property(nonatomic, strong, nullable) NSNumber *imageQuality; -@property(nonatomic, assign) BOOL requestFullMetadata; -@property(nonatomic, assign) BOOL allowMultiple; -@property(nonatomic, strong, nullable) NSNumber *limit; + imageQuality:(nullable NSNumber *)imageQuality + requestFullMetadata:(BOOL )requestFullMetadata + allowMultiple:(BOOL )allowMultiple + limit:(nullable NSNumber *)limit; +@property(nonatomic, strong) FLTMaxSize * maxSize; +@property(nonatomic, strong, nullable) NSNumber * imageQuality; +@property(nonatomic, assign) BOOL requestFullMetadata; +@property(nonatomic, assign) BOOL allowMultiple; +@property(nonatomic, strong, nullable) NSNumber * limit; @end @interface FLTSourceSpecification : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera; ++ (instancetype)makeWithType:(FLTSourceType)type + camera:(FLTSourceCamera)camera; @property(nonatomic, assign) FLTSourceType type; @property(nonatomic, assign) FLTSourceCamera camera; @end @@ -72,35 +86,16 @@ typedef NS_ENUM(NSUInteger, FLTSourceType) { NSObject *FLTGetMessagesCodec(void); @protocol FLTImagePickerApi -- (void)pickImageWithSource:(FLTSourceSpecification *)source - maxSize:(FLTMaxSize *)maxSize - quality:(nullable NSNumber *)imageQuality - fullMetadata:(BOOL)requestFullMetadata - completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; -- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize - quality:(nullable NSNumber *)imageQuality - fullMetadata:(BOOL)requestFullMetadata - limit:(nullable NSNumber *)limit - completion:(void (^)(NSArray *_Nullable, - FlutterError *_Nullable))completion; -- (void)pickVideoWithSource:(FLTSourceSpecification *)source - maxDuration:(nullable NSNumber *)maxDurationSeconds - completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; -- (void)pickMultiVideoWithMaxDuration:(nullable NSNumber *)maxDurationSeconds - limit:(nullable NSNumber *)limit - completion:(void (^)(NSArray *_Nullable, - FlutterError *_Nullable))completion; +- (void)pickImageWithSource:(FLTSourceSpecification *)source maxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality fullMetadata:(BOOL)requestFullMetadata completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality fullMetadata:(BOOL)requestFullMetadata limit:(nullable NSNumber *)limit completion:(void (^)(NSArray *_Nullable, FlutterError *_Nullable))completion; +- (void)pickVideoWithSource:(FLTSourceSpecification *)source maxDuration:(nullable NSNumber *)maxDurationSeconds videoQuality:(nullable FLTApiVideoQualityBox *)videoQualityBoxed completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickMultiVideoWithMaxDuration:(nullable NSNumber *)maxDurationSeconds limit:(nullable NSNumber *)limit completion:(void (^)(NSArray *_Nullable, FlutterError *_Nullable))completion; /// Selects images and videos and returns their paths. -- (void)pickMediaWithMediaSelectionOptions:(FLTMediaSelectionOptions *)mediaSelectionOptions - completion:(void (^)(NSArray *_Nullable, - FlutterError *_Nullable))completion; +- (void)pickMediaWithMediaSelectionOptions:(FLTMediaSelectionOptions *)mediaSelectionOptions completion:(void (^)(NSArray *_Nullable, FlutterError *_Nullable))completion; @end -extern void SetUpFLTImagePickerApi(id binaryMessenger, - NSObject *_Nullable api); +extern void SetUpFLTImagePickerApi(id binaryMessenger, NSObject *_Nullable api); -extern void SetUpFLTImagePickerApiWithSuffix(id binaryMessenger, - NSObject *_Nullable api, - NSString *messageChannelSuffix); +extern void SetUpFLTImagePickerApiWithSuffix(id binaryMessenger, NSObject *_Nullable api, NSString *messageChannelSuffix); NS_ASSUME_NONNULL_END diff --git a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/messages.g.m b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/messages.g.m index 727a649d2030..12f3d89ee12c 100644 --- a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/messages.g.m +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/messages.g.m @@ -1,19 +1,15 @@ // Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// Autogenerated from Pigeon (v26.1.7), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "./include/image_picker_ios/messages.g.h" #if TARGET_OS_OSX -#import +@import FlutterMacOS; #else -#import -#endif - -#if !__has_feature(objc_arc) -#error File requires ARC to be enabled. +@import Flutter; #endif static NSArray *wrapResult(id result, FlutterError *error) { @@ -50,6 +46,16 @@ - (instancetype)initWithValue:(FLTSourceType)value { } @end +@implementation FLTApiVideoQualityBox +- (instancetype)initWithValue:(FLTApiVideoQuality)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + @interface FLTMaxSize () + (FLTMaxSize *)fromList:(NSArray *)list; + (nullable FLTMaxSize *)nullableFromList:(NSArray *)list; @@ -69,8 +75,9 @@ + (nullable FLTSourceSpecification *)nullableFromList:(NSArray *)list; @end @implementation FLTMaxSize -+ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height { - FLTMaxSize *pigeonResult = [[FLTMaxSize alloc] init]; ++ (instancetype)makeWithWidth:(nullable NSNumber *)width + height:(nullable NSNumber *)height { + FLTMaxSize* pigeonResult = [[FLTMaxSize alloc] init]; pigeonResult.width = width; pigeonResult.height = height; return pigeonResult; @@ -94,11 +101,11 @@ + (nullable FLTMaxSize *)nullableFromList:(NSArray *)list { @implementation FLTMediaSelectionOptions + (instancetype)makeWithMaxSize:(FLTMaxSize *)maxSize - imageQuality:(nullable NSNumber *)imageQuality - requestFullMetadata:(BOOL)requestFullMetadata - allowMultiple:(BOOL)allowMultiple - limit:(nullable NSNumber *)limit { - FLTMediaSelectionOptions *pigeonResult = [[FLTMediaSelectionOptions alloc] init]; + imageQuality:(nullable NSNumber *)imageQuality + requestFullMetadata:(BOOL )requestFullMetadata + allowMultiple:(BOOL )allowMultiple + limit:(nullable NSNumber *)limit { + FLTMediaSelectionOptions* pigeonResult = [[FLTMediaSelectionOptions alloc] init]; pigeonResult.maxSize = maxSize; pigeonResult.imageQuality = imageQuality; pigeonResult.requestFullMetadata = requestFullMetadata; @@ -130,8 +137,9 @@ + (nullable FLTMediaSelectionOptions *)nullableFromList:(NSArray *)list { @end @implementation FLTSourceSpecification -+ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera { - FLTSourceSpecification *pigeonResult = [[FLTSourceSpecification alloc] init]; ++ (instancetype)makeWithType:(FLTSourceType)type + camera:(FLTSourceCamera)camera { + FLTSourceSpecification* pigeonResult = [[FLTSourceSpecification alloc] init]; pigeonResult.type = type; pigeonResult.camera = camera; return pigeonResult; @@ -162,21 +170,21 @@ - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 129: { NSNumber *enumAsNumber = [self readValue]; - return enumAsNumber == nil - ? nil - : [[FLTSourceCameraBox alloc] initWithValue:[enumAsNumber integerValue]]; + return enumAsNumber == nil ? nil : [[FLTSourceCameraBox alloc] initWithValue:[enumAsNumber integerValue]]; } case 130: { NSNumber *enumAsNumber = [self readValue]; - return enumAsNumber == nil - ? nil - : [[FLTSourceTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + return enumAsNumber == nil ? nil : [[FLTSourceTypeBox alloc] initWithValue:[enumAsNumber integerValue]]; + } + case 131: { + NSNumber *enumAsNumber = [self readValue]; + return enumAsNumber == nil ? nil : [[FLTApiVideoQualityBox alloc] initWithValue:[enumAsNumber integerValue]]; } - case 131: + case 132: return [FLTMaxSize fromList:[self readValue]]; - case 132: + case 133: return [FLTMediaSelectionOptions fromList:[self readValue]]; - case 133: + case 134: return [FLTSourceSpecification fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -196,14 +204,18 @@ - (void)writeValue:(id)value { FLTSourceTypeBox *box = (FLTSourceTypeBox *)value; [self writeByte:130]; [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; - } else if ([value isKindOfClass:[FLTMaxSize class]]) { + } else if ([value isKindOfClass:[FLTApiVideoQualityBox class]]) { + FLTApiVideoQualityBox *box = (FLTApiVideoQualityBox *)value; [self writeByte:131]; + [self writeValue:(value == nil ? [NSNull null] : [NSNumber numberWithInteger:box.value])]; + } else if ([value isKindOfClass:[FLTMaxSize class]]) { + [self writeByte:132]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FLTMediaSelectionOptions class]]) { - [self writeByte:132]; + [self writeByte:133]; [self writeValue:[value toList]]; } else if ([value isKindOfClass:[FLTSourceSpecification class]]) { - [self writeByte:133]; + [self writeByte:134]; [self writeValue:[value toList]]; } else { [super writeValue:value]; @@ -226,140 +238,97 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - FLTMessagesPigeonCodecReaderWriter *readerWriter = - [[FLTMessagesPigeonCodecReaderWriter alloc] init]; + FLTMessagesPigeonCodecReaderWriter *readerWriter = [[FLTMessagesPigeonCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void SetUpFLTImagePickerApi(id binaryMessenger, - NSObject *api) { +void SetUpFLTImagePickerApi(id binaryMessenger, NSObject *api) { SetUpFLTImagePickerApiWithSuffix(binaryMessenger, api, @""); } -void SetUpFLTImagePickerApiWithSuffix(id binaryMessenger, - NSObject *api, - NSString *messageChannelSuffix) { - messageChannelSuffix = messageChannelSuffix.length > 0 - ? [NSString stringWithFormat:@".%@", messageChannelSuffix] - : @""; +void SetUpFLTImagePickerApiWithSuffix(id binaryMessenger, NSObject *api, NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 ? [NSString stringWithFormat: @".%@", messageChannelSuffix] : @""; { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName: - [NSString - stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickImage", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickImage", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FLTGetMessagesCodec()]; + codec:FLTGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector - (pickImageWithSource:maxSize:quality:fullMetadata:completion:)], - @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 1); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 2); BOOL arg_requestFullMetadata = [GetNullableObjectAtIndex(args, 3) boolValue]; - [api pickImageWithSource:arg_source - maxSize:arg_maxSize - quality:arg_imageQuality - fullMetadata:arg_requestFullMetadata - completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickImageWithSource:arg_source maxSize:arg_maxSize quality:arg_imageQuality fullMetadata:arg_requestFullMetadata completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.image_picker_ios." - @"ImagePickerApi.pickMultiImage", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickMultiImage", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FLTGetMessagesCodec()]; + codec:FLTGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector - (pickMultiImageWithMaxSize:quality:fullMetadata:limit:completion:)], - @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickMultiImageWithMaxSize:quality:fullMetadata:limit:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:fullMetadata:limit:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickMultiImageWithMaxSize:quality:fullMetadata:limit:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 0); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 1); BOOL arg_requestFullMetadata = [GetNullableObjectAtIndex(args, 2) boolValue]; NSNumber *arg_limit = GetNullableObjectAtIndex(args, 3); - [api pickMultiImageWithMaxSize:arg_maxSize - quality:arg_imageQuality - fullMetadata:arg_requestFullMetadata - limit:arg_limit - completion:^(NSArray *_Nullable output, - FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickMultiImageWithMaxSize:arg_maxSize quality:arg_imageQuality fullMetadata:arg_requestFullMetadata limit:arg_limit completion:^(NSArray *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName: - [NSString - stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickVideo", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickVideo", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FLTGetMessagesCodec()]; + codec:FLTGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:completion:)], - @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickVideoWithSource:maxDuration:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:videoQuality:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickVideoWithSource:maxDuration:videoQuality:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); NSNumber *arg_maxDurationSeconds = GetNullableObjectAtIndex(args, 1); - [api pickVideoWithSource:arg_source - maxDuration:arg_maxDurationSeconds - completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + FLTApiVideoQualityBox *arg_videoQuality = GetNullableObjectAtIndex(args, 2); + [api pickVideoWithSource:arg_source maxDuration:arg_maxDurationSeconds videoQuality:arg_videoQuality completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.image_picker_ios." - @"ImagePickerApi.pickMultiVideo", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickMultiVideo", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FLTGetMessagesCodec()]; + codec:FLTGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickMultiVideoWithMaxDuration:limit:completion:)], - @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickMultiVideoWithMaxDuration:limit:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(pickMultiVideoWithMaxDuration:limit:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickMultiVideoWithMaxDuration:limit:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_maxDurationSeconds = GetNullableObjectAtIndex(args, 0); NSNumber *arg_limit = GetNullableObjectAtIndex(args, 1); - [api pickMultiVideoWithMaxDuration:arg_maxDurationSeconds - limit:arg_limit - completion:^(NSArray *_Nullable output, - FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickMultiVideoWithMaxDuration:arg_maxDurationSeconds limit:arg_limit completion:^(NSArray *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; } else { [channel setMessageHandler:nil]; @@ -367,27 +336,19 @@ void SetUpFLTImagePickerApiWithSuffix(id binaryMessenger } /// Selects images and videos and returns their paths. { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName: - [NSString - stringWithFormat:@"%@%@", - @"dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickMedia", - messageChannelSuffix] + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickMedia", messageChannelSuffix] binaryMessenger:binaryMessenger - codec:FLTGetMessagesCodec()]; + codec:FLTGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickMediaWithMediaSelectionOptions:completion:)], - @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickMediaWithMediaSelectionOptions:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(pickMediaWithMediaSelectionOptions:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickMediaWithMediaSelectionOptions:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTMediaSelectionOptions *arg_mediaSelectionOptions = GetNullableObjectAtIndex(args, 0); - [api pickMediaWithMediaSelectionOptions:arg_mediaSelectionOptions - completion:^(NSArray *_Nullable output, - FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickMediaWithMediaSelectionOptions:arg_mediaSelectionOptions completion:^(NSArray *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; } else { [channel setMessageHandler:nil]; diff --git a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart index fa373a31bb0c..204b75bafb11 100644 --- a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart +++ b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart @@ -42,6 +42,25 @@ SourceCamera _convertCamera(CameraDevice camera) { throw UnimplementedError('Unknown camera: $camera'); } +// Converts a [VideoQuality] to the corresponding Pigeon API enum value. +ApiVideoQuality _convertVideoQuality(VideoQuality quality) { + switch (quality) { + case VideoQuality.low: + return ApiVideoQuality.low; + case VideoQuality.medium: + return ApiVideoQuality.medium; + case VideoQuality.high: + return ApiVideoQuality.high; + } + // The enum comes from a different package, which could get a new value at + // any time, so a fallback case is necessary. Since there is no reasonable + // default behavior, throw to alert the client that they need an updated + // version. This is deliberately outside the switch rather than a `default` + // so that the linter will flag the switch as needing an update. + // ignore: dead_code + throw UnimplementedError('Unknown video quality: $quality'); +} + /// An implementation of [ImagePickerPlatform] for iOS. class ImagePickerIOS extends ImagePickerPlatform { /// Creates a new plugin implementation instance. @@ -258,11 +277,13 @@ class ImagePickerIOS extends ImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) async { final String? path = await _pickVideoAsPath( source: source, maxDuration: maxDuration, preferredCameraDevice: preferredCameraDevice, + quality: quality, ); return path != null ? PickedFile(path) : null; } @@ -271,6 +292,7 @@ class ImagePickerIOS extends ImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) { return _hostApi.pickVideo( SourceSpecification( @@ -278,6 +300,7 @@ class ImagePickerIOS extends ImagePickerPlatform { camera: _convertCamera(preferredCameraDevice), ), maxDuration?.inSeconds, + _convertVideoQuality(quality), ); } @@ -330,11 +353,13 @@ class ImagePickerIOS extends ImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) async { final String? path = await _pickVideoAsPath( source: source, maxDuration: maxDuration, preferredCameraDevice: preferredCameraDevice, + quality: quality, ); return path != null ? XFile(path) : null; } diff --git a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart index 81aedec71b51..0a6c6b1df90e 100644 --- a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart +++ b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart @@ -1,9 +1,9 @@ // Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// Autogenerated from Pigeon (v26.1.7), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -40,6 +40,8 @@ enum SourceCamera { rear, front } enum SourceType { camera, gallery } +enum ApiVideoQuality { low, medium, high } + class MaxSize { MaxSize({this.width, this.height}); @@ -191,14 +193,17 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is SourceType) { buffer.putUint8(130); writeValue(buffer, value.index); - } else if (value is MaxSize) { + } else if (value is ApiVideoQuality) { buffer.putUint8(131); + writeValue(buffer, value.index); + } else if (value is MaxSize) { + buffer.putUint8(132); writeValue(buffer, value.encode()); } else if (value is MediaSelectionOptions) { - buffer.putUint8(132); + buffer.putUint8(133); writeValue(buffer, value.encode()); } else if (value is SourceSpecification) { - buffer.putUint8(133); + buffer.putUint8(134); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -209,16 +214,19 @@ class _PigeonCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 129: - final int? value = readValue(buffer) as int?; + final value = readValue(buffer) as int?; return value == null ? null : SourceCamera.values[value]; case 130: - final int? value = readValue(buffer) as int?; + final value = readValue(buffer) as int?; return value == null ? null : SourceType.values[value]; case 131: - return MaxSize.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : ApiVideoQuality.values[value]; case 132: - return MediaSelectionOptions.decode(readValue(buffer)!); + return MaxSize.decode(readValue(buffer)!); case 133: + return MediaSelectionOptions.decode(readValue(buffer)!); + case 134: return SourceSpecification.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -249,19 +257,17 @@ class ImagePickerApi { int? imageQuality, bool requestFullMetadata, ) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickImage$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send( [source, maxSize, imageQuality, requestFullMetadata], ); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -281,19 +287,17 @@ class ImagePickerApi { bool requestFullMetadata, int? limit, ) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickMultiImage$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send( [maxSize, imageQuality, requestFullMetadata, limit], ); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -315,20 +319,19 @@ class ImagePickerApi { Future pickVideo( SourceSpecification source, int? maxDurationSeconds, + ApiVideoQuality? videoQuality, ) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickVideo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [source, maxDurationSeconds], + [source, maxDurationSeconds, videoQuality], ); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -346,19 +349,17 @@ class ImagePickerApi { int? maxDurationSeconds, int? limit, ) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickMultiVideo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send( [maxDurationSeconds, limit], ); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -381,19 +382,17 @@ class ImagePickerApi { Future> pickMedia( MediaSelectionOptions mediaSelectionOptions, ) async { - final String pigeonVar_channelName = + final pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_ios.ImagePickerApi.pickMedia$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); final Future pigeonVar_sendFuture = pigeonVar_channel.send( [mediaSelectionOptions], ); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { diff --git a/packages/image_picker/image_picker_ios/pigeons/messages.dart b/packages/image_picker/image_picker_ios/pigeons/messages.dart index a52653380a2b..7982c78e0555 100644 --- a/packages/image_picker/image_picker_ios/pigeons/messages.dart +++ b/packages/image_picker/image_picker_ios/pigeons/messages.dart @@ -45,6 +45,10 @@ enum SourceCamera { rear, front } // Corresponds to `ImageSource` from the platform interface package. enum SourceType { camera, gallery } +// Corresponds to `VideoQuality` from the platform interface package. +// Named ApiVideoQuality to avoid a name collision when importing the generated Dart. +enum ApiVideoQuality { low, medium, high } + class SourceSpecification { SourceSpecification(this.type, this.camera); SourceType type; @@ -70,8 +74,12 @@ abstract class ImagePickerApi { int? limit, ); @async - @ObjCSelector('pickVideoWithSource:maxDuration:') - String? pickVideo(SourceSpecification source, int? maxDurationSeconds); + @ObjCSelector('pickVideoWithSource:maxDuration:videoQuality:') + String? pickVideo( + SourceSpecification source, + int? maxDurationSeconds, + ApiVideoQuality? videoQuality, + ); @async @ObjCSelector('pickMultiVideoWithMaxDuration:limit:') List pickMultiVideo(int? maxDurationSeconds, int? limit); diff --git a/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart index d423acd05de8..9dbe6d754d02 100644 --- a/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart +++ b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart @@ -940,6 +940,7 @@ class _FakeImagePickerApi implements ImagePickerApi { Future pickVideo( SourceSpecification source, int? maxDurationSeconds, + ApiVideoQuality? videoQuality, ) async { passedSelectionType = _SelectionType.video; passedSource = source; diff --git a/packages/image_picker/image_picker_linux/lib/image_picker_linux.dart b/packages/image_picker/image_picker_linux/lib/image_picker_linux.dart index bd6c87fbaa7e..6ac05f19621e 100644 --- a/packages/image_picker/image_picker_linux/lib/image_picker_linux.dart +++ b/packages/image_picker/image_picker_linux/lib/image_picker_linux.dart @@ -128,6 +128,7 @@ class ImagePickerLinux extends CameraDelegatingImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) async { switch (source) { case ImageSource.camera: @@ -135,6 +136,7 @@ class ImagePickerLinux extends CameraDelegatingImagePickerPlatform { source: source, preferredCameraDevice: preferredCameraDevice, maxDuration: maxDuration, + quality: quality, ); case ImageSource.gallery: const typeGroup = XTypeGroup( diff --git a/packages/image_picker/image_picker_macos/lib/image_picker_macos.dart b/packages/image_picker/image_picker_macos/lib/image_picker_macos.dart index 886b5cc9b875..086688d783a4 100644 --- a/packages/image_picker/image_picker_macos/lib/image_picker_macos.dart +++ b/packages/image_picker/image_picker_macos/lib/image_picker_macos.dart @@ -128,6 +128,7 @@ class ImagePickerMacOS extends CameraDelegatingImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) async { switch (source) { case ImageSource.camera: @@ -135,6 +136,7 @@ class ImagePickerMacOS extends CameraDelegatingImagePickerPlatform { source: source, preferredCameraDevice: preferredCameraDevice, maxDuration: maxDuration, + quality: quality, ); case ImageSource.gallery: const typeGroup = XTypeGroup( diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index 7ed9dc66df5e..4e2cc3af6000 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -281,6 +281,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) async { final String? path = await _getVideoPath( source: source, diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index 703c7a960ec8..3d3b33692a82 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -241,6 +241,8 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device. /// Defaults to [CameraDevice.rear]. /// + /// The [quality] argument specifies the video quality for recording/picking. Defaults to [VideoQuality.high]. + /// /// In Android, the MainActivity can be destroyed for various reasons. If that happens, the result will be lost /// in this call. You can then call [getLostData] when your app relaunches to retrieve the lost data. /// @@ -249,6 +251,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) { throw UnimplementedError('getVideo() has not been implemented.'); } @@ -390,6 +393,7 @@ abstract class CameraDelegatingImagePickerPlatform extends ImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) async { if (source == ImageSource.camera) { final ImagePickerCameraDelegate? delegate = cameraDelegate; @@ -410,6 +414,7 @@ abstract class CameraDelegatingImagePickerPlatform extends ImagePickerPlatform { source: source, preferredCameraDevice: preferredCameraDevice, maxDuration: maxDuration, + quality: quality, ); } } diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart index 91661d154e2b..a9b01395a71a 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart @@ -13,6 +13,7 @@ export 'multi_image_picker_options.dart'; export 'multi_video_picker_options.dart'; export 'picked_file/picked_file.dart'; export 'retrieve_type.dart'; +export 'video_quality.dart'; /// Denotes that an image is being picked. const String kTypeImage = 'image'; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/video_quality.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/video_quality.dart new file mode 100644 index 000000000000..843e2050df75 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/video_quality.dart @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Video quality setting for video recording/picking. +/// +/// This enum corresponds to `UIImagePickerControllerQualityType` on iOS. +enum VideoQuality { + /// Low quality video. + /// + /// Corresponds to `UIImagePickerControllerQualityTypeLow` on iOS. + low, + + /// Medium quality video. + /// + /// Corresponds to `UIImagePickerControllerQualityTypeMedium` on iOS. + medium, + + /// High quality video. + /// + /// Corresponds to `UIImagePickerControllerQualityTypeHigh` on iOS. + /// This is the default quality setting. + high, +} diff --git a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart index 3456562909f7..4482d93dbde1 100644 --- a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart +++ b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart @@ -154,6 +154,7 @@ class ImagePickerWindows extends CameraDelegatingImagePickerPlatform { required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, + VideoQuality quality = VideoQuality.high, }) async { switch (source) { case ImageSource.camera: @@ -161,6 +162,7 @@ class ImagePickerWindows extends CameraDelegatingImagePickerPlatform { source: source, preferredCameraDevice: preferredCameraDevice, maxDuration: maxDuration, + quality: quality, ); case ImageSource.gallery: const typeGroup = XTypeGroup(label: 'Videos', extensions: videoFormats);