369 lines
14 KiB
Objective-C
369 lines
14 KiB
Objective-C
//
|
|
// ContainerViewController.m
|
|
// PhotoShareExtension
|
|
//
|
|
// Created by Russell on 27/5/15.
|
|
//
|
|
//
|
|
|
|
#import "ContainerViewController.h"
|
|
#import "ImageCaptionViewController.h"
|
|
#import <ImageIO/ImageIO.h>
|
|
|
|
@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
|