From fc168b7272eb051dad1e26ac97d1666d1c799faa Mon Sep 17 00:00:00 2001 From: WenHaozhan <36674458+WenHaozhan@users.noreply.github.com> Date: Wed, 27 Nov 2024 00:16:58 -0500 Subject: [PATCH 1/3] fix: Removes use of PHAsset on IOS 14+, removed unused code and tests --- .../ios/RunnerTests/ImagePickerPluginTests.m | 27 +-------- .../ios/RunnerTests/PhotoAssetUtilTests.m | 12 ---- .../ImagePickerFromLimitedGalleryUITests.m | 21 ------- .../FLTImagePickerPhotoAssetUtil.m | 6 -- .../image_picker_ios/FLTImagePickerPlugin.m | 40 +------------ .../FLTPHPickerSaveImageToPathOperation.m | 60 +++---------------- .../FLTImagePickerPhotoAssetUtil.h | 2 - 7 files changed, 11 insertions(+), 157 deletions(-) diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index 3ade851e1ec9..eb6bcd29b84f 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -495,11 +495,11 @@ - (void)testSavesImages API_AVAILABLE(ios(14)) { [self waitForExpectationsWithTimeout:30 handler:nil]; } -- (void)testPickImageRequestAuthorization API_AVAILABLE(ios(14)) { +- (void)testPickImageDoesntRequestAuthorization API_AVAILABLE(ios(14)) { id mockPhotoLibrary = OCMClassMock([PHPhotoLibrary class]); OCMStub([mockPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite]) .andReturn(PHAuthorizationStatusNotDetermined); - OCMExpect([mockPhotoLibrary requestAuthorizationForAccessLevel:PHAccessLevelReadWrite + OCMReject([mockPhotoLibrary requestAuthorizationForAccessLevel:PHAccessLevelReadWrite handler:OCMOCK_ANY]); FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; @@ -514,29 +514,6 @@ - (void)testPickImageRequestAuthorization API_AVAILABLE(ios(14)) { OCMVerifyAll(mockPhotoLibrary); } -- (void)testPickImageAuthorizationDenied API_AVAILABLE(ios(14)) { - id mockPhotoLibrary = OCMClassMock([PHPhotoLibrary class]); - OCMStub([mockPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite]) - .andReturn(PHAuthorizationStatusDenied); - - FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; - - XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result"]; - - [plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeGallery - camera:FLTSourceCameraFront] - maxSize:[[FLTMaxSize alloc] init] - quality:nil - fullMetadata:YES - completion:^(NSString *result, FlutterError *error) { - XCTAssertNil(result); - XCTAssertEqualObjects(error.code, @"photo_access_denied"); - XCTAssertEqualObjects(error.message, @"The user did not allow photo access."); - [resultExpectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:30 handler:nil]; -} - - (void)testPickMultiImageDuplicateCallCancels API_AVAILABLE(ios(14)) { id mockPhotoLibrary = OCMClassMock([PHPhotoLibrary class]); OCMStub([mockPhotoLibrary authorizationStatusForAccessLevel:PHAccessLevelReadWrite]) diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PhotoAssetUtilTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PhotoAssetUtilTests.m index 45ad94a00e02..16aeb4f518b9 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PhotoAssetUtilTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PhotoAssetUtilTests.m @@ -20,18 +20,6 @@ - (void)getAssetFromImagePickerInfoShouldReturnNilIfNotAvailable { XCTAssertNil([FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:mockData]); } -- (void)testGetAssetFromPHPickerResultShouldReturnNilIfNotAvailable API_AVAILABLE(ios(14)) { - if (@available(iOS 14, *)) { - PHPickerResult *mockData; - [mockData.itemProvider - loadObjectOfClass:[UIImage class] - completionHandler:^(__kindof id _Nullable image, - NSError *_Nullable error) { - XCTAssertNil([FLTImagePickerPhotoAssetUtil getAssetFromPHPickerResult:mockData]); - }]; - } -} - - (void)testSaveImageWithOriginalImageData_ShouldSaveWithTheCorrectExtentionAndMetaData { // test jpg NSData *dataJPG = ImagePickerTestImages.JPGTestData; diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m index d65b16d70fb2..bc5f8add84f8 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m @@ -104,8 +104,6 @@ - (void)testSelectingFromGallery API_AVAILABLE(ios(14)) { } [pickButton tap]; - [self handlePermissionInterruption]; - // Find an image and tap on it. NSPredicate *imagePredicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH 'Photo, '"]; XCUIElementQuery *imageQuery = [self.app.images matchingPredicate:imagePredicate]; @@ -119,25 +117,6 @@ - (void)testSelectingFromGallery API_AVAILABLE(ios(14)) { [aImage tap]; - // Find and tap on the `Done` button. - XCUIElement *doneButton = self.app.buttons[@"Done"].firstMatch; - if (![doneButton waitForExistenceWithTimeout:kLimitedElementWaitingTime]) { - os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); - XCTSkip(@"Permissions popup could not fired so the test is skipped..."); - } - [doneButton tap]; - - // Find an image and tap on it to have access to selected photos. - aImage = imageQuery.firstMatch; - - os_log_error(OS_LOG_DEFAULT, "description before picking image %@", self.app.debugDescription); - if (![aImage waitForExistenceWithTimeout:kLimitedElementWaitingTime]) { - os_log_error(OS_LOG_DEFAULT, "%@", self.app.debugDescription); - XCTFail(@"Failed due to not able to find an image with %@ seconds", - @(kLimitedElementWaitingTime)); - } - [aImage tap]; - // Find the picked image. XCUIElement *pickedImage = self.app.images[@"image_picker_example_picked_image"].firstMatch; if (![pickedImage waitForExistenceWithTimeout:kLimitedElementWaitingTime]) { diff --git a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPhotoAssetUtil.m b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPhotoAssetUtil.m index 294bbc77947a..a4a3e5ac0fa8 100644 --- a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPhotoAssetUtil.m +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPhotoAssetUtil.m @@ -14,12 +14,6 @@ + (PHAsset *)getAssetFromImagePickerInfo:(NSDictionary *)info { return info[UIImagePickerControllerPHAsset]; } -+ (PHAsset *)getAssetFromPHPickerResult:(PHPickerResult *)result API_AVAILABLE(ios(14)) { - PHFetchResult *fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[ result.assetIdentifier ] - options:nil]; - return fetchResult.firstObject; -} - + (NSURL *)saveVideoFromURL:(NSURL *)videoURL { if (![[NSFileManager defaultManager] isReadableFileAtPath:[videoURL path]]) { return nil; diff --git a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m index f54db3b9fef7..fd88b8c46968 100644 --- a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m @@ -113,11 +113,7 @@ - (void)launchPHPickerWithContext:(nonnull FLTImagePickerMethodCallContext *)con pickerViewController.presentationController.delegate = self; self.callContext = context; - if (context.requestFullMetadata) { - [self checkPhotoAuthorizationWithPHPicker:pickerViewController]; - } else { - [self showPhotoLibraryWithPHPicker:pickerViewController]; - } + [self showPhotoLibraryWithPHPicker:pickerViewController]; } - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source @@ -390,40 +386,6 @@ - (void)checkPhotoAuthorizationWithImagePicker:(UIImagePickerController *)imageP } } -- (void)checkPhotoAuthorizationWithPHPicker:(PHPickerViewController *)pickerViewController - API_AVAILABLE(ios(14)) { - PHAccessLevel requestedAccessLevel = PHAccessLevelReadWrite; - PHAuthorizationStatus status = - [PHPhotoLibrary authorizationStatusForAccessLevel:requestedAccessLevel]; - switch (status) { - case PHAuthorizationStatusNotDetermined: { - [PHPhotoLibrary - requestAuthorizationForAccessLevel:requestedAccessLevel - handler:^(PHAuthorizationStatus status) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (status == PHAuthorizationStatusAuthorized) { - [self showPhotoLibraryWithPHPicker:pickerViewController]; - } else if (status == PHAuthorizationStatusLimited) { - [self showPhotoLibraryWithPHPicker:pickerViewController]; - } else { - [self errorNoPhotoAccess:status]; - } - }); - }]; - break; - } - case PHAuthorizationStatusAuthorized: - case PHAuthorizationStatusLimited: - [self showPhotoLibraryWithPHPicker:pickerViewController]; - break; - case PHAuthorizationStatusDenied: - case PHAuthorizationStatusRestricted: - default: - [self errorNoPhotoAccess:status]; - break; - } -} - - (void)errorNoCameraAccess:(AVAuthorizationStatus)status { switch (status) { case AVAuthorizationStatusRestricted: diff --git a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTPHPickerSaveImageToPathOperation.m b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTPHPickerSaveImageToPathOperation.m index bf3922ae7d3d..2eee7133a56d 100644 --- a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTPHPickerSaveImageToPathOperation.m +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTPHPickerSaveImageToPathOperation.m @@ -128,64 +128,20 @@ - (void)start { - (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. - if (self.requestFullMetadata) { - originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromPHPickerResult:self.result]; - } - if (self.maxWidth != nil || self.maxHeight != nil) { localImage = [FLTImagePickerImageUtil scaledImage:localImage maxWidth:self.maxWidth maxHeight:self.maxHeight isMetadataAvailable:YES]; } - if (originalAsset) { - void (^resultHandler)(NSData *imageData, NSString *dataUTI, NSDictionary *info) = - ^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, NSDictionary *_Nullable info) { - // maxWidth and maxHeight are used only for GIF images. - NSString *savedPath = [FLTImagePickerPhotoAssetUtil - saveImageWithOriginalImageData:imageData - image:localImage - maxWidth:self.maxWidth - maxHeight:self.maxHeight - imageQuality:self.desiredImageQuality]; - [self completeOperationWithPath:savedPath error:nil]; - }; - if (@available(iOS 13.0, *)) { - [[PHImageManager defaultManager] - requestImageDataAndOrientationForAsset:originalAsset - options:nil - resultHandler:^(NSData *_Nullable imageData, - NSString *_Nullable dataUTI, - CGImagePropertyOrientation orientation, - NSDictionary *_Nullable info) { - resultHandler(imageData, dataUTI, info); - }]; - } else { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [[PHImageManager defaultManager] - requestImageDataForAsset:originalAsset - options:nil - resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, - UIImageOrientation orientation, NSDictionary *_Nullable info) { - resultHandler(imageData, dataUTI, info); - }]; -#pragma clang diagnostic pop - } - } 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 - maxWidth:self.maxWidth - maxHeight:self.maxHeight - imageQuality:self.desiredImageQuality]; - [self completeOperationWithPath:savedPath error:nil]; - } + // maxWidth and maxHeight are used only for GIF images. + NSString *savedPath = + [FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:pickerImageData + image:localImage + maxWidth:self.maxWidth + maxHeight:self.maxHeight + imageQuality:self.desiredImageQuality]; + [self completeOperationWithPath:savedPath error:nil]; } /// Processes the video. diff --git a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/FLTImagePickerPhotoAssetUtil.h b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/FLTImagePickerPhotoAssetUtil.h index 212f09236b0f..fa175be78f95 100644 --- a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/FLTImagePickerPhotoAssetUtil.h +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/FLTImagePickerPhotoAssetUtil.h @@ -14,8 +14,6 @@ NS_ASSUME_NONNULL_BEGIN + (nullable PHAsset *)getAssetFromImagePickerInfo:(NSDictionary *)info; -+ (nullable PHAsset *)getAssetFromPHPickerResult:(PHPickerResult *)result API_AVAILABLE(ios(14)); - // Saves video to temporary URL. Returns nil on failure; + (NSURL *)saveVideoFromURL:(NSURL *)videoURL; From d58afb8e39313fd8bc3a600bc95f225a383be53e Mon Sep 17 00:00:00 2001 From: WenHaozhan <36674458+WenHaozhan@users.noreply.github.com> Date: Wed, 27 Nov 2024 00:27:35 -0500 Subject: [PATCH 2/3] Updated version and changelog --- packages/image_picker/image_picker_ios/CHANGELOG.md | 4 ++++ packages/image_picker/image_picker_ios/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index f31aa5c36662..5a5c692e9b2a 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.12+2 + +* Removes the need for user permissions to pick an image on iOS 14+. + ## 0.8.12+1 * Updates Pigeon for non-nullable collection type support. diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index a1f752f33bce..c246e76a1417 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/packages/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.12+1 +version: 0.8.12+2 environment: sdk: ^3.3.0 From 1e2622378e51ae3b7851cef2ee64610551b28a40 Mon Sep 17 00:00:00 2001 From: WenHaozhan <36674458+WenHaozhan@users.noreply.github.com> Date: Wed, 27 Nov 2024 09:30:38 -0500 Subject: [PATCH 3/3] fix formatting --- .../image_picker_ios/FLTPHPickerSaveImageToPathOperation.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTPHPickerSaveImageToPathOperation.m b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTPHPickerSaveImageToPathOperation.m index 2eee7133a56d..c1befa3f61f8 100644 --- a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTPHPickerSaveImageToPathOperation.m +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTPHPickerSaveImageToPathOperation.m @@ -137,9 +137,9 @@ - (void)processImage:(NSData *)pickerImageData API_AVAILABLE(ios(14)) { // maxWidth and maxHeight are used only for GIF images. NSString *savedPath = [FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:pickerImageData - image:localImage + image:localImage maxWidth:self.maxWidth - maxHeight:self.maxHeight + maxHeight:self.maxHeight imageQuality:self.desiredImageQuality]; [self completeOperationWithPath:savedPath error:nil]; }