From dbfd988b670c89c6ca2846fdf009f989e01529ea Mon Sep 17 00:00:00 2001 From: Ahmed Ghalab <0xcodezero@gmail.com> Date: Mon, 2 Dec 2019 17:17:14 +0300 Subject: [PATCH 01/10] [ImagePicker][iOS] Support Camera Video quality & duration parameters --- .../ios/Classes/ImagePickerPlugin.m | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/ios/Classes/ImagePickerPlugin.m b/packages/image_picker/ios/Classes/ImagePickerPlugin.m index 5ac31a695a58..bfc6c9d17f04 100644 --- a/packages/image_picker/ios/Classes/ImagePickerPlugin.m +++ b/packages/image_picker/ios/Classes/ImagePickerPlugin.m @@ -22,6 +22,10 @@ @interface FLTImagePickerPlugin () Date: Mon, 2 Dec 2019 17:17:24 +0300 Subject: [PATCH 02/10] [ImagePicker][Android] Support Camera Video quality & duration parameters --- .../imagepicker/ImagePickerDelegate.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java index e34a3b5632c0..fb37d891be42 100644 --- a/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java +++ b/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java @@ -75,6 +75,13 @@ public class ImagePickerDelegate @VisibleForTesting static final int REQUEST_EXTERNAL_VIDEO_STORAGE_PERMISSION = 2354; @VisibleForTesting static final int REQUEST_CAMERA_VIDEO_PERMISSION = 2355; + @VisibleForTesting static final int VIDEO_QUALITY_HIGH = 1; + @VisibleForTesting static final int VIDEO_QUALITY_LOW = 0; + + @VisibleForTesting static final int FLUTTER_VIDEO_QUALITY_HIGH = 0; + @VisibleForTesting static final int FLUTTER_VIDEO_QUALITY_MEDIUM = 1; // Mapped to Low Quality Video Recording + @VisibleForTesting static final int FLUTTER_VIDEO_QUALITY_LOW = 2; + @VisibleForTesting final String fileProviderName; private final Activity activity; @@ -280,6 +287,26 @@ private void launchTakeVideoWithCameraIntent() { return; } + if (methodCall != null) { + int quality = + methodCall.argument("quality") == null + ? FLUTTER_VIDEO_QUALITY_HIGH + : (int) methodCall.argument("quality"); + int videoQuality = (quality == FLUTTER_VIDEO_QUALITY_HIGH) ? VIDEO_QUALITY_HIGH : VIDEO_QUALITY_LOW; + intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, videoQuality); + + + int durationInSeconds = + methodCall.argument("duration") == null + ? 0 // Zero means a limitless video recording session + : (int) methodCall.argument("duration"); + if (durationInSeconds < 0) { + finishWithError("not_valid_duration_input", "Duration in seconds can not be a negative number"); + return; + } + intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, durationInSeconds); + } + File videoFile = createTemporaryWritableVideoFile(); pendingCameraMediaUri = Uri.parse("file:" + videoFile.getAbsolutePath()); From 6aaf25e087dd33e0bc47185038fc93f40ea144d9 Mon Sep 17 00:00:00 2001 From: Ahmed Ghalab <0xcodezero@gmail.com> Date: Mon, 2 Dec 2019 17:21:46 +0300 Subject: [PATCH 03/10] [ImagePicker][Dart] Support Video quality & duration parameters selected from Camera --- packages/image_picker/lib/image_picker.dart | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/image_picker/lib/image_picker.dart b/packages/image_picker/lib/image_picker.dart index 19cd0752ced3..303703cfec46 100755 --- a/packages/image_picker/lib/image_picker.dart +++ b/packages/image_picker/lib/image_picker.dart @@ -23,6 +23,18 @@ enum ImageSource { gallery, } +/// Specifies the video quality to be picked from Camera. +enum VideoQuality { + /// High Qulaity Video with Maximum size. + High, + + /// Medium Qulaity Video. + Medium, + + /// Low Qulaity Video. + Low, +} + /// Provides an easy way to pick an image/video from the image library, /// or to take a picture/video with the camera. class ImagePicker { @@ -79,16 +91,25 @@ class ImagePicker { /// The [source] argument controls where the video comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. /// + /// The [quality] optional argument controls the video quality and its size on correspondence. This can + /// be either [VideoQuality.High] (default), [VideoQuality.Medium](supported in iOS only) or [VideoQuality.Low]. + /// + /// The [durationInSeconds] optional argument controls the video length in seconds. The default value is Zero - which means a limitless video length. + /// /// In Android, the MainActivity can be destroyed for various fo 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. static Future pickVideo({ @required ImageSource source, + VideoQuality quality = VideoQuality.High, + int durationInSeconds = 0, // Zero Means limitless video duration }) async { assert(source != null); final String path = await _channel.invokeMethod( 'pickVideo', { 'source': source.index, + 'quality' : quality.index, + 'duration' : durationInSeconds, }, ); return path == null ? null : File(path); From 1c5eca534604ba3091fe07e2b3f5992088092fee Mon Sep 17 00:00:00 2001 From: Ahmed Ghalab <0xcodezero@gmail.com> Date: Tue, 3 Dec 2019 14:08:50 +0300 Subject: [PATCH 04/10] [image-picker][test-cases] Adjust the unit test cases, to be adjusted with the new parameters --- packages/image_picker/lib/image_picker.dart | 5 +++ .../image_picker/test/image_picker_test.dart | 41 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/packages/image_picker/lib/image_picker.dart b/packages/image_picker/lib/image_picker.dart index 303703cfec46..df47f02a6b53 100755 --- a/packages/image_picker/lib/image_picker.dart +++ b/packages/image_picker/lib/image_picker.dart @@ -104,6 +104,11 @@ class ImagePicker { int durationInSeconds = 0, // Zero Means limitless video duration }) async { assert(source != null); + + if (durationInSeconds != null && durationInSeconds < 0) { + throw ArgumentError.value(durationInSeconds, 'DurationInSeconds can not be a negative number'); + } + final String path = await _channel.invokeMethod( 'pickVideo', { diff --git a/packages/image_picker/test/image_picker_test.dart b/packages/image_picker/test/image_picker_test.dart index 28eae8eaa46d..c838c1a8ed3b 100644 --- a/packages/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/test/image_picker_test.dart @@ -147,20 +147,61 @@ void main() { test('passes the image source argument correctly', () async { await ImagePicker.pickVideo(source: ImageSource.camera); await ImagePicker.pickVideo(source: ImageSource.gallery); + await ImagePicker.pickVideo(source: ImageSource.camera, quality: VideoQuality.Medium, ); + await ImagePicker.pickVideo(source: ImageSource.camera, quality: VideoQuality.Low); + await ImagePicker.pickVideo(source: ImageSource.camera, quality: VideoQuality.Low, durationInSeconds: 15); + await ImagePicker.pickVideo(source: ImageSource.camera, durationInSeconds: 15); expect( log, [ isMethodCall('pickVideo', arguments: { 'source': 0, + 'quality': 0, + 'duration': 0 }), isMethodCall('pickVideo', arguments: { 'source': 1, + 'quality': 0, + 'duration': 0 + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'quality': 1, + 'duration': 0 + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'quality': 2, + 'duration': 0 + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'quality': 2, + 'duration': 15 + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'quality': 0, + 'duration': 15 }), ], ); }); + test('does not accept a negative durationInSeconds argument', () { + expect( + ImagePicker.pickVideo(source: ImageSource.camera, durationInSeconds: -10), + throwsArgumentError, + ); + + expect( + ImagePicker.pickVideo(source: ImageSource.camera, quality: VideoQuality.Low, durationInSeconds: -10), + throwsArgumentError, + ); + + }); + test('handles a null image path response gracefully', () async { channel.setMockMethodCallHandler((MethodCall methodCall) => null); From e0b6a9d8bd7a1a93b2151878275261297b7ed0f1 Mon Sep 17 00:00:00 2001 From: Ahmed Ghalab <0xcodezero@gmail.com> Date: Tue, 3 Dec 2019 14:28:06 +0300 Subject: [PATCH 05/10] [Version-Update] upgrade ImagePicker version to 0.6.3 --- packages/image_picker/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/image_picker/pubspec.yaml b/packages/image_picker/pubspec.yaml index 0e47c53e6911..c49cc174a9b5 100755 --- a/packages/image_picker/pubspec.yaml +++ b/packages/image_picker/pubspec.yaml @@ -5,7 +5,7 @@ authors: - Flutter Team - Rhodes Davis Jr. homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker -version: 0.6.2+1 +version: 0.6.3 flutter: plugin: From a6c2b18bcccf9c05111e6dac4ffcfca755eba19c Mon Sep 17 00:00:00 2001 From: Ahmed Ghalab <0xcodezero@gmail.com> Date: Tue, 3 Dec 2019 14:34:04 +0300 Subject: [PATCH 06/10] [Release] Updating the CHANGELOG.md --- packages/image_picker/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/image_picker/CHANGELOG.md b/packages/image_picker/CHANGELOG.md index 96bb4fcad8c3..2aa76e4ce279 100644 --- a/packages/image_picker/CHANGELOG.md +++ b/packages/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.3 + +* Video Capture from Camera is supporting Duration and Quality parameters. + ## 0.6.2+1 * Android: Fix a crash when a non-image file is picked. From 1ec99c1610cba5ee56e2a5d375f664d88800674b Mon Sep 17 00:00:00 2001 From: Ahmed Ghalab <0xcodezero@gmail.com> Date: Tue, 3 Dec 2019 14:50:31 +0300 Subject: [PATCH 07/10] [Formatting] Adjusting the code format inside the test cases Dart File --- .../image_picker/test/image_picker_test.dart | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/packages/image_picker/test/image_picker_test.dart b/packages/image_picker/test/image_picker_test.dart index c838c1a8ed3b..934fa0fec11d 100644 --- a/packages/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/test/image_picker_test.dart @@ -147,10 +147,23 @@ void main() { test('passes the image source argument correctly', () async { await ImagePicker.pickVideo(source: ImageSource.camera); await ImagePicker.pickVideo(source: ImageSource.gallery); - await ImagePicker.pickVideo(source: ImageSource.camera, quality: VideoQuality.Medium, ); - await ImagePicker.pickVideo(source: ImageSource.camera, quality: VideoQuality.Low); - await ImagePicker.pickVideo(source: ImageSource.camera, quality: VideoQuality.Low, durationInSeconds: 15); - await ImagePicker.pickVideo(source: ImageSource.camera, durationInSeconds: 15); + await ImagePicker.pickVideo( + source: ImageSource.camera, + quality: VideoQuality.Medium, + ); + await ImagePicker.pickVideo( + source: ImageSource.camera, + quality: VideoQuality.Low, + ); + await ImagePicker.pickVideo( + source: ImageSource.camera, + quality: VideoQuality.Low, + durationInSeconds: 15, + ); + await ImagePicker.pickVideo( + source: ImageSource.camera, + durationInSeconds: 15, + ); expect( log, @@ -191,15 +204,21 @@ void main() { test('does not accept a negative durationInSeconds argument', () { expect( - ImagePicker.pickVideo(source: ImageSource.camera, durationInSeconds: -10), + ImagePicker.pickVideo( + source: ImageSource.camera, + durationInSeconds: -10, + ), throwsArgumentError, ); expect( - ImagePicker.pickVideo(source: ImageSource.camera, quality: VideoQuality.Low, durationInSeconds: -10), + ImagePicker.pickVideo( + source: ImageSource.camera, + quality: VideoQuality.Low, + durationInSeconds: -10, + ), throwsArgumentError, ); - }); test('handles a null image path response gracefully', () async { From 5d5987ced4c969b9a22e2c3e0acb978101ca4fa7 Mon Sep 17 00:00:00 2001 From: Ahmed Ghalab <0xcodezero@gmail.com> Date: Tue, 3 Dec 2019 15:21:37 +0300 Subject: [PATCH 08/10] [Formatting] Adjust Formatting according to cirrus logs --- .../imagepicker/ImagePickerDelegate.java | 23 +++++++++++-------- .../ios/Classes/ImagePickerPlugin.m | 22 ++++++++++-------- packages/image_picker/lib/image_picker.dart | 7 +++--- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java index fb37d891be42..5f29ec9c5bf8 100644 --- a/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java +++ b/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java @@ -79,7 +79,10 @@ public class ImagePickerDelegate @VisibleForTesting static final int VIDEO_QUALITY_LOW = 0; @VisibleForTesting static final int FLUTTER_VIDEO_QUALITY_HIGH = 0; - @VisibleForTesting static final int FLUTTER_VIDEO_QUALITY_MEDIUM = 1; // Mapped to Low Quality Video Recording + + @VisibleForTesting + static final int FLUTTER_VIDEO_QUALITY_MEDIUM = 1; // Mapped to Low Quality Video Recording + @VisibleForTesting static final int FLUTTER_VIDEO_QUALITY_LOW = 2; @VisibleForTesting final String fileProviderName; @@ -289,19 +292,21 @@ private void launchTakeVideoWithCameraIntent() { if (methodCall != null) { int quality = - methodCall.argument("quality") == null - ? FLUTTER_VIDEO_QUALITY_HIGH - : (int) methodCall.argument("quality"); - int videoQuality = (quality == FLUTTER_VIDEO_QUALITY_HIGH) ? VIDEO_QUALITY_HIGH : VIDEO_QUALITY_LOW; + methodCall.argument("quality") == null + ? FLUTTER_VIDEO_QUALITY_HIGH + : (int) methodCall.argument("quality"); + int videoQuality = + (quality == FLUTTER_VIDEO_QUALITY_HIGH) ? VIDEO_QUALITY_HIGH : VIDEO_QUALITY_LOW; intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, videoQuality); int durationInSeconds = - methodCall.argument("duration") == null - ? 0 // Zero means a limitless video recording session - : (int) methodCall.argument("duration"); + methodCall.argument("duration") == null + ? 0 // Zero means a limitless video recording session + : (int) methodCall.argument("duration"); if (durationInSeconds < 0) { - finishWithError("not_valid_duration_input", "Duration in seconds can not be a negative number"); + finishWithError( + "not_valid_duration_input", "Duration in seconds can not be a negative number"); return; } intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, durationInSeconds); diff --git a/packages/image_picker/ios/Classes/ImagePickerPlugin.m b/packages/image_picker/ios/Classes/ImagePickerPlugin.m index bfc6c9d17f04..5858c22ff647 100644 --- a/packages/image_picker/ios/Classes/ImagePickerPlugin.m +++ b/packages/image_picker/ios/Classes/ImagePickerPlugin.m @@ -95,8 +95,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result self.result = result; _arguments = call.arguments; - NSNumber* quality = [_arguments objectForKey:@"quality"]; - int videoQuality = ([quality respondsToSelector:@selector(intValue)]) ? [quality intValue] : QUALITY_HIGH; + NSNumber *quality = [_arguments objectForKey:@"quality"]; + int videoQuality = + ([quality respondsToSelector:@selector(intValue)]) ? [quality intValue] : QUALITY_HIGH; switch (videoQuality) { case QUALITY_HIGH: _imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; @@ -112,14 +113,15 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result break; } - NSNumber* duration = [_arguments objectForKey:@"duration"]; - int videoDuration = ([duration respondsToSelector:@selector(intValue)]) ? [duration intValue] : 0; - if(videoDuration < 0){ - result([FlutterError errorWithCode:@"not_valid_duration_input" - message:@"Duration in seconds can not be a negative number" - details:nil]); - return; - } + NSNumber *duration = [_arguments objectForKey:@"duration"]; + int videoDuration = + ([duration respondsToSelector:@selector(intValue)]) ? [duration intValue] : 0; + if (videoDuration < 0) { + result([FlutterError errorWithCode:@"not_valid_duration_input" + message:@"Duration in seconds can not be a negative number" + details:nil]); + return; + } _imagePickerController.videoMaximumDuration = videoDuration; int imageSource = [[_arguments objectForKey:@"source"] intValue]; diff --git a/packages/image_picker/lib/image_picker.dart b/packages/image_picker/lib/image_picker.dart index df47f02a6b53..552f2d08ecd9 100755 --- a/packages/image_picker/lib/image_picker.dart +++ b/packages/image_picker/lib/image_picker.dart @@ -106,15 +106,16 @@ class ImagePicker { assert(source != null); if (durationInSeconds != null && durationInSeconds < 0) { - throw ArgumentError.value(durationInSeconds, 'DurationInSeconds can not be a negative number'); + throw ArgumentError.value( + durationInSeconds, 'DurationInSeconds can not be a negative number'); } final String path = await _channel.invokeMethod( 'pickVideo', { 'source': source.index, - 'quality' : quality.index, - 'duration' : durationInSeconds, + 'quality': quality.index, + 'duration': durationInSeconds, }, ); return path == null ? null : File(path); From a1c3f7dcf34bb7bc3530a7792d4fd0543d36320f Mon Sep 17 00:00:00 2001 From: Ahmed Ghalab <0xcodezero@gmail.com> Date: Tue, 3 Dec 2019 15:37:12 +0300 Subject: [PATCH 09/10] [Formatting] Adjust Formatting according to cirrus logs --- .../plugins/imagepicker/ImagePickerDelegate.java | 1 - packages/image_picker/ios/Classes/ImagePickerPlugin.m | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java index 5f29ec9c5bf8..9219f57fdaf9 100644 --- a/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java +++ b/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java @@ -299,7 +299,6 @@ private void launchTakeVideoWithCameraIntent() { (quality == FLUTTER_VIDEO_QUALITY_HIGH) ? VIDEO_QUALITY_HIGH : VIDEO_QUALITY_LOW; intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, videoQuality); - int durationInSeconds = methodCall.argument("duration") == null ? 0 // Zero means a limitless video recording session diff --git a/packages/image_picker/ios/Classes/ImagePickerPlugin.m b/packages/image_picker/ios/Classes/ImagePickerPlugin.m index 5858c22ff647..9698ff1de571 100644 --- a/packages/image_picker/ios/Classes/ImagePickerPlugin.m +++ b/packages/image_picker/ios/Classes/ImagePickerPlugin.m @@ -91,13 +91,13 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result (NSString *)kUTTypeMovie, (NSString *)kUTTypeAVIMovie, (NSString *)kUTTypeVideo, (NSString *)kUTTypeMPEG4 ]; - + self.result = result; _arguments = call.arguments; - + NSNumber *quality = [_arguments objectForKey:@"quality"]; int videoQuality = - ([quality respondsToSelector:@selector(intValue)]) ? [quality intValue] : QUALITY_HIGH; + ([quality respondsToSelector:@selector(intValue)]) ? [quality intValue] : QUALITY_HIGH; switch (videoQuality) { case QUALITY_HIGH: _imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; @@ -112,10 +112,10 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result _imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; break; } - + NSNumber *duration = [_arguments objectForKey:@"duration"]; int videoDuration = - ([duration respondsToSelector:@selector(intValue)]) ? [duration intValue] : 0; + ([duration respondsToSelector:@selector(intValue)]) ? [duration intValue] : 0; if (videoDuration < 0) { result([FlutterError errorWithCode:@"not_valid_duration_input" message:@"Duration in seconds can not be a negative number" From d58ef404b78c1316c1ebf0284e14dc42693bf551 Mon Sep 17 00:00:00 2001 From: Ahmed Ghalab <0xcodezero@gmail.com> Date: Wed, 4 Dec 2019 01:36:40 +0300 Subject: [PATCH 10/10] [ImagePicker][Typo] Fixing Typo according to peer review --- packages/image_picker/lib/image_picker.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/image_picker/lib/image_picker.dart b/packages/image_picker/lib/image_picker.dart index 552f2d08ecd9..84712afee99c 100755 --- a/packages/image_picker/lib/image_picker.dart +++ b/packages/image_picker/lib/image_picker.dart @@ -25,13 +25,13 @@ enum ImageSource { /// Specifies the video quality to be picked from Camera. enum VideoQuality { - /// High Qulaity Video with Maximum size. + /// High Quality Video with Maximum size. High, - /// Medium Qulaity Video. + /// Medium Quality Video. Medium, - /// Low Qulaity Video. + /// Low Quality Video. Low, } @@ -94,7 +94,8 @@ class ImagePicker { /// The [quality] optional argument controls the video quality and its size on correspondence. This can /// be either [VideoQuality.High] (default), [VideoQuality.Medium](supported in iOS only) or [VideoQuality.Low]. /// - /// The [durationInSeconds] optional argument controls the video length in seconds. The default value is Zero - which means a limitless video length. + /// The [durationInSeconds] optional argument controls the video length in seconds. + /// The default value is Zero - which means a limitless video length. /// /// In Android, the MainActivity can be destroyed for various fo 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.