Files
2025-08-06 13:49:11 +08:00

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