Replaces BSImagePicker with DKImagePickerController for multi picks of Photos app on iOS (#233)

This commit is contained in:
Miguel Ruivo 2020-04-26 21:47:49 +01:00
parent f79c0a3281
commit 6afd2aa1a8
8 changed files with 185 additions and 120 deletions

View File

@ -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);

View File

@ -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

View File

@ -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";

View File

@ -2,9 +2,9 @@
#import "FileUtils.h"
#import "ImageUtils.h"
@import BSImagePicker;
@import DKImagePickerController;
@interface FilePickerPlugin() <UIImagePickerControllerDelegate, MPMediaPickerControllerDelegate>
@interface FilePickerPlugin() <UIImagePickerControllerDelegate, MPMediaPickerControllerDelegate, DKImageAssetExporterObserver>
@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<PHAsset *> * _Nonnull canceledAsset) {
self->_result(nil);
self->_result = nil;
}
finish:^(NSArray<PHAsset *> * _Nonnull assets) {
int totalRemoteAssets = [FileUtils countRemoteAssets:assets];
NSMutableArray<NSString*> *paths = [[NSMutableArray<NSString*> alloc] init];
NSMutableDictionary<NSString*, NSNumber*> * progresses = [[NSMutableDictionary<NSString*, NSNumber*> 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<DKAsset*> * __nonnull DKAssets) {
NSMutableArray<NSString*>* paths = [[NSMutableArray<NSString*> 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<NSURL *> *)urls{
[picker dismissViewControllerAnimated:YES completion:NULL];
}
#pragma mark - Alert dialog
@end

View File

@ -16,8 +16,6 @@
@interface FileUtils : NSObject
+ (NSArray<NSString*>*) resolveType:(NSString*)type withAllowedExtensions:(NSArray<NSString*>*)allowedExtensions;
+ (NSArray*) resolvePath:(NSArray<NSURL *> *)urls;
+ (int) countRemoteAssets:(NSArray<PHAsset*> *)assets;
+ (BOOL) isLocalAsset:(PHAsset *) asset;
@end

View File

@ -57,20 +57,4 @@
return paths;
}
+ (int)countRemoteAssets:(NSArray<PHAsset*> *)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

View File

@ -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

View File

@ -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: