1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-03 09:57:46 +01:00
Telegram/Telegraph/TGMusicPlayerCompleteView.m
2016-02-25 01:03:51 +01:00

866 lines
34 KiB
Objective-C

#import "TGMusicPlayerCompleteView.h"
#import "TGMusicPlayerScrubbingArea.h"
#import "TGImageUtils.h"
#import "TGFont.h"
#import "TGModernButton.h"
#import "TGMusicPlayerModeButton.h"
#import "TGMusicPlayer.h"
#import "TGTelegraph.h"
#import "TGImageView.h"
#import <pop/POP.h>
#import <MediaPlayer/MediaPlayer.h>
@interface TGMusicPlayerCompleteViewLayer : CALayer
@property (nonatomic) bool ignoreLayout;
@end
@implementation TGMusicPlayerCompleteViewLayer
- (void)setNeedsLayout
{
if (!_ignoreLayout)
[super setNeedsLayout];
}
@end
@interface TGMusicPlayerCompleteView ()
{
UIView *_albumArtBackgroundView;
UIImageView *_albumArtPlaceholderView;
TGImageView *_albumArtImageView;
TGMusicPlayerScrubbingArea *_scrubbingArea;
UIView *_scrubbingBackground;
UIView *_playbackScrubbingForeground;
UIView *_downloadingScrubbingForeground;
UIImageView *_scrubbingHandle;
UILabel *_titleLabel;
UILabel *_performerLabel;
TGModernButton *_controlBackButton;
TGModernButton *_controlForwardButton;
TGModernButton *_controlPlayButton;
TGModernButton *_controlPauseButton;
TGMusicPlayerModeButton *_controlShuffleButton;
TGMusicPlayerModeButton *_controlRepeatButton;
CGFloat _labelsEdge;
UILabel *_positionLabel;
int _positionLabelValue;
UILabel *_durationLabel;
int _durationLabelValue;
int _progressLabelValueCurrent;
int _progressLabelValueTotal;
UILabel *_progressLabel;
UIView *_volumeView;
UIImageView *_volumeControlLeftIcon;
UIImageView *_volumeControlRightIcon;
bool _scrubbing;
CGPoint _scrubbingReferencePoint;
CGFloat _scrubbingReferenceOffset;
CGFloat _scrubbingOffset;
id<SDisposable> _playerStatusDisposable;
TGMusicPlayerStatus *_currentStatus;
NSString *_title;
NSString *_performer;
TGMusicPlayerItemPosition _currentItemPosition;
CGFloat _playbackOffset;
CGFloat _downloadProgress;
bool _updateLabelsLayout;
}
@end
@implementation TGMusicPlayerCompleteView
+ (Class)layerClass
{
return [TGMusicPlayerCompleteViewLayer class];
}
- (void)setIgnoreLayout:(bool)ignoreLayout
{
((TGMusicPlayerCompleteViewLayer *)self.layer).ignoreLayout = ignoreLayout;
}
- (bool)ignoreLayout
{
return ((TGMusicPlayerCompleteViewLayer *)self.layer).ignoreLayout;
}
- (instancetype)initWithFrame:(CGRect)frame setTitle:(void (^)(NSString *))setTitle actionsEnabled:(void (^)(bool))actionsEnabled
{
self = [super initWithFrame:frame];
if (self != nil)
{
_setTitle = [setTitle copy];
_actionsEnabled = [actionsEnabled copy];
_albumArtBackgroundView = [[UIView alloc] init];
_albumArtBackgroundView.backgroundColor = UIColorRGB(0xf0f0f4);
[self addSubview:_albumArtBackgroundView];
_albumArtPlaceholderView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MusicPlayerAlbumArtPlaceholder.png"]];
[_albumArtBackgroundView addSubview:_albumArtPlaceholderView];
_albumArtImageView = [[TGImageView alloc] init];
[_albumArtBackgroundView addSubview:_albumArtImageView];
_scrubbingBackground = [[UIView alloc] init];
_scrubbingBackground.backgroundColor = UIColorRGB(0xcccccc);
[self addSubview:_scrubbingBackground];
_playbackScrubbingForeground = [[UIView alloc] init];
_playbackScrubbingForeground.backgroundColor = TGAccentColor();
[self addSubview:_playbackScrubbingForeground];
_downloadingScrubbingForeground = [[UIView alloc] init];
_downloadingScrubbingForeground.backgroundColor = TGAccentColor();
[self addSubview:_downloadingScrubbingForeground];
_scrubbingHandle = [[UIImageView alloc] initWithImage:[self handleImage]];
[self addSubview:_scrubbingHandle];
_scrubbingArea = [[TGMusicPlayerScrubbingArea alloc] init];
__weak TGMusicPlayerCompleteView *weakSelf = self;
_scrubbingArea.didBeginDragging = ^(UITouch *touch)
{
__strong TGMusicPlayerCompleteView *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf beginScrubbingAtPoint:[strongSelf scrubbingLocationForTouch:touch]];
};
_scrubbingArea.willMove = ^(UITouch *touch)
{
__strong TGMusicPlayerCompleteView *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf continueScrubbingAtPoint:[strongSelf scrubbingLocationForTouch:touch]];
};
_scrubbingArea.didFinishDragging = ^
{
__strong TGMusicPlayerCompleteView *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf finishScrubbing];
};
_scrubbingArea.didCancelDragging = ^
{
__strong TGMusicPlayerCompleteView *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf cancelScrubbing];
};
[self addSubview:_scrubbingArea];
_positionLabel = [[UILabel alloc] init];
_positionLabel.backgroundColor = [UIColor whiteColor];
_positionLabel.textColor = UIColorRGB(0x474747);
_positionLabel.font = TGSystemFontOfSize(12.0f);
[self addSubview:_positionLabel];
_positionLabelValue = INT_MIN;
_durationLabel = [[UILabel alloc] init];
_durationLabel.backgroundColor = [UIColor whiteColor];
_durationLabel.textColor = UIColorRGB(0x474747);
_durationLabel.font = TGSystemFontOfSize(12.0f);
[self addSubview:_durationLabel];
_durationLabelValue = INT_MIN;
static UIImage *minimumTrackImage = nil;
static UIImage *maximumTrackImage = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(4.0f, 2.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, UIColorRGB(0x7f7f7f).CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 2.0f, 2.0f));
CGContextFillEllipseInRect(context, CGRectMake(2.0f, 0.0f, 2.0f, 2.0f));
CGContextFillRect(context, CGRectMake(1.0f, 0.0f, 2.0f, 2.0f));
minimumTrackImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:2.0f topCapHeight:0.0f];
UIGraphicsEndImageContext();
}
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(4.0f, 2.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, UIColorRGB(0xd0d0d0).CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 2.0f, 2.0f));
CGContextFillEllipseInRect(context, CGRectMake(2.0f, 0.0f, 2.0f, 2.0f));
CGContextFillRect(context, CGRectMake(1.0f, 0.0f, 2.0f, 2.0f));
maximumTrackImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:2.0f topCapHeight:0.0f];
UIGraphicsEndImageContext();
}
});
CGFloat titleFontSize = 16.0f + TGRetinaPixel;
CGFloat performerFontSize = 12.0f;
switch ([self interfaceType])
{
case TGMusicPlayerInterfaceExtraLarge:
titleFontSize = 19.0f;
performerFontSize = 13.0f;
break;
default:
break;
}
_titleLabel = [[UILabel alloc] init];
_titleLabel.backgroundColor = [UIColor whiteColor];
_titleLabel.textColor = [UIColor blackColor];
_titleLabel.font = TGMediumSystemFontOfSize(titleFontSize);
[self addSubview:_titleLabel];
_performerLabel = [[UILabel alloc] init];
_performerLabel.backgroundColor = [UIColor whiteColor];
_performerLabel.textColor = UIColorRGB(0x474747);
_performerLabel.font = TGSystemFontOfSize(performerFontSize);
[self addSubview:_performerLabel];
_controlPlayButton = [[TGModernButton alloc] init];
[_controlPlayButton setImage:[UIImage imageNamed:@"MusicPlayerControlPlay.png"] forState:UIControlStateNormal];
[_controlPlayButton setContentEdgeInsets:UIEdgeInsetsMake(0.0f, 5.0f, 0.0f, 0.0f)];
[_controlPlayButton addTarget:self action:@selector(controlPlay) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_controlPlayButton];
_controlPauseButton = [[TGModernButton alloc] init];
[_controlPauseButton setImage:[UIImage imageNamed:@"MusicPlayerControlPause.png"] forState:UIControlStateNormal];
[_controlPauseButton addTarget:self action:@selector(controlPause) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_controlPauseButton];
_controlBackButton = [[TGModernButton alloc] init];
[_controlBackButton setImage:[UIImage imageNamed:@"MusicPlayerControlBack.png"] forState:UIControlStateNormal];
[_controlBackButton addTarget:self action:@selector(controlBack) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_controlBackButton];
_controlForwardButton = [[TGModernButton alloc] init];
[_controlForwardButton setImage:[UIImage imageNamed:@"MusicPlayerControlForward.png"] forState:UIControlStateNormal];
[_controlForwardButton addTarget:self action:@selector(controlForward) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_controlForwardButton];
_controlShuffleButton = [[TGMusicPlayerModeButton alloc] init];
[_controlShuffleButton setImage:[UIImage imageNamed:@"MusicPlayerControlShuffle.png"] forState:UIControlStateNormal];
[_controlShuffleButton addTarget:self action:@selector(controlShuffle) forControlEvents:UIControlEventTouchUpInside];
//[self addSubview:_controlShuffleButton];
_controlRepeatButton = [[TGMusicPlayerModeButton alloc] init];
[_controlRepeatButton setImage:[UIImage imageNamed:@"MusicPlayerControlRepeat.png"] forState:UIControlStateNormal];
[_controlRepeatButton addTarget:self action:@selector(controlRepeat) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_controlRepeatButton];
#if TARGET_IPHONE_SIMULATOR
UISlider *sliderView = [[UISlider alloc] init];
[sliderView setMinimumTrackImage:minimumTrackImage forState:UIControlStateNormal];
[sliderView setMaximumTrackImage:maximumTrackImage forState:UIControlStateNormal];
[sliderView setThumbImage:[UIImage imageNamed:@"VolumeControlSliderButton.png"] forState:UIControlStateNormal];
[sliderView setValue:0.5f];
_volumeView = sliderView;
#else
MPVolumeView *volumeView = [[MPVolumeView alloc] init];
[volumeView setMinimumVolumeSliderImage:minimumTrackImage forState:UIControlStateNormal];
[volumeView setMaximumVolumeSliderImage:maximumTrackImage forState:UIControlStateNormal];
[volumeView setVolumeThumbImage:[UIImage imageNamed:@"VolumeControlSliderButton.png"] forState:UIControlStateNormal];
volumeView.showsRouteButton = false;
_volumeView = volumeView;
#endif
[self addSubview:_volumeView];
_volumeControlLeftIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"VolumeControlVolumeIcon.png"]];
[self addSubview:_volumeControlLeftIcon];
_volumeControlRightIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"VolumeControlVolumeUpIcon.png"]];
[self addSubview:_volumeControlRightIcon];
_currentItemPosition = (TGMusicPlayerItemPosition){.index = 0, .count = -1};
_playerStatusDisposable = [[TGTelegraphInstance.musicPlayer playingStatus] startWithNext:^(TGMusicPlayerStatus *status)
{
__strong TGMusicPlayerCompleteView *strongSelf = weakSelf;
if (strongSelf != nil)
{
[strongSelf setStatus:status];
}
}];
_updateLabelsLayout = true;
}
return self;
}
- (void)dealloc
{
[_playerStatusDisposable dispose];
}
- (void)setFrame:(CGRect)frame
{
_updateLabelsLayout = ABS(self.frame.size.width - frame.size.width) > FLT_EPSILON;
[super setFrame:frame];
if (_updateLabelsLayout)
[self setNeedsLayout];
}
- (CGPoint)scrubbingLocationForTouch:(UITouch *)touch
{
return [touch locationInView:_scrubbingArea];
}
- (void)beginScrubbingAtPoint:(CGPoint)point
{
_scrubbing = true;
_scrubbingReferencePoint = point;
_scrubbingOffset = _playbackOffset;
_scrubbingReferenceOffset = _playbackOffset;
[TGTelegraphInstance.musicPlayer controlPause];
}
- (void)continueScrubbingAtPoint:(CGPoint)point
{
if (_scrubbingArea.frame.size.width > FLT_EPSILON)
{
_scrubbingOffset = MAX(0.0f, MIN(1.0f, _scrubbingReferenceOffset + (point.x - _scrubbingReferencePoint.x) / _scrubbingArea.frame.size.width));
[self layoutScrubbingIndicator];
}
}
- (void)finishScrubbing
{
[TGTelegraphInstance.musicPlayer controlSeekToPosition:_scrubbingOffset];
[TGTelegraphInstance.musicPlayer controlPlay];
_scrubbing = false;
_playbackOffset = _scrubbingOffset;
_scrubbingOffset = 0.0f;
[self layoutScrubbingIndicator];
}
- (void)cancelScrubbing
{
_scrubbing = false;
_scrubbingOffset = 0.0f;
[self layoutScrubbingIndicator];
[TGTelegraphInstance.musicPlayer controlPlay];
}
- (void)setTopInset:(CGFloat)topInset
{
_topInset = topInset;
[self setNeedsLayout];
}
- (CGFloat)progressHeight
{
return 4.0f;
}
- (CGSize)handleSize
{
return CGSizeMake(2.0f, 16.0f);
}
- (UIImage *)handleImage
{
static UIImage *image = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(2.0f, 4.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, TGAccentColor().CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 2.0f, 2.0f, 2.0f));
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 2.0f, 3.0f));
image = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:0.0f topCapHeight:1.0f];
UIGraphicsEndImageContext();
});
return image;
}
typedef enum {
TGMusicPlayerInterfaceCompact = 0,
TGMusicPlayerInterfaceMedium = 1,
TGMusicPlayerInterfaceLarge = 2,
TGMusicPlayerInterfaceExtraLarge = 3
} TGMusicPlayerInterfaceType;
- (TGMusicPlayerInterfaceType)interfaceType
{
CGSize screenSize = self.bounds.size;
CGFloat screenHeight = MAX(screenSize.width, screenSize.height);
if (screenHeight > 667.0f + FLT_EPSILON)
return TGMusicPlayerInterfaceExtraLarge;
else if (screenHeight > 568.0f + FLT_EPSILON)
return TGMusicPlayerInterfaceLarge;
else if (screenHeight > 480.0f + FLT_EPSILON)
return TGMusicPlayerInterfaceMedium;
else
return TGMusicPlayerInterfaceCompact;
}
- (void)layoutScrubbingIndicator
{
bool ignoreLayout = [self ignoreLayout];
[self setIgnoreLayout:true];
CGFloat displayOffset = _scrubbing ? _scrubbingOffset : _playbackOffset;
CGFloat progressHeight = [self progressHeight];
CGSize handleSize = [self handleSize];
CGFloat albumArtEdge = CGRectGetMaxY(_albumArtBackgroundView.frame);
CGFloat side = MIN(self.frame.size.width, self.frame.size.height);
CGFloat handleOriginX = TGScreenPixelFloor((side - handleSize.width) * displayOffset);
_playbackScrubbingForeground.frame = CGRectMake(0.0f, albumArtEdge, handleOriginX, progressHeight);
_downloadingScrubbingForeground.frame = CGRectMake(0.0f, albumArtEdge, _downloadProgress * side, progressHeight);
_scrubbingHandle.frame = CGRectMake(handleOriginX, albumArtEdge, handleSize.width, handleSize.height);
int positionLabelValue = (int)(displayOffset * _currentStatus.duration);
int durationLabelValue = (int)(_currentStatus.duration) - positionLabelValue;
if (ABS(_labelsEdge - albumArtEdge) > FLT_EPSILON)
{
_labelsEdge = albumArtEdge;
_positionLabelValue = INT_MIN;
_durationLabelValue = INT_MIN;
}
if (_positionLabelValue != positionLabelValue)
{
_positionLabelValue = positionLabelValue;
if (positionLabelValue > 60 * 60)
{
_positionLabel.text = [[NSString alloc] initWithFormat:@"%d:%02d:%02d", positionLabelValue / (60 * 60), (positionLabelValue % (60 * 60)) / 60, positionLabelValue % 60];
}
else
{
_positionLabel.text = [[NSString alloc] initWithFormat:@"%d:%02d", positionLabelValue / 60, positionLabelValue % 60];
}
[_positionLabel sizeToFit];
_positionLabel.frame = CGRectMake(11.0f + TGRetinaPixel, albumArtEdge + 17.0f - TGRetinaPixel, _positionLabel.frame.size.width, _positionLabel.frame.size.height);
}
if (_durationLabelValue != durationLabelValue)
{
_durationLabelValue = durationLabelValue;
if (durationLabelValue > 60 * 60)
{
_durationLabel.text = [[NSString alloc] initWithFormat:@"-%d:%02d:%02d", durationLabelValue / (60 * 60), (durationLabelValue % (60 * 60)) / 60, durationLabelValue % 60];
}
else
{
_durationLabel.text = [[NSString alloc] initWithFormat:@"-%d:%02d", durationLabelValue / 60, durationLabelValue % 60];
}
[_durationLabel sizeToFit];
_durationLabel.frame = CGRectMake(side - 11.0f - TGRetinaPixel - _durationLabel.frame.size.width, albumArtEdge + 17.0f - TGRetinaPixel, _durationLabel.frame.size.width, _durationLabel.frame.size.height);
}
[self setIgnoreLayout:ignoreLayout];
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat verticalOffset = 0.0f;
if (self.bounds.size.height > 667.0f + FLT_EPSILON) {
verticalOffset = CGFloor((self.bounds.size.height - 667.0f) / 2.0f);
}
CGFloat side = MIN(self.frame.size.width, self.frame.size.height);
side = MIN(side, 414.0f);
CGFloat scrubbingHeight = 32.0f;
CGFloat progressHeight = [self progressHeight];
CGFloat albumArtHeight = side;
CGSize albumArtImageSize = CGSizeZero;
switch ([self interfaceType])
{
case TGMusicPlayerInterfaceCompact:
albumArtHeight = 232.0f;
albumArtImageSize = CGSizeMake(200.0f, 200.0f);
break;
default:
albumArtHeight = side;
albumArtImageSize = CGSizeMake(side, side);
break;
}
_albumArtBackgroundView.frame = CGRectMake(0.0f, 0.0f, self.bounds.size.width, albumArtHeight + _topInset + verticalOffset);
_albumArtImageView.frame = CGRectMake(CGFloor((self.frame.size.width - albumArtImageSize.width) / 2.0f), _topInset + verticalOffset + CGFloor((albumArtHeight - albumArtImageSize.height) / 2.0f), albumArtImageSize.width, albumArtImageSize.height);
_albumArtPlaceholderView.frame = CGRectMake(CGFloor((self.frame.size.width - _albumArtPlaceholderView.frame.size.width) / 2.0f) - 8.0f, _topInset + verticalOffset + CGFloor((albumArtHeight - _albumArtPlaceholderView.frame.size.height) / 2.0f), _albumArtPlaceholderView.frame.size.width, _albumArtPlaceholderView.frame.size.height);
CGFloat albumArtEdge = CGRectGetMaxY(_albumArtBackgroundView.frame);
_scrubbingArea.frame = CGRectMake(0.0f, albumArtEdge + progressHeight / 2.0f - scrubbingHeight / 2.0f, self.bounds.size.width, scrubbingHeight);
_scrubbingBackground.frame = CGRectMake(0.0f, albumArtEdge, self.bounds.size.width, progressHeight);
CGFloat titleOffset = albumArtEdge + 31.0f;
CGFloat controlButtonsOffset = albumArtEdge + 76.0f;
CGFloat controlButtonSize = 60.0f;
CGFloat controlButtonSpread = 100.0f;
CGFloat volumeControlBottomOffset = 58.0f;
CGFloat volumeControlSideInset = 47.0f;
CGFloat volumeControlOffset = 0.0f;
#if TARGET_IPHONE_SIMULATOR
#else
volumeControlOffset = 16.0f;
#endif
switch ([self interfaceType])
{
case TGMusicPlayerInterfaceCompact:
titleOffset = albumArtEdge + 30.0f;
controlButtonsOffset = albumArtEdge + 75.0f;
volumeControlBottomOffset = 58.0f;
break;
case TGMusicPlayerInterfaceMedium:
titleOffset = albumArtEdge + 31.0f;
controlButtonsOffset = albumArtEdge + 75.0f;
volumeControlBottomOffset = 58.0f;
break;
case TGMusicPlayerInterfaceLarge:
titleOffset = albumArtEdge + 51.0f;
controlButtonsOffset = albumArtEdge + 96.0f;
volumeControlBottomOffset = 59.0f;
volumeControlSideInset = 76.0f;
break;
case TGMusicPlayerInterfaceExtraLarge:
titleOffset = albumArtEdge + 57.0f;
controlButtonsOffset = albumArtEdge + 107.0f;
volumeControlBottomOffset = 70.0f;
volumeControlSideInset = 86.0f;
break;
}
CGSize titleSize = _titleLabel.frame.size;
CGSize performerSize = _performerLabel.frame.size;
if (_updateLabelsLayout)
{
_updateLabelsLayout = false;
titleSize = [_titleLabel.text sizeWithFont:_titleLabel.font];
performerSize = [_performerLabel.text sizeWithFont:_performerLabel.font];
CGFloat maxWidth = self.bounds.size.width - 32.0f;
titleSize.width = MIN(titleSize.width, maxWidth);
performerSize.width = MIN(performerSize.width, maxWidth);
}
_titleLabel.frame = CGRectMake(CGFloor((self.bounds.size.width - titleSize.width) / 2.0f), titleOffset + TGRetinaPixel, titleSize.width, titleSize.height);
_performerLabel.frame = CGRectMake(CGFloor((self.bounds.size.width - performerSize.width) / 2.0f), titleOffset + titleSize.height + 6.0f, performerSize.width, performerSize.height);
_controlPauseButton.frame = _controlPlayButton.frame = CGRectMake(CGFloor((self.bounds.size.width - controlButtonSize) / 2.0f), controlButtonsOffset, controlButtonSize, controlButtonSize);
_controlBackButton.frame = CGRectMake(CGFloor((self.bounds.size.width - controlButtonSpread) / 2.0f) - controlButtonSize, controlButtonsOffset, controlButtonSize, controlButtonSize);
_controlForwardButton.frame = CGRectMake(CGFloor((self.bounds.size.width + controlButtonSpread) / 2.0f), controlButtonsOffset, controlButtonSize, controlButtonSize);
CGSize modeButtonSize = CGSizeMake(28.0f, 21.0f);
_controlShuffleButton.frame = CGRectMake(16.0f, _controlPlayButton.frame.origin.y + 19.0f, modeButtonSize.width, modeButtonSize.height);
_controlRepeatButton.frame = CGRectMake(self.bounds.size.width - 44.0f, _controlShuffleButton.frame.origin.y, modeButtonSize.width, modeButtonSize.height);
[UIView performWithoutAnimation:^
{
_volumeView.frame = CGRectMake(volumeControlSideInset, self.frame.size.height - volumeControlBottomOffset + volumeControlOffset, self.bounds.size.width - volumeControlSideInset * 2.0f, 50.0f);
}];
_volumeControlLeftIcon.frame = CGRectMake(_volumeView.frame.origin.x - 16.0f, _volumeView.frame.origin.y + 20.0f - volumeControlOffset, _volumeControlLeftIcon.frame.size.width, _volumeControlLeftIcon.frame.size.height);
_volumeControlRightIcon.frame = CGRectMake(CGRectGetMaxX(_volumeView.frame) + 8.0f, _volumeView.frame.origin.y + 18.0f - volumeControlOffset, _volumeControlRightIcon.frame.size.width, _volumeControlRightIcon.frame.size.height);
[self layoutScrubbingIndicator];
}
- (void)setStatus:(TGMusicPlayerStatus *)status
{
TGMusicPlayerStatus *previousStatus = _currentStatus;
_currentStatus = status;
if (_currentItemPosition.index != status.position.index || _currentItemPosition.count != status.position.count)
{
_currentItemPosition = status.position;
NSString *title = [[NSString alloc] initWithFormat:@"%d %@ %d", (int)_currentItemPosition.index + 1, TGLocalized(@"Common.of"), (int)_currentItemPosition.count];
if (_setTitle)
_setTitle(title);
}
if (!TGObjectCompare(status.item.key, previousStatus.item.key))
{
NSString *title = nil;
NSString *performer = nil;
if ([status.item.media isKindOfClass:[TGDocumentMediaAttachment class]]) {
TGDocumentMediaAttachment *document = status.item.media;
for (id attribute in document.attributes)
{
if ([attribute isKindOfClass:[TGDocumentAttributeAudio class]])
{
title = ((TGDocumentAttributeAudio *)attribute).title;
performer = ((TGDocumentAttributeAudio *)attribute).performer;
break;
}
}
if (title.length == 0)
{
title = document.fileName;
}
}
if (title.length == 0)
title = @"Unknown Track";
if (performer.length == 0)
performer = @"Unknown Artist";
if (status != nil)
{
if (!TGStringCompare(_title, title) || !TGStringCompare(_performer, performer))
{
_title = title;
_performer = performer;
_updateLabelsLayout = true;
_titleLabel.text = title;
_performerLabel.text = performer;
[self setNeedsLayout];
}
}
}
if (status.albumArtSync != previousStatus.albumArtSync)
{
[_albumArtImageView reset];
if (status.albumArtSync != nil)
{
_albumArtImageView.contentMode = UIViewContentModeScaleAspectFit;
[_albumArtImageView setSignal:status.albumArtSync];
}
}
_controlPlayButton.hidden = !status.paused;
_controlPauseButton.hidden = status.paused;
_controlShuffleButton.selected = status.shuffle;
if (status.repeatType != previousStatus.repeatType)
{
switch (status.repeatType)
{
case TGMusicPlayerRepeatTypeNone:
[_controlRepeatButton setImage:[UIImage imageNamed:@"MusicPlayerControlRepeat.png"] forState:UIControlStateNormal];
_controlRepeatButton.selected = false;
break;
case TGMusicPlayerRepeatTypeAll:
[_controlRepeatButton setImage:[UIImage imageNamed:@"MusicPlayerControlRepeat.png"] forState:UIControlStateNormal];
_controlRepeatButton.selected = true;
break;
case TGMusicPlayerRepeatTypeOne:
[_controlRepeatButton setImage:[UIImage imageNamed:@"MusicPlayerControlRepeatOne.png"] forState:UIControlStateNormal];
_controlRepeatButton.selected = true;
break;
}
}
CGFloat disabledAlpha = 0.6f;
_scrubbingHandle.hidden = !status.downloadedStatus.downloaded;
bool buttonsEnabled = status.downloadedStatus.downloaded;
if (buttonsEnabled != _controlPlayButton.enabled)
{
_controlPlayButton.enabled = buttonsEnabled;
_controlPauseButton.enabled = buttonsEnabled;
_controlPlayButton.alpha = buttonsEnabled ? 1.0f : disabledAlpha;
_controlPauseButton.alpha = buttonsEnabled ? 1.0f : disabledAlpha;
_scrubbingArea.enabled = buttonsEnabled;
if (_actionsEnabled) {
_actionsEnabled(buttonsEnabled);
}
}
static POPAnimatableProperty *playbackOffsetProperty = nil;
static POPAnimatableProperty *downloadProgressProperty = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
playbackOffsetProperty = [POPAnimatableProperty propertyWithName:@"playbackOffset" initializer:^(POPMutableAnimatableProperty *prop)
{
prop.readBlock = ^(TGMusicPlayerCompleteView *strongSelf, CGFloat *values)
{
values[0] = strongSelf->_playbackOffset;
};
prop.writeBlock = ^(TGMusicPlayerCompleteView *strongSelf, CGFloat const *values)
{
strongSelf->_playbackOffset = values[0];
if (!strongSelf->_scrubbing)
[strongSelf layoutScrubbingIndicator];
};
}];
downloadProgressProperty = [POPAnimatableProperty propertyWithName:@"downloadProgress" initializer:^(POPMutableAnimatableProperty *prop)
{
prop.readBlock = ^(TGMusicPlayerCompleteView *strongSelf, CGFloat *values)
{
values[0] = strongSelf->_downloadProgress;
};
prop.writeBlock = ^(TGMusicPlayerCompleteView *strongSelf, CGFloat const *values)
{
strongSelf->_downloadProgress = values[0];
[strongSelf layoutScrubbingIndicator];
};
}];
});
if (!status.downloadedStatus.downloaded)
{
if (status.downloadedStatus.downloading)
{
_downloadingScrubbingForeground.alpha = 1.0f;
if (TGObjectCompare(previousStatus.item.key, status.item.key))
{
[self pop_removeAnimationForKey:@"downloadIndicator"];
POPBasicAnimation *animation = [self pop_animationForKey:@"downloadIndicator"];
if (animation == nil)
{
animation = [POPBasicAnimation linearAnimation];
[animation setProperty:downloadProgressProperty];
animation.removedOnCompletion = true;
animation.fromValue = @(_downloadProgress);
animation.toValue = @(status.downloadedStatus.progress);
animation.beginTime = status.timestamp;
animation.duration = 0.25;
[self pop_addAnimation:animation forKey:@"downloadIndicator"];
}
}
else
{
[self pop_removeAnimationForKey:@"downloadIndicator"];
_downloadProgress = status.downloadedStatus.progress;
[self layoutScrubbingIndicator];
}
}
else
{
_downloadProgress = status.downloadedStatus.progress;
_downloadingScrubbingForeground.alpha = 0.0f;
[self layoutScrubbingIndicator];
}
}
else
{
if (TGObjectCompare(previousStatus.item.key, status.item.key))
{
if (!previousStatus.downloadedStatus.downloaded)
{
[self pop_removeAnimationForKey:@"downloadIndicator"];
POPBasicAnimation *animation = [self pop_animationForKey:@"downloadIndicator"];
if (animation == nil)
{
animation = [POPBasicAnimation linearAnimation];
[animation setProperty:downloadProgressProperty];
animation.removedOnCompletion = true;
animation.fromValue = @(_downloadProgress);
animation.toValue = @(1.0f);
animation.beginTime = status.timestamp;
animation.duration = 0.25;
__weak TGMusicPlayerCompleteView *weakSelf = self;
animation.completionBlock = ^(__unused POPAnimation *animation, BOOL finished)
{
if (finished)
{
__strong TGMusicPlayerCompleteView *strongSelf = weakSelf;
if (strongSelf != nil)
{
[UIView animateWithDuration:0.3 animations:^
{
strongSelf->_downloadingScrubbingForeground.alpha = 0.0f;
}];
}
}
};
[self pop_addAnimation:animation forKey:@"downloadIndicator"];
}
}
}
else
_downloadingScrubbingForeground.alpha = 0.0f;
}
if (status == nil || status.paused || status.duration < FLT_EPSILON)
{
[self pop_removeAnimationForKey:@"scrubbingIndicator"];
_playbackOffset = status.offset;
[self layoutScrubbingIndicator];
}
else
{
[self pop_removeAnimationForKey:@"scrubbingIndicator"];
POPBasicAnimation *animation = [self pop_animationForKey:@"scrubbingIndicator"];
if (animation == nil)
{
animation = [POPBasicAnimation linearAnimation];
[animation setProperty:playbackOffsetProperty];
animation.removedOnCompletion = true;
_playbackOffset = status.offset;
animation.fromValue = @(status.offset);
animation.toValue = @(1.0f);
animation.beginTime = status.timestamp;
animation.duration = (1.0f - status.offset) * status.duration;
[self pop_addAnimation:animation forKey:@"scrubbingIndicator"];
}
}
}
- (void)controlPlay
{
[TGTelegraphInstance.musicPlayer controlPlay];
}
- (void)controlPause
{
[TGTelegraphInstance.musicPlayer controlPause];
}
- (void)controlBack
{
[TGTelegraphInstance.musicPlayer controlPrevious];
}
- (void)controlForward
{
[TGTelegraphInstance.musicPlayer controlNext];
}
- (void)controlShuffle
{
[TGTelegraphInstance.musicPlayer controlShuffle];
}
- (void)controlRepeat
{
[TGTelegraphInstance.musicPlayer controlRepeat];
}
@end