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

Add new setting to toggle seek on progress bar on lock screen #1250

Merged
merged 4 commits into from
Feb 6, 2025
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
7 changes: 6 additions & 1 deletion BookPlayer/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, BPLogger {
return .success
}

center.changePlaybackPositionCommand.isEnabled = true
setupChangePlaybackPositionCommand()
}

func setupChangePlaybackPositionCommand() {
let center = MPRemoteCommandCenter.shared()
center.changePlaybackPositionCommand.isEnabled = UserDefaults.standard.bool(forKey: Constants.UserDefaults.seekProgressBarEnabled)
center.changePlaybackPositionCommand.addTarget { [weak self] remoteEvent in
guard
let playerManager = self?.coreServices?.playerManager,
Expand Down
2 changes: 2 additions & 0 deletions BookPlayer/Base.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -346,3 +346,5 @@ We're working hard on providing a seamless experience, if possible, please conta
"runtime_unknown" = "Unknown duration";
"subscription_required_title" = "Subscription required";
"Swipe rows to see download options" = "Swipe rows to see download options";
"settings_seekprogressbar_title" = "Progress Bar Seeking";
"settings_seekprogressbar_description" = "Enable seeking on the lock screen";
11 changes: 11 additions & 0 deletions BookPlayer/Coordinators/PlayerControlsCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,21 @@ class PlayerControlsCoordinator: Coordinator {
switch routes {
case .dismiss:
self.flow.finishPresentation(animated: true)
case .more:
self.showMoreControls()
}
}
let vc = PlayerControlsViewController.instantiate(from: .Player)
vc.viewModel = viewModel
flow.startPresentation(vc, animated: true)
}

func showMoreControls() {
let vc = PlayerSettingsViewController.instantiate(from: .Settings)
vc.navigationItem.largeTitleDisplayMode = .never
let nav = AppNavigationController.instantiate(from: .Main)
nav.viewControllers = [vc]

flow.navigationController.present(nav, animated: true)
}
}
51 changes: 30 additions & 21 deletions BookPlayer/Player/Base.lproj/Player.storyboard

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ class PlayerControlsViewController: UIViewController, Storyboarded {
@IBOutlet weak var boostLabel: UILabel!
@IBOutlet weak var boostWarningLabel: UILabel!
@IBOutlet weak var boostSwitchControl: UISwitch!
@IBOutlet weak var moreButton: UIButton!

private var boostVolumeObserver: NSKeyValueObservation?

private var disposeBag = Set<AnyCancellable>()

Expand All @@ -49,6 +52,7 @@ class PlayerControlsViewController: UIViewController, Storyboarded {
self.playbackLabel.text = "player_speed_title".localized
self.boostLabel.text = "settings_boostvolume_title".localized
self.boostWarningLabel.text = "settings_boostvolume_description".localized
self.moreButton.setTitle("more_title".localized, for: .normal)

self.speedFirstQuickActionButton.layer.masksToBounds = true
self.speedFirstQuickActionButton.layer.cornerRadius = 5
Expand Down Expand Up @@ -91,14 +95,20 @@ class PlayerControlsViewController: UIViewController, Storyboarded {

func setupAccessibility() {
self.boostLabel.accessibilityHint = "settings_boostvolume_description".localized
self.decrementSpeedButton.accessibilityLabel = "➖"
self.currentSpeedSlider.accessibilityValue = "\(self.viewModel.getCurrentSpeed())"
self.incrementSpeedButton.accessibilityLabel = "➕"
self.boostWarningLabel.isAccessibilityElement = false
self.currentSpeedLabel.isAccessibilityElement = false

self.mainContainterStackView.accessibilityElements = [
self.playbackLabel!,
self.decrementSpeedButton!,
self.currentSpeedSlider!,
self.incrementSpeedButton!,
self.boostLabel!,
self.boostSwitchControl!
self.boostSwitchControl!,
self.moreButton!
]
}

Expand All @@ -110,6 +120,14 @@ class PlayerControlsViewController: UIViewController, Storyboarded {
self?.viewModel.handleBoostVolumeToggle(flag: switchControl.isOn)
}
.store(in: &disposeBag)

boostVolumeObserver = UserDefaults.standard.observe(
\.userSettingsBoostVolume,
options: [.new]
) { [weak self] _, change in
guard let newValue = change.newValue else { return }
self?.boostSwitchControl.setOn(newValue, animated: false)
}
}

func bindSpeedObservers() {
Expand Down Expand Up @@ -168,13 +186,18 @@ class PlayerControlsViewController: UIViewController, Storyboarded {
}

private func setSliderSpeed(_ value: Float) {
self.currentSpeedSlider.accessibilityValue = "\(value)"
self.currentSpeedSlider.value = value
self.viewModel.handleSpeedChange(newValue: value)
}

@IBAction func done(_ sender: UIBarButtonItem?) {
self.viewModel.dismiss()
}

@IBAction func handleMoreAction(_ sender: UIButton) {
viewModel.showMoreControls()
}
}

extension PlayerControlsViewController: Themeable {
Expand Down Expand Up @@ -203,6 +226,8 @@ extension PlayerControlsViewController: Themeable {
self.incrementSpeedButton.tintColor = theme.linkColor
}

self.moreButton.tintColor = theme.linkColor

self.overrideUserInterfaceStyle = theme.useDarkVariant
? UIUserInterfaceStyle.dark
: UIUserInterfaceStyle.light
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Foundation

class PlayerControlsViewModel {
enum Routes {
case more
case dismiss
}

Expand Down Expand Up @@ -65,4 +66,8 @@ class PlayerControlsViewModel {
func dismiss() {
onTransition?(.dismiss)
}

func showMoreControls() {
onTransition?(.more)
}
}
17 changes: 17 additions & 0 deletions BookPlayer/Player/Player Screen/PlayerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class PlayerViewController: UIViewController, MVVMControllerProtocol, Storyboard
private var disposeBag = Set<AnyCancellable>()
private var playingProgressSubscriber: AnyCancellable?
private var currentChapterSubscriber: AnyCancellable?
private var updateProgressObserver: NSKeyValueObservation?

/// Reference to displayed alert, to update the message label with the ongoing timer
weak var sleepTimerAlert: UIAlertController?
Expand Down Expand Up @@ -279,6 +280,22 @@ extension PlayerViewController {
self.updateView(with: progressObject)
}.store(in: &disposeBag)

updateProgressObserver = UserDefaults.standard.observe(
\.userSettingsUpdateProgress,
options: [.new]
) { [weak self] object, change in
guard
let self,
let newValue = change.newValue,
newValue == true
else { return }
self.viewModel.reloadSharedFlags()
let progressObject = self.viewModel.getCurrentProgressState()
self.updateView(with: progressObject)

object.set(false, forKey: Constants.UserDefaults.updateProgress)
}

self.progressButton.publisher(for: .touchUpInside)
.merge(with: self.chapterTitleButton.publisher(for: .touchUpInside))
.sink { [weak self] _ in
Expand Down
7 changes: 7 additions & 0 deletions BookPlayer/Player/Player Screen/PlayerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,13 @@ class PlayerViewModel: ViewModelProtocol {
)
}

func reloadSharedFlags() {
/// Flags are not a getter to avoid reading too often from defaults, as progress objects can be
/// triggered multiple times in a second
self.prefersChapterContext = sharedDefaults.bool(forKey: Constants.UserDefaults.chapterContextEnabled)
self.prefersRemainingTime = sharedDefaults.bool(forKey: Constants.UserDefaults.remainingTimeEnabled)
}

func processToggleMaxTime() -> ProgressObject {
self.prefersRemainingTime = !self.prefersRemainingTime
sharedDefaults.set(self.prefersRemainingTime, forKey: Constants.UserDefaults.remainingTimeEnabled)
Expand Down
30 changes: 30 additions & 0 deletions BookPlayer/Player/Player Screen/Views/PlayerJumpIcon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ extension PlayerJumpIcon: Themeable {
}

class PlayerJumpIconForward: PlayerJumpIcon {
private var intervalObserver: NSKeyValueObservation?

override var backgroundImage: UIImage {
get {
return #imageLiteral(resourceName: "playerIconForward")
Expand All @@ -110,10 +112,25 @@ class PlayerJumpIconForward: PlayerJumpIcon {

self.title = "+\(Int(PlayerManager.forwardInterval.rounded())) "
self.actionButton.accessibilityLabel = VoiceOverService.fastForwardText()

self.bindObserver()
}

func bindObserver() {
intervalObserver = UserDefaults.standard.observe(
\.userSettingsForwardInterval,
options: [.new]
) { [weak self] _, change in
guard let newValue = change.newValue else { return }
self?.title = "+\(Int(newValue.rounded())) "
self?.actionButton.accessibilityLabel = VoiceOverService.fastForwardText()
}
}
}

class PlayerJumpIconRewind: PlayerJumpIcon {
private var intervalObserver: NSKeyValueObservation?

override var backgroundImage: UIImage {
get {
return #imageLiteral(resourceName: "playerIconRewind")
Expand All @@ -128,5 +145,18 @@ class PlayerJumpIconRewind: PlayerJumpIcon {

self.title = "−\(Int(PlayerManager.rewindInterval.rounded())) "
self.actionButton.accessibilityLabel = VoiceOverService.rewindText()

self.bindObserver()
}

func bindObserver() {
intervalObserver = UserDefaults.standard.observe(
\.userSettingsRewindInterval,
options: [.new]
) { [weak self] _, change in
guard let newValue = change.newValue else { return }
self?.title = "-\(Int(newValue.rounded())) "
self?.actionButton.accessibilityLabel = VoiceOverService.rewindText()
}
}
}
Loading