photo-ios/Trovebox/JobUploaderController.m
2014-05-06 17:59:28 -03:00

390 lines
No EOL
18 KiB
Objective-C

//
// JobUploaderController.m
// Trovebox
//
// Created by Patrick Santana on 03/07/12.
// Copyright 2013 Trovebox
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "JobUploaderController.h"
#import "PhotoUploader.h"
@interface JobUploaderController (){
BOOL running;
ALAssetsLibrary *library;
NSArray *imagesAlreadySynced;
PhotoUploader *uploader;
}
- (void) executeUploadJob;
- (void) executeSyncJob;
- (void) loadPhotosToDatabase:(ALAssetsGroup *) group;
@end
@implementation JobUploaderController
+ (JobUploaderController*) getController
{
static dispatch_once_t pred;
static JobUploaderController *shared = nil;
dispatch_once(&pred, ^{
shared = [[JobUploaderController alloc] init];
});
return shared;
}
- (id)init
{
self = [super init];
if (self) {
library = [[ALAssetsLibrary alloc] init];
uploader = [[PhotoUploader alloc]init];
}
return self;
}
- (void) start
{
running = YES;
dispatch_queue_t jobQueue = dispatch_queue_create("job_queue", NULL);
dispatch_async(jobQueue, ^{
@try {
// start the thread
while (running) {
// sleep for 3 seconds
[NSThread sleepForTimeInterval:3];
// execute the method
[self executeUploadJob];
// execute Sync
[self executeSyncJob];
}
}@catch (NSException *exception) {
NSLog(@"Error in the job %@", [exception description]);
}
});
}
- (void) stop
{
// this will stop the thread
running = NO;
}
- (BOOL) isRunning
{
return running;
}
- (void) executeSyncJob
{
dispatch_async(dispatch_get_main_queue(), ^{
#ifdef DEVELOPMENT_ENABLED
NSLog(@"Job: sync");
#endif
// check if users enables sync and there is internet
if ([[NSUserDefaults standardUserDefaults] boolForKey:kAutoSyncEnabled] == YES &&
[SharedAppDelegate internetActive] == YES && [SharedAppDelegate wifi] == YES){
int uploading = [Timeline howEntitiesTimelineInManagedObjectContext:[SharedAppDelegate managedObjectContext] type:kUploadStatusTypeUploading];
int created = [Timeline howEntitiesTimelineInManagedObjectContext:[SharedAppDelegate managedObjectContext] type:kUploadStatusTypeCreated];
if (created == 0 && uploading == 0){
// queue is EMPTY, take some pictures from the camera roll. But just
// do it in the case where there are no pictures being uploaded.
#ifdef DEVELOPMENT_ENABLED
NSLog(@"Job: queue is empty");
#endif
// load images from sync already uploded
imagesAlreadySynced = [Synced getPathsInManagedObjectContext:[SharedAppDelegate managedObjectContext]];
// get 30 images from the Gallery that were not uploaded yet
// Group enumerator Block
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop)
{
if (group == nil)
{
return;
}
if ( [[group valueForProperty:ALAssetsGroupPropertyType] intValue] == ALAssetsGroupSavedPhotos) {
// with the local group, we can load the images
[self performSelectorInBackground:@selector(loadPhotosToDatabase:) withObject:group];
}
};
// Group Enumerator Failure Block
void (^assetGroupEnumberatorFailure)(NSError *) = ^(NSError *error) {
NSLog(@"A problem occured %@", [error description]);
};
// Show only the Saved Photos
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:assetGroupEnumerator
failureBlock:assetGroupEnumberatorFailure];
}
}
});
}
-(void)loadPhotosToDatabase:(ALAssetsGroup *) group {
// put them in the table to upload
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
__block int assetsNotUploaded = 0;
@autoreleasepool {
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop)
{
if(result == nil)
{
return;
}
if ( assetsNotUploaded < 30){
// check if user already uploaded
NSString *asset = [AssetsLibraryUtilities getAssetsUrlId:result.defaultRepresentation.url] ;
BOOL alreadyUploaded = [imagesAlreadySynced containsObject:asset];
if (!alreadyUploaded){
assetsNotUploaded++;
#ifdef DEVELOPMENT_ENABLED
NSLog(@"Photo not uploaded, adding %d", assetsNotUploaded);
#endif
NSURL *url = [[result valueForProperty:ALAssetPropertyURLs] valueForKey:[[[result valueForProperty:ALAssetPropertyURLs] allKeys] objectAtIndex:0]];
[uploader loadDataAndSaveEntityUploadDate:[NSDate date]
shareFacebook:[NSNumber numberWithBool:NO]
shareTwitter:[NSNumber numberWithBool:NO]
permission:[NSNumber numberWithBool:NO]
tags:@""
albums:@""
title:@""
url:url
groupUrl:nil];
}
}else{
// stop the enumeration
*stop = YES;
// send to database
}
}];
#ifdef DEVELOPMENT_ENABLED
NSLog(@"done enumerating photos");
#endif
}
}
- (void) executeUploadJob
{
dispatch_async(dispatch_get_main_queue(), ^{
#ifdef DEVELOPMENT_ENABLED
NSLog(@"Job: uploader");
#endif
int i = [Timeline howEntitiesTimelineInManagedObjectContext:[SharedAppDelegate managedObjectContext] type:kUploadStatusTypeUploading];
int created = [Timeline howEntitiesTimelineInManagedObjectContext:[SharedAppDelegate managedObjectContext] type:kUploadStatusTypeCreated];
// disable or enable the lock screen
if (created > 0){
// disable lock screen
[UIApplication sharedApplication].idleTimerDisabled = YES;
}else{
// enable lock screen
[UIApplication sharedApplication].idleTimerDisabled = NO;
}
if (i < 2 && created > 0){
// looks for uploads in the state WAITING
NSArray *waitings = [Timeline getNextWaitingToUploadInManagedObjectContext:[SharedAppDelegate managedObjectContext] qtd:2-i];
// loop in the list and start to upload
for (Timeline *photo in waitings){
photo.status = kUploadStatusTypeUploading;
// create a delegate
JobUploaderDelegate *delegate = [[JobUploaderDelegate alloc] initWithPhoto:photo size:[NSNumber numberWithInteger:0]];
NSDictionary *dictionary = nil;
@try {
dictionary = [photo toDictionary];
}
@catch (NSException *e) {
photo.status = kUploadStatusTypeFailed;
break;
}
// send
NSURL *storedURL = [NSURL URLWithString:photo.photoDataTempUrl];
NSData *data = [[NSData alloc] initWithContentsOfURL:storedURL];
dispatch_queue_t uploaderQueue = dispatch_queue_create("job_uploader", NULL);
dispatch_async(uploaderQueue, ^{
@try{
// prepare the data to upload
NSString *filename = photo.fileName;
if (![photo.copyFromFriend boolValue]){
// on upload, not copying
// set size
delegate.totalSize = [NSNumber numberWithInteger:data.length];
}
// create the service, check photo exists and send the request
WebService *service = [[WebService alloc] init];
// before check if the photo already exist
if (![photo.copyFromFriend boolValue] &&[service isPhotoAlreadyOnServer:[SHA1 sha1File:data]]){
@throw [NSException exceptionWithName:@"Failed to upload" reason:@"409" userInfo: nil];
}else{
NSDictionary *response;
if ([photo.copyFromFriend boolValue]){
// copy
response= [service copyPictureWithUrl:photo.photoUrl];
}else{
response= [service uploadPicture:data metadata:dictionary fileName:filename delegate:delegate];
}
#ifdef DEVELOPMENT_ENABLED
NSLog(@"Photo uploaded correctly");
#endif
dispatch_async(dispatch_get_main_queue(), ^{
// save the url
if (photo.syncedUrl){
// add to the sync list, with that we don't need to show photos already uploaded.
// in the case of edited images via Aviary, we don't save it.
Synced *sync = [NSEntityDescription insertNewObjectForEntityForName:@"Synced"
inManagedObjectContext:[SharedAppDelegate managedObjectContext]];
sync.filePath = photo.syncedUrl;
sync.status = kSyncedStatusTypeUploaded;
// used to say which user uploaded this image
sync.userUrl = [SharedAppDelegate userHost];
}
photo.status = kUploadStatusTypeUploadFinished;
photo.photoUploadResponse = [NSDictionarySerializer nsDictionaryToNSData:[response objectForKey:@"result"]];
// delete local file
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
BOOL fileExists = [fileManager fileExistsAtPath:photo.photoDataTempUrl];
#ifdef DEVELOPMENT_ENABLED
NSLog(@"Path to file: %@", photo.photoDataTempUrl);
NSLog(@"File exists: %d", fileExists);
NSLog(@"Is deletable file at path: %d", [fileManager isDeletableFileAtPath:photo.photoDataTempUrl]);
#endif
if (fileExists)
{
BOOL success = [fileManager removeItemAtPath:photo.photoDataTempUrl error:&error];
if (!success) NSLog(@"Error: %@", [error localizedDescription]);
}
// check if there is more files to upload
// if not, refresh the Home page
if ( [Timeline howEntitiesTimelineInManagedObjectContext:[SharedAppDelegate managedObjectContext] type:kUploadStatusTypeUploading] == 0 &&
[Timeline howEntitiesTimelineInManagedObjectContext:[SharedAppDelegate managedObjectContext] type:kUploadStatusTypeCreated] == 0){
// set that needs update - Home
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationNeededsUpdateHome object:nil];
// refresh profile details
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationProfileRefresh object:nil userInfo:nil];
// also lets save the Managed Context
NSError *saveError = nil;
if (![[SharedAppDelegate managedObjectContext] save:&saveError]){
NSLog(@"Error to save context = %@",[saveError localizedDescription]);
}
}
});
}
}@catch (NSException* e) {
NSLog(@"Error to upload image:%@", [e description]);
// if it fails for any reason, set status FAILED in the main thread
dispatch_async(dispatch_get_main_queue(), ^{
// check if it is duplicated
if ([[e description] hasPrefix:@"Error: 409 - This photo already exists based on a"] ||
[[e description] hasPrefix:@"409"]){
// this photo is already uploaded
if (photo.syncedUrl){
// add to the sync list, with that we don't need to show photos already uploaded.
// in the case of edited images via Aviary, we don't save it.
Synced *sync = [NSEntityDescription insertNewObjectForEntityForName:@"Synced"
inManagedObjectContext:[SharedAppDelegate managedObjectContext]];
sync.filePath = photo.syncedUrl;
sync.status = kSyncedStatusTypeUploaded;
// used to say which user uploaded this image
sync.userUrl = [SharedAppDelegate userHost];
}
photo.status = kUploadStatusTypeDuplicated;
}else if ([[e description] hasPrefix:@"402"]){
photo.status = kUploadStatusTypeLimitReached;
photo.photoUploadProgress = 0;
}else {
photo.status = kUploadStatusTypeFailed;
photo.photoUploadProgress = 0;
}
if ( [Timeline howEntitiesTimelineInManagedObjectContext:[SharedAppDelegate managedObjectContext] type:kUploadStatusTypeUploading] == 0 &&
[Timeline howEntitiesTimelineInManagedObjectContext:[SharedAppDelegate managedObjectContext] type:kUploadStatusTypeCreated] == 0){
// set that needs update - Home
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationNeededsUpdateHome object:nil];
}
});
}@finally{
// delete local file
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
BOOL fileExists = [fileManager fileExistsAtPath:photo.photoDataTempUrl];
#ifdef DEVELOPMENT_ENABLED
NSLog(@"Path to file: %@", photo.photoDataTempUrl);
NSLog(@"File exists: %d", fileExists);
NSLog(@"Is deletable file at path: %d", [fileManager isDeletableFileAtPath:photo.photoDataTempUrl]);
#endif
if (fileExists)
{
BOOL success = [fileManager removeItemAtPath:photo.photoDataTempUrl error:&error];
if (!success) NSLog(@"Error: %@", [error localizedDescription]);
}
}
});
}
}
});
}
@end