400 lines
16 KiB
Objective-C
400 lines
16 KiB
Objective-C
//
|
|
// ShareViewController.m
|
|
// PhotoShareExtension
|
|
//
|
|
// Created by Russell on 27/4/15.
|
|
//
|
|
//
|
|
|
|
#import "ImageCaptionViewController.h"
|
|
#import <ImageIO/ImageIO.h>
|
|
#import <SDWebImage/UIImageView+WebCache.h>
|
|
|
|
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
|
|
|