add keyCommands to VLCPlayerViewController

This commit is contained in:
PangMo5 2022-01-29 06:17:58 +09:00
parent 6bd07817f7
commit a5fabe3224
4 changed files with 123 additions and 37 deletions

View File

@ -54,6 +54,8 @@ internal enum L10n {
internal static var chapters: String { return L10n.tr("Localizable", "chapters") }
/// Cinematic Views
internal static var cinematicViews: String { return L10n.tr("Localizable", "cinematicViews") }
/// Close
internal static var close: String { return L10n.tr("Localizable", "close") }
/// Closed Captions
internal static var closedCaptions: String { return L10n.tr("Localizable", "closedCaptions") }
/// Compact
@ -122,8 +124,12 @@ internal enum L10n {
internal static var information: String { return L10n.tr("Localizable", "information") }
/// Items
internal static var items: String { return L10n.tr("Localizable", "items") }
/// Jump Backward
internal static var jumpBackward: String { return L10n.tr("Localizable", "jumpBackward") }
/// Jump Backward Length
internal static var jumpBackwardLength: String { return L10n.tr("Localizable", "jumpBackwardLength") }
/// Jump Forward
internal static var jumpForward: String { return L10n.tr("Localizable", "jumpForward") }
/// Jump Forward Length
internal static var jumpForwardLength: String { return L10n.tr("Localizable", "jumpForwardLength") }
/// Jump Gestures Enabled
@ -176,6 +182,8 @@ internal enum L10n {
internal static var networkTimedOut: String { return L10n.tr("Localizable", "networkTimedOut") }
/// Next
internal static var next: String { return L10n.tr("Localizable", "next") }
/// Next Item
internal static var nextItem: String { return L10n.tr("Localizable", "nextItem") }
/// Next Up
internal static var nextUp: String { return L10n.tr("Localizable", "nextUp") }
/// No Cast devices found..
@ -226,6 +234,8 @@ internal enum L10n {
internal static var password: String { return L10n.tr("Localizable", "password") }
/// Play
internal static var play: String { return L10n.tr("Localizable", "play") }
/// Play / Pause
internal static var playAndPause: String { return L10n.tr("Localizable", "playAndPause") }
/// Playback settings
internal static var playbackSettings: String { return L10n.tr("Localizable", "playbackSettings") }
/// Playback Speed
@ -242,6 +252,8 @@ internal enum L10n {
internal static var present: String { return L10n.tr("Localizable", "present") }
/// Press Down for Menu
internal static var pressDownForMenu: String { return L10n.tr("Localizable", "pressDownForMenu") }
/// Previous Item
internal static var previousItem: String { return L10n.tr("Localizable", "previousItem") }
/// Programs
internal static var programs: String { return L10n.tr("Localizable", "programs") }
/// Rated

View File

@ -38,4 +38,46 @@ enum PlaybackSpeed: Double, CaseIterable {
return "2x"
}
}
var previous: PlaybackSpeed? {
switch self {
case .quarter:
return nil
case .half:
return .quarter
case .threeQuarter:
return .half
case .one:
return .threeQuarter
case .oneQuarter:
return .one
case .oneHalf:
return .oneQuarter
case .oneThreeQuarter:
return .oneHalf
case .two:
return .oneThreeQuarter
}
}
var next: PlaybackSpeed? {
switch self {
case .quarter:
return .half
case .half:
return .threeQuarter
case .threeQuarter:
return .one
case .one:
return .oneQuarter
case .oneQuarter:
return .oneHalf
case .oneHalf:
return .oneThreeQuarter
case .oneThreeQuarter:
return .two
case .two:
return nil
}
}
}

View File

@ -19,7 +19,6 @@ import UIKit
// TODO: Look at making the VLC player layer a view
class VLCPlayerViewController: UIViewController {
// MARK: variables
private var viewModel: VideoPlayerViewModel
@ -50,10 +49,36 @@ class VLCPlayerViewController: UIViewController {
private var currentJumpBackwardOverlayView: UIImageView?
private var currentJumpForwardOverlayView: UIImageView?
override var keyCommands: [UIKeyCommand]? {
var commands = [
UIKeyCommand(title: L10n.playAndPause, action: #selector(didSelectMain), input: " "),
UIKeyCommand(title: L10n.jumpForward, action: #selector(didSelectForward), input: UIKeyCommand.inputRightArrow),
UIKeyCommand(title: L10n.jumpBackward, action: #selector(didSelectBackward), input: UIKeyCommand.inputLeftArrow),
UIKeyCommand(title: L10n.nextItem, action: #selector(didSelectPlayNextItem), input: UIKeyCommand.inputRightArrow,
modifierFlags: .command),
UIKeyCommand(title: L10n.previousItem, action: #selector(didSelectPlayPreviousItem), input: UIKeyCommand.inputLeftArrow,
modifierFlags: .command),
UIKeyCommand(title: L10n.close, action: #selector(didSelectClose), input: UIKeyCommand.inputEscape),
]
if let previous = viewModel.playbackSpeed.previous {
commands.append(.init(title: "\(L10n.playbackSpeed) \(previous.displayTitle)",
action: #selector(didSelectPreviousPlaybackSpeed), input: "[", modifierFlags: .command))
}
if let next = viewModel.playbackSpeed.next {
commands.append(.init(title: "\(L10n.playbackSpeed) \(next.displayTitle)", action: #selector(didSelectNextPlaybackSpeed),
input: "]", modifierFlags: .command))
}
if viewModel.playbackSpeed != .one {
commands.append(.init(title: "\(L10n.playbackSpeed) \(PlaybackSpeed.one.displayTitle)",
action: #selector(didSelectNormalPlaybackSpeed), input: "\\", modifierFlags: .command))
}
commands.forEach { $0.wantsPriorityOverSystemBehavior = true }
return commands
}
// MARK: init
init(viewModel: VideoPlayerViewModel) {
self.viewModel = viewModel
self.vlcMediaPlayer = VLCMediaPlayer()
@ -190,17 +215,17 @@ class VLCPlayerViewController: UIViewController {
@objc
private func didTap() {
self.didGenerallyTap()
didGenerallyTap()
}
@objc
private func didRightSwipe() {
self.didSelectForward()
didSelectForward()
}
@objc
private func didLeftSwipe() {
self.didSelectBackward()
didSelectBackward()
}
@objc
@ -208,10 +233,10 @@ class VLCPlayerViewController: UIViewController {
if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
pinchScale = gestureRecognizer.scale
} else {
if pinchScale > 1 && !isScreenFilled {
if pinchScale > 1, !isScreenFilled {
isScreenFilled.toggle()
fillScreen()
} else if pinchScale < 1 && isScreenFilled {
} else if pinchScale < 1, isScreenFilled {
isScreenFilled.toggle()
shrinkScreen()
}
@ -221,7 +246,6 @@ class VLCPlayerViewController: UIViewController {
// MARK: setupOverlayHostingController
private func setupOverlayHostingController(viewModel: VideoPlayerViewModel) {
// TODO: Look at injecting viewModel into the environment so it updates the current overlay
if let currentOverlayHostingController = currentOverlayHostingController {
// UX fade-out
@ -260,7 +284,7 @@ class VLCPlayerViewController: UIViewController {
newOverlayHostingController.view.alpha = 1
}
self.currentOverlayHostingController = newOverlayHostingController
currentOverlayHostingController = newOverlayHostingController
if let currentChapterOverlayHostingController = currentChapterOverlayHostingController {
UIView.animate(withDuration: 0.5) {
@ -292,14 +316,13 @@ class VLCPlayerViewController: UIViewController {
newChapterOverlayHostingController.view.rightAnchor.constraint(equalTo: videoContentView.rightAnchor),
])
self.currentChapterOverlayHostingController = newChapterOverlayHostingController
currentChapterOverlayHostingController = newChapterOverlayHostingController
// There is a weird behavior when after setting the new overlays that the navigation bar pops up, re-hide it
self.navigationController?.isNavigationBarHidden = true
navigationController?.isNavigationBarHidden = true
}
private func refreshJumpBackwardOverlayView(with jumpBackwardLength: VideoPlayerJumpLength) {
if let currentJumpBackwardOverlayView = currentJumpBackwardOverlayView {
currentJumpBackwardOverlayView.removeFromSuperview()
}
@ -324,7 +347,6 @@ class VLCPlayerViewController: UIViewController {
}
private func refreshJumpForwardOverlayView(with jumpForwardLength: VideoPlayerJumpLength) {
if let currentJumpForwardOverlayView = currentJumpForwardOverlayView {
currentJumpForwardOverlayView.removeFromSuperview()
}
@ -352,13 +374,11 @@ class VLCPlayerViewController: UIViewController {
// MARK: setupMediaPlayer
extension VLCPlayerViewController {
/// Main function that handles setting up the media player with the current VideoPlayerViewModel
/// and also takes the role of setting the 'viewModel' property with the given viewModel
///
/// Use case for this is setting new media within the same VLCPlayerViewController
func setupMediaPlayer(newViewModel: VideoPlayerViewModel) {
// remove old player
if vlcMediaPlayer.media != nil {
@ -454,7 +474,6 @@ extension VLCPlayerViewController {
// MARK: setupViewModelListeners
private func setupViewModelListeners(viewModel: VideoPlayerViewModel) {
viewModel.$playbackSpeed.sink { newSpeed in
self.vlcMediaPlayer.rate = Float(newSpeed.rawValue)
}.store(in: &viewModelListeners)
@ -508,7 +527,6 @@ extension VLCPlayerViewController {
// MARK: Show/Hide Overlay
extension VLCPlayerViewController {
private func showOverlay() {
guard let overlayHostingController = currentOverlayHostingController else { return }
@ -545,7 +563,6 @@ extension VLCPlayerViewController {
// MARK: Show/Hide Jump
extension VLCPlayerViewController {
private func flashJumpBackwardOverlay() {
guard !displayingOverlay, let currentJumpBackwardOverlayView = currentJumpBackwardOverlayView else { return }
@ -590,7 +607,6 @@ extension VLCPlayerViewController {
// MARK: Hide/Show Chapters
extension VLCPlayerViewController {
private func showChaptersOverlay() {
guard let overlayHostingController = currentChapterOverlayHostingController else { return }
@ -615,40 +631,37 @@ extension VLCPlayerViewController {
// MARK: OverlayTimer
extension VLCPlayerViewController {
private func restartOverlayDismissTimer(interval: Double = 3) {
self.overlayDismissTimer?.invalidate()
self.overlayDismissTimer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(dismissTimerFired),
userInfo: nil, repeats: false)
overlayDismissTimer?.invalidate()
overlayDismissTimer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(dismissTimerFired),
userInfo: nil, repeats: false)
}
@objc
private func dismissTimerFired() {
self.hideOverlay()
hideOverlay()
}
private func stopOverlayDismissTimer() {
self.overlayDismissTimer?.invalidate()
overlayDismissTimer?.invalidate()
}
}
// MARK: VLCMediaPlayerDelegate
extension VLCPlayerViewController: VLCMediaPlayerDelegate {
// MARK: mediaPlayerStateChanged
func mediaPlayerStateChanged(_ aNotification: Notification!) {
// Don't show buffering if paused, usually here while scrubbing
if vlcMediaPlayer.state == .buffering && viewModel.playerState == .paused {
if vlcMediaPlayer.state == .buffering, viewModel.playerState == .paused {
return
}
viewModel.playerState = vlcMediaPlayer.state
if vlcMediaPlayer.state == VLCMediaPlayerState.ended {
if viewModel.autoplayEnabled && viewModel.nextItemVideoPlayerViewModel != nil {
if viewModel.autoplayEnabled, viewModel.nextItemVideoPlayerViewModel != nil {
didSelectPlayNextItem()
} else {
didSelectClose()
@ -659,7 +672,6 @@ extension VLCPlayerViewController: VLCMediaPlayerDelegate {
// MARK: mediaPlayerTimeChanged
func mediaPlayerTimeChanged(_ aNotification: Notification!) {
if !viewModel.sliderIsScrubbing {
viewModel.sliderPercentage = Double(vlcMediaPlayer.position)
}
@ -671,8 +683,8 @@ extension VLCPlayerViewController: VLCMediaPlayerDelegate {
}
// If needing to fix subtitle streams during playback
if vlcMediaPlayer.currentVideoSubTitleIndex != viewModel.selectedSubtitleStreamIndex &&
viewModel.subtitlesEnabled
if vlcMediaPlayer.currentVideoSubTitleIndex != viewModel.selectedSubtitleStreamIndex,
viewModel.subtitlesEnabled
{
didSelectSubtitleStream(index: viewModel.selectedSubtitleStreamIndex)
}
@ -696,7 +708,6 @@ extension VLCPlayerViewController: VLCMediaPlayerDelegate {
// MARK: PlayerOverlayDelegate and more
extension VLCPlayerViewController: PlayerOverlayDelegate {
func didSelectAudioStream(index: Int) {
vlcMediaPlayer.currentAudioTrackIndex = Int32(index)
@ -715,6 +726,7 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
lastProgressReportTicks = currentPlayerTicks
}
@objc
func didSelectClose() {
vlcMediaPlayer.stop()
@ -741,8 +753,8 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
restartOverlayDismissTimer()
}
@objc
func didSelectBackward() {
flashJumpBackwardOverlay()
vlcMediaPlayer.jumpBackward(viewModel.jumpBackwardLength.rawValue)
@ -756,8 +768,8 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
lastProgressReportTicks = currentPlayerTicks
}
@objc
func didSelectForward() {
flashJumpFowardOverlay()
vlcMediaPlayer.jumpForward(viewModel.jumpForwardLength.rawValue)
@ -771,8 +783,8 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
lastProgressReportTicks = currentPlayerTicks
}
@objc
func didSelectMain() {
switch viewModel.playerState {
case .buffering:
vlcMediaPlayer.play()
@ -809,6 +821,7 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
lastProgressReportTicks = currentPlayerTicks
}
@objc
func didSelectPlayPreviousItem() {
if let previousItemVideoPlayerViewModel = viewModel.previousItemVideoPlayerViewModel {
setupMediaPlayer(newViewModel: previousItemVideoPlayerViewModel)
@ -816,6 +829,7 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
}
}
@objc
func didSelectPlayNextItem() {
if let nextItemVideoPlayerViewModel = viewModel.nextItemVideoPlayerViewModel {
setupMediaPlayer(newViewModel: nextItemVideoPlayerViewModel)
@ -823,6 +837,25 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
}
}
@objc
func didSelectPreviousPlaybackSpeed() {
if let previousPlaybackSpeed = viewModel.playbackSpeed.previous {
viewModel.playbackSpeed = previousPlaybackSpeed
}
}
@objc
func didSelectNextPlaybackSpeed() {
if let nextPlaybackSpeed = viewModel.playbackSpeed.next {
viewModel.playbackSpeed = nextPlaybackSpeed
}
}
@objc
func didSelectNormalPlaybackSpeed() {
viewModel.playbackSpeed = .one
}
func didSelectChapters() {
if displayingChapterOverlay {
hideChaptersOverlay()
@ -847,7 +880,6 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
}
func didSelectScreenFill() {
isScreenFilled.toggle()
if isScreenFilled {