Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support videos in media view #1844

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions NextcloudTalk/BaseChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,7 @@
}
}

func didPressDelete(for message: NCChatMessage) {

Check warning on line 1100 in NextcloudTalk/BaseChatViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Cyclomatic Complexity Violation: Function should have complexity 10 or less; currently complexity is 11 (cyclomatic_complexity)
if message.sendingFailed || message.isOfflineMessage {
self.removePermanentlyTemporaryMessage(temporaryMessage: message)
return
Expand Down Expand Up @@ -1196,7 +1196,7 @@

func sendStartedTypingMessage(to sessionId: String) {
// Workaround: TypingPrivacy should be checked locally, not from the remote server, use serverCapabilities for now
// TODO: Remove workaround for federated typing indicators.

Check warning on line 1199 in NextcloudTalk/BaseChatViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Todo Violation: TODOs should be resolved (Remove workaround for federate...) (todo)
guard let serverCapabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: self.room.accountId)
else { return }

Expand All @@ -1213,7 +1213,7 @@

func sendStartedTypingMessageToAll() {
// Workaround: TypingPrivacy should be checked locally, not from the remote server, use serverCapabilities for now
// TODO: Remove workaround for federated typing indicators.

Check warning on line 1216 in NextcloudTalk/BaseChatViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Todo Violation: TODOs should be resolved (Remove workaround for federate...) (todo)
guard let serverCapabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: self.room.accountId),
!serverCapabilities.typingPrivacy,
let signalingController = NCSettingsController.sharedInstance().externalSignalingController(forAccountId: self.room.accountId)
Expand Down Expand Up @@ -1921,7 +1921,7 @@
return super.gestureRecognizerShouldBegin(gestureRecognizer)
}

func handleLongPressInVoiceMessageRecordButton(gestureRecognizer: UILongPressGestureRecognizer) {

Check warning on line 1924 in NextcloudTalk/BaseChatViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Cyclomatic Complexity Violation: Function should have complexity 10 or less; currently complexity is 13 (cyclomatic_complexity)
if self.rightButton.tag != sendButtonTagVoice {
return
}
Expand Down Expand Up @@ -3378,6 +3378,20 @@
return
}

let filePath = fileParameter.path ?? ""
let fileExtension = URL(fileURLWithPath: filePath).pathExtension.lowercased()

if NCUtils.isVideo(fileType: fileParameter.mimetype) {
// Skip unsupported formats here ("webm" and "mkv") and use VLC later
if !fileExtension.isEmpty, !VLCKitVideoViewController.supportedFileExtensions.contains(fileExtension) {
let mediaViewController = NCMediaViewerViewController(initialMessage: message)
let navController = CustomPresentableNavigationController(rootViewController: mediaViewController)

self.present(navController, interactiveDismissalType: .standard)
return
}
}

if fileParameter.fileStatus != nil && fileParameter.fileStatus?.isDownloading ?? false {
print("File already downloading -> skipping new download")
return
Expand Down Expand Up @@ -3553,7 +3567,7 @@

// MARK: - NCChatFileControllerDelegate

public func fileControllerDidLoadFile(_ fileController: NCChatFileController, with fileStatus: NCChatFileStatus) {

Check warning on line 3570 in NextcloudTalk/BaseChatViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Cyclomatic Complexity Violation: Function should have complexity 10 or less; currently complexity is 11 (cyclomatic_complexity)
if fileController.messageType == kMessageTypeVoiceMessage {
if fileController.actionType == actionTypeTranscribeVoiceMessage {
self.transcribeVoiceMessage(with: fileStatus)
Expand Down
92 changes: 79 additions & 13 deletions NextcloudTalk/NCMediaViewerPageViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//

import AVKit
import AVFoundation
import Foundation
import UIKit
import SwiftyGif

@objc protocol NCMediaViewerPageViewControllerDelegate {
@objc func mediaViewerPageZoomDidChange(_ controller: NCMediaViewerPageViewController, _ scale: Double)
@objc func mediaViewerPageImageDidLoad(_ controller: NCMediaViewerPageViewController)
@objc func mediaViewerPageMediaDidLoad(_ controller: NCMediaViewerPageViewController)
}

@objcMembers class NCMediaViewerPageViewController: UIViewController, NCChatFileControllerDelegate, NCZoomableViewDelegate {
Expand Down Expand Up @@ -72,6 +74,10 @@ import SwiftyGif
return self.imageView.image
}

public var currentVideoURL: URL?

private var playerViewController: AVPlayerViewController?

private lazy var activityIndicator = {
let indicator = NCActivityIndicator(frame: .init(x: 0, y: 0, width: 100, height: 100))
indicator.translatesAutoresizingMaskIntoConstraints = false
Expand Down Expand Up @@ -121,6 +127,7 @@ import SwiftyGif

func showErrorView() {
self.imageView.image = nil
self.removePlayerViewControllerIfNeeded()
self.view.addSubview(self.errorView)

NSLayoutConstraint.activate([
Expand All @@ -136,24 +143,18 @@ import SwiftyGif
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true

guard let localPath = fileStatus.fileLocalPath, let image = UIImage(contentsOfFile: localPath) else {
guard let localPath = fileStatus.fileLocalPath else {
self.showErrorView()
return
}

if let file = message.file(), message.isAnimatableGif,
let data = try? Data(contentsOf: URL(fileURLWithPath: localPath)), let gifImage = try? UIImage(gifData: data) {

self.imageView.setGifImage(gifImage)
if NCUtils.isImage(fileType: message.file().mimetype) {
displayImage(from: localPath)
} else if NCUtils.isVideo(fileType: message.file().mimetype) {
playVideo(from: localPath)
} else {
self.imageView.image = image
self.showErrorView()
}

// Adjust the view to the new image (use the non-gif version here for correct dimensions)
self.zoomableView.contentViewSize = image.size
self.zoomableView.resizeContentView()

self.delegate?.mediaViewerPageImageDidLoad(self)
}

func fileControllerDidFailLoadingFile(_ fileController: NCChatFileController, withErrorDescription errorDescription: String) {
Expand Down Expand Up @@ -188,4 +189,69 @@ import SwiftyGif
func contentViewZoomDidChange(_ view: NCZoomableView, _ scale: Double) {
self.delegate?.mediaViewerPageZoomDidChange(self, scale)
}

private func displayImage(from localPath: String) {
guard let image = UIImage(contentsOfFile: localPath) else {
self.showErrorView()
return
}

if let file = message.file(), message.isAnimatableGif,
let data = try? Data(contentsOf: URL(fileURLWithPath: localPath)),
let gifImage = try? UIImage(gifData: data) {

self.imageView.setGifImage(gifImage)
} else {
self.imageView.image = image
}

// Adjust the view to the new image (use the non-gif version here for correct dimensions)
self.zoomableView.contentViewSize = image.size
self.zoomableView.resizeContentView()

self.zoomableView.isHidden = false
self.imageView.isHidden = false

removePlayerViewControllerIfNeeded()
self.delegate?.mediaViewerPageMediaDidLoad(self)
}

private func playVideo(from localPath: String) {
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback, options: [])
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("Failed to set audio session category: \(error)")
}

let videoURL = URL(fileURLWithPath: localPath)
self.currentVideoURL = videoURL
let player = AVPlayer(url: videoURL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.playerViewController = playerViewController

self.addChild(playerViewController)
self.view.addSubview(playerViewController.view)
playerViewController.view.frame = self.view.bounds
playerViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
playerViewController.didMove(toParent: self)

self.zoomableView.contentViewSize = playerViewController.view.bounds.size
self.zoomableView.resizeContentView()
self.zoomableView.isHidden = false
self.imageView.isHidden = true

self.delegate?.mediaViewerPageMediaDidLoad(self)
}

private func removePlayerViewControllerIfNeeded() {
if let playerVC = self.playerViewController {
playerVC.willMove(toParent: nil)
playerVC.view.removeFromSuperview()
playerVC.removeFromParent()
self.playerViewController = nil
self.currentVideoURL = nil
}
}
}
42 changes: 31 additions & 11 deletions NextcloudTalk/NCMediaViewerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@ import UIKit

private lazy var shareButton = {
let shareButton = UIBarButtonItem(title: nil, style: .plain, target: nil, action: nil)

shareButton.isEnabled = false
shareButton.primaryAction = UIAction(title: "", image: .init(systemName: "square.and.arrow.up"), handler: { [unowned self, unowned shareButton] _ in
guard let mediaPageViewController = self.getCurrentPageViewController(),
let image = mediaPageViewController.currentImage
else { return }
guard let mediaPageViewController = self.getCurrentPageViewController() else { return }

var itemsToShare: [Any] = []

let activityViewController = UIActivityViewController(activityItems: [image], applicationActivities: nil)
if let image = mediaPageViewController.currentImage {
itemsToShare.append(image)
} else if let videoURL = mediaPageViewController.currentVideoURL {
itemsToShare.append(videoURL)
} else {
return
}
let activityViewController = UIActivityViewController(activityItems: itemsToShare, applicationActivities: nil)
activityViewController.popoverPresentationController?.barButtonItem = shareButton

self.present(activityViewController, animated: true)
Expand Down Expand Up @@ -110,11 +116,18 @@ import UIKit
let messageObject = queriedObjects.lastObject()

if let message = messageObject as? NCChatMessage {
if NCUtils.isImage(fileType: message.file().mimetype) {
guard let filePath = message.file().path else {
return self.getPreviousFileMessage(from: message)
}

let fileType = message.file().mimetype
let isSupportedMedia = NCUtils.isImage(fileType: fileType) || NCUtils.isVideo(fileType: fileType)
let isUnsupportedExtension = VLCKitVideoViewController.supportedFileExtensions.contains(URL(fileURLWithPath: filePath).pathExtension.lowercased())

if isSupportedMedia && !isUnsupportedExtension {
return message
}

// The current message contains a file, but not an image -> try to find another message
return self.getPreviousFileMessage(from: message)
}

Expand All @@ -127,11 +140,18 @@ import UIKit
guard let messageObject = self.getAllFileMessages()?.objects(with: prevQuery).firstObject() else { return nil }

if let message = messageObject as? NCChatMessage {
if NCUtils.isImage(fileType: message.file().mimetype) {
guard let filePath = message.file().path else {
return self.getNextFileMessage(from: message)
}

let fileType = message.file().mimetype
let isSupportedMedia = NCUtils.isImage(fileType: fileType) || NCUtils.isVideo(fileType: fileType)
let isUnsupportedExtension = VLCKitVideoViewController.supportedFileExtensions.contains(URL(fileURLWithPath: filePath).pathExtension.lowercased())

if isSupportedMedia && !isUnsupportedExtension {
return message
}

// The current message contains a file, but not an image -> try to find another message
return self.getNextFileMessage(from: message)
}

Expand Down Expand Up @@ -163,7 +183,7 @@ import UIKit
guard let mediaPageViewController = self.getCurrentPageViewController() else { return }
self.navigationItem.title = mediaPageViewController.navigationItem.title

self.shareButton.isEnabled = (mediaPageViewController.currentImage != nil)
self.shareButton.isEnabled = (mediaPageViewController.currentImage != nil) || (mediaPageViewController.currentVideoURL != nil)
}

// MARK: - NCMediaViewerPageViewController delegate
Expand All @@ -183,7 +203,7 @@ import UIKit
}
}

func mediaViewerPageImageDidLoad(_ controller: NCMediaViewerPageViewController) {
func mediaViewerPageMediaDidLoad(_ controller: NCMediaViewerPageViewController) {
if let mediaPageViewController = self.getCurrentPageViewController(), mediaPageViewController.isEqual(controller) {
self.shareButton.isEnabled = true
}
Expand Down
Loading