Merge branch 'charlesmchen/audioPlayer2'

pull/1/head
Matthew Chen 7 years ago
commit 52ab0df3f3

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

@ -2,6 +2,7 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSAudioAttachmentPlayer.h"
#import "OWSMessageEditing.h"
#import "OWSMessageMediaAdapter.h"
#import <JSQMessagesViewController/JSQVideoMediaItem.h>
@ -10,22 +11,16 @@ NS_ASSUME_NONNULL_BEGIN
@class TSAttachmentStream;
@interface TSVideoAttachmentAdapter : JSQVideoMediaItem <OWSMessageEditing, OWSMessageMediaAdapter>
@interface TSVideoAttachmentAdapter
: JSQVideoMediaItem <OWSMessageEditing, OWSMessageMediaAdapter, OWSAudioAttachmentPlayerDelegate>
@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

@ -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")]

@ -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"

@ -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()
var subviews = [UIView]()
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.autoCenterInSuperview()
stackView.autoPinWidthToSuperview()
stackView.autoVCenterInSuperview()
}
private func createHeroViewSize() -> CGFloat {
return ScaleFromIPhone5To7Plus(175, 225)
}
let imageSize = ScaleFromIPhone5To7Plus(175, 225)
let image = UIImage(named:"file-icon-large")
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)
private func createFileExtensionLabel() -> UIView? {
guard let fileExtension = attachment.fileExtension else {
return nil
}
fileExtensionLabel.textColor = UIColor.white
fileExtensionLabel.font = labelFont
fileExtensionLabel.textAlignment = .center
stackView.addSubview(fileExtensionLabel)
fileExtensionLabel.autoHCenterInSuperview()
fileExtensionLabel.autoPinEdge(.top, to:.bottom, of:lastView, withOffset:10)
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)
lastView = fileExtensionLabel
}
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)
}
}

@ -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];
}
}

@ -9,13 +9,39 @@ NS_ASSUME_NONNULL_BEGIN
@class TSVideoAttachmentAdapter;
@class YapDatabaseConnection;
@protocol OWSAudioAttachmentPlayerDelegate <NSObject>
- (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 <AVAudioPlayerDelegate>
@property (nonatomic, readonly) TSVideoAttachmentAdapter *mediaAdapter;
@property (nonatomic, readonly, weak) id<OWSAudioAttachmentPlayerDelegate> 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<OWSAudioAttachmentPlayerDelegate>)delegate;
- (void)play;
- (void)pause;
- (void)stop;

@ -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<OWSAudioAttachmentPlayerDelegate>)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];
}

Loading…
Cancel
Save