diff --git a/CHANGELOG.md b/CHANGELOG.md index c32ae5e..236e747 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ +## 1.7.1 +Updates iOS multi gallery picker dependency and adds a modal loading while fetching exporting assets. + ## 1.7.0 **Breaking change** -Added support for multi-picks from Photos app on iOS through [BSImagePicker](https://github.com/mikaoj/BSImagePicker) — use any of the `getMulti` methods with `FileType.image`. From now on, you'll need to set your iOS minimum version to 10.0 and add `use_frameworks!` in your ios/Podfile. + +Added support for multi-picks of videos and photos from Photos app on iOS through [DKImagePicker](https://github.com/zhangao0086/DKImagePickerController) — use any of the `getMulti` methods with `FileType.image` or `FileType.video`. From now on, you'll need to add `use_frameworks!` in your ios/Podfile. ## 1.6.3+2 * Fixes a crash on Android when a file has an id that can't be resolved and uses a name instead (#221); diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 2d1a726..e990691 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,11 +1,67 @@ PODS: - - BSImagePicker (3.1.3) + - CropViewController (2.5.2) + - DKCamera (1.6.7) + - DKImagePickerController (4.2.2): + - DKImagePickerController/Camera (= 4.2.2) + - DKImagePickerController/Core (= 4.2.2) + - DKImagePickerController/ImageDataManager (= 4.2.2) + - DKImagePickerController/InlineCamera (= 4.2.2) + - DKImagePickerController/PhotoEditor (= 4.2.2) + - DKImagePickerController/PhotoGallery (= 4.2.2) + - DKImagePickerController/Resource (= 4.2.2) + - DKImagePickerController/Camera (4.2.2): + - DKCamera + - DKImagePickerController/Core + - DKImagePickerController/Core (4.2.2): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.2.2) + - DKImagePickerController/InlineCamera (4.2.2): + - DKCamera + - DKImagePickerController/Core + - DKImagePickerController/PhotoEditor (4.2.2): + - CropViewController (~> 2.5) + - DKImagePickerController/Core + - DKImagePickerController/PhotoGallery (4.2.2): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.2.2) + - DKPhotoGallery (0.0.14): + - DKPhotoGallery/Core (= 0.0.14) + - DKPhotoGallery/Model (= 0.0.14) + - DKPhotoGallery/Preview (= 0.0.14) + - DKPhotoGallery/Resource (= 0.0.14) + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Core (0.0.14): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Model (0.0.14): + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Preview (0.0.14): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SDWebImageFLPlugin + - DKPhotoGallery/Resource (0.0.14): + - SDWebImage + - SDWebImageFLPlugin - file_picker (0.0.1): - - BSImagePicker (~> 3.1.3) + - DKImagePickerController - Flutter + - FLAnimatedImage (1.0.12) - Flutter (1.0.0) - flutter_plugin_android_lifecycle (0.0.1): - Flutter + - SDWebImage (5.7.3): + - SDWebImage/Core (= 5.7.3) + - SDWebImage/Core (5.7.3) + - SDWebImageFLPlugin (0.4.0): + - FLAnimatedImage (>= 1.0.11) + - SDWebImage/Core (~> 5.6) DEPENDENCIES: - file_picker (from `.symlinks/plugins/file_picker/ios`) @@ -14,7 +70,13 @@ DEPENDENCIES: SPEC REPOS: trunk: - - BSImagePicker + - CropViewController + - DKCamera + - DKImagePickerController + - DKPhotoGallery + - FLAnimatedImage + - SDWebImage + - SDWebImageFLPlugin EXTERNAL SOURCES: file_picker: @@ -25,10 +87,16 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios" SPEC CHECKSUMS: - BSImagePicker: 5798cfaadb386866b10d60bd1133065dc52cf273 - file_picker: 696824dae531ce6ed877fd8c1d145f006387c9fb + CropViewController: 1aac40093c5739e60d992b04724a55d12a689a4c + DKCamera: a902b66921fca14b7a75266feb8c7568aa7caa71 + DKImagePickerController: 4a3e7948a848c4348e600b3fe5ce41478835fa10 + DKPhotoGallery: 0290d32343574f06eaa4c26f8f2f8a1035e916be + file_picker: f9134d4ef376b10e950694e6b96e81dafea9ceda + FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31 Flutter: 0e3d915762c693b495b44d77113d4970485de6ec flutter_plugin_android_lifecycle: 47de533a02850f070f5696a623995e93eddcdb9b + SDWebImage: 97351f6582ceca541ea294ba66a1fcb342a331c2 + SDWebImageFLPlugin: 6c2295fb1242d44467c6c87dc5db6b0a13228fd8 PODFILE CHECKSUM: 27341c1ab2a92ab24c1e60a397ab924b94c94ff5 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 085575b..879e3e3 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -269,15 +269,27 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/BSImagePicker/BSImagePicker.framework", + "${BUILT_PRODUCTS_DIR}/CropViewController/CropViewController.framework", + "${BUILT_PRODUCTS_DIR}/DKCamera/DKCamera.framework", + "${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework", + "${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework", + "${BUILT_PRODUCTS_DIR}/FLAnimatedImage/FLAnimatedImage.framework", "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", + "${BUILT_PRODUCTS_DIR}/SDWebImageFLPlugin/SDWebImageFLPlugin.framework", "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework", "${BUILT_PRODUCTS_DIR}/flutter_plugin_android_lifecycle/flutter_plugin_android_lifecycle.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BSImagePicker.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CropViewController.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKCamera.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FLAnimatedImage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageFLPlugin.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_plugin_android_lifecycle.framework", ); @@ -369,7 +381,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -419,7 +431,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/Classes/FilePickerPlugin.m b/ios/Classes/FilePickerPlugin.m index 654f2e9..81fe01d 100644 --- a/ios/Classes/FilePickerPlugin.m +++ b/ios/Classes/FilePickerPlugin.m @@ -2,9 +2,9 @@ #import "FileUtils.h" #import "ImageUtils.h" -@import BSImagePicker; +@import DKImagePickerController; -@interface FilePickerPlugin() +@interface FilePickerPlugin() @property (nonatomic) FlutterResult result; @property (nonatomic) UIViewController *viewController; @property (nonatomic) UIImagePickerController *galleryPickerController; @@ -49,7 +49,7 @@ _result = result; NSDictionary * arguments = call.arguments; BOOL isMultiplePick = ((NSNumber*)[arguments valueForKey:@"allowMultipleSelection"]).boolValue; - if((isMultiplePick && ![call.method isEqualToString:@"IMAGE"]) || [call.method isEqualToString:@"ANY"] || [call.method containsString:@"CUSTOM"]) { + if([call.method isEqualToString:@"ANY"] || [call.method containsString:@"CUSTOM"]) { self.allowedExtensions = [FileUtils resolveType:call.method withAllowedExtensions: [arguments valueForKey:@"allowedExtensions"]]; if(self.allowedExtensions == nil) { _result([FlutterError errorWithCode:@"Unsupported file extension" @@ -60,7 +60,7 @@ [self resolvePickDocumentWithMultipleSelection:isMultiplePick]; } } else if([call.method isEqualToString:@"VIDEO"]) { - [self resolvePickVideo]; + [self resolvePickVideo:isMultiplePick]; } else if([call.method isEqualToString:@"AUDIO"]) { [self resolvePickAudio]; } else if([call.method isEqualToString:@"IMAGE"]) { @@ -102,81 +102,88 @@ - (void) resolvePickImage:(BOOL)withMultiPick { - if(!withMultiPick) { - self.galleryPickerController = [[UIImagePickerController alloc] init]; - self.galleryPickerController.delegate = self; - self.galleryPickerController.modalPresentationStyle = UIModalPresentationCurrentContext; - self.galleryPickerController.mediaTypes = @[(NSString *)kUTTypeImage]; - self.galleryPickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; - - [_viewController presentViewController:self.galleryPickerController animated:YES completion:nil]; - } else { - ImagePickerController * multiImagePickerController = [[ImagePickerController alloc] initWithSelectedAssets:@[]]; - - UIProgressView * progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0.0, 0.0, multiImagePickerController.view.bounds.size.width, 10.0)]; - CGAffineTransform transform = CGAffineTransformMakeScale(1.0f, 4.0f); - progressView.transform = transform; - - [_viewController presentImagePicker:multiImagePickerController - animated:YES - select:^(PHAsset * _Nonnull selectedAsset) {} - deselect:^(PHAsset * _Nonnull deselectedAsset) {} - cancel:^(NSArray * _Nonnull canceledAsset) { - self->_result(nil); - self->_result = nil; - } - finish:^(NSArray * _Nonnull assets) { - int totalRemoteAssets = [FileUtils countRemoteAssets:assets]; - NSMutableArray *paths = [[NSMutableArray alloc] init]; - NSMutableDictionary * progresses = [[NSMutableDictionary alloc] initWithCapacity: totalRemoteAssets]; - - if(totalRemoteAssets > 0) { - [multiImagePickerController.view addSubview:progressView]; - } - - if(assets.count > 0) { - dispatch_semaphore_t completer = dispatch_semaphore_create(0); - __block int processedAssets = 0; - - for(PHAsset* asset in assets){ - PHContentEditingInputRequestOptions * options = [[PHContentEditingInputRequestOptions alloc] init]; - options.networkAccessAllowed = YES; - - if(![FileUtils isLocalAsset:asset]){ - options.progressHandler = ^(double progress, BOOL * _Nonnull stop) { - progresses[asset.localIdentifier] = [NSNumber numberWithFloat:progress]; - @synchronized(progresses){ - dispatch_async(dispatch_get_main_queue(), ^{ - progressView.progress = [[[progresses allValues] valueForKeyPath:@"@sum.self"] floatValue] / totalRemoteAssets; - }); - }; - }; - } - - [asset requestContentEditingInputWithOptions:options completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) { - NSURL *imageURL = contentEditingInput.fullSizeImageURL; - [paths addObject:imageURL.path]; - - if(++processedAssets == assets.count) { - dispatch_semaphore_signal(completer); - } - }]; - } - - if (![NSThread isMainThread]) { - dispatch_semaphore_wait(completer, DISPATCH_TIME_FOREVER); - } else { - while (dispatch_semaphore_wait(completer, DISPATCH_TIME_NOW)) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]]; - } - } - } - - self->_result(paths); - self->_result = nil; - - } completion:^{}]; + if(withMultiPick){ + [self resolveMultiPickFromGallery:NO]; + return; } + + self.galleryPickerController = [[UIImagePickerController alloc] init]; + self.galleryPickerController.delegate = self; + self.galleryPickerController.modalPresentationStyle = UIModalPresentationCurrentContext; + self.galleryPickerController.mediaTypes = @[(NSString *)kUTTypeImage]; + self.galleryPickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; + + [_viewController presentViewController:self.galleryPickerController animated:YES completion:nil]; + +} + +- (void) resolvePickVideo:(BOOL)withMultiPick { + + if(withMultiPick) { + [self resolveMultiPickFromGallery:YES]; + return; + } + + self.galleryPickerController = [[UIImagePickerController alloc] init]; + self.galleryPickerController.delegate = self; + self.galleryPickerController.modalPresentationStyle = UIModalPresentationCurrentContext; + self.galleryPickerController.mediaTypes = @[(NSString*)kUTTypeMovie, (NSString*)kUTTypeAVIMovie, (NSString*)kUTTypeVideo, (NSString*)kUTTypeMPEG4]; + self.galleryPickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; + + [self.viewController presentViewController:self.galleryPickerController animated:YES completion:nil]; +} + +- (void) resolveMultiPickFromGallery:(BOOL)withVideo { + DKImagePickerController * dkImagePickerController = [[DKImagePickerController alloc] init]; + + // Create alert dialog for asset caching + UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"" message:@"" preferredStyle:UIAlertControllerStyleAlert]; + [alert.view setCenter: _viewController.view.center]; + [alert.view addConstraint: [NSLayoutConstraint constraintWithItem:alert.view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:100]]; + + UIActivityIndicatorView* indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; + indicator.hidesWhenStopped = YES; + [indicator setCenter: alert.view.center]; + indicator.autoresizingMask = (UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin); + [alert.view addSubview: indicator]; + + dkImagePickerController.exportsWhenCompleted = YES; + dkImagePickerController.showsCancelButton = YES; + dkImagePickerController.sourceType = DKImagePickerControllerSourceTypePhoto; + dkImagePickerController.assetType = withVideo ? DKImagePickerControllerAssetTypeAllVideos : DKImagePickerControllerAssetTypeAllPhotos; + + // Export status changed + [dkImagePickerController setExportStatusChanged:^(enum DKImagePickerControllerExportStatus status) { + + if(status == DKImagePickerControllerExportStatusExporting && dkImagePickerController.selectedAssets.count > 0){ + Log("Exporting assets, this operation may take a while if remote (iCloud) assets are being cached."); + [indicator startAnimating]; + [self->_viewController showViewController:alert sender:nil]; + } else { + [indicator stopAnimating]; + [alert dismissViewControllerAnimated:YES completion:nil]; + } + }]; + + // Did cancel + [dkImagePickerController setDidCancel:^(){ + self->_result(nil); + self->_result = nil; + }]; + + // Did select + [dkImagePickerController setDidSelectAssets:^(NSArray * __nonnull DKAssets) { + NSMutableArray* paths = [[NSMutableArray alloc] init]; + + for(DKAsset * asset in DKAssets){ + [paths addObject:asset.localTemporaryPath.path]; + } + + self->_result([paths count] > 0 ? paths : nil); + self->_result = nil; + }]; + + [_viewController presentViewController:dkImagePickerController animated:YES completion:nil]; } - (void) resolvePickAudio { @@ -190,17 +197,6 @@ [self.viewController presentViewController:self.audioPickerController animated:YES completion:nil]; } -- (void) resolvePickVideo { - - self.galleryPickerController = [[UIImagePickerController alloc] init]; - self.galleryPickerController.delegate = self; - self.galleryPickerController.modalPresentationStyle = UIModalPresentationCurrentContext; - self.galleryPickerController.mediaTypes = @[(NSString*)kUTTypeMovie, (NSString*)kUTTypeAVIMovie, (NSString*)kUTTypeVideo, (NSString*)kUTTypeMPEG4]; - self.galleryPickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; - - [self.viewController presentViewController:self.galleryPickerController animated:YES completion:nil]; -} - #pragma mark - Delegates // DocumentPicker delegate - iOS 10 only @@ -317,4 +313,7 @@ didPickDocumentsAtURLs:(NSArray *)urls{ [picker dismissViewControllerAnimated:YES completion:NULL]; } +#pragma mark - Alert dialog + + @end diff --git a/ios/Classes/FileUtils.h b/ios/Classes/FileUtils.h index 07dea33..990a8d1 100644 --- a/ios/Classes/FileUtils.h +++ b/ios/Classes/FileUtils.h @@ -16,8 +16,6 @@ @interface FileUtils : NSObject + (NSArray*) resolveType:(NSString*)type withAllowedExtensions:(NSArray*)allowedExtensions; + (NSArray*) resolvePath:(NSArray *)urls; -+ (int) countRemoteAssets:(NSArray *)assets; -+ (BOOL) isLocalAsset:(PHAsset *) asset; @end diff --git a/ios/Classes/FileUtils.m b/ios/Classes/FileUtils.m index e8b5b13..03e32c0 100644 --- a/ios/Classes/FileUtils.m +++ b/ios/Classes/FileUtils.m @@ -57,20 +57,4 @@ return paths; } -+ (int)countRemoteAssets:(NSArray *)assets { - int total = 0; - for(PHAsset * asset in assets) { - NSArray *resourceArray = [PHAssetResource assetResourcesForAsset:asset]; - if(![[resourceArray.firstObject valueForKey:@"locallyAvailable"] boolValue]) { - total++; - } - } - return total; -} - -+ (BOOL)isLocalAsset:(PHAsset *) asset { - NSArray *resourceArray = [PHAssetResource assetResourcesForAsset:asset]; - return [[resourceArray.firstObject valueForKey:@"locallyAvailable"] boolValue]; -} - @end diff --git a/ios/file_picker.podspec b/ios/file_picker.podspec index cc8cfce..d1acda3 100644 --- a/ios/file_picker.podspec +++ b/ios/file_picker.podspec @@ -15,7 +15,7 @@ A new flutter plugin project. s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - s.dependency 'BSImagePicker', '~> 3.1.3' - s.ios.deployment_target = '10.0' + s.dependency 'DKImagePickerController' + s.ios.deployment_target = '8.0' end diff --git a/pubspec.yaml b/pubspec.yaml index 4175276..4e64e72 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: file_picker description: A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support. homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker -version: 1.7.0 +version: 1.7.1 dependencies: flutter: