From 041badd29548765cc3bba801716209d1ad0a384d Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 20 Apr 2017 10:06:31 -0400 Subject: [PATCH] Decouple the audio attachment player from the video attachment adapter. // FREEBIE --- .../TSVideoAttachmentAdapter.h | 11 +-- .../TSVideoAttachmentAdapter.m | 12 +-- .../ViewControllers/MessagesViewController.m | 5 +- .../OWSAudioAttachmentPlayer.h | 28 +++++- .../OWSAudioAttachmentPlayer.m | 88 ++++++++++++------- 5 files changed, 90 insertions(+), 54 deletions(-) diff --git a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h index 0f972b508..1b6282c5b 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 3f3c0736c..8474db5e7 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/ViewControllers/MessagesViewController.m b/Signal/src/ViewControllers/MessagesViewController.m index 28d3f1264..dcecdee09 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 9071cd07d..44da6d8cb 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 9181be7c8..6b06340a6 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]; }