Play video inline in approval view

Previously we launched the MPMoviePlayerController

// FREEBIE
This commit is contained in:
Michael Kirk 2018-01-16 17:31:22 -05:00
parent 0c6a42003f
commit 94d58b88b8
5 changed files with 196 additions and 33 deletions

View File

@ -247,7 +247,6 @@
452D1EE81DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452D1EE71DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift */; };
452EA09E1EA7ABE00078744B /* AttachmentPointerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */; };
452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */; };
453034AB200289F50018945D /* VideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 453034AA200289F50018945D /* VideoPlayerView.swift */; };
4535186B1FC635DD00210559 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4535186A1FC635DD00210559 /* ShareViewController.swift */; };
4535186E1FC635DD00210559 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4535186C1FC635DD00210559 /* MainInterface.storyboard */; };
453518721FC635DD00210559 /* SignalShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 453518681FC635DD00210559 /* SignalShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@ -271,6 +270,7 @@
455AC69E1F4F8B0300134004 /* ImageCacheTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 455AC69D1F4F8B0300134004 /* ImageCacheTest.swift */; };
45638BDC1F3DD0D400128435 /* DebugUICalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45638BDB1F3DD0D400128435 /* DebugUICalling.swift */; };
45638BDF1F3DDB2200128435 /* MessageSender+Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45638BDE1F3DDB2200128435 /* MessageSender+Promise.swift */; };
4565ED06200EA29900C46DBB /* VideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 453034AA200289F50018945D /* VideoPlayerView.swift */; };
45666F581D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F571D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m */; };
456C38961DC7B882007536A7 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 451DE9F11DC1585F00810E42 /* PromiseKit.framework */; };
456F6E2F1E261D1000FD2210 /* PeerConnectionClientTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 456F6E2E1E261D1000FD2210 /* PeerConnectionClientTest.swift */; };
@ -1571,6 +1571,7 @@
34D913491F62D4A500722898 /* SignalAttachment.swift */,
34CA1C281F7164F700E51C51 /* MediaMessageView.swift */,
45BC829C1FD9C4B400011CF3 /* ShareViewDelegate.swift */,
453034AA200289F50018945D /* VideoPlayerView.swift */,
);
path = attachments;
sourceTree = "<group>";
@ -1748,7 +1749,6 @@
76EB052B18170B33006006FC /* Views */ = {
isa = PBXGroup;
children = (
453034AA200289F50018945D /* VideoPlayerView.swift */,
45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */,
452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */,
34E3E5671EC4B19400495BAC /* AudioProgressView.swift */,
@ -2795,6 +2795,7 @@
454A965A1FD6017E008D2A0E /* SignalAttachment.swift in Sources */,
454A965B1FD601BF008D2A0E /* MediaMessageView.swift in Sources */,
45BC829D1FD9C4B400011CF3 /* ShareViewDelegate.swift in Sources */,
4565ED06200EA29900C46DBB /* VideoPlayerView.swift in Sources */,
3461295B1FD1D74C00532771 /* Environment.m in Sources */,
346129D51FD20ADC00532771 /* UIViewController+OWS.m in Sources */,
451F8A431FD714FE005CB9DA /* AvatarImageView.swift in Sources */,
@ -2925,7 +2926,6 @@
34B3F8771E8DF1700035BE1A /* ContactsPicker.swift in Sources */,
45C0DC1B1E68FE9000E04C47 /* UIApplication+OWS.swift in Sources */,
45638BDF1F3DDB2200128435 /* MessageSender+Promise.swift in Sources */,
453034AB200289F50018945D /* VideoPlayerView.swift in Sources */,
45FBC5C81DF8575700E9B410 /* CallKitCallManager.swift in Sources */,
34B3F8911E8DF1710035BE1A /* ShowGroupMembersViewController.m in Sources */,
4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */,

View File

@ -332,7 +332,6 @@ NS_ASSUME_NONNULL_BEGIN
[playVideoButton autoCenterInSuperview];
}
// Don't show footer bar after tapping approval-view
if (self.viewItem) {
UIToolbar *footerBar = [UIToolbar new];

View File

@ -3,6 +3,7 @@
//
import Foundation
import AVFoundation
import MediaPlayer
@objc
@ -12,7 +13,7 @@ public protocol AttachmentApprovalViewControllerDelegate: class {
}
@objc
public class AttachmentApprovalViewController: OWSViewController, CaptioningToolbarDelegate {
public class AttachmentApprovalViewController: OWSViewController, CaptioningToolbarDelegate, PlayerProgressBarDelegate {
let TAG = "[AttachmentApprovalViewController]"
weak var delegate: AttachmentApprovalViewControllerDelegate?
@ -26,10 +27,13 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
// MARK: Properties
let attachment: SignalAttachment
private var videoPlayer: AVPlayer?
private(set) var bottomToolbar: UIView!
private(set) var mediaMessageView: MediaMessageView!
private(set) var scrollView: UIScrollView!
private(set) var contentContainer: UIView!
private(set) var playVideoButton: UIView?
// MARK: Initializers
@ -97,10 +101,15 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
self.mediaMessageView = MediaMessageView(attachment: attachment, mode: .attachmentApproval)
// Anything that should be shrunk when user pops keyboard lives in the contentContainer.
let contentContainer = UIView()
self.contentContainer = contentContainer
view.addSubview(contentContainer)
contentContainer.autoPinEdgesToSuperviewEdges()
// Scroll View - used to zoom/pan on images and video
scrollView = UIScrollView()
view.addSubview(scrollView)
contentContainer.addSubview(scrollView)
scrollView.delegate = self
scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false
@ -139,23 +148,6 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
topGradient.autoSetDimension(.height, toSize: ScaleFromIPhone5(60))
}
// Hide the play button embedded in the MediaView and replace it with our own.
// This allows us to zoom in on the media view without zooming in on the button
if attachment.isVideo {
self.mediaMessageView.videoPlayButton?.isHidden = true
let playButton = UIButton()
playButton.accessibilityLabel = NSLocalizedString("PLAY_BUTTON_ACCESSABILITY_LABEL", comment: "accessability label for button to start media playback")
playButton.setBackgroundImage(#imageLiteral(resourceName: "play_button"), for: .normal)
playButton.contentMode = .scaleAspectFit
let playButtonWidth = ScaleFromIPhone5(70)
playButton.autoSetDimensions(to: CGSize(width: playButtonWidth, height: playButtonWidth))
self.view.addSubview(playButton)
playButton.addTarget(self, action: #selector(playButtonTapped), for: .touchUpInside)
playButton.autoCenterInSuperview()
}
// Top Toolbar
let topToolbar = makeClearToolbar()
@ -173,6 +165,67 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
let captioningToolbar = CaptioningToolbar()
captioningToolbar.captioningToolbarDelegate = self
self.bottomToolbar = captioningToolbar
// Hide the play button embedded in the MediaView and replace it with our own.
// This allows us to zoom in on the media view without zooming in on the button
if attachment.isVideo {
if #available(iOS 9.0, *) {
guard let videoURL = attachment.dataUrl else {
owsFail("Missing videoURL")
return
}
let player = AVPlayer(url: videoURL)
self.videoPlayer = player
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidPlayToCompletion(_:)),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: player.currentItem)
let playerView = VideoPlayerView()
playerView.player = player
self.mediaMessageView.addSubview(playerView)
playerView.autoPinEdgesToSuperviewEdges()
let pauseGesture = UITapGestureRecognizer(target: self, action: #selector(didTapPlayerView(_:)))
playerView.addGestureRecognizer(pauseGesture)
let progressBar = PlayerProgressBar()
progressBar.player = player
progressBar.delegate = self
// we don't want the progress bar to zoom during "pinch-to-zoom"
// but we do want it to shrink with the media content when the user
// pops the keyboard.
contentContainer.addSubview(progressBar)
progressBar.autoPinEdge(.top, to: .bottom, of: topToolbar)
progressBar.autoPinWidthToSuperview()
progressBar.autoSetDimension(.height, toSize: 44)
}
self.mediaMessageView.videoPlayButton?.isHidden = true
let playButton = UIButton()
self.playVideoButton = playButton
playButton.accessibilityLabel = NSLocalizedString("PLAY_BUTTON_ACCESSABILITY_LABEL", comment: "accessability label for button to start media playback")
playButton.setBackgroundImage(#imageLiteral(resourceName: "play_button"), for: .normal)
playButton.contentMode = .scaleAspectFit
let playButtonWidth = ScaleFromIPhone5(70)
playButton.autoSetDimensions(to: CGSize(width: playButtonWidth, height: playButtonWidth))
self.contentContainer.addSubview(playButton)
playButton.addTarget(self, action: #selector(playButtonTapped), for: .touchUpInside)
playButton.autoCenterInSuperview()
}
}
@available(iOS 9, *)
public func didTapPlayerView(_ gestureRecognizer: UIGestureRecognizer) {
assert(self.videoPlayer != nil)
self.pauseVideo()
}
override public var inputAccessoryView: UIView? {
@ -202,8 +255,7 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
@objc
public func playButtonTapped() {
// FIXME - use built in AVPlayer controls like MediaDetailViewController
// mediaMessageView.playVideo()
self.playVideo()
}
func cancelPressed(sender: UIButton) {
@ -228,6 +280,120 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
Logger.info("Changed height: \(newHeight)")
}
// MARK: Video
private func playVideo() {
Logger.info("\(TAG) in \(#function)")
if #available(iOS 9, *) {
guard let videoPlayer = self.videoPlayer else {
owsFail("\(TAG) video player was unexpectedly nil")
return
}
guard let playVideoButton = self.playVideoButton else {
owsFail("\(TAG) playVideoButton was unexpectedly nil")
return
}
UIView.animate(withDuration: 0.1) {
playVideoButton.alpha = 0.0
}
guard let item = videoPlayer.currentItem else {
owsFail("\(TAG) video player item was unexpectedly nil")
return
}
if item.currentTime() == item.duration {
// Rewind for repeated plays, but only if it previously played to end.
videoPlayer.seek(to: kCMTimeZero)
}
videoPlayer.play()
} else {
self.playLegacyVideo()
}
}
private func playLegacyVideo() {
if #available(iOS 9, *) {
owsFail("should only use legacy video on iOS8")
}
guard let videoURL = self.attachment.dataUrl else {
owsFail("videoURL was unexpectedly nil")
return
}
guard let playerVC = MPMoviePlayerViewController(contentURL: videoURL) else {
owsFail("failed to init legacy video player")
return
}
self.present(playerVC, animated: true)
}
@available(iOS 9, *)
private func pauseVideo() {
guard let videoPlayer = self.videoPlayer else {
owsFail("\(TAG) video player was unexpectedly nil")
return
}
videoPlayer.pause()
guard let playVideoButton = self.playVideoButton else {
owsFail("\(TAG) playVideoButton was unexpectedly nil")
return
}
UIView.animate(withDuration: 0.1) {
playVideoButton.alpha = 1.0
}
}
@objc
private func playerItemDidPlayToCompletion(_ notification: Notification) {
guard let playVideoButton = self.playVideoButton else {
owsFail("\(TAG) playVideoButton was unexpectedly nil")
return
}
UIView.animate(withDuration: 0.1) {
playVideoButton.alpha = 1.0
}
}
@available(iOS 9.0, *)
public func playerProgressBarDidStartScrubbing(_ playerProgressBar: PlayerProgressBar) {
// [self.videoPlayer pause];
guard let videoPlayer = self.videoPlayer else {
owsFail("\(TAG) video player was unexpectedly nil")
return
}
videoPlayer.pause()
}
@available(iOS 9.0, *)
public func playerProgressBar(_ playerProgressBar: PlayerProgressBar, scrubbedToTime time: CMTime) {
guard let videoPlayer = self.videoPlayer else {
owsFail("\(TAG) video player was unexpectedly nil")
return
}
videoPlayer.seek(to: time)
}
@available(iOS 9.0, *)
public func playerProgressBar(_ playerProgressBar: PlayerProgressBar, didFinishScrubbingAtTime time: CMTime, shouldResumePlayback: Bool) {
guard let videoPlayer = self.videoPlayer else {
owsFail("\(TAG) video player was unexpectedly nil")
return
}
videoPlayer.seek(to: time)
if (shouldResumePlayback) {
videoPlayer.play()
}
}
// MARK: Helpers
var isZoomable: Bool {
@ -252,9 +418,9 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
private func scaleAttachmentView(_ fit: AttachmentViewScale) {
guard shouldAllowAttachmentViewResizing else {
if self.scrollView.transform != CGAffineTransform.identity {
if self.contentContainer.transform != CGAffineTransform.identity {
UIView.animate(withDuration: 0.2) {
self.scrollView.transform = CGAffineTransform.identity
self.contentContainer.transform = CGAffineTransform.identity
}
}
return
@ -263,7 +429,7 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
switch fit {
case .fullsize:
UIView.animate(withDuration: 0.2) {
self.scrollView.transform = CGAffineTransform.identity
self.contentContainer.transform = CGAffineTransform.identity
}
case .compact:
UIView.animate(withDuration: 0.2) {
@ -277,7 +443,7 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
let heightDelta = originalHeight * (1 - kScaleFactor)
let translate = CGAffineTransform(translationX: 0, y: -heightDelta / 2)
self.scrollView.transform = scale.concatenating(translate)
self.contentContainer.transform = scale.concatenating(translate)
}
}
}

View File

@ -32,9 +32,6 @@ public class MediaMessageView: UIView, OWSAudioAttachmentPlayerDelegate {
@objc
public let attachment: SignalAttachment
@objc
public var videoPlayer: MPMoviePlayerController?
@objc
public var audioPlayer: OWSAudioAttachmentPlayer?

View File

@ -3,6 +3,7 @@
//
import Foundation
import AVFoundation
@available(iOS 9.0, *)
@objc