From ecf6c45f5b49ae0ac2396207b95df2e911020585 Mon Sep 17 00:00:00 2001 From: Victoria Ashworth Date: Thu, 2 Feb 2023 10:17:28 -0600 Subject: [PATCH 1/2] Fix GIF files not animating if permission not given. Fix PNG files getting convert to JPG if permission not given --- .../PickerSaveImageToPathOperationTests.m | 60 +++++++++++++++---- .../FLTPHPickerSaveImageToPathOperation.m | 15 +++-- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m index 091755ca163b..027e287d1586 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m @@ -21,7 +21,7 @@ - (void)testSaveWebPImage API_AVAILABLE(ios(14)) { NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; - [self verifySavingImageWithPickerResult:result fullMetadata:YES]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES withExtension:@"jpg"]; } - (void)testSavePNGImage API_AVAILABLE(ios(14)) { @@ -30,7 +30,7 @@ - (void)testSavePNGImage API_AVAILABLE(ios(14)) { NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; - [self verifySavingImageWithPickerResult:result fullMetadata:YES]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES withExtension:@"png"]; } - (void)testSaveJPGImage API_AVAILABLE(ios(14)) { @@ -39,7 +39,7 @@ - (void)testSaveJPGImage API_AVAILABLE(ios(14)) { NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; - [self verifySavingImageWithPickerResult:result fullMetadata:YES]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES withExtension:@"jpg"]; } - (void)testSaveGIFImage API_AVAILABLE(ios(14)) { @@ -48,7 +48,39 @@ - (void)testSaveGIFImage API_AVAILABLE(ios(14)) { NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; - [self verifySavingImageWithPickerResult:result fullMetadata:YES]; + NSData *dataGIF = [NSData dataWithContentsOfURL:imageURL]; + CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)dataGIF, nil); + size_t numberOfFrames = CGImageSourceGetCount(imageSource); + + XCTestExpectation *pathExpectation = [self expectationWithDescription:@"Path was created"]; + XCTestExpectation *operationExpectation = + [self expectationWithDescription:@"Operation completed"]; + + FLTPHPickerSaveImageToPathOperation *operation = [[FLTPHPickerSaveImageToPathOperation alloc] + initWithResult:result + maxHeight:@100 + maxWidth:@100 + desiredImageQuality:@100 + fullMetadata:NO + savedPathBlock:^(NSString *savedPath, FlutterError *error) { + XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:savedPath]); + + // Ensure gif is animated. + XCTAssertEqualObjects([NSURL URLWithString:savedPath].pathExtension, @"gif"); + NSData *newDataGIF = [NSData dataWithContentsOfFile:savedPath]; + CGImageSourceRef newImageSource = + CGImageSourceCreateWithData((__bridge CFDataRef)newDataGIF, nil); + size_t newNumberOfFrames = CGImageSourceGetCount(newImageSource); + XCTAssertEqual(numberOfFrames, newNumberOfFrames); + [pathExpectation fulfill]; + }]; + operation.completionBlock = ^{ + [operationExpectation fulfill]; + }; + + [operation start]; + [self waitForExpectationsWithTimeout:30 handler:nil]; + XCTAssertTrue(operation.isFinished); } - (void)testSaveBMPImage API_AVAILABLE(ios(14)) { @@ -57,7 +89,7 @@ - (void)testSaveBMPImage API_AVAILABLE(ios(14)) { NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; - [self verifySavingImageWithPickerResult:result fullMetadata:YES]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES withExtension:@"jpg"]; } - (void)testSaveHEICImage API_AVAILABLE(ios(14)) { @@ -66,7 +98,7 @@ - (void)testSaveHEICImage API_AVAILABLE(ios(14)) { NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; - [self verifySavingImageWithPickerResult:result fullMetadata:YES]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES withExtension:@"jpg"]; } - (void)testSaveICNSImage API_AVAILABLE(ios(14)) { @@ -75,7 +107,7 @@ - (void)testSaveICNSImage API_AVAILABLE(ios(14)) { NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; - [self verifySavingImageWithPickerResult:result fullMetadata:YES]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES withExtension:@"jpg"]; } - (void)testSaveICOImage API_AVAILABLE(ios(14)) { @@ -84,7 +116,7 @@ - (void)testSaveICOImage API_AVAILABLE(ios(14)) { NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; - [self verifySavingImageWithPickerResult:result fullMetadata:YES]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES withExtension:@"jpg"]; } - (void)testSaveProRAWImage API_AVAILABLE(ios(14)) { @@ -93,7 +125,7 @@ - (void)testSaveProRAWImage API_AVAILABLE(ios(14)) { NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; - [self verifySavingImageWithPickerResult:result fullMetadata:YES]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES withExtension:@"jpg"]; } - (void)testSaveSVGImage API_AVAILABLE(ios(14)) { @@ -102,7 +134,7 @@ - (void)testSaveSVGImage API_AVAILABLE(ios(14)) { NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; - [self verifySavingImageWithPickerResult:result fullMetadata:YES]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES withExtension:@"jpg"]; } - (void)testSaveTIFFImage API_AVAILABLE(ios(14)) { @@ -110,7 +142,7 @@ - (void)testSaveTIFFImage API_AVAILABLE(ios(14)) { withExtension:@"tiff"]; NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; - [self verifySavingImageWithPickerResult:result fullMetadata:YES]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES withExtension:@"jpg"]; } - (void)testNonexistentImage API_AVAILABLE(ios(14)) { @@ -176,7 +208,7 @@ - (void)testSavePNGImageWithoutFullMetadata API_AVAILABLE(ios(14)) { PHPickerResult *result = [self createPickerResultWithProvider:itemProvider]; OCMReject([photoAssetUtil fetchAssetsWithLocalIdentifiers:OCMOCK_ANY options:OCMOCK_ANY]); - [self verifySavingImageWithPickerResult:result fullMetadata:NO]; + [self verifySavingImageWithPickerResult:result fullMetadata:NO withExtension:@"png"]; OCMVerifyAll(photoAssetUtil); } @@ -204,7 +236,8 @@ - (PHPickerResult *)createPickerResultWithProvider:(NSItemProvider *)itemProvide * @param result the picker result */ - (void)verifySavingImageWithPickerResult:(PHPickerResult *)result - fullMetadata:(BOOL)fullMetadata API_AVAILABLE(ios(14)) { + fullMetadata:(BOOL)fullMetadata + withExtension:(NSString *)extension API_AVAILABLE(ios(14)) { XCTestExpectation *pathExpectation = [self expectationWithDescription:@"Path was created"]; XCTestExpectation *operationExpectation = [self expectationWithDescription:@"Operation completed"]; @@ -217,6 +250,7 @@ - (void)verifySavingImageWithPickerResult:(PHPickerResult *)result fullMetadata:fullMetadata savedPathBlock:^(NSString *savedPath, FlutterError *error) { XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:savedPath]); + XCTAssertEqualObjects([NSURL URLWithString:savedPath].pathExtension, extension); [pathExpectation fulfill]; }]; operation.completionBlock = ^{ diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m index 11fedfb73846..0d45714b17d3 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m @@ -98,8 +98,7 @@ - (void)start { completionHandler:^(NSData *_Nullable data, NSError *_Nullable error) { if (data != nil) { - UIImage *image = [[UIImage alloc] initWithData:data]; - [self processImage:image]; + [self processImage:data]; } else { FlutterError *flutterError = [FlutterError errorWithCode:@"invalid_image" @@ -122,7 +121,9 @@ - (void)start { /** * Processes the image. */ -- (void)processImage:(UIImage *)localImage API_AVAILABLE(ios(14)) { +- (void)processImage:(NSData *)pickerImageData API_AVAILABLE(ios(14)) { + UIImage *localImage = [[UIImage alloc] initWithData:pickerImageData]; + PHAsset *originalAsset; // Only if requested, fetch the full "PHAsset" metadata, which requires "Photo Library Usage" // permissions. @@ -173,9 +174,11 @@ - (void)processImage:(UIImage *)localImage API_AVAILABLE(ios(14)) { } else { // Image picked without an original asset (e.g. User pick image without permission) NSString *savedPath = - [FLTImagePickerPhotoAssetUtil saveImageWithPickerInfo:nil - image:localImage - imageQuality:self.desiredImageQuality]; + [FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:pickerImageData + image:localImage + maxWidth:self.maxWidth + maxHeight:self.maxHeight + imageQuality:self.desiredImageQuality]; [self completeOperationWithPath:savedPath error:nil]; } } From 119f17eea21fc52d0e3c5e4f05aa330b31afeaf7 Mon Sep 17 00:00:00 2001 From: Victoria Ashworth Date: Thu, 2 Feb 2023 10:57:41 -0600 Subject: [PATCH 2/2] updated changelog and pubspec, added a comment --- packages/image_picker/image_picker_ios/CHANGELOG.md | 3 ++- .../ios/Classes/FLTPHPickerSaveImageToPathOperation.m | 1 + packages/image_picker/image_picker_ios/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 04bb4dfdf890..b75e6333b94e 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.6+7 +* Fixes issue where GIF file would not animate without `Photo Library Usage` permissions. Fixes issue where PNG and GIF files were converted to JPG, but only when they are do not have `Photo Library Usage` permissions. * Updates minimum Flutter version to 3.0. ## 0.8.6+6 diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m index 0d45714b17d3..efcbdbeec897 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m @@ -173,6 +173,7 @@ - (void)processImage:(NSData *)pickerImageData API_AVAILABLE(ios(14)) { } } else { // Image picked without an original asset (e.g. User pick image without permission) + // maxWidth and maxHeight are used only for GIF images. NSString *savedPath = [FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:pickerImageData image:localImage diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 5bfb8852d2cc..1f4e2af4cb96 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the image_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.6+6 +version: 0.8.6+7 environment: sdk: ">=2.14.0 <3.0.0"