1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-03 09:57:46 +01:00
Telegram/Telegraph/TGSharedMediaFileItemView.m
2015-10-01 19:19:52 +03:00

501 lines
19 KiB
Objective-C

#import "TGSharedMediaFileItemView.h"
#import "TGDocumentMediaAttachment.h"
#import "TGFont.h"
#import "TGImageUtils.h"
#import "TGStringUtils.h"
#import "TGDateUtils.h"
#import "TGImageView.h"
#import "TGMessageImageViewOverlayView.h"
#import "TGSharedMediaAvailabilityState.h"
#import "TGSharedMediaFileThumbnailView.h"
#import "TGSharedFileSignals.h"
#import "TGViewController.h"
#import "TGSharedMediaUtils.h"
#import "TGSharedMediaCheckButton.h"
@interface TGSharedMediaFileItemView ()
{
UIView *_separatorView;
UILabel *_titleLabel;
UILabel *_descriptionLabel;
NSUInteger _descriptionLabelSize;
TGSharedMediaFileThumbnailView *_genericIconView;
UILabel *_genericIconExtensionLabel;
TGImageView *_thumbnailIconView;
NSString *_extension;
NSString *_legacyThumbnailUrl;
UIView *_progressView;
TGDocumentMediaAttachment *_documentAttachment;
int32_t _date;
TGSharedMediaAvailabilityState *_availabilityState;
UIImageView *_availabilityStateIconView;
TGSharedMediaCheckButton *_checkButton;
UIGestureRecognizer *_tapRecognizer;
}
@end
@implementation TGSharedMediaFileItemView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
_separatorView = [[UIView alloc] init];
_separatorView.backgroundColor = TGSeparatorColor();
[self.contentView addSubview:_separatorView];
_titleLabel = [[UILabel alloc] init];
_titleLabel.backgroundColor = [UIColor clearColor];
_titleLabel.textColor = [UIColor blackColor];
_titleLabel.font = TGMediumSystemFontOfSize(15.0f);
_titleLabel.numberOfLines = 1;
_titleLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
[self.contentView addSubview:_titleLabel];
_descriptionLabel = [[UILabel alloc] init];
_descriptionLabel.backgroundColor = [UIColor clearColor];
_descriptionLabel.textColor = UIColorRGB(0xa8a8a8);
_descriptionLabel.font = TGSystemFontOfSize(13.0f);
[self.contentView addSubview:_descriptionLabel];
_genericIconView = [[TGSharedMediaFileThumbnailView alloc] init];
_genericIconExtensionLabel = [[UILabel alloc] init];
_genericIconExtensionLabel.backgroundColor = [UIColor clearColor];
_genericIconExtensionLabel.textColor = [UIColor whiteColor];
_genericIconExtensionLabel.font = TGMediumSystemFontOfSize(13.0f);
_thumbnailIconView = [[TGImageView alloc] init];
_progressView = [[UIView alloc] init];
_progressView.backgroundColor = TGAccentColor();
self.selectedBackgroundView = [[UIView alloc] init];
self.selectedBackgroundView.backgroundColor = TGSelectionColor();
_availabilityStateIconView = [[UIImageView alloc] init];
_checkButton = [[TGSharedMediaCheckButton alloc] init];
_checkButton.userInteractionEnabled = false;
[self.contentView addSubview:_checkButton];
_tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)];
_tapRecognizer.enabled = false;
_tapRecognizer.cancelsTouchesInView = true;
[self.contentView addGestureRecognizer:_tapRecognizer];
}
return self;
}
- (void)prepareForReuse
{
[super prepareForReuse];
_availabilityState = nil;
}
- (UIImage *)availabilityStateIconDownload
{
static UIImage *image = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
image = [UIImage imageNamed:@"SharedMediaDocumentStatusDownload.png"];
});
return image;
}
- (UIImage *)availabilityStateIconPause
{
static UIImage *image = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(11.0f, 11.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, UIColorRGB(0x0080fc).CGColor);
CGContextFillRect(context, CGRectMake(2.0f, 0.0f, 2.0f, 11.0f - 1.0f));
CGContextFillRect(context, CGRectMake(2.0f + 2.0f + 2.0f, 0.0f, 2.0f, 11.0f - 1.0f));
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
return image;
}
- (void)setDocumentMediaAttachment:(TGDocumentMediaAttachment *)documentMediaAttachment date:(int)date lastInSection:(bool)__unused lastInSection availabilityState:(TGSharedMediaAvailabilityState *)availabilityState thumbnailColors:(NSArray *)thumbnailColors
{
_documentAttachment = documentMediaAttachment;
_date = date;
_titleLabel.text = documentMediaAttachment.fileName;
_separatorView.hidden = false;
bool isSticker = false;
for (id attribute in documentMediaAttachment.attributes)
{
if ([attribute isKindOfClass:[TGDocumentAttributeSticker class]])
{
isSticker = true;
break;
}
}
_extension = [[documentMediaAttachment.fileName pathExtension] lowercaseString];
bool useThumbnail = false;
if (!isSticker && documentMediaAttachment.thumbnailInfo != nil && !documentMediaAttachment.thumbnailInfo.empty)
{
CGSize dimensions = CGSizeZero;
NSString *_legacyThumbnailCacheUri = [documentMediaAttachment.thumbnailInfo closestImageUrlWithSize:CGSizeZero resultingSize:&dimensions];
useThumbnail = true;
_legacyThumbnailUrl = _legacyThumbnailCacheUri;
}
else
{
useThumbnail = false;
_legacyThumbnailUrl = nil;
[_genericIconView setStyle:TGSharedMediaFileThumbnailViewStyleRounded colors:thumbnailColors];
}
if (useThumbnail)
[self setUseThumbnail];
else
[self setUseGenericIconWithExtension:_extension];
[self setAvailabilityState:availabilityState animated:false];
[self setNeedsLayout];
}
- (void)addPositionAnimationToLayer:(CALayer *)layer from:(CGPoint)fromPoint to:(CGPoint)toPoint duration:(NSTimeInterval)duration
{
layer.position = fromPoint;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:fromPoint];
animation.toValue = [NSValue valueWithCGPoint:toPoint];
animation.duration = duration;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.removedOnCompletion = true;
animation.fillMode = kCAFillModeForwards;
[layer addAnimation:animation forKey:@"position"];
layer.position = toPoint;
}
- (void)addBoundsAnimationToLayer:(CALayer *)layer from:(CGRect)fromBounds to:(CGRect)toBounds duration:(NSTimeInterval)duration
{
layer.bounds = fromBounds;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
animation.fromValue = [NSValue valueWithCGRect:fromBounds];
animation.toValue = [NSValue valueWithCGRect:toBounds];
animation.duration = duration;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.removedOnCompletion = true;
animation.fillMode = kCAFillModeForwards;
[layer addAnimation:animation forKey:@"bounds"];
layer.bounds = toBounds;
}
- (void)addFrameAnimationToView:(UIView *)view from:(CGRect)fromFrame to:(CGRect)toFrame duration:(NSTimeInterval)duration
{
{
[view.layer removeAllAnimations];
[self addPositionAnimationToLayer:view.layer from:CGPointMake(CGRectGetMidX(fromFrame), CGRectGetMidY(fromFrame)) to:CGPointMake(CGRectGetMidX(toFrame), CGRectGetMidY(toFrame)) duration:duration];
[self addBoundsAnimationToLayer:view.layer from:CGRectMake(0.0f, 0.0f, fromFrame.size.width, fromFrame.size.height) to:CGRectMake(0.0f, 0.0f, toFrame.size.width, toFrame.size.height) duration:duration];
}
}
- (void)setAvailabilityState:(TGSharedMediaAvailabilityState *)availabilityState animated:(bool)animated
{
if (TGObjectCompare(_availabilityState, availabilityState))
return;
_availabilityState = availabilityState;
UIEdgeInsets insets = UIEdgeInsetsMake(4.0f, self.editing ? [self editingInset] : 65.0f, 6.0f, 10.0f);
NSString *descriptionText = nil;
bool updateLayout = false;
switch (availabilityState.type)
{
case TGSharedMediaAvailabilityStateAvailable:
case TGSharedMediaAvailabilityStateNotAvailable:
{
descriptionText = [[NSString alloc] initWithFormat:@"%@ • %@", [TGStringUtils stringForFileSize:_documentAttachment.size], [TGDateUtils stringForPreciseDate:_date]];
if (availabilityState.type == TGSharedMediaAvailabilityStateNotAvailable)
{
[_progressView removeFromSuperview];
if (_availabilityStateIconView.superview == nil)
{
[self addSubview:_availabilityStateIconView];
updateLayout = true;
}
_availabilityStateIconView.image = [self availabilityStateIconDownload];
}
else
{
if (_availabilityStateIconView.superview != nil)
{
[_availabilityStateIconView removeFromSuperview];
updateLayout = true;
}
if (_progressView.superview != nil)
{
UIView *animationProgressView = [[UIView alloc] initWithFrame:((CALayer *)_progressView.layer.presentationLayer).frame];
animationProgressView.backgroundColor = _progressView.backgroundColor;
[[_progressView superview] insertSubview:animationProgressView aboveSubview:_progressView];
[_progressView removeFromSuperview];
[UIView animateWithDuration:0.15 animations:^
{
animationProgressView.frame = CGRectMake(insets.left, self.frame.size.height - 2.0f, (self.frame.size.width - insets.left) * 1.0f, 2.0f);
} completion:^(BOOL finished)
{
if (finished)
{
[UIView animateWithDuration:0.3 animations:^
{
animationProgressView.alpha = 0.0f;
} completion:^(__unused BOOL finished)
{
[animationProgressView removeFromSuperview];
}];
}
else
[animationProgressView removeFromSuperview];
}];
}
}
break;
}
case TGSharedMediaAvailabilityStateDownloading:
{
if (_progressView.superview == nil)
{
animated = false;
[self.contentView addSubview:_progressView];
_progressView.frame = CGRectMake(insets.left, self.frame.size.height - 2.0f, (self.frame.size.width - insets.left) * 0.0f, 2.0f);
}
_progressView.alpha = 1.0f;
if (animated)
{
/*[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionBeginFromCurrentState animations:^
{
_progressView.frame = CGRectMake(insets.left, self.frame.size.height - 2.0f, (self.frame.size.width - insets.left) * _availabilityState.progress, 2.0f);
} completion:nil];*/
CALayer *layer = _progressView.layer.presentationLayer;
if (layer == nil)
layer = _progressView.layer;
[self addFrameAnimationToView:_progressView from:layer.frame to:CGRectMake(insets.left, self.frame.size.height - 2.0f, (self.frame.size.width - insets.left) * _availabilityState.progress, 2.0f) duration:0.2];
}
else
{
[_progressView.layer removeAllAnimations];
_progressView.frame = CGRectMake(insets.left, self.frame.size.height - 2.0f, (self.frame.size.width - insets.left) * _availabilityState.progress, 2.0f);
}
descriptionText = [[NSString alloc] initWithFormat:TGLocalized(@"DownloadingStatus"), [TGStringUtils stringForFileSize:(int32_t)(_documentAttachment.size * availabilityState.progress)], [TGStringUtils stringForFileSize:_documentAttachment.size]];
_availabilityStateIconView.image = [self availabilityStateIconPause];
updateLayout = true;
break;
}
}
if (!TGStringCompare(descriptionText, _descriptionLabel.text))
{
_descriptionLabel.text = descriptionText;
updateLayout = true;
}
if (updateLayout)
[self layoutDescriptionLabel];
}
- (void)setUseGenericIconWithExtension:(NSString *)extension
{
[_thumbnailIconView reset];
[_thumbnailIconView removeFromSuperview];
[self.contentView addSubview:_genericIconView];
[self.contentView addSubview:_genericIconExtensionLabel];
_genericIconExtensionLabel.text = extension.length > 5 ? [extension substringToIndex:5] : extension;
[_genericIconExtensionLabel sizeToFit];
[self setNeedsLayout];
}
- (void)setUseThumbnail
{
[_genericIconView removeFromSuperview];
[_genericIconExtensionLabel removeFromSuperview];
[self.contentView addSubview:_thumbnailIconView];
[_thumbnailIconView setSignal:[self _imageSignal]];
}
- (void)imageThumbnailUpdated:(NSString *)thumbnaiUri
{
if ([thumbnaiUri isEqualToString:_legacyThumbnailUrl])
{
[_thumbnailIconView setSignal:[self _imageSignal]];
}
}
- (SSignal *)_imageSignal
{
return [TGSharedFileSignals squareFileThumbnail:_documentAttachment ofSize:![TGViewController isWidescreen] ? CGSizeMake(70.0f, 70.0f) : CGSizeMake(90.0f, 90.0f) threadPool:[TGSharedMediaUtils sharedMediaImageProcessingThreadPool] memoryCache:[TGSharedMediaUtils sharedMediaMemoryImageCache] pixelProcessingBlock:nil];
}
- (void)setHighlighted:(BOOL)highlighted
{
[super setHighlighted:highlighted];
if (highlighted)
{
UIView *topSibling = nil;
for (UIView *view in self.superview.subviews.reverseObjectEnumerator)
{
if (view != self)
{
topSibling = view;
break;
}
}
if (topSibling != nil)
{
[self.superview exchangeSubviewAtIndex:[self.superview.subviews indexOfObject:self] withSubviewAtIndex:[self.superview.subviews indexOfObject:topSibling]];
}
}
}
- (void)setSelected:(BOOL)selected
{
[super setSelected:selected];
if (selected)
{
UIView *topSibling = nil;
for (UIView *view in self.superview.subviews.reverseObjectEnumerator)
{
if (view != self)
{
topSibling = view;
break;
}
}
if (topSibling != nil)
{
[self.superview exchangeSubviewAtIndex:[self.superview.subviews indexOfObject:self] withSubviewAtIndex:[self.superview.subviews indexOfObject:topSibling]];
}
}
}
- (void)layoutDescriptionLabel
{
UIEdgeInsets insets = UIEdgeInsetsMake(4.0f, self.editing ? [self editingInset] : 65.0f, 7.0f, 10.0f);
if (_availabilityStateIconView.superview != nil)
{
_availabilityStateIconView.frame = CGRectMake(insets.left - 1.0f, 32.0f, _availabilityStateIconView.image.size.width, _availabilityStateIconView.image.size.height);
insets.left += 12.0f;
}
CGSize descriptionSize = [_descriptionLabel.text sizeWithFont:_descriptionLabel.font constrainedToSize:CGSizeMake(self.frame.size.width - insets.left - insets.right - 1.0f + 1000.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByTruncatingMiddle];
descriptionSize.width = CGCeil(descriptionSize.width);
descriptionSize.height = CGCeil(descriptionSize.height);
_descriptionLabel.frame = CGRectMake(insets.left + 1.0f, self.frame.size.height - insets.bottom - descriptionSize.height, descriptionSize.width, descriptionSize.height);
}
- (void)setEditing:(bool)editing animated:(bool)animated delay:(NSTimeInterval)delay
{
[super setEditing:editing animated:animated delay:delay];
if (animated)
{
[UIView animateWithDuration:0.3 delay:delay options:[TGViewController preferredAnimationCurve] << 16 animations:^
{
[self layoutSubviews];
} completion:nil];
}
_tapRecognizer.enabled = editing;
}
- (void)updateItemSelected
{
[super updateItemSelected];
[_checkButton setChecked:self.isItemSelected && self.item != nil && self.isItemSelected(self.item) animated:false];
}
- (void)tapGesture:(UITapGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateEnded)
{
if (self.toggleItemSelection && self.item != nil)
self.toggleItemSelection(self.item);
[_checkButton setChecked:self.isItemSelected && self.item != nil && self.isItemSelected(self.item) animated:true];
}
}
- (CGFloat)editingInset
{
return 68.0f + 42.0f;
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat separatorHeight = TGIsRetina() ? 0.5f : 1.0f;
UIEdgeInsets insets = UIEdgeInsetsMake(8.0f, self.editing ? [self editingInset] : 65.0f, 6.0f, 10.0f);
_separatorView.frame = CGRectMake(insets.left, self.frame.size.height - separatorHeight, self.frame.size.width - insets.left, separatorHeight);
self.selectedBackgroundView.frame = CGRectMake(0.0f, -separatorHeight, self.frame.size.width, self.frame.size.height + separatorHeight);
CGSize titleSize = [_titleLabel.text sizeWithFont:_titleLabel.font constrainedToSize:CGSizeMake(self.frame.size.width - insets.left - insets.right - 1.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByTruncatingMiddle];
titleSize.width = CGCeil(titleSize.width);
titleSize.height = MIN(21.0f, CGCeil(titleSize.height));
_titleLabel.frame = CGRectMake(insets.left + 1.0f, insets.top, titleSize.width, titleSize.height);
[self layoutDescriptionLabel];
CGFloat iconOffset = (self.editing ? [self editingInset] : 65.0f) - 65.0f;
_genericIconView.frame = (CGRect){{9.0f + TGRetinaPixel + iconOffset, 5.0f}, {42.0f, 42.0f}};
_genericIconExtensionLabel.frame = (CGRect){{CGFloor(_genericIconView.frame.origin.x + (_genericIconView.frame.size.width - _genericIconExtensionLabel.frame.size.width) / 2.0f), 1.0f + CGFloor(_genericIconView.frame.origin.y + (_genericIconView.frame.size.height - _genericIconExtensionLabel.frame.size.height) / 2.0f)}, _genericIconExtensionLabel.frame.size};
_thumbnailIconView.frame = (CGRect){{9.0f + TGRetinaPixel + iconOffset, 5.0f}, {42.0f, 42.0f}};
[_progressView.layer removeAllAnimations];
_progressView.frame = CGRectMake(insets.left, self.frame.size.height - 2.0f, (self.frame.size.width - insets.left) * _availabilityState.progress, 2.0f);
_checkButton.frame = CGRectMake(self.editing ? 14.0f : -100.0f, 14.0f, 24.0f, 24.0f);
}
@end