diff --git a/Signal/Images.xcassets/audio_pause_white_large.imageset/Contents.json b/Signal/Images.xcassets/audio_pause_white_large.imageset/Contents.json new file mode 100644 index 0000000000..ae1d595fdc --- /dev/null +++ b/Signal/Images.xcassets/audio_pause_white_large.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "audio_pause_white_large@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "audio_pause_white_large@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "audio_pause_white_large@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/audio_pause_white_large.imageset/audio_pause_white_large@1x.png b/Signal/Images.xcassets/audio_pause_white_large.imageset/audio_pause_white_large@1x.png new file mode 100644 index 0000000000..4539bc0c1c Binary files /dev/null and b/Signal/Images.xcassets/audio_pause_white_large.imageset/audio_pause_white_large@1x.png differ diff --git a/Signal/Images.xcassets/audio_pause_white_large.imageset/audio_pause_white_large@2x.png b/Signal/Images.xcassets/audio_pause_white_large.imageset/audio_pause_white_large@2x.png new file mode 100644 index 0000000000..27c7a8376f Binary files /dev/null and b/Signal/Images.xcassets/audio_pause_white_large.imageset/audio_pause_white_large@2x.png differ diff --git a/Signal/Images.xcassets/audio_pause_white_large.imageset/audio_pause_white_large@3x.png b/Signal/Images.xcassets/audio_pause_white_large.imageset/audio_pause_white_large@3x.png new file mode 100644 index 0000000000..abdc8097e7 Binary files /dev/null and b/Signal/Images.xcassets/audio_pause_white_large.imageset/audio_pause_white_large@3x.png differ diff --git a/Signal/Images.xcassets/audio_play_white_large.imageset/Contents.json b/Signal/Images.xcassets/audio_play_white_large.imageset/Contents.json new file mode 100644 index 0000000000..349353a52d --- /dev/null +++ b/Signal/Images.xcassets/audio_play_white_large.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "audio_play_white_large@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "audio_play_white_large@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "audio_play_white_large@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/audio_play_white_large.imageset/audio_play_white_large@1x.png b/Signal/Images.xcassets/audio_play_white_large.imageset/audio_play_white_large@1x.png new file mode 100644 index 0000000000..af18c795e4 Binary files /dev/null and b/Signal/Images.xcassets/audio_play_white_large.imageset/audio_play_white_large@1x.png differ diff --git a/Signal/Images.xcassets/audio_play_white_large.imageset/audio_play_white_large@2x.png b/Signal/Images.xcassets/audio_play_white_large.imageset/audio_play_white_large@2x.png new file mode 100644 index 0000000000..c14e50fd0d Binary files /dev/null and b/Signal/Images.xcassets/audio_play_white_large.imageset/audio_play_white_large@2x.png differ diff --git a/Signal/Images.xcassets/audio_play_white_large.imageset/audio_play_white_large@3x.png b/Signal/Images.xcassets/audio_play_white_large.imageset/audio_play_white_large@3x.png new file mode 100644 index 0000000000..da1178b12b Binary files /dev/null and b/Signal/Images.xcassets/audio_play_white_large.imageset/audio_play_white_large@3x.png differ diff --git a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h index 0f972b5084..1b6282c5bf 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h +++ b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h @@ -2,6 +2,7 @@ // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // +#import "OWSAudioAttachmentPlayer.h" #import "OWSMessageEditing.h" #import "OWSMessageMediaAdapter.h" #import @@ -10,22 +11,16 @@ NS_ASSUME_NONNULL_BEGIN @class TSAttachmentStream; -@interface TSVideoAttachmentAdapter : JSQVideoMediaItem +@interface TSVideoAttachmentAdapter + : JSQVideoMediaItem @property NSString *attachmentId; @property (nonatomic, strong) NSString *contentType; -@property (nonatomic) BOOL isAudioPlaying; -@property (nonatomic) BOOL isPaused; - (instancetype)initWithAttachment:(TSAttachmentStream *)attachment incoming:(BOOL)incoming; - (BOOL)isAudio; - (BOOL)isVideo; -- (void)setAudioProgressFromFloat:(float)progress; -- (void)setAudioIconToPlay; -- (void)setAudioIconToPause; -- (void)setDurationOfAudio:(NSTimeInterval)duration; -- (void)resetAudioDuration; @end diff --git a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m index 3f3c0736c7..8474db5e79 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m @@ -28,6 +28,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable) UILabel *durationLabel; @property (nonatomic) BOOL incoming; @property (nonatomic, nullable) AttachmentUploadView *attachmentUploadView; +@property (nonatomic) BOOL isAudioPlaying; +@property (nonatomic) BOOL isPaused; @end @@ -96,16 +98,6 @@ NS_ASSUME_NONNULL_BEGIN }); } -- (void)resetAudioDuration { - NSError *err; - AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:_attachment.mediaURL error:&err]; - _durationLabel.text = [self formatDuration:player.duration]; -} - -- (void)setDurationOfAudio:(NSTimeInterval)duration { - _durationLabel.text = [self formatDuration:duration]; -} - - (void)setAudioIconToPlay { [_audioPlayPauseButton setBackgroundImage:[UIImage imageNamed:(_incoming ? @"audio_play_button_blue" : @"audio_play_button")] diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index ea74aff408..dce5e82cad 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -10,6 +10,7 @@ #import "FLAnimatedImage.h" #import "NotificationsManager.h" #import "OWSAnyTouchGestureRecognizer.h" +#import "OWSAudioAttachmentPlayer.h" #import "OWSCallNotificationsAdaptee.h" #import "OWSContactAvatarBuilder.h" #import "OWSContactsManager.h" diff --git a/Signal/src/ViewControllers/AttachmentApprovalViewController.swift b/Signal/src/ViewControllers/AttachmentApprovalViewController.swift index 1e6e536e19..c8b7d464cc 100644 --- a/Signal/src/ViewControllers/AttachmentApprovalViewController.swift +++ b/Signal/src/ViewControllers/AttachmentApprovalViewController.swift @@ -5,7 +5,7 @@ import Foundation import MediaPlayer -class AttachmentApprovalViewController: UIViewController { +class AttachmentApprovalViewController: UIViewController, OWSAudioAttachmentPlayerDelegate { let TAG = "[AttachmentApprovalViewController]" @@ -17,6 +17,11 @@ class AttachmentApprovalViewController: UIViewController { var videoPlayer: MPMoviePlayerController? + var audioPlayer: OWSAudioAttachmentPlayer? + var audioPlayButton: UIButton? + var isAudioPlayingFlag = false + var isAudioPaused = false + // MARK: Initializers @available(*, unavailable, message:"use attachment: constructor instead.") @@ -89,9 +94,63 @@ class AttachmentApprovalViewController: UIViewController { } } + private func wrapViewsInVerticalStack(subviews: [UIView]) -> UIView { + assert(subviews.count > 0) + + let stackView = UIView() + + var lastView: UIView? + for subview in subviews { + + stackView.addSubview(subview) + subview.autoHCenterInSuperview() + + if lastView == nil { + subview.autoPinEdge(toSuperviewEdge:.top) + } else { + subview.autoPinEdge(.top, to:.bottom, of:lastView!, withOffset:10) + } + + lastView = subview + } + + lastView?.autoPinEdge(toSuperviewEdge:.bottom) + + return stackView + } + private func createAudioPreview(attachmentPreviewView: UIView) { - // TODO: Add audio player. - createGenericPreview(attachmentPreviewView:attachmentPreviewView) + guard let dataUrl = attachment.getTemporaryDataUrl() else { + createGenericPreview(attachmentPreviewView:attachmentPreviewView) + return + } + + audioPlayer = OWSAudioAttachmentPlayer(mediaUrl: dataUrl, delegate: self) + + var subviews = [UIView]() + + let audioPlayButton = UIButton() + self.audioPlayButton = audioPlayButton + setAudioIconToPlay() + audioPlayButton.imageView?.layer.minificationFilter = kCAFilterTrilinear + audioPlayButton.imageView?.layer.magnificationFilter = kCAFilterTrilinear + audioPlayButton.addTarget(self, action:#selector(audioPlayButtonPressed), for:.touchUpInside) + let buttonSize = createHeroViewSize() + audioPlayButton.autoSetDimension(.width, toSize:buttonSize) + audioPlayButton.autoSetDimension(.height, toSize:buttonSize) + subviews.append(audioPlayButton) + + if let fileExtensionLabel = createFileExtensionLabel() { + subviews.append(fileExtensionLabel) + } + + let fileSizeLabel = createFileSizeLabel() + subviews.append(fileSizeLabel) + + let stackView = wrapViewsInVerticalStack(subviews:subviews) + attachmentPreviewView.addSubview(stackView) + stackView.autoPinWidthToSuperview() + stackView.autoVCenterInSuperview() } private func createAnimatedPreview(attachmentPreviewView: UIView) { @@ -148,42 +207,63 @@ class AttachmentApprovalViewController: UIViewController { } private func createGenericPreview(attachmentPreviewView: UIView) { - let stackView = UIView() - attachmentPreviewView.addSubview(stackView) - stackView.autoCenterInSuperview() + var subviews = [UIView]() - let imageSize = ScaleFromIPhone5To7Plus(175, 225) - let image = UIImage(named:"file-icon-large") + let imageView = createHeroImageView(imageName: "file-icon-large") + subviews.append(imageView) + + if let fileExtensionLabel = createFileExtensionLabel() { + subviews.append(fileExtensionLabel) + } + + let fileSizeLabel = createFileSizeLabel() + subviews.append(fileSizeLabel) + + let stackView = wrapViewsInVerticalStack(subviews:subviews) + attachmentPreviewView.addSubview(stackView) + stackView.autoPinWidthToSuperview() + stackView.autoVCenterInSuperview() + } + + private func createHeroViewSize() -> CGFloat { + return ScaleFromIPhone5To7Plus(175, 225) + } + + private func createHeroImageView(imageName: String) -> UIView { + let imageSize = createHeroViewSize() + let image = UIImage(named:imageName) assert(image != nil) let imageView = UIImageView(image:image) imageView.layer.minificationFilter = kCAFilterTrilinear imageView.layer.magnificationFilter = kCAFilterTrilinear - stackView.addSubview(imageView) - imageView.autoHCenterInSuperview() - imageView.autoPinEdge(toSuperviewEdge:.top) imageView.autoSetDimension(.width, toSize:imageSize) imageView.autoSetDimension(.height, toSize:imageSize) - var lastView: UIView = imageView + return imageView + } - let labelFont = UIFont.ows_regularFont(withSize:ScaleFromIPhone5To7Plus(18, 24)) + private func labelFont() -> UIFont { + return UIFont.ows_regularFont(withSize:ScaleFromIPhone5To7Plus(18, 24)) + } - if let fileExtension = attachment.fileExtension { - let fileExtensionLabel = UILabel() - fileExtensionLabel.text = String(format:NSLocalizedString("ATTACHMENT_APPROVAL_FILE_EXTENSION_FORMAT", - comment: "Format string for file extension label in call interstitial view"), - fileExtension.capitalized) - - fileExtensionLabel.textColor = UIColor.white - fileExtensionLabel.font = labelFont - fileExtensionLabel.textAlignment = .center - stackView.addSubview(fileExtensionLabel) - fileExtensionLabel.autoHCenterInSuperview() - fileExtensionLabel.autoPinEdge(.top, to:.bottom, of:lastView, withOffset:10) - - lastView = fileExtensionLabel + private func createFileExtensionLabel() -> UIView? { + guard let fileExtension = attachment.fileExtension else { + return nil } + let fileExtensionLabel = UILabel() + fileExtensionLabel.text = String(format:NSLocalizedString("ATTACHMENT_APPROVAL_FILE_EXTENSION_FORMAT", + comment: "Format string for file extension label in call interstitial view"), + fileExtension.capitalized) + + fileExtensionLabel.textColor = UIColor.white + fileExtensionLabel.font = labelFont() + fileExtensionLabel.textAlignment = .center + + return fileExtensionLabel + } + + private func createFileSizeLabel() -> UIView { let numberFormatter = NumberFormatter() numberFormatter.numberStyle = NumberFormatter.Style.decimal let fileSizeLabel = UILabel() @@ -200,12 +280,10 @@ class AttachmentApprovalViewController: UIViewController { fileSizeText) fileSizeLabel.textColor = UIColor.white - fileSizeLabel.font = labelFont + fileSizeLabel.font = labelFont() fileSizeLabel.textAlignment = .center - stackView.addSubview(fileSizeLabel) - fileSizeLabel.autoHCenterInSuperview() - fileSizeLabel.autoPinEdge(.top, to:.bottom, of:lastView, withOffset:10) - fileSizeLabel.autoPinEdge(toSuperviewEdge:.bottom) + + return fileSizeLabel } private func createButtonRow(attachmentPreviewView: UIView) { @@ -282,4 +360,42 @@ class AttachmentApprovalViewController: UIViewController { successCompletion?() }) } + + func audioPlayButtonPressed(sender: UIButton) { + audioPlayer?.togglePlayState() + } + + // MARK: - OWSAudioAttachmentPlayerDelegate + + public func isAudioPlaying() -> Bool { + return isAudioPlayingFlag + } + + public func setIsAudioPlaying(_ isAudioPlaying: Bool) { + isAudioPlayingFlag = isAudioPlaying + } + + public func isPaused() -> Bool { + return isAudioPaused + } + + public func setIsPaused(_ isPaused: Bool) { + isAudioPaused = isPaused + } + + public func setAudioProgressFrom(_ progress: Float) { + // Ignore + } + + public func setAudioIconToPlay() { + let image = UIImage(named:"audio_play_white_large") + assert(image != nil) + audioPlayButton?.setImage(image, for:.normal) + } + + public func setAudioIconToPause() { + let image = UIImage(named:"audio_pause_white_large") + assert(image != nil) + audioPlayButton?.setImage(image, for:.normal) + } } diff --git a/Signal/src/ViewControllers/MessagesViewController.m b/Signal/src/ViewControllers/MessagesViewController.m index 28d3f12649..dcecdee099 100644 --- a/Signal/src/ViewControllers/MessagesViewController.m +++ b/Signal/src/ViewControllers/MessagesViewController.m @@ -1789,7 +1789,8 @@ typedef enum : NSUInteger { } } else if ([messageMedia isAudio]) { if (self.audioAttachmentPlayer) { - if (self.audioAttachmentPlayer.mediaAdapter == messageMedia) { + // Is this player associated with this media adapter? + if (self.audioAttachmentPlayer.owner == messageMedia) { // Tap to pause & unpause. [self.audioAttachmentPlayer togglePlayState]; return; @@ -1800,6 +1801,8 @@ typedef enum : NSUInteger { self.audioAttachmentPlayer = [[OWSAudioAttachmentPlayer alloc] initWithMediaAdapter:messageMedia databaseConnection:self.uiDatabaseConnection]; + // Associate the player with this media adapter. + self.audioAttachmentPlayer.owner = messageMedia; [self.audioAttachmentPlayer play]; } } diff --git a/Signal/src/ViewControllers/OWSAudioAttachmentPlayer.h b/Signal/src/ViewControllers/OWSAudioAttachmentPlayer.h index 9071cd07de..44da6d8cb6 100644 --- a/Signal/src/ViewControllers/OWSAudioAttachmentPlayer.h +++ b/Signal/src/ViewControllers/OWSAudioAttachmentPlayer.h @@ -9,13 +9,39 @@ NS_ASSUME_NONNULL_BEGIN @class TSVideoAttachmentAdapter; @class YapDatabaseConnection; +@protocol OWSAudioAttachmentPlayerDelegate + +- (BOOL)isAudioPlaying; +- (void)setIsAudioPlaying:(BOOL)isAudioPlaying; + +- (BOOL)isPaused; +- (void)setIsPaused:(BOOL)isPaused; + +- (void)setAudioProgressFromFloat:(float)progress; +- (void)setAudioIconToPlay; +- (void)setAudioIconToPause; + +@end + +#pragma mark - + @interface OWSAudioAttachmentPlayer : NSObject -@property (nonatomic, readonly) TSVideoAttachmentAdapter *mediaAdapter; +@property (nonatomic, readonly, weak) id delegate; +// This property can be used to associate instances of the player with view +// or model objects. +@property (nonatomic, weak) id owner; + +// A convenience initializer for MessagesViewController. +// +// It assumes the delegate (e.g. view) for this player will be the adapter. - (instancetype)initWithMediaAdapter:(TSVideoAttachmentAdapter *)mediaAdapter databaseConnection:(YapDatabaseConnection *)databaseConnection; +// An generic initializer. +- (instancetype)initWithMediaUrl:(NSURL *)mediaUrl delegate:(id)delegate; + - (void)play; - (void)pause; - (void)stop; diff --git a/Signal/src/ViewControllers/OWSAudioAttachmentPlayer.m b/Signal/src/ViewControllers/OWSAudioAttachmentPlayer.m index 9181be7c83..6b06340a62 100644 --- a/Signal/src/ViewControllers/OWSAudioAttachmentPlayer.m +++ b/Signal/src/ViewControllers/OWSAudioAttachmentPlayer.m @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSAudioAttachmentPlayer () -@property (nonatomic) TSAttachmentStream *attachmentStream; +@property (nonatomic, readonly) NSURL *mediaUrl; @property (nonatomic, nullable) AVAudioPlayer *audioPlayer; @property (nonatomic, nullable) NSTimer *audioPlayerPoller; @@ -24,31 +24,49 @@ NS_ASSUME_NONNULL_BEGIN @implementation OWSAudioAttachmentPlayer -- (instancetype)initWithMediaAdapter:(TSVideoAttachmentAdapter *)mediaAdapter - databaseConnection:(YapDatabaseConnection *)databaseConnection ++ (NSURL *)mediaUrlForMediaAdapter:(TSVideoAttachmentAdapter *)mediaAdapter + databaseConnection:(YapDatabaseConnection *)databaseConnection { - self = [super init]; - if (!self) { - return self; - } - OWSAssert(mediaAdapter); OWSAssert([mediaAdapter isAudio]); OWSAssert(mediaAdapter.attachmentId); OWSAssert(databaseConnection); - _mediaAdapter = mediaAdapter; - __block TSAttachment *attachment = nil; [databaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { attachment = [TSAttachment fetchObjectWithUniqueID:mediaAdapter.attachmentId transaction:transaction]; }]; OWSAssert(attachment); + TSAttachmentStream *attachmentStream = nil; if ([attachment isKindOfClass:[TSAttachmentStream class]]) { - self.attachmentStream = (TSAttachmentStream *)attachment; + attachmentStream = (TSAttachmentStream *)attachment; } - OWSAssert(self.attachmentStream); + OWSAssert(attachmentStream); + + return attachmentStream.mediaURL; +} + +- (instancetype)initWithMediaAdapter:(TSVideoAttachmentAdapter *)mediaAdapter + databaseConnection:(YapDatabaseConnection *)databaseConnection +{ + return [self initWithMediaUrl:[OWSAudioAttachmentPlayer mediaUrlForMediaAdapter:mediaAdapter + databaseConnection:databaseConnection] + delegate:mediaAdapter]; +} + +- (instancetype)initWithMediaUrl:(NSURL *)mediaUrl delegate:(id)delegate +{ + self = [super init]; + if (!self) { + return self; + } + + OWSAssert(mediaUrl); + OWSAssert(delegate); + + _delegate = delegate; + _mediaUrl = mediaUrl; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) @@ -74,20 +92,21 @@ NS_ASSUME_NONNULL_BEGIN - (void)play { - OWSAssert(self.attachmentStream); - OWSAssert(![self.mediaAdapter isAudioPlaying]); + OWSAssert([NSThread isMainThread]); + OWSAssert(self.mediaUrl); + OWSAssert(![self.delegate isAudioPlaying]); [ViewControllerUtils setAudioIgnoresHardwareMuteSwitch:YES]; [self.audioPlayerPoller invalidate]; - self.mediaAdapter.isAudioPlaying = YES; - self.mediaAdapter.isPaused = NO; - [self.mediaAdapter setAudioIconToPause]; + self.delegate.isAudioPlaying = YES; + self.delegate.isPaused = NO; + [self.delegate setAudioIconToPause]; if (!self.audioPlayer) { NSError *error; - self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:self.attachmentStream.mediaURL error:&error]; + self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:self.mediaUrl error:&error]; if (error) { DDLogError(@"%@ error: %@", self.tag, error); [self stop]; @@ -107,35 +126,34 @@ NS_ASSUME_NONNULL_BEGIN - (void)pause { - OWSAssert(self.attachmentStream); + OWSAssert([NSThread isMainThread]); - self.mediaAdapter.isAudioPlaying = NO; - self.mediaAdapter.isPaused = YES; + self.delegate.isAudioPlaying = NO; + self.delegate.isPaused = YES; [self.audioPlayer pause]; [self.audioPlayerPoller invalidate]; double current = [self.audioPlayer currentTime] / [self.audioPlayer duration]; - [self.mediaAdapter setAudioProgressFromFloat:(float)current]; - [self.mediaAdapter setAudioIconToPlay]; + [self.delegate setAudioProgressFromFloat:(float)current]; + [self.delegate setAudioIconToPlay]; } - (void)stop { - OWSAssert(self.attachmentStream); + OWSAssert([NSThread isMainThread]); [self.audioPlayer pause]; [self.audioPlayerPoller invalidate]; - [self.mediaAdapter setAudioProgressFromFloat:0]; - [self.mediaAdapter setDurationOfAudio:self.audioPlayer.duration]; - [self.mediaAdapter setAudioIconToPlay]; - self.mediaAdapter.isAudioPlaying = NO; - self.mediaAdapter.isPaused = NO; + [self.delegate setAudioProgressFromFloat:0]; + [self.delegate setAudioIconToPlay]; + self.delegate.isAudioPlaying = NO; + self.delegate.isPaused = NO; } - (void)togglePlayState { - OWSAssert(self.attachmentStream); + OWSAssert([NSThread isMainThread]); - if (self.mediaAdapter.isAudioPlaying) { + if (self.delegate.isAudioPlaying) { [self pause]; } else { [self play]; @@ -146,17 +164,19 @@ NS_ASSUME_NONNULL_BEGIN - (void)audioPlayerUpdated:(NSTimer *)timer { + OWSAssert([NSThread isMainThread]); + OWSAssert(self.audioPlayer); OWSAssert(self.audioPlayerPoller); double current = [self.audioPlayer currentTime] / [self.audioPlayer duration]; - double interval = [self.audioPlayer duration] - [self.audioPlayer currentTime]; - [self.mediaAdapter setDurationOfAudio:interval]; - [self.mediaAdapter setAudioProgressFromFloat:(float)current]; + [self.delegate setAudioProgressFromFloat:(float)current]; } - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { + OWSAssert([NSThread isMainThread]); + [self stop]; }