From 25276b03ea02223cd08f770200b0ef4e36b84065 Mon Sep 17 00:00:00 2001 From: Jerry Tian Date: Wed, 20 Jan 2021 16:04:15 +0800 Subject: [PATCH 1/8] return original file instead of compressed one for video selection. --- .../image_picker/ios/Classes/FLTImagePickerPlugin.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index 8d260f31b055..1efddc0d5727 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -287,7 +287,13 @@ - (void)imagePickerController:(UIImagePickerController *)picker videoURL = destination; } } - self.result(videoURL.path); + + NSString* origAssetUrl = [info objectForKey:UIImagePickerControllerReferenceURL]; + if (origAssetUrl != nil} { + self.result(origAssetUrl); + } else { + self.result(videoURL.path); + } self.result = nil; _arguments = nil; } else { From 6857a0622b8352eed749db5084ceca4ca7316246 Mon Sep 17 00:00:00 2001 From: Jerry Tian Date: Wed, 20 Jan 2021 16:14:30 +0800 Subject: [PATCH 2/8] fix typo --- .../image_picker/ios/Classes/FLTImagePickerPlugin.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index 1efddc0d5727..a4197b6e1970 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -289,7 +289,7 @@ - (void)imagePickerController:(UIImagePickerController *)picker } NSString* origAssetUrl = [info objectForKey:UIImagePickerControllerReferenceURL]; - if (origAssetUrl != nil} { + if (origAssetUrl != nil) { self.result(origAssetUrl); } else { self.result(videoURL.path); From 3a53008e8b438569efb382232661209f49fda941 Mon Sep 17 00:00:00 2001 From: Jerry Tian Date: Wed, 20 Jan 2021 17:46:10 +0800 Subject: [PATCH 3/8] use AVAssetExportPresetPassthrough to get original video. --- .../image_picker/ios/Classes/FLTImagePickerPlugin.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index a4197b6e1970..5207d601fc27 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -103,7 +103,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result (NSString *)kUTTypeMPEG4 ]; _imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; - + if (@available(iOS 11.0, *)) { + _imagePickerController.videoExportPreset = AVAssetExportPresetPassthrough; + } self.result = result; _arguments = call.arguments; From 3f4ca3517d1d5953e0292263ba4a3c891145e9bd Mon Sep 17 00:00:00 2001 From: Jerry Tian Date: Wed, 20 Jan 2021 17:50:25 +0800 Subject: [PATCH 4/8] remove tmp&dirty hack. --- .../image_picker/ios/Classes/FLTImagePickerPlugin.m | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index 5207d601fc27..24abf0f151e0 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -289,13 +289,8 @@ - (void)imagePickerController:(UIImagePickerController *)picker videoURL = destination; } } - - NSString* origAssetUrl = [info objectForKey:UIImagePickerControllerReferenceURL]; - if (origAssetUrl != nil) { - self.result(origAssetUrl); - } else { - self.result(videoURL.path); - } + + self.result(videoURL.path); self.result = nil; _arguments = nil; } else { From 1689eb97d096887c8c792d4083a20845b13c8891 Mon Sep 17 00:00:00 2001 From: Jerry Tian Date: Mon, 25 Jan 2021 19:01:01 +0800 Subject: [PATCH 5/8] iOS Picker API behave differently on old/new devices, fix it. --- .../ios/Classes/FLTImagePickerPlugin.m | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index 24abf0f151e0..87d62b175960 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -104,6 +104,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result ]; _imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; if (@available(iOS 11.0, *)) { + NSLog(@"enable passthrough mode in video-picking mode."); _imagePickerController.videoExportPreset = AVAssetExportPresetPassthrough; } self.result = result; @@ -258,7 +259,18 @@ - (void)showPhotoLibrary { - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { - NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL]; + + NSString * mediaType = info[ UIImagePickerControllerMediaType]; + NSURL *videoURL = nil; + + if (CFStringCompare ((__bridge CFStringRef) mediaType, kUTTypeMovie, 0) == kCFCompareEqualTo) { + videoURL = [info objectForKey:UIImagePickerControllerMediaURL]; + + if (videoURL == nil) { + videoURL = (NSURL *) info[UIImagePickerControllerReferenceURL]; + } + NSLog(@"has video URL? %@", videoURL); + } [_imagePickerController dismissViewControllerAnimated:YES completion:nil]; // The method dismissViewControllerAnimated does not immediately prevent // further didFinishPickingMediaWithInfo invocations. A nil check is necessary @@ -268,28 +280,83 @@ - (void)imagePickerController:(UIImagePickerController *)picker return; } if (videoURL != nil) { - if (@available(iOS 13.0, *)) { + if (@available(iOS 11.0, *)) { + NSLog(@"will try to copy resource at asset URL: %@", videoURL); NSString *fileName = [videoURL lastPathComponent]; NSURL *destination = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]]; + NSLog(@"copy detination: %@", destination); + // two different code paths for get accessible file URL, even OS version is the same. + // the first half is for newer devices(tested with Xs, 11 and 12. + // the second half is for older devices(tested with 7). if ([[NSFileManager defaultManager] isReadableFileAtPath:[videoURL path]]) { NSError *error; if (![[videoURL path] isEqualToString:[destination path]]) { [[NSFileManager defaultManager] copyItemAtURL:videoURL toURL:destination error:&error]; if (error) { + NSLog(@"failed to copy resource, error: %@", error); self.result([FlutterError errorWithCode:@"flutter_image_picker_copy_video_error" message:@"Could not cache the video file." details:nil]); self.result = nil; return; + } else { + NSLog(@"ok with copying resource, destination: %@", destination); } + } else { + NSLog(@"no need to copy resource, destination = srouce."); } videoURL = destination; + + NSNumber *fileSizeValue = nil; + NSError *fileSizeError = nil; + [videoURL getResourceValue:&fileSizeValue + forKey:NSURLFileSizeKey + error:&fileSizeError]; + if (fileSizeValue) { + NSLog(@"file size for %@ is %@", videoURL, fileSizeValue); + } + else { + NSLog(@"error getting size for url %@ error was %@", videoURL, fileSizeError); + } + } else { + NSLog(@"source not readable, using PhotoKit for acccesing: %@", destination); + + PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; + + PHVideoRequestOptions *options = [PHVideoRequestOptions new]; + options.version = PHVideoRequestOptionsVersionOriginal; + + __weak typeof(self) weakSelf = self; + [[PHImageManager defaultManager] requestAVAssetForVideo:originalAsset + options:options + resultHandler:^(AVAsset * _Nullable avasset, + AVAudioMix * _Nullable audioMix, + NSDictionary * _Nullable info) + { + NSError *error; + AVURLAsset *avAsset = (AVURLAsset*) avasset; + + [[NSFileManager defaultManager] removeItemAtURL:destination error:&error]; + // Write to documents folder + if ([[NSFileManager defaultManager] copyItemAtURL:avAsset.URL + toURL:destination + error:&error]) { + NSLog(@"successfully copy asset from PhotoKit correctly from %@ to %@", avAsset.URL, destination); + weakSelf.result(destination.path); + weakSelf.result = nil; + } else { + NSLog(@"copy asset from PhotoKit from %@ to %@ failed: %@", videoURL, destination, error); + } + }]; + + return; } } + self.result(videoURL.path); self.result = nil; _arguments = nil; From 82909166ec95b319affe20c90d7ef544fea0a1a7 Mon Sep 17 00:00:00 2001 From: Jerry Tian Date: Mon, 25 Jan 2021 19:34:53 +0800 Subject: [PATCH 6/8] clean comment. --- .../ios/Classes/FLTImagePickerPlugin.m | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index 87d62b175960..280f7d25b728 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -287,9 +287,14 @@ - (void)imagePickerController:(UIImagePickerController *)picker [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]]; NSLog(@"copy detination: %@", destination); - // two different code paths for get accessible file URL, even OS version is the same. - // the first half is for newer devices(tested with Xs, 11 and 12. - // the second half is for older devices(tested with 7). + // Two different code paths exist for getting accessible real file path, + // even with the same OS version( on iOS 14.3). + // + // The first half is for newer devices(tested with iPhone Xs, 11 and 12), + // in which "videoURL" is prefixed with "file://". + // + // The second half is for older devices(tested with iPhone 7), in which + // "videoURL" is prefixed with "assets-library://". if ([[NSFileManager defaultManager] isReadableFileAtPath:[videoURL path]]) { NSError *error; if (![[videoURL path] isEqualToString:[destination path]]) { @@ -306,21 +311,20 @@ - (void)imagePickerController:(UIImagePickerController *)picker NSLog(@"ok with copying resource, destination: %@", destination); } } else { - NSLog(@"no need to copy resource, destination = srouce."); + NSLog(@"no need to copy resource, destination = srouce."); } videoURL = destination; - NSNumber *fileSizeValue = nil; - NSError *fileSizeError = nil; - [videoURL getResourceValue:&fileSizeValue - forKey:NSURLFileSizeKey - error:&fileSizeError]; - if (fileSizeValue) { - NSLog(@"file size for %@ is %@", videoURL, fileSizeValue); - } - else { - NSLog(@"error getting size for url %@ error was %@", videoURL, fileSizeError); - } +// NSNumber *fileSizeValue = nil; +// NSError *fileSizeError = nil; +// [videoURL getResourceValue:&fileSizeValue +// forKey:NSURLFileSizeKey +// error:&fileSizeError]; +// if (fileSizeValue) { +// NSLog(@"file size for %@ is %@", videoURL, fileSizeValue); +// } else { +// NSLog(@"error getting size for url %@ error was %@", videoURL, fileSizeError); +// } } else { NSLog(@"source not readable, using PhotoKit for acccesing: %@", destination); @@ -339,8 +343,9 @@ - (void)imagePickerController:(UIImagePickerController *)picker NSError *error; AVURLAsset *avAsset = (AVURLAsset*) avasset; + // Destination is a tmp file, reset/drop it before copy operation anyway. [[NSFileManager defaultManager] removeItemAtURL:destination error:&error]; - // Write to documents folder + // Write to app tmp folder. if ([[NSFileManager defaultManager] copyItemAtURL:avAsset.URL toURL:destination error:&error]) { @@ -350,13 +355,12 @@ - (void)imagePickerController:(UIImagePickerController *)picker } else { NSLog(@"copy asset from PhotoKit from %@ to %@ failed: %@", videoURL, destination, error); } - }]; + }]; return; } } - self.result(videoURL.path); self.result = nil; _arguments = nil; From 2aa3a5e5d0804b77304f71b075904724188d45d9 Mon Sep 17 00:00:00 2001 From: Jerry Tian Date: Tue, 13 Apr 2021 20:00:28 +0800 Subject: [PATCH 7/8] remove NSLog for debuging, though some logging is kept as comment. --- .../ios/Classes/FLTImagePickerPlugin.m | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index 1dafcfb6c972..101b1f80a254 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -104,7 +104,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result ]; _imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; if (@available(iOS 11.0, *)) { - NSLog(@"enable passthrough mode in video-picking mode."); + // Enable passthrough mode in video-picking mode. _imagePickerController.videoExportPreset = AVAssetExportPresetPassthrough; } self.result = result; @@ -269,7 +269,6 @@ - (void)imagePickerController:(UIImagePickerController *)picker if (videoURL == nil) { videoURL = (NSURL *) info[UIImagePickerControllerReferenceURL]; } - NSLog(@"has video URL? %@", videoURL); } [_imagePickerController dismissViewControllerAnimated:YES completion:nil]; // The method dismissViewControllerAnimated does not immediately prevent @@ -281,11 +280,9 @@ - (void)imagePickerController:(UIImagePickerController *)picker } if (videoURL != nil) { if (@available(iOS 11.0, *)) { - NSLog(@"will try to copy resource at asset URL: %@", videoURL); NSString *fileName = [videoURL lastPathComponent]; NSURL *destination = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]]; - NSLog(@"copy detination: %@", destination); // Two different code paths exist for getting accessible real file path, // even with the same OS version( on iOS 14.3). @@ -301,32 +298,16 @@ - (void)imagePickerController:(UIImagePickerController *)picker [[NSFileManager defaultManager] copyItemAtURL:videoURL toURL:destination error:&error]; if (error) { - NSLog(@"failed to copy resource, error: %@", error); self.result([FlutterError errorWithCode:@"flutter_image_picker_copy_video_error" message:@"Could not cache the video file." details:nil]); self.result = nil; return; - } else { - NSLog(@"ok with copying resource, destination: %@", destination); } - } else { - NSLog(@"no need to copy resource, destination = srouce."); } videoURL = destination; - -// NSNumber *fileSizeValue = nil; -// NSError *fileSizeError = nil; -// [videoURL getResourceValue:&fileSizeValue -// forKey:NSURLFileSizeKey -// error:&fileSizeError]; -// if (fileSizeValue) { -// NSLog(@"file size for %@ is %@", videoURL, fileSizeValue); -// } else { -// NSLog(@"error getting size for url %@ error was %@", videoURL, fileSizeError); -// } } else { - NSLog(@"source not readable, using PhotoKit for acccesing: %@", destination); + // PhotoKit for "assets-library://" schema handling. PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; @@ -349,11 +330,14 @@ - (void)imagePickerController:(UIImagePickerController *)picker if ([[NSFileManager defaultManager] copyItemAtURL:avAsset.URL toURL:destination error:&error]) { - NSLog(@"successfully copy asset from PhotoKit correctly from %@ to %@", avAsset.URL, destination); weakSelf.result(destination.path); weakSelf.result = nil; } else { - NSLog(@"copy asset from PhotoKit from %@ to %@ failed: %@", videoURL, destination, error); + self.result([FlutterError errorWithCode:@"flutter_image_picker_copy_video_error" + message:@"Could not cache the video file." + details:nil]); + self.result = nil; + return; } }]; From 21feecb3ddf1fa4bcf8c5574b5c634d77ec51614 Mon Sep 17 00:00:00 2001 From: Jerry Tian Date: Wed, 21 Apr 2021 15:45:31 +0800 Subject: [PATCH 8/8] fix code format issue by calling clang-format(the one used in CI). --- .../ios/Classes/FLTImagePickerPlugin.m | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index 101b1f80a254..b6ebc60a4a2f 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -99,8 +99,10 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result _imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; _imagePickerController.delegate = self; _imagePickerController.mediaTypes = @[ - (NSString *)kUTTypeMovie, (NSString *)kUTTypeAVIMovie, (NSString *)kUTTypeVideo, - (NSString *)kUTTypeMPEG4 + (NSString *)kUTTypeMovie, + (NSString *)kUTTypeAVIMovie, + (NSString *)kUTTypeVideo, + (NSString *)kUTTypeMPEG4, ]; _imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; if (@available(iOS 11.0, *)) { @@ -259,15 +261,14 @@ - (void)showPhotoLibrary { - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { - - NSString * mediaType = info[ UIImagePickerControllerMediaType]; + NSString *mediaType = info[UIImagePickerControllerMediaType]; NSURL *videoURL = nil; - - if (CFStringCompare ((__bridge CFStringRef) mediaType, kUTTypeMovie, 0) == kCFCompareEqualTo) { + + if (CFStringCompare((__bridge CFStringRef)mediaType, kUTTypeMovie, 0) == kCFCompareEqualTo) { videoURL = [info objectForKey:UIImagePickerControllerMediaURL]; - + if (videoURL == nil) { - videoURL = (NSURL *) info[UIImagePickerControllerReferenceURL]; + videoURL = (NSURL *)info[UIImagePickerControllerReferenceURL]; } } [_imagePickerController dismissViewControllerAnimated:YES completion:nil]; @@ -308,43 +309,43 @@ - (void)imagePickerController:(UIImagePickerController *)picker videoURL = destination; } else { // PhotoKit for "assets-library://" schema handling. - + PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; - + PHVideoRequestOptions *options = [PHVideoRequestOptions new]; options.version = PHVideoRequestOptionsVersionOriginal; - + __weak typeof(self) weakSelf = self; - [[PHImageManager defaultManager] requestAVAssetForVideo:originalAsset - options:options - resultHandler:^(AVAsset * _Nullable avasset, - AVAudioMix * _Nullable audioMix, - NSDictionary * _Nullable info) - { - NSError *error; - AVURLAsset *avAsset = (AVURLAsset*) avasset; - - // Destination is a tmp file, reset/drop it before copy operation anyway. - [[NSFileManager defaultManager] removeItemAtURL:destination error:&error]; - // Write to app tmp folder. - if ([[NSFileManager defaultManager] copyItemAtURL:avAsset.URL - toURL:destination - error:&error]) { - weakSelf.result(destination.path); - weakSelf.result = nil; - } else { - self.result([FlutterError errorWithCode:@"flutter_image_picker_copy_video_error" - message:@"Could not cache the video file." - details:nil]); - self.result = nil; - return; - } - }]; - + [[PHImageManager defaultManager] + requestAVAssetForVideo:originalAsset + options:options + resultHandler:^(AVAsset *_Nullable avasset, AVAudioMix *_Nullable audioMix, + NSDictionary *_Nullable info) { + NSError *error; + AVURLAsset *avAsset = (AVURLAsset *)avasset; + + // Destination is a tmp file, reset/drop it before copy operation anyway. + [[NSFileManager defaultManager] removeItemAtURL:destination error:&error]; + // Write to app tmp folder. + if ([[NSFileManager defaultManager] copyItemAtURL:avAsset.URL + toURL:destination + error:&error]) { + weakSelf.result(destination.path); + weakSelf.result = nil; + } else { + self.result([FlutterError + errorWithCode:@"flutter_image_picker_copy_video_error" + message:@"Could not cache the video file." + details:nil]); + self.result = nil; + return; + } + }]; + return; } } - + self.result(videoURL.path); self.result = nil; _arguments = nil;