FLTPHPickerSaveImageToPathOperation.m
9.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Flutter/Flutter.h>
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
#import "FLTPHPickerSaveImageToPathOperation.h"
#import <os/log.h>
API_AVAILABLE(ios(14))
@interface FLTPHPickerSaveImageToPathOperation ()
@property(strong, nonatomic) PHPickerResult *result;
@property(strong, nonatomic) NSNumber *maxHeight;
@property(strong, nonatomic) NSNumber *maxWidth;
@property(strong, nonatomic) NSNumber *desiredImageQuality;
@property(assign, nonatomic) BOOL requestFullMetadata;
@end
@implementation FLTPHPickerSaveImageToPathOperation {
BOOL executing;
BOOL finished;
FLTGetSavedPath getSavedPath;
}
- (instancetype)initWithResult:(PHPickerResult *)result
maxHeight:(NSNumber *)maxHeight
maxWidth:(NSNumber *)maxWidth
desiredImageQuality:(NSNumber *)desiredImageQuality
fullMetadata:(BOOL)fullMetadata
savedPathBlock:(FLTGetSavedPath)savedPathBlock API_AVAILABLE(ios(14)) {
if (self = [super init]) {
if (result) {
self.result = result;
self.maxHeight = maxHeight;
self.maxWidth = maxWidth;
self.desiredImageQuality = desiredImageQuality;
self.requestFullMetadata = fullMetadata;
getSavedPath = savedPathBlock;
executing = NO;
finished = NO;
} else {
return nil;
}
return self;
} else {
return nil;
}
}
- (BOOL)isConcurrent {
return YES;
}
- (BOOL)isExecuting {
return executing;
}
- (BOOL)isFinished {
return finished;
}
- (void)setFinished:(BOOL)isFinished {
[self willChangeValueForKey:@"isFinished"];
self->finished = isFinished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)isExecuting {
[self willChangeValueForKey:@"isExecuting"];
self->executing = isExecuting;
[self didChangeValueForKey:@"isExecuting"];
}
- (void)completeOperationWithPath:(NSString *)savedPath error:(FlutterError *)error {
getSavedPath(savedPath, error);
[self setExecuting:NO];
[self setFinished:YES];
}
- (void)start {
if ([self isCancelled]) {
[self setFinished:YES];
return;
}
if (@available(iOS 14, *)) {
[self setExecuting:YES];
// This supports uniform types that conform to UTTypeImage.
// This includes UTTypeHEIC, UTTypeHEIF, UTTypeLivePhoto, UTTypeICO, UTTypeICNS, UTTypePNG
// UTTypeGIF, UTTypeJPEG, UTTypeWebP, UTTypeTIFF, UTTypeBMP, UTTypeSVG, UTTypeRAWImage
if ([self.result.itemProvider hasItemConformingToTypeIdentifier:UTTypeImage.identifier]) {
[self.result.itemProvider
loadDataRepresentationForTypeIdentifier:UTTypeImage.identifier
completionHandler:^(NSData *_Nullable data,
NSError *_Nullable error) {
if (data != nil) {
[self processImage:data];
} else {
FlutterError *flutterError =
[FlutterError errorWithCode:@"invalid_image"
message:error.localizedDescription
details:error.domain];
[self completeOperationWithPath:nil error:flutterError];
}
}];
} else if ([self.result.itemProvider
// This supports uniform types that conform to UTTypeMovie.
// This includes kUTTypeVideo, kUTTypeMPEG4, public.3gpp, kUTTypeMPEG,
// public.3gpp2, public.avi, kUTTypeQuickTimeMovie.
hasItemConformingToTypeIdentifier:UTTypeMovie.identifier]) {
[self processVideo];
} else {
FlutterError *flutterError = [FlutterError errorWithCode:@"invalid_source"
message:@"Invalid media source."
details:nil];
[self completeOperationWithPath:nil error:flutterError];
}
} else {
[self setFinished:YES];
}
}
/**
* Processes the image.
*/
- (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];
}
}
/**
* Processes the video.
*/
- (void)processVideo API_AVAILABLE(ios(14)) {
NSString *typeIdentifier = self.result.itemProvider.registeredTypeIdentifiers.firstObject;
[self.result.itemProvider
loadFileRepresentationForTypeIdentifier:typeIdentifier
completionHandler:^(NSURL *_Nullable videoURL,
NSError *_Nullable error) {
if (error != nil) {
FlutterError *flutterError =
[FlutterError errorWithCode:@"invalid_image"
message:error.localizedDescription
details:error.domain];
[self completeOperationWithPath:nil error:flutterError];
return;
}
NSURL *destination =
[FLTImagePickerPhotoAssetUtil saveVideoFromURL:videoURL];
if (destination == nil) {
[self
completeOperationWithPath:nil
error:[FlutterError
errorWithCode:
@"flutter_image_picker_copy_"
@"video_error"
message:@"Could not cache "
@"the video file."
details:nil]];
return;
}
[self completeOperationWithPath:[destination path] error:nil];
}];
}
@end