diff --git a/BoxPreviewSDK.xcodeproj/project.pbxproj b/BoxPreviewSDK.xcodeproj/project.pbxproj index 941d839..649fc67 100644 --- a/BoxPreviewSDK.xcodeproj/project.pbxproj +++ b/BoxPreviewSDK.xcodeproj/project.pbxproj @@ -55,6 +55,7 @@ 3549BB501DA38A2000C63030 /* BoxPreviewSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = 3549BB181DA3890B00C63030 /* BoxPreviewSDK.h */; settings = {ATTRIBUTES = (Public, ); }; }; 359649031FB18CC000FC7ADC /* BoxPreviewSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 359649021FB18CBF00FC7ADC /* BoxPreviewSDK.swift */; }; 919E896722DFC45700E94CCA /* BoxPreviewError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 919E896622DFC45700E94CCA /* BoxPreviewError.swift */; }; + F9D5628B23AC63FE003553D8 /* AVViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9D5628A23AC63FE003553D8 /* AVViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -133,6 +134,7 @@ 35B7422D1F82A5DF00829B21 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = ""; }; 35B7422E1F82A5E000829B21 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = ""; }; 919E896622DFC45700E94CCA /* BoxPreviewError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxPreviewError.swift; sourceTree = ""; }; + F9D5628A23AC63FE003553D8 /* AVViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -265,6 +267,7 @@ 0CEFC90122C219640013B84B /* Viewers */ = { isa = PBXGroup; children = ( + F9D5628923AC63E3003553D8 /* AV */, 0C7B97A622CB73560086F6A1 /* QuickLook */, 0CEFC90322C219760013B84B /* Image */, 0CEFC90222C219720013B84B /* PDF */, @@ -396,6 +399,14 @@ path = Errors; sourceTree = ""; }; + F9D5628923AC63E3003553D8 /* AV */ = { + isa = PBXGroup; + children = ( + F9D5628A23AC63FE003553D8 /* AVViewController.swift */, + ); + path = AV; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -614,6 +625,7 @@ 0C02F8E422E06A7300B6419F /* CustomProgressView.swift in Sources */, 0C871F76230ACCE000F51102 /* SearchNavigationView.swift in Sources */, 0C61953922CA3874004B7302 /* ThumbnailGridViewController.swift in Sources */, + F9D5628B23AC63FE003553D8 /* AVViewController.swift in Sources */, 17B32FE322FC64AC0043CFBE /* ThumbnailHorizontalGridViewController.swift in Sources */, 0CAF0A7223055D7A00F685C2 /* SearchHistoryHeaderView.swift in Sources */, 0C5FD99022DF177C002F47A5 /* UIDevice+Box.swift in Sources */, diff --git a/BoxPreviewSampleApp/ViewControllers/ViewController.swift b/BoxPreviewSampleApp/ViewControllers/ViewController.swift index 09116c4..e15fbdb 100644 --- a/BoxPreviewSampleApp/ViewControllers/ViewController.swift +++ b/BoxPreviewSampleApp/ViewControllers/ViewController.swift @@ -67,7 +67,7 @@ class ViewController: UITableViewController { override func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { let item = folderItems[indexPath.row] if case let .file(file) = item { - showPreviewViewController(fileId: file.id) + showPreviewViewController(file: file) } } } @@ -124,8 +124,8 @@ private extension ViewController { } } - func showPreviewViewController(fileId: String) { - let previewController: PreviewViewController = previewSDK.openFile(fileId: fileId, delegate: self) + func showPreviewViewController(file: File) { + let previewController: PreviewViewController = previewSDK.openFile(file: file, delegate: self) navigationController?.pushViewController(previewController, animated: true) } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 24b01fb..5c8e20b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ Changelog ========= +# Next Release + +__Breaking Changes:__ + +__New Features and Enhancements:__ + +- Add audio and video previewing + ## v3.1.0 [2020-01-10] __Breaking Changes:__ diff --git a/SampleApps/JWTSampleApp/JWTSampleApp/ViewControllers/ViewController.swift b/SampleApps/JWTSampleApp/JWTSampleApp/ViewControllers/ViewController.swift index d02e9b8..d7df974 100644 --- a/SampleApps/JWTSampleApp/JWTSampleApp/ViewControllers/ViewController.swift +++ b/SampleApps/JWTSampleApp/JWTSampleApp/ViewControllers/ViewController.swift @@ -112,7 +112,7 @@ class ViewController: UITableViewController { override func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { let item = folderItems[indexPath.row] if case let .file(file) = item { - showPreviewViewController(fileId: file.id) + showPreviewViewController(file: file) } } } @@ -207,8 +207,8 @@ private extension ViewController { } } - func showPreviewViewController(fileId: String) { - let previewController: PreviewViewController? = previewSDK?.openFile(fileId: fileId, delegate: self) + func showPreviewViewController(file: File) { + let previewController: PreviewViewController? = previewSDK?.openFile(file: file, delegate: self) guard let unwrappedPreviewController = previewController else { return diff --git a/SampleApps/OAuth2SampleApp/OAuth2SampleApp/ViewControllers/ViewController.swift b/SampleApps/OAuth2SampleApp/OAuth2SampleApp/ViewControllers/ViewController.swift index 28d691d..ff08fd1 100644 --- a/SampleApps/OAuth2SampleApp/OAuth2SampleApp/ViewControllers/ViewController.swift +++ b/SampleApps/OAuth2SampleApp/OAuth2SampleApp/ViewControllers/ViewController.swift @@ -130,7 +130,7 @@ class ViewController: UITableViewController, ASWebAuthenticationPresentationCont override func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { let item = folderItems[indexPath.row] if case let .file(file) = item { - showPreviewViewController(fileId: file.id) + showPreviewViewController(file: file) } } } @@ -209,8 +209,8 @@ private extension ViewController { } } - func showPreviewViewController(fileId: String) { - let previewController: PreviewViewController? = previewSDK?.openFile(fileId: fileId, delegate: self) + func showPreviewViewController(file: File) { + let previewController: PreviewViewController? = previewSDK?.openFile(file: file, delegate: self) guard let unwrappedPreviewController = previewController else { return diff --git a/Sources/Core/BoxPreviewSDK.swift b/Sources/Core/BoxPreviewSDK.swift index 4dec6f9..ce9f55c 100644 --- a/Sources/Core/BoxPreviewSDK.swift +++ b/Sources/Core/BoxPreviewSDK.swift @@ -65,4 +65,19 @@ public extension BoxPreviewSDK { allowedAction: [FileInteractions] = FileInteractions.allCases) -> PreviewViewController { return PreviewViewController(client: client, fileId: fileId, delegate: delegate, allowedActions: allowedAction) } + + /// Creates UIViewController for previewing file detail. This is the preferred openFile method as it makes less API calls than calling openFile with just the fileID. + /// + /// - Parameters: + /// - file: File Info object of the file to preview. + /// - delegate: Delegate for catching the errors for further handling. + /// - allowedAction: Actions on image user can perform such as saving to library or files. By default all actions are allowed. + /// - customErrorView: Error view with custom design. To implement your own error view, implement ErrorView protocol and pass new error view here. + /// Error view is then displayed full screen. + /// - Returns: Returns UIViewController displaying detail of the file. + func openFile(file: File, + delegate: PreviewViewControllerDelegate? = nil, + allowedAction: [FileInteractions] = FileInteractions.allCases) -> PreviewViewController { + return PreviewViewController(client: client, file: file, delegate: delegate, allowedActions: allowedAction) + } } diff --git a/Sources/Core/UI/PreviewHelper.swift b/Sources/Core/UI/PreviewHelper.swift index 758754d..66b461e 100644 --- a/Sources/Core/UI/PreviewHelper.swift +++ b/Sources/Core/UI/PreviewHelper.swift @@ -15,37 +15,23 @@ internal class PreviewHelper { // MARK: - Properties var client: BoxClient - var fileId: String + var file: File? + var fileName: String? var filePath: URL? - private var supportedFileFormat = ["pdf", "jpg", "jpeg", "png", "tiff", "tif", "gif", "bmp", "BMPf", "ico", "cur", "xbm"] + var AVFileFormat = ["mp4", "mov", "wmv", "flv", "avi", "mp3"] + var otherFileFormat = ["pdf", "jpg", "jpeg", "png", "tiff", "tif", "gif", "bmp", "BMPf", "ico", "cur", "xbm"] + var supportedFileFormat: [String] { + return AVFileFormat + otherFileFormat + } // MARK: - Init - public init(client: BoxClient, fileId: String) { + public init(client: BoxClient) { self.client = client - self.fileId = fileId } // MARK: - Helpers - func downloadBoxFile(progress: @escaping (Progress) -> Void, completion: @escaping (Result) -> Void) { - client.files.get(fileId: fileId) { [weak self] (result: Result) in - guard let self = self else { - return - } - - switch result { - case let .failure(error): - completion(.failure(error)) - return - - case let .success(file): - self.downloadFile(file: file, progress: progress, completion: completion) - } - } - } - - func getChildViewController(withActions actions: [FileInteractions]) -> Result { var childViewController: PreviewItemChildViewController @@ -58,11 +44,11 @@ internal class PreviewHelper { do { let data = try Data(contentsOf: unwrappedFileURL) if let document = PDFDocument(data: data) { - childViewController = PDFViewController(document: document, title: unwrappedFileURL.lastPathComponent, actions: actions) + childViewController = PDFViewController(document: document, title: fileName, actions: actions) return .success(childViewController) } else { - return .failure(BoxPreviewError(message: .unableToReadFile(fileId))) + return .failure(BoxPreviewError(message: .unableToReadFile(file?.id ?? ""))) } } catch { @@ -72,32 +58,51 @@ internal class PreviewHelper { do { let data = try Data(contentsOf: unwrappedFileURL) if let image = UIImage(data: data) { - childViewController = ImageViewController(image: image, title: unwrappedFileURL.lastPathComponent, actions: actions) + childViewController = ImageViewController(image: image, title: fileName, actions: actions) return .success(childViewController) } else { - return .failure(BoxPreviewError(message: .unableToReadFile(fileId))) + return .failure(BoxPreviewError(message: .unableToReadFile(file?.id ?? ""))) } } catch { return .failure(BoxPreviewError(error: error)) } + case "mp4", "mov", "wmv", "flv", "avi", "mp3": + childViewController = AVViewController(url: unwrappedFileURL, file: file, actions: actions) + return .success(childViewController) + case "m3u8": + childViewController = AVViewController(url: unwrappedFileURL, file: file, client: client, actions: actions) + return .success(childViewController) default: return .failure(BoxPreviewError(message: .unknownFileType(unwrappedFileURL.pathExtension))) } } - - // MARK: - Private helpers - - private func downloadFile(file: File, progress: @escaping (Progress) -> Void, - completion: @escaping (Result) -> Void) { + func downloadFile(file: File, + representations: [FileRepresentation]? = nil, + progress: @escaping (Progress) -> Void, + completion: @escaping (Result) -> Void) { guard let fileName = file.name, let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return } + self.fileName = fileName + self.file = file + var fileURL = documentsURL.appendingPathComponent(fileName) - if supportedFileFormat.contains(fileURL.pathExtension) { + if let streamRepresentation = representations?.first(where: { $0.representation == "hls" }) { + guard let streamURL = streamRepresentation.content?.urlTemplate else { + return + } + // Gets content URL for a stream from the API, then replaces the last path component `{asset+path}` with `master.m3u8`. + // This newly formed URL is the HLS stream + fileURL = URL(fileURLWithPath: streamURL) + fileURL.deleteLastPathComponent() + fileURL.appendPathComponent("master.m3u8") + completion(self.processFileDownload(to: fileURL, result: .success(()))) + } + else if supportedFileFormat.contains(fileURL.pathExtension) { self.client.files.download( fileId: file.id, destinationURL: fileURL, @@ -106,7 +111,6 @@ internal class PreviewHelper { guard let self = self else { return } - completion(self.processFileDownload(to: fileURL, result: result)) } ) @@ -126,6 +130,8 @@ internal class PreviewHelper { } } + // MARK: - Private helpers + private func processFileDownload(to fileURL: URL, result: Result) -> Result { switch result { case .success: diff --git a/Sources/Core/UI/PreviewItemChildViewController.swift b/Sources/Core/UI/PreviewItemChildViewController.swift index fe9e138..6e34460 100644 --- a/Sources/Core/UI/PreviewItemChildViewController.swift +++ b/Sources/Core/UI/PreviewItemChildViewController.swift @@ -122,4 +122,13 @@ extension PreviewItemChildViewController { let activityController: UIActivityViewController = UIActivityViewController(activityItems: [path], applicationActivities: nil) present(activityController, animated: true, completion: nil) } + + /// Displays available share and other options for a file. + /// + /// - Parameters: + /// - filePath: URL of the file path to the downloaded file + func displayAllShareOptions(filePath: URL) { + let activityController: UIActivityViewController = UIActivityViewController(activityItems: [filePath], applicationActivities: nil) + present(activityController, animated: true, completion: nil) + } } diff --git a/Sources/Core/UI/PreviewViewController.swift b/Sources/Core/UI/PreviewViewController.swift index bb2e8bf..98be263 100644 --- a/Sources/Core/UI/PreviewViewController.swift +++ b/Sources/Core/UI/PreviewViewController.swift @@ -12,19 +12,19 @@ import UIKit // MARK: - Preview actions public enum FileInteractions: CaseIterable { - + // MARK: - Image-only actions - + /// Saves image to image library case saveImageToLibrary - + // MARK: - Common actions - + /// Print image or PDF case print /// Saves file to file system case saveToFiles - + /// Allow all share and save actions that iOS offers including printing /// Automatically replaces print, saveToFiles and saveImageToLibrary actions case allShareAndSaveActions @@ -38,58 +38,74 @@ public protocol PreviewViewControllerDelegate: class { } public class PreviewViewController: UIViewController { - + // MARK: - Properties - + var client: BoxClient + var fileId: String? + var file: File? var shouldHideToolbarWhenDisappearing: Bool = true - + weak var parentWithToolbar: UIViewController? weak var delegate: PreviewViewControllerDelegate? weak var fullScreenDelegate: PreviewItemFullScreenDelegate? private let itemActions: [FileInteractions] - + private lazy var progressView: CustomProgressView = { let progressView = CustomProgressView() return progressView }() - + private lazy var errorView: ErrorView = { let errorView = self.delegate?.makeCustomErrorView() ?? DefaultErrorView() errorView.translatesAutoresizingMaskIntoConstraints = false return errorView }() - + // swiftlint:disable:next implicitly_unwrapped_optional private var previewHelper: PreviewHelper! - + // MARK: - Inits - + public init(client: BoxClient, fileId: String, delegate: PreviewViewControllerDelegate? = nil, allowedActions: [FileInteractions] = FileInteractions.allCases) { - previewHelper = PreviewHelper(client: client, fileId: fileId) + self.client = client + self.fileId = fileId + previewHelper = PreviewHelper(client: client) self.delegate = delegate itemActions = allowedActions super.init(nibName: nil, bundle: nil) } - + + public init(client: BoxClient, + file: File, + delegate: PreviewViewControllerDelegate? = nil, + allowedActions: [FileInteractions] = FileInteractions.allCases) { + self.client = client + self.file = file + previewHelper = PreviewHelper(client: client) + self.delegate = delegate + itemActions = allowedActions + super.init(nibName: nil, bundle: nil) + } + @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } - + // MARK: - View life cycle - + public override func viewDidLoad() { super.viewDidLoad() setupView() - downloadFile() + previewFile() } - + public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - + // Sets toolbar items for a parent in case of paging // Needs to be set every time view appears or otherwise parent will remain with old items in a toolbar. if !itemActions.isEmpty { @@ -97,26 +113,26 @@ public class PreviewViewController: UIViewController { parentWithToolbar?.toolbarItems = toolbarItems } } - + public override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if shouldHideToolbarWhenDisappearing { navigationController?.isToolbarHidden = true } } - + public override var prefersStatusBarHidden: Bool { return navigationController?.isNavigationBarHidden == true } - + public override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { return .fade } - + public override var prefersHomeIndicatorAutoHidden: Bool { return true } - + public override var childForHomeIndicatorAutoHidden: UIViewController? { return nil } @@ -129,9 +145,8 @@ private extension PreviewViewController { view.backgroundColor = .white navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) navigationController?.interactivePopGestureRecognizer?.isEnabled = false - addProgressView() } - + func addProgressView() { view.addSubview(progressView) NSLayoutConstraint.activate([ @@ -141,16 +156,16 @@ private extension PreviewViewController { progressView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) } - + func removeProgressView() { DispatchQueue.main.async { self.progressView.removeFromSuperview() } } - + func addErrorView(with error: BoxPreviewError) { - - + + view.addSubview(errorView) NSLayoutConstraint.activate([ errorView.leadingAnchor.constraint(equalTo: view.leadingAnchor), @@ -160,15 +175,72 @@ private extension PreviewViewController { ]) errorView.displayError(error) } - + func removeErrorView() { DispatchQueue.main.async { self.errorView.removeFromSuperview() } } - - func downloadFile() { - previewHelper.downloadBoxFile( + + func previewFile() { + if let unwrappedFile = file, unwrappedFile.name != nil { + previewFile(file: unwrappedFile) + } + else if let unwrappedFileId = fileId { + client.files.get(fileId: unwrappedFileId) { [weak self] (result: Result) in + guard let self = self else { + return + } + switch result { + case let .success(file): + self.previewFile(file: file) + case let .failure(error): + self.previewViewControllerFailed(error: BoxPreviewError(message: .contentSDKError, error: error)) + } + } + } + } + + func previewFile(file: File) { + if let fileName = file.name, previewHelper.AVFileFormat.contains(URL(fileURLWithPath: fileName).pathExtension) { + self.client.files.listRepresentations( + fileId: file.id, + representationHint: .customValue("[hls]"), + completion: { [weak self] (result: Result<[FileRepresentation], BoxSDKError>) in + guard let self = self else { + return + } + switch result { + case let .success(representations): + if representations.isEmpty { + DispatchQueue.main.async { + self.addProgressView() + } + self.downloadFile(file: file) + } + else { + self.downloadFile(file: file, representations: representations) + } + case .failure: + DispatchQueue.main.async { + self.addProgressView() + } + self.downloadFile(file: file) + } + }) + } + else { + DispatchQueue.main.async { + self.addProgressView() + } + self.downloadFile(file: file) + } + } + + func downloadFile(file: File, representations: [FileRepresentation]? = nil) { + previewHelper.downloadFile( + file: file, + representations: representations, progress: { [weak self] progress in self?.progressView.setProgress(progress.fractionCompleted) }, @@ -176,51 +248,40 @@ private extension PreviewViewController { guard let self = self else { return } - switch result { - case .success: + DispatchQueue.main.async { [weak self] in + guard let self = self else { + return + } self.removeProgressView() - let result = self.previewHelper.getChildViewController(withActions: self.itemActions) switch result { - case let .success(childViewController): - DispatchQueue.main.async { [weak self] in - guard let self = self else { - return - } + case .success: + let result = self.previewHelper.getChildViewController(withActions: self.itemActions) + switch result { + case let .success(childViewController): self.displayChild(contentController: childViewController, on: self.view) - } - case let .failure(getChildViewControllerError): - DispatchQueue.main.async { [weak self] in - guard let self = self else { - return - } + case let .failure(getChildViewControllerError): self.previewViewControllerFailed(error: getChildViewControllerError) } - } - case let .failure(downloadFileError): - DispatchQueue.main.async { [weak self] in - guard let self = self else { - return - } - self.removeProgressView() + case let .failure(downloadFileError): self.previewViewControllerFailed(error: BoxPreviewError(message: .contentSDKError, error: downloadFileError)) } } } ) } - + func displayChild(contentController content: UIViewController, on view: UIView) { addChild(content) content.view.frame = view.bounds view.addSubview(content.view) content.didMove(toParent: self) - + if let itemViewController = content as? PreviewItemChildViewController { itemViewController.fullScreenDelegate = fullScreenDelegate setToolbar(for: itemViewController) } } - + func setToolbar(for itemViewController: PreviewItemChildViewController) { navigationController?.isToolbarHidden = itemViewController.toolbarButtons.isEmpty toolbarItems = createToolbarItems(for: itemViewController) @@ -238,7 +299,7 @@ private extension PreviewViewController { } return toolbarItems } - + func previewViewControllerFailed(error: BoxPreviewError) { DispatchQueue.main.async { [weak self] in self?.addErrorView(with: error) } delegate?.previewViewControllerFailed(error: error) diff --git a/Sources/Core/UI/Viewers/AV/AVViewController.swift b/Sources/Core/UI/Viewers/AV/AVViewController.swift new file mode 100644 index 0000000..08bfd57 --- /dev/null +++ b/Sources/Core/UI/Viewers/AV/AVViewController.swift @@ -0,0 +1,152 @@ +// +// AVViewController.swift +// BoxPreviewSDK-iOS +// +// Created by Sujay Garlanka on 12/19/19. +// Copyright © 2019 Box. All rights reserved. +// + +import BoxSDK +import Foundation +import AVKit + +public class AVViewController: UIViewController, PreviewItemChildViewController { + + // MARK: - Properties + + weak var fullScreenDelegate: PreviewItemFullScreenDelegate? + var toolbarButtons: [UIBarButtonItem] = [] + + private var url: URL + private var file: File? + private var AVPlayerVC: AVPlayerViewController + private var client: BoxClient? + + private lazy var titleView: CustomTitleView = { + let view: CustomTitleView = CustomTitleView() + return view + }() + + // MARK: - Initializer + + public init(url: URL, file: File? = nil, client: BoxClient? = nil, actions: [FileInteractions]) { + self.url = url + self.file = file + self.AVPlayerVC = AVPlayerViewController() + self.client = client + super.init(nibName: nil, bundle: nil) + // Not showing controls, so when video players doesn't flash a play button before it automatically starts playing + self.AVPlayerVC.showsPlaybackControls = false + self.setupPlayer() + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - View life cycle + + public override func viewDidLoad() { + super.viewDidLoad() + setupView() + } +} + +// MARK: - Setup video player + +private extension AVViewController { + func setupPlayer() { + if let unwrappedClient = client { + self.getToken(client: unwrappedClient) { result in + switch result { + case let .success(accessToken): + let headers: [String: String] = [ + "Authorization": "Bearer \(accessToken)" + ] + DispatchQueue.main.async { + let asset = AVURLAsset(url: self.url, options: ["AVURLAssetHTTPHeaderFieldsKey": headers]) + let playerItem = AVPlayerItem(asset: asset) + self.AVPlayerVC.player = AVPlayer(playerItem: playerItem) + self.AVPlayerVC.player?.play() + self.AVPlayerVC.showsPlaybackControls = true + } + case .failure: + DispatchQueue.main.async { + self.showAlertWith(title: "Error", message: "Unable to connect to Box account.") + } + } + } + } + else { + let asset = AVURLAsset(url: url) + let playerItem = AVPlayerItem(asset: asset) + DispatchQueue.main.async { + self.AVPlayerVC.player = AVPlayer(playerItem: playerItem) + self.AVPlayerVC.player?.play() + self.AVPlayerVC.showsPlaybackControls = true + } + } + } + + // Gets a fresh token that will last for around an hour if the session allows for a new token + // swiftlint:disable cyclomatic_complexity + func getToken(client: BoxClient, completion: @escaping (Result) -> Void) { + if let oauthSession = client.session as? OAuth2Session { + oauthSession.refreshToken { result in + switch result { + case let .success(accessToken): + completion(.success(accessToken)) + case let .failure(error): + completion(.failure(error)) + } + } + } + else if let delegatedSession = client.session as? DelegatedAuthSession { + delegatedSession.revokeTokens { revokeResult in + switch revokeResult { + case .success: + delegatedSession.getAccessToken { result in + switch result { + case let .success(accessToken): + completion(.success(accessToken)) + case let .failure(error): + completion(.failure(error)) + } + } + case let .failure(error): + completion(.failure(error)) + } + } + } + else if let singleTokenSession = client.session as? SingleTokenSession { + singleTokenSession.getAccessToken { result in + switch result { + case let .success(accessToken): + completion(.success(accessToken)) + case let .failure(error): + completion(.failure(error)) + } + } + } + } + // swiftlint:enable cyclomatic_complexity +} + +// MARK: - Set up view + +private extension AVViewController { + func setupView() { + titleView.title = file?.name + parent?.navigationItem.titleView = titleView + // This makes sure the video player is bounded by the navigation bar and toolbar and does not go below the two + self.AVPlayerVC.view.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(self.AVPlayerVC.view) + NSLayoutConstraint.activate([ + self.AVPlayerVC.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + self.AVPlayerVC.view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + self.AVPlayerVC.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + self.AVPlayerVC.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ]) + } +}