Skip to content

Commit

Permalink
- ComposedMessageView: apply syntactic sugar
Browse files Browse the repository at this point in the history
- MediaDisplayViewController: when streaming, show a loading indicator (fixing finding (5) in #1095)
  • Loading branch information
felix-schwarz committed Jun 14, 2023
1 parent bc972a1 commit 73a0eb7
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 32 deletions.
128 changes: 97 additions & 31 deletions ownCloud/Client/Viewer/Media/MediaDisplayViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,41 @@ class MediaDisplayViewController : DisplayViewController {
NotificationCenter.default.removeObserver(self, name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
}

var showLoadingIndicator: Bool = false {
didSet {
if oldValue != showLoadingIndicator {
if showLoadingIndicator {
// Show loading indicator
let indeterminateProgress: Progress = .indeterminate()
indeterminateProgress.isCancellable = false

let messageView = ComposedMessageView.infoBox(additionalElements: [
.spacing(25),
.progressCircle(with: indeterminateProgress),
.spacing(25),
.title("Loading…".localized, alignment: .centered)
], withRoundedBackgroundView: true)

loadingIndicator = messageView
} else {
// Remove loading indicator
loadingIndicator = nil
}
}
}
}

private var loadingIndicator: ComposedMessageView? {
willSet {
loadingIndicator?.removeFromSuperview()
}
didSet {
if let loadingIndicator {
view.embed(centered: loadingIndicator)
}
}
}

override func viewDidLoad() {
super.viewDidLoad()

Expand Down Expand Up @@ -131,6 +166,8 @@ class MediaDisplayViewController : DisplayViewController {
return (OCAppIdentity.shared.userDefaults?.downloadMediaFiles ?? false)
}

private var timeControlStatusObservation: NSKeyValueObservation?

override func renderItem(completion: @escaping (Bool) -> Void) {
if let directURL = itemDirectURL {
playerItemStatusObservation?.invalidate()
Expand All @@ -157,50 +194,30 @@ class MediaDisplayViewController : DisplayViewController {
}
}

// Add artwork to the player overlay if corresponding meta data item is available in the asset
if !(player?.isVideoAvailable ?? false), let artworkMetadataItem = asset.commonMetadata.filter({$0.commonKey == AVMetadataKey.commonKeyArtwork}).first,
let imageData = artworkMetadataItem.dataValue,
let overlayView = playerViewController?.contentOverlayView {

if let artworkImage = UIImage(data: imageData) {

// Construct image view overlay for AVPlayerViewController
let imageView = UIImageView(image: artworkImage)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
playerViewController?.contentOverlayView?.addSubview(imageView)

NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: overlayView.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: overlayView.trailingAnchor),
imageView.topAnchor.constraint(equalTo: overlayView.topAnchor),
imageView.bottomAnchor.constraint(equalTo: overlayView.bottomAnchor)
])

// Create MPMediaItemArtwork to be shown in 'now playing' in the lock screen
mediaItemArtwork = MPMediaItemArtwork(boundsSize: artworkImage.size, requestHandler: { (_) -> UIImage in
return artworkImage
})
}
}

// Extract title meta-data item
mediaItemTitle = asset.commonMetadata.filter({$0.commonKey == AVMetadataKey.commonKeyTitle}).first?.value as? String
// Start with the loading indicator active
showLoadingIndicator = true

// Extract artist meta-data item
mediaItemArtist = asset.commonMetadata.filter({$0.commonKey == AVMetadataKey.commonKeyArtist}).first?.value as? String
// .. it will be updated as soon as the player starts playing ..
timeControlStatusObservation = player?.observe(\AVPlayer.timeControlStatus, changeHandler: { [weak self] player, change in
self?.updateLoadingIndicator()
})

// Setup player status observation handler
playerStatusObservation = player!.observe(\AVPlayer.status, options: [.initial, .new], changeHandler: { [weak self] (player, _) in
if player.status == .readyToPlay {
self?.updateMediaMetadata()

self?.setupRemoteTransportControls()

try? AVAudioSession.sharedInstance().setCategory(.playback)
try? AVAudioSession.sharedInstance().setActive(true)

if (self?.hasFocus)! {
// .. with playback starting here.
self?.player?.play()
} else {
// .. or the loading indicator being updated when the file is ready to play, here.
self?.updateLoadingIndicator()
}

self?.updateNowPlayingInfoCenter()
Expand All @@ -218,6 +235,55 @@ class MediaDisplayViewController : DisplayViewController {
}
}

private func updateLoadingIndicator() {
if let player {
let showLoadingIndicator = (player.timeControlStatus == .waitingToPlayAtSpecifiedRate)

OnMainThread(inline: true) {
self.showLoadingIndicator = showLoadingIndicator
}
}
}

private func updateMediaMetadata() {
guard let asset = playerItem?.asset else { return }

// Add artwork to the player overlay if corresponding meta data item is available in the asset
if !(player?.isVideoAvailable ?? false), let artworkMetadataItem = asset.commonMetadata.filter({$0.commonKey == AVMetadataKey.commonKeyArtwork}).first,
let imageData = artworkMetadataItem.dataValue,
let overlayView = playerViewController?.contentOverlayView {

if let artworkImage = UIImage(data: imageData) {

// Construct image view overlay for AVPlayerViewController
OnMainThread(inline: true) { [weak self] in
let imageView = UIImageView(image: artworkImage)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
self?.playerViewController?.contentOverlayView?.addSubview(imageView)

NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: overlayView.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: overlayView.trailingAnchor),
imageView.topAnchor.constraint(equalTo: overlayView.topAnchor),
imageView.bottomAnchor.constraint(equalTo: overlayView.bottomAnchor)
])
}

// Create MPMediaItemArtwork to be shown in 'now playing' in the lock screen
mediaItemArtwork = MPMediaItemArtwork(boundsSize: artworkImage.size, requestHandler: { (_) -> UIImage in
return artworkImage
})
}
}

// Extract title meta-data item
mediaItemTitle = asset.commonMetadata.filter({$0.commonKey == AVMetadataKey.commonKeyTitle}).first?.value as? String

// Extract artist meta-data item
mediaItemArtist = asset.commonMetadata.filter({$0.commonKey == AVMetadataKey.commonKeyArtist}).first?.value as? String
}

private func present(error:Error?) {
guard let error = error else { return }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ public extension ComposedMessageView {
static func infoBox(image: UIImage? = nil, title: String? = nil, subtitle: String? = nil, additionalElements: [ComposedMessageElement]? = nil, withRoundedBackgroundView: Bool = true) -> ComposedMessageView {
var elements: [ComposedMessageElement] = []

if let image = image {
if let image {
let imageElement: ComposedMessageElement = .image(image, size: CGSize(width: 48, height: 48), alignment: .centered, cssSelectors: [.icon])
imageElement.insets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 10, trailing: 10)
elements.append(imageElement)
Expand Down

0 comments on commit 73a0eb7

Please sign in to comment.