Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ 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;
Expand Down Expand Up @@ -280,6 +290,27 @@ 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());

Expand Down
34 changes: 33 additions & 1 deletion packages/image_picker/ios/Classes/ImagePickerPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ @interface FLTImagePickerPlugin () <UINavigationControllerDelegate, UIImagePicke
static const int SOURCE_CAMERA = 0;
static const int SOURCE_GALLERY = 1;

static const int QUALITY_HIGH = 0;
static const int QUALITY_MEDIUM = 1;
static const int QUALITY_LOW = 2;

@implementation FLTImagePickerPlugin {
NSDictionary *_arguments;
UIImagePickerController *_imagePickerController;
Expand Down Expand Up @@ -87,11 +91,39 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
(NSString *)kUTTypeMovie, (NSString *)kUTTypeAVIMovie, (NSString *)kUTTypeVideo,
(NSString *)kUTTypeMPEG4
];
_imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;

self.result = result;
_arguments = call.arguments;

NSNumber *quality = [_arguments objectForKey:@"quality"];
int videoQuality =
([quality respondsToSelector:@selector(intValue)]) ? [quality intValue] : QUALITY_HIGH;
switch (videoQuality) {
case QUALITY_HIGH:
_imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;
break;
case QUALITY_MEDIUM:
_imagePickerController.videoQuality = UIImagePickerControllerQualityTypeMedium;
break;
case QUALITY_LOW:
_imagePickerController.videoQuality = UIImagePickerControllerQualityTypeLow;
break;
default:
_imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;
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;
}
_imagePickerController.videoMaximumDuration = videoDuration;

int imageSource = [[_arguments objectForKey:@"source"] intValue];

switch (imageSource) {
Expand Down
28 changes: 28 additions & 0 deletions packages/image_picker/lib/image_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ enum ImageSource {
gallery,
}

/// Specifies the video quality to be picked from Camera.
enum VideoQuality {
/// High Quality Video with Maximum size.
High,

/// Medium Quality Video.
Medium,

/// Low Quality 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 {
Expand Down Expand Up @@ -79,16 +91,32 @@ 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<File> pickVideo({
@required ImageSource source,
VideoQuality quality = VideoQuality.High,
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<String>(
'pickVideo',
<String, dynamic>{
'source': source.index,
'quality': quality.index,
'duration': durationInSeconds,
},
);
return path == null ? null : File(path);
Expand Down
2 changes: 1 addition & 1 deletion packages/image_picker/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors:
- Flutter Team <[email protected]>
- Rhodes Davis Jr. <[email protected]>
homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker
version: 0.6.2+1
version: 0.6.3

flutter:
plugin:
Expand Down
60 changes: 60 additions & 0 deletions packages/image_picker/test/image_picker_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,80 @@ 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,
<Matcher>[
isMethodCall('pickVideo', arguments: <String, dynamic>{
'source': 0,
'quality': 0,
'duration': 0
}),
isMethodCall('pickVideo', arguments: <String, dynamic>{
'source': 1,
'quality': 0,
'duration': 0
}),
isMethodCall('pickVideo', arguments: <String, dynamic>{
'source': 0,
'quality': 1,
'duration': 0
}),
isMethodCall('pickVideo', arguments: <String, dynamic>{
'source': 0,
'quality': 2,
'duration': 0
}),
isMethodCall('pickVideo', arguments: <String, dynamic>{
'source': 0,
'quality': 2,
'duration': 15
}),
isMethodCall('pickVideo', arguments: <String, dynamic>{
'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);

Expand Down