// // ContainerViewController.m // PhotoShareExtension // // Created by Russell on 27/5/15. // // #import "ContainerViewController.h" #import "ImageCaptionViewController.h" #import @interface ContainerViewController () @property (assign) ImageCaptionViewController *svc; - (BOOL)tokenExists; - (void)handleUploadError:(NSError *)error; @end @implementation ContainerViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [self performSegueWithIdentifier:@"InitialSegue" sender:nil]; self.api = [[NixAPI alloc] init]; self.uploadHandler = [[NixUpload alloc] initWithAPI:self.api]; self.uploadHandler.delegate = self; self.uploadHandler.uploadSessionIdentifier = @"com.creedon.Nixplay.uploadsession.appext"; [self.uploadHandler createSession]; // rejoin the session [self.uploadHandler loadUploadState]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. self.svc = ((UINavigationController *)segue.destinationViewController) .childViewControllers[0]; self.svc.containerViewController = self; } #pragma mark - Actions - (void)verifyServiceAvailable { [self verifyServiceAvailableWithCompletionHandler:nil]; } - (void)verifyServiceAvailableWithCompletionHandler:(void (^)(BOOL))completionHandler { [self.uploadHandler getCurrentTasks:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { NSLog(@"uploadTasks: %@", uploadTasks); }]; if (!self.api.apiURL.length) { [self showAlertViewWithTitle:NSLocalizedString(@"app_never_launched_title", nil) message:NSLocalizedString(@"app_never_launched_message", nil) actionWithTitle:NSLocalizedString(@"close", nil) handler:^(UIAlertAction *action) { [self cancel]; }]; completionHandler(NO); return; } if (![self tokenExists]) { [self showAlertViewWithTitle:NSLocalizedString(@"no_account_title", nil) message:NSLocalizedString(@"no_account_message", nil) actionWithTitle:NSLocalizedString(@"close", nil) handler:^(UIAlertAction *action) { [self cancel]; }]; completionHandler(NO); } else { [self pingWithCompletionHandler:^(NSError *error) { if (error) { if (error.code == 401) { [self showAlertViewWithTitle:NSLocalizedString(@"signin_again_title", nil) message:NSLocalizedString(@"signin_again_message", nil) actionWithTitle:NSLocalizedString(@"close", nil) handler:^(UIAlertAction *action) { [self cancel]; }]; } else { [self showAlertViewWithTitle:NSLocalizedString(@"server_connection_error_title", nil) message:NSLocalizedString(@"server_connection_error_message", nil) actionWithTitle:NSLocalizedString(@"close", nil) handler:^(UIAlertAction *action) { [self cancel]; }]; } completionHandler(NO); } else { completionHandler(YES); } }]; } } - (void)pingWithCompletionHandler:(void (^)(NSError *))completionHandler { [self.api ping:^(id data, NSURLResponse *response, NSError *error) { NSLog(@"pingError: %@", error); completionHandler(error); }]; } - (void)send { NSMutableIndexSet *imageErrorIndexes = [NSMutableIndexSet indexSet]; for (NSUInteger i = 0; i < self.imageErrors.count; i++) { NSError *error = self.imageErrors[i]; if ([error isKindOfClass:[NSError class]]) { [imageErrorIndexes addIndex:i]; } } NSMutableArray *mutableImageUrls = [NSMutableArray arrayWithArray:self.imageUrls]; NSMutableArray *mutableCaptions = [NSMutableArray arrayWithArray:self.captions]; [mutableImageUrls removeObjectsAtIndexes:imageErrorIndexes]; [mutableCaptions removeObjectsAtIndexes:imageErrorIndexes]; if (mutableImageUrls.count == 0) { NSError *error = [NSError errorWithDomain:[[NSBundle mainBundle] bundleIdentifier] code:0 userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(@"upload_error_no_files_selected", nil) }]; [self handleUploadError:error]; return; } //process image before upload //if user choose optimize photo size if( [self isOptimizeResolution ]){ //optimize photo size dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ SDImageCache *imageCache = [SDImageCache sharedImageCache]; //store images to /tmp NSString *defaultCachePath = NSTemporaryDirectory(); //nix opt. size float optimizedDimension = 1820.0f; NSNumber *maxWidth = [NSNumber numberWithFloat:optimizedDimension]; for (NSUInteger i = 0; i < mutableImageUrls.count; i++) { @autoreleasepool { NSURL *imageUrl = mutableImageUrls[i]; CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)imageUrl, nil); // load the image at the desired size NSDictionary* options = @{ (id)kCGImageSourceShouldAllowFloat: (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform: (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageAlways: (id)kCFBooleanTrue, (id)kCGImageSourceThumbnailMaxPixelSize: maxWidth }; CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options); //80% jpg quality NSData * data = UIImageJPEGRepresentation([UIImage imageWithCGImage:imageRef], 0.8f); //retrieve cache path from Table view SDImageCache NSURL* cachePathURL = [[NSURL alloc] initWithString:[imageCache cachePathForKey:[imageUrl absoluteString]]] ; NSURL* newPathURL = [[NSURL alloc] initWithString: [NSString stringWithFormat:@"%@/%@.JPG",defaultCachePath, [cachePathURL lastPathComponent]]]; NSDictionary *metaoptions = @{(NSString *)kCGImageSourceShouldCache : [NSNumber numberWithBool:NO]}; CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)metaoptions); if (imageProperties) { NSDictionary *metadata = (__bridge NSDictionary *)imageProperties; NSMutableDictionary *mutableMetadata = [NSMutableDictionary dictionaryWithDictionary:metadata]; [mutableMetadata setValue:[NSNumber numberWithInt:1] forKey:@"Orientation"]; [self writeData: [self writeMetadataIntoImageData:data metadata:mutableMetadata ] toPath: [newPathURL absoluteString]]; } else{ //wirte to file [self writeData: data toPath: [newPathURL absoluteString]]; } //replace imageURLs object [mutableImageUrls replaceObjectAtIndex:i withObject:[newPathURL absoluteString] ]; } } [self.uploadHandler uploadWithImageUrls:[mutableImageUrls copy] captions:[mutableCaptions copy] playlists:self.playlistIds friends:self.friendUsernames camera:NO]; }); } else{ [mutableImageUrls enumerateObjectsUsingBlock:^(NSURL* url, NSUInteger idx, BOOL * _Nonnull stop) { [mutableImageUrls replaceObjectAtIndex:idx withObject:[url absoluteString]]; }]; [self.uploadHandler uploadWithImageUrls:[mutableImageUrls copy] captions:[mutableCaptions copy] playlists:self.playlistIds friends:self.friendUsernames camera:NO]; } } - (void)cancel { dispatch_async(dispatch_get_main_queue(), ^{ [self dismissViewControllerAnimated:YES completion:^{ NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was cancelled.", nil), NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"Operation was cancelled.", nil), NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Operation was cancelled.", nil) }; NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:-57 userInfo:userInfo]; [self.extensionContext cancelRequestWithError:error]; }]; }); } #pragma mark - Private - (BOOL)tokenExists { return [[self.api getAccessToken] length]; } - (void)handleUploadError:(NSError *)error { if (error) { UINavigationController *navigationController = (UINavigationController *)self.presentedViewController; ReceiverTableViewController *receiverTableVC = (ReceiverTableViewController *)navigationController.topViewController; if ([receiverTableVC isKindOfClass:[ReceiverTableViewController class]]) { [receiverTableVC hideActivityIndicator]; receiverTableVC.navigationItem.rightBarButtonItem.enabled = YES; } NSString *errorMessage = [error localizedDescription]; switch (error.code) { case kNixIoFileTooLargeError: errorMessage = NSLocalizedString(@"upload_error_file_too_large", nil); break; } [self showAlertViewWithTitle:NSLocalizedString(@"upload_error_title", nil) message:errorMessage actionWithTitle:NSLocalizedString(@"close", nil) handler:^(UIAlertAction *action) { [self cancel]; }]; } //delete cache image [self deleteCache]; } -(BOOL) isOptimizeResolution { NSUserDefaults *preferences = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.creedon.Nixplay"] ; NSString *optimizeResolutionKey = @"nixSettings.settings.resolution"; if ([preferences objectForKey:optimizeResolutionKey] == nil) { NSLog(@"%@ not exist",optimizeResolutionKey); return false; } else { // Get current level const Boolean optimizeResolution = [preferences boolForKey:optimizeResolutionKey]; NSLog(@"currentLevel %i",optimizeResolution); return optimizeResolution; } return false; } - (BOOL)writeData:(NSData*) data toPath:(NSString *)path { NSError* errorWrite = nil; BOOL write = [data writeToFile:path atomically:YES]; if(errorWrite != NULL){ NSLog(@"Failed to write to '%@': %@", path , [errorWrite localizedDescription]); } return write; } -(void)deleteCache{ NSArray* tmpDirectory = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:NSTemporaryDirectory() error:NULL]; for (NSString *file in tmpDirectory) { [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), file] error:NULL]; } } //http://stackoverflow.com/questions/9006759/how-to-write-exif-metadata-to-an-image-not-the-camera-roll-just-a-uiimage-or-j -(NSData *)writeMetadataIntoImageData:(NSData *)imageData metadata:(NSMutableDictionary *)metadata { // create an imagesourceref CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL); // this is the type of image (e.g., public.jpeg) CFStringRef UTI = CGImageSourceGetType(source); // create a new data object and write the new image into it NSMutableData *dest_data = [NSMutableData data]; CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)dest_data, UTI, 1, NULL); if (!destination) { NSLog(@"Error: Could not create image destination"); } // add the image contained in the image source to the destination, overidding the old metadata with our modified metadata CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef) metadata); BOOL success = NO; success = CGImageDestinationFinalize(destination); if (!success) { NSLog(@"Error: Could not create data from image destination"); } CFRelease(destination); CFRelease(source); return dest_data; } #pragma mark - NixUploadDelegate - (void)didStartUploadWithTasks:(NSArray *)tasks { [self dismissViewControllerAnimated:YES completion:^{ [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil]; }]; } - (void)didUploadImageAtIndex:(NSUInteger)index error:(NSError *)error { [self handleUploadError:error]; } - (void)didFinishUploadingWithError:(NSError *)error { [self handleUploadError:error]; } -(void) showAlertViewWithTitle:(NSString*)title message:(NSString*)message actionWithTitle:actiontitle handler:(void (^ __nullable)(UIAlertAction *action))handler{ UIAlertController* alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"close", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self cancel]; }]; [alert addAction:defaultAction]; [self.svc presentViewController:alert animated:YES completion:nil]; } @end