// // ShareViewController.m // PhotoShareExtension // // Created by Russell on 27/4/15. // // #import "ImageCaptionViewController.h" #import #import NSString *const kNixAppUrlScheme = @"nixplay"; NSString *const kNixAppUrlActionPhotoShare = @"photo_share"; NSString *const kNixAppUrlQueryKeyImageUrl = @"image_url"; NSString *const kNixAppUrlQueryKeyCaption = @"caption"; NSString *const kNixAppUrlQueryKeyFrameId = @"frame_id"; NSUInteger const kNixCaptionMaxLength = 160; NSUInteger const kNixInputFieldCells = 0; @interface ImageCaptionViewController () @property (nonatomic, strong) NSMutableArray *imageErrors; @property (nonatomic, strong) NSArray *imageUrls; @property (nonatomic, strong) NSMutableArray *captions; @property (nonatomic, strong) NSNumber *selectedFrameId; @property (nonatomic, strong) NSString *selectedFrameName; @property (nonatomic, strong) IBOutlet UITableView *tableView; @property (nonatomic, strong) NSIndexPath *activeCellIndexPath; @property (nonatomic, assign) BOOL keyboardShown; @property (nonatomic, assign) CGFloat keyboardOverlap; - (void)loadExtensionItems; - (void)validateImages; - (void)loadImagesIntoCache; @end @interface FrameTableViewCell : UITableViewCell @end @interface ImageCaptionTableViewCell : UITableViewCell @property (nonatomic, strong) NSError *error; @property (nonatomic, strong) IBOutlet UIImageView *thumbnailView; //@property (nonatomic, strong) IBOutlet UITextView *textView; @property (nonatomic, strong) IBOutlet UITextView *textView; @property (nonatomic, strong) IBOutlet UILabel *errorLabel; @property (nonatomic, strong) IBOutlet UIActivityIndicatorView *activityIndicatorView; @end @implementation ImageCaptionViewController - (ContainerViewController*)containerViewController { if (_containerViewController == nil) { _containerViewController = [[ContainerViewController alloc] init]; } return _containerViewController; } #pragma mark - IB - (IBAction)cancel:(id)sender { [self.containerViewController cancel]; } #pragma mark - UIViewController - (void)loadView { [super loadView]; [self loadExtensionItems]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.navigationItem.rightBarButtonItem.enabled = YES; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillTerminate:) name:UIApplicationWillTerminateNotification object:nil]; [self verifyUser]; } -(void)appWillBecomeActive:(NSNotification*)note { NSLog(@"appWillBecomeActive: %@", note); // re-validate again if the user is active logged-in [self verifyUser]; } -(void)appWillResignActive:(NSNotification*)note { NSLog(@"appWillResignActive: %@", note); } -(void)appWillTerminate:(NSNotification*)note { NSLog(@"appWillTerminate: %@", note); [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; /* * This works around a quirk on iOS version < 8.3 where the contents of the table do not load (after the VC has been * loaded once) on subsequent loads until the user scrolls the tableview. */ [self.tableView reloadData]; [self loadImagesIntoCache]; // [self.containerViewController verifyServiceAvailableWithCompletionHandler:^(BOOL success) { // dispatch_async(dispatch_get_main_queue(), ^{ // self.navigationItem.rightBarButtonItem.enabled = success; // }); // }]; // must be in viewWillAppear (doesn't work in viewDidAppear) } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"pushToPlaylistsTableView"]) { [self.view endEditing:YES]; ReceiverTableViewController *playlistTableVC = (ReceiverTableViewController *)segue.destinationViewController; playlistTableVC.containerViewController = self.containerViewController; self.containerViewController.imageErrors = [self.imageErrors copy]; self.containerViewController.imageUrls = [self.imageUrls copy]; self.containerViewController.captions = [self.captions copy]; } } #pragma mark - UITextViewDelegate - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { if([text isEqualToString:@"\n"]) { [textView resignFirstResponder]; return NO; } if ([text isEqualToString:@""]) return YES; return textView.text.length < kNixCaptionMaxLength; } - (void)textViewDidEndEditing:(UITextView *)textView { NSUInteger index = textView.tag; self.captions[index] = textView.text; self.activeCellIndexPath = nil; } - (void)textViewDidBeginEditing:(UITextView *)textView { self.activeCellIndexPath = [NSIndexPath indexPathForRow:textView.tag inSection:0]; } #pragma mark - UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.imageUrls count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = nil; NSUInteger imageIndex = indexPath.row; cell = [tableView dequeueReusableCellWithIdentifier:@"ImageCaptionCell" forIndexPath:indexPath]; cell.selectionStyle = UITableViewCellSelectionStyleNone; NSURL *imageUrl = self.imageUrls[indexPath.row]; ImageCaptionTableViewCell *imageCaptionCell = (ImageCaptionTableViewCell *)cell; imageCaptionCell.thumbnailView.contentMode = UIViewContentModeScaleAspectFit; imageCaptionCell.thumbnailView.image = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:[imageUrl absoluteString]]; if (imageCaptionCell.thumbnailView.image) { [imageCaptionCell.activityIndicatorView stopAnimating]; } imageCaptionCell.textView.tag = imageIndex; imageCaptionCell.textView.delegate = self; NSError *imageError; if (imageIndex < self.imageErrors.count) { imageError = self.imageErrors[imageIndex]; } imageCaptionCell.errorLabel.hidden = YES; imageCaptionCell.textView.hidden = YES; if (![imageError isKindOfClass:[NSNull class]]) { imageCaptionCell.errorLabel.hidden = NO; imageCaptionCell.errorLabel.text = [imageError localizedDescription]; } else { imageCaptionCell.textView.hidden = NO; imageCaptionCell.textView.layer.borderWidth = 0.5f; imageCaptionCell.textView.layer.borderColor = [[UIColor lightGrayColor] CGColor]; imageCaptionCell.textView.layer.cornerRadius = 2.0f; imageCaptionCell.textView.textContainer.lineBreakMode = NSLineBreakByWordWrapping; } NSString *caption = [self.captions objectAtIndex:imageIndex]; if ([caption isKindOfClass:[NSString class]]) imageCaptionCell.textView.text = caption; else imageCaptionCell.textView.text = @""; return cell; } #pragma mark - UITableViewDelegate - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath{ // @try{ // UIImage *image = [[SDImageCache sharedImageCache] // imageFromMemoryCacheForKey:[self.imageUrls[indexPath.row] absoluteString]]; // CGFloat height = image.size.height > image.size.width ? image.size.height : 256; // return height > 0 ? height : 256; // } // @catch(NSException * exception){ // NSLog(@"estimatedHeightForRowAtIndexPath Exception %@",exception); // } return 300;// UITableViewAutomaticDimension; } #pragma mark - Private #pragma mark App flow - (void)loadExtensionItems { NSExtensionItem *extensionItem = self.containerViewController.extensionContext.inputItems.firstObject; NSArray *inputItems = extensionItem.attachments; NSMutableArray *mutableImageUrls = [[NSMutableArray alloc] init]; dispatch_group_t group = dispatch_group_create(); // Load images from selection [inputItems enumerateObjectsUsingBlock:^(NSItemProvider *itemProvider, NSUInteger idx, BOOL *stop) { dispatch_group_enter(group); if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]) { [itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler: ^(NSURL *imageUrl, NSError *error) { [mutableImageUrls addObject:imageUrl]; dispatch_group_leave(group); }]; } else { dispatch_group_leave(group); } }]; dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ self.imageUrls = [NSArray arrayWithArray:mutableImageUrls]; NSUInteger imagesCount = [self.imageUrls count]; self.imageErrors = [NSMutableArray arrayWithCapacity:imagesCount]; self.captions = [[NSMutableArray alloc] initWithCapacity:imagesCount]; for (NSUInteger i = 0; i < imagesCount; i++) { [self.captions addObject:[NSNull null]]; } dispatch_async(dispatch_get_main_queue(), ^{ [self validateImages]; [self.tableView reloadData]; }); }); } -(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; } - (void)validateImages { NSFileManager *fm = [NSFileManager defaultManager]; NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; for (NSUInteger i = 0; i < self.imageUrls.count; i++) { @autoreleasepool { NSURL *imageUrl = self.imageUrls[i]; NSDictionary *fileAttrs = [fm attributesOfItemAtPath:[imageUrl path] error:nil]; NSNumber *fileSizeLimit = self.containerViewController.uploadHandler.fileSizeLimit; NSNumber *fileSize = [fileAttrs objectForKey:NSFileSize]; NSError *fileError; //https://www.wrike.com/open.htm?id=190326881 //allow proess photo when isOptimizeResolution if(![self isOptimizeResolution]){ if ([fileSize longLongValue] > [fileSizeLimit longLongValue]) { fileError = [NSError errorWithDomain:bundleIdentifier code:kNixIoFileTooLargeError userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(@"selection_error_file_too_large", nil) }]; } } if (fileError) { [self.imageErrors addObject:fileError]; } else { [self.imageErrors addObject:[NSNull null]]; } } } } - (void)loadImagesIntoCache { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ SDImageCache *imageCache = [SDImageCache sharedImageCache]; CGRect screenBounds = [[UIScreen mainScreen] bounds]; CGFloat screenScale = [[UIScreen mainScreen] scale]; NSNumber *maxWidth = [NSNumber numberWithFloat:screenBounds.size.width * screenScale * 0.75]; // Create thumbnail and save to cache for (NSUInteger i = 0; i < self.imageUrls.count; i++) { @autoreleasepool { NSURL *imageUrl = self.imageUrls[i]; if (![imageCache imageFromMemoryCacheForKey:[imageUrl absoluteString]]) { CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)imageUrl, nil); NSDictionary *options = @{ (id)kCGImageSourceCreateThumbnailWithTransform: (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent: (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageAlways: (id)kCFBooleanTrue, (id)kCGImageSourceThumbnailMaxPixelSize: (id)maxWidth }; CGImageRef thumbnailRef = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options); UIImage *scaledImage = [UIImage imageWithCGImage:thumbnailRef]; [imageCache storeImage:scaledImage forKey:[imageUrl absoluteString] completion:^{ }]; } } } // Update visible cells with images dispatch_async(dispatch_get_main_queue(), ^{ NSArray *visibleIndexPaths = [self.tableView indexPathsForVisibleRows]; for (NSIndexPath *visibleIndexPath in visibleIndexPaths) { @autoreleasepool { NSUInteger i = visibleIndexPath.row; NSURL *imageUrl = self.imageUrls[i]; // Set up NSError to insert into cell dispatch_async(dispatch_get_main_queue(), ^{ ImageCaptionTableViewCell *cell = (ImageCaptionTableViewCell *)[self.tableView cellForRowAtIndexPath:visibleIndexPath]; if (!cell.thumbnailView.image) { cell.thumbnailView.alpha = 0.f; cell.thumbnailView.image = [imageCache imageFromMemoryCacheForKey:[imageUrl absoluteString]]; [UIView animateWithDuration:0.35f animations:^{ cell.thumbnailView.alpha = 1.f; }]; [self.tableView beginUpdates]; [self.tableView reloadRowsAtIndexPaths:@[visibleIndexPath] withRowAnimation:UITableViewRowAnimationFade]; [self.tableView endUpdates]; } [cell.activityIndicatorView stopAnimating]; }); } } }); }); } #pragma mark - User - (void) verifyUser { [self.containerViewController verifyServiceAvailableWithCompletionHandler:^(BOOL success) { dispatch_async(dispatch_get_main_queue(), ^{ self.navigationItem.rightBarButtonItem.enabled = success; }); }]; // must be in viewWillAppear (doesn't work in viewDidAppear) } @end #pragma mark - @implementation FrameTableViewCell @end @implementation ImageCaptionTableViewCell @end