From b440966f227b02f36a3c49ef32e566682abffaf6 Mon Sep 17 00:00:00 2001 From: Mayank Sanganeria Date: Fri, 16 Mar 2018 16:26:06 -0700 Subject: [PATCH 1/7] Add autoplay setting --- Audiobook Player/Base.lproj/Main.storyboard | 32 +++++++++++++++++++ Audiobook Player/Constants.swift | 1 + Audiobook Player/SettingsViewController.swift | 9 +++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/Audiobook Player/Base.lproj/Main.storyboard b/Audiobook Player/Base.lproj/Main.storyboard index 1ebaa55ba..bcf3c2d4b 100644 --- a/Audiobook Player/Base.lproj/Main.storyboard +++ b/Audiobook Player/Base.lproj/Main.storyboard @@ -775,6 +775,37 @@ + + + + + + + + + + + + + + + + + + + @@ -792,6 +823,7 @@ + diff --git a/Audiobook Player/Constants.swift b/Audiobook Player/Constants.swift index 13c7a4ce2..e65376bd6 100644 --- a/Audiobook Player/Constants.swift +++ b/Audiobook Player/Constants.swift @@ -14,4 +14,5 @@ struct UserDefaultsConstants { static let smartRewindEnabled = "userSettingsSmartRewind"; static let boostVolumeEnabled = "userSettingsBoostVolume"; static let globalSpeedEnabled = "userSettingsGlobalSpeed"; + static let autoplayEnabled = "userSettingsAutoplay"; } diff --git a/Audiobook Player/SettingsViewController.swift b/Audiobook Player/SettingsViewController.swift index 9ad90a066..376fab735 100644 --- a/Audiobook Player/SettingsViewController.swift +++ b/Audiobook Player/SettingsViewController.swift @@ -14,6 +14,7 @@ class SettingsViewController: UITableViewController { @IBOutlet weak var smartRewindSwitch: UISwitch! @IBOutlet weak var boostVolumeSwitch: UISwitch! @IBOutlet weak var globalSpeedSwitch: UISwitch! + @IBOutlet weak var autoplaySwitch: UISwitch! let defaults:UserDefaults = UserDefaults.standard @@ -23,6 +24,7 @@ class SettingsViewController: UITableViewController { smartRewindSwitch.addTarget(self, action: #selector(self.rewindToggleDidChange), for: .valueChanged) boostVolumeSwitch.addTarget(self, action: #selector(self.boostVolumeToggleDidChange), for: .valueChanged) globalSpeedSwitch.addTarget(self, action: #selector(self.globalSpeedToggleDidChange), for: .valueChanged) + autoplaySwitch.addTarget(self, action: #selector(self.autoplayToggleDidChange), for: .valueChanged) //set colors self.navigationController?.navigationBar.barTintColor = UIColor.flatSkyBlue() @@ -31,6 +33,7 @@ class SettingsViewController: UITableViewController { smartRewindSwitch.setOn(defaults.bool(forKey: UserDefaultsConstants.smartRewindEnabled), animated: false) boostVolumeSwitch.setOn(defaults.bool(forKey: UserDefaultsConstants.boostVolumeEnabled), animated: false) globalSpeedSwitch.setOn(defaults.bool(forKey: UserDefaultsConstants.globalSpeedEnabled), animated: false) + autoplaySwitch.setOn(defaults.bool(forKey: UserDefaultsConstants.autoplayEnabled), animated: false) // FileManager } @@ -47,6 +50,10 @@ class SettingsViewController: UITableViewController { defaults.set(globalSpeedSwitch.isOn, forKey:UserDefaultsConstants.globalSpeedEnabled) } + @objc func autoplayToggleDidChange(){ + defaults.set(autoplaySwitch.isOn, forKey:UserDefaultsConstants.autoplayEnabled) + } + @IBAction func didPressClose(_ sender: UIBarButtonItem) { self.dismiss(animated: true, completion: nil) } @@ -58,7 +65,7 @@ class SettingsViewController: UITableViewController { case 0, 1: return 0 default: - return 3 + return 4 } } } From b012415a6c55e0afb549f1cc8a98f26c90fb6028 Mon Sep 17 00:00:00 2001 From: Mayank Sanganeria Date: Sat, 17 Mar 2018 18:51:53 -0700 Subject: [PATCH 2/7] change current book to array --- .../ListBooksViewController.swift | 6 ++--- Audiobook Player/PlayerManager.swift | 21 +++++++++-------- Audiobook Player/PlayerViewController.swift | 23 ++++++++++--------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/Audiobook Player/ListBooksViewController.swift b/Audiobook Player/ListBooksViewController.swift index 1a0cc6ce2..39f2bf78c 100644 --- a/Audiobook Player/ListBooksViewController.swift +++ b/Audiobook Player/ListBooksViewController.swift @@ -302,18 +302,18 @@ extension ListBooksViewController: UITableViewDelegate { } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let book = self.bookArray[indexPath.row] + let books = Array(self.bookArray.suffix(from: indexPath.row)) let storyboard = UIStoryboard(name: "Main", bundle: nil) let playerVC = storyboard.instantiateViewController(withIdentifier: "PlayerViewController") as! PlayerViewController - playerVC.currentBook = book + playerVC.currentBooks = books guard let cell = tableView.cellForRow(at: indexPath) as? BookCellView else { self.presentModal(playerVC, animated: true) return } - let title = cell.titleLabel.text ?? book.fileURL.lastPathComponent + let title = cell.titleLabel.text ?? books.first!.fileURL.lastPathComponent let author = cell.authorLabel.text ?? "Unknown Author" //show the current player diff --git a/Audiobook Player/PlayerManager.swift b/Audiobook Player/PlayerManager.swift index 5dc544beb..a3a4be9f6 100644 --- a/Audiobook Player/PlayerManager.swift +++ b/Audiobook Player/PlayerManager.swift @@ -16,7 +16,7 @@ class PlayerManager: NSObject { //current item to play var playerItem:AVPlayerItem! - var currentBook:Book! + var currentBooks:[Book]! var fileURL:URL! @@ -69,18 +69,21 @@ class PlayerManager: NSObject { - func load(_ book:Book, completion:@escaping (AVAudioPlayer?) -> Void) { + func load(_ books:[Book], completion:@escaping (AVAudioPlayer?) -> Void) { - if let player = self.audioPlayer { - player.stop() - //notify? + if self.currentBooks != nil && self.currentBooks!.count == books.count { //todo : fix logic + if let player = self.audioPlayer { + player.stop() + //notify? + } } - + + self.currentBooks = books + let book = books.first! self.playerItem = AVPlayerItem(asset: book.asset) self.fileURL = book.fileURL self.identifier = book.identifier - self.currentBook = book self.currentChapter = nil self.chapterArray = [] //notify metadata @@ -347,7 +350,7 @@ extension PlayerManager: AVAudioPlayerDelegate { "percentage":percentage, "percentageString":percentageString, "hasChapters":!self.chapterArray.isEmpty, - "fileURL":self.currentBook.fileURL] as [String : Any] + "fileURL":self.currentBooks.first!.fileURL] as [String : Any] //notify percentage if storedPercentage != percentageString { @@ -387,7 +390,7 @@ extension PlayerManager: AVAudioPlayerDelegate { let chapterString = "Chapter \(chapter.index) of \(self.chapterArray.count)" //notify let userInfo = ["chapterString": chapterString, - "fileURL":self.currentBook.fileURL] as [String : Any] + "fileURL":self.currentBooks.first!.fileURL] as [String : Any] //notify NotificationCenter.default.post(name: Notification.Name.AudiobookPlayer.updateChapter, object: nil, userInfo: userInfo) diff --git a/Audiobook Player/PlayerViewController.swift b/Audiobook Player/PlayerViewController.swift index f5724610d..042484877 100644 --- a/Audiobook Player/PlayerViewController.swift +++ b/Audiobook Player/PlayerViewController.swift @@ -44,7 +44,7 @@ class PlayerViewController: UIViewController { let playImage = UIImage(named: "playButton") let pauseImage = UIImage(named: "pauseButton") - var currentBook: Book! + var currentBooks: [Book]! //timer to update sleep time var sleepTimer:Timer! @@ -53,6 +53,7 @@ class PlayerViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + let currentBook = self.currentBooks.first! let averageArtworkColor = UIColor(averageColorFrom: currentBook.artwork) ?? UIColor.flatSkyBlueColorDark() //set UI colors @@ -104,15 +105,15 @@ class PlayerViewController: UIViewController { self.sliderView.maximumValue = 100 self.sliderView.value = 0 - self.titleLabel.text = self.currentBook.title - self.authorLabel.text = self.currentBook.author + self.titleLabel.text = self.currentBooks.first!.title + self.authorLabel.text = self.currentBooks.first!.author //set percentage label to stored value - let currentPercentage = UserDefaults.standard.string(forKey: self.currentBook.identifier+"_percentage") ?? "0%" + let currentPercentage = UserDefaults.standard.string(forKey: self.currentBooks.first!.identifier+"_percentage") ?? "0%" self.percentageLabel.text = currentPercentage //get stored value for current time of book - let currentTime = UserDefaults.standard.integer(forKey: self.currentBook.identifier) + let currentTime = UserDefaults.standard.integer(forKey: self.currentBooks.first!.identifier) //update UI if needed and set player to stored time if currentTime > 0 { @@ -121,11 +122,11 @@ class PlayerViewController: UIViewController { } //update max duration label of book - let maxDuration = self.currentBook.duration + let maxDuration = self.currentBooks.first!.duration self.maxTimeLabel.text = self.formatTime(maxDuration) //make sure player is for a different book - guard PlayerManager.sharedInstance.fileURL != self.currentBook.fileURL else { + guard PlayerManager.sharedInstance.fileURL != self.currentBooks.first!.fileURL else { if PlayerManager.sharedInstance.isPlaying() { self.playButton.setImage(self.pauseImage, for: UIControlState()) } else { @@ -148,7 +149,7 @@ class PlayerViewController: UIViewController { MBProgressHUD.showAdded(to: self.view, animated: true) //replace player with new one - PlayerManager.sharedInstance.load(self.currentBook) { (audioPlayer) in + PlayerManager.sharedInstance.load(self.currentBooks) { (audioPlayer) in //currentChapter is not reliable because of currentTime is not ready, set to blank if !PlayerManager.sharedInstance.chapterArray.isEmpty { self.percentageLabel.text = "" @@ -388,7 +389,7 @@ extension PlayerViewController: AVAudioPlayerDelegate { let fileURL = userInfo["fileURL"] as? URL, let timeText = userInfo["timeString"] as? String, let percentage = userInfo["percentage"] as? Float, - fileURL == self.currentBook.fileURL else { + fileURL == self.currentBooks.first!.fileURL else { return } @@ -403,7 +404,7 @@ extension PlayerViewController: AVAudioPlayerDelegate { @objc func updatePercentage(_ notification:Notification) { guard let userInfo = notification.userInfo, let fileURL = userInfo["fileURL"] as? URL, - fileURL == self.currentBook.fileURL, + fileURL == self.currentBooks.first!.fileURL, let percentageString = userInfo["percentageString"] as? String, let hasChapters = userInfo["hasChapters"] as? Bool, !hasChapters else { @@ -451,7 +452,7 @@ extension PlayerViewController: AVAudioPlayerDelegate { guard let userInfo = notification.userInfo, let fileURL = userInfo["fileURL"] as? URL, let chapterString = userInfo["chapterString"] as? String, - fileURL == self.currentBook.fileURL else { + fileURL == self.currentBooks.first!.fileURL else { return } From 3ecae2a8043867e897887535cd12061225275bca Mon Sep 17 00:00:00 2001 From: Mayank Sanganeria Date: Sat, 17 Mar 2018 19:38:58 -0700 Subject: [PATCH 3/7] autoplay next book --- Audiobook Player/Extensions.swift | 1 + .../ListBooksViewController.swift | 52 ++++++++++---- Audiobook Player/PlayerManager.swift | 11 +++ Audiobook Player/PlayerViewController.swift | 68 ++++++++++++------- 4 files changed, 93 insertions(+), 39 deletions(-) diff --git a/Audiobook Player/Extensions.swift b/Audiobook Player/Extensions.swift index d4964ce5b..8d55ada99 100644 --- a/Audiobook Player/Extensions.swift +++ b/Audiobook Player/Extensions.swift @@ -67,6 +67,7 @@ extension Notification.Name { public static let bookPlayed = Notification.Name(rawValue: "com.tortugapower.audiobookplayer.book.play") public static let bookPaused = Notification.Name(rawValue: "com.tortugapower.audiobookplayer.book.pause") public static let bookEnd = Notification.Name(rawValue: "com.tortugapower.audiobookplayer.book.end") + public static let bookChange = Notification.Name(rawValue: "com.tortugapower.audiobookplayer.book.change") } } diff --git a/Audiobook Player/ListBooksViewController.swift b/Audiobook Player/ListBooksViewController.swift index 39f2bf78c..bf21c8e48 100644 --- a/Audiobook Player/ListBooksViewController.swift +++ b/Audiobook Player/ListBooksViewController.swift @@ -25,6 +25,7 @@ class ListBooksViewController: UIViewController, UIGestureRecognizerDelegate { //keep in memory images to toggle play/pause let miniPlayImage = UIImage(named: "miniPlayButton") let miniPauseButton = UIImage(named: "miniPauseButton") + var currentBooks: [Book] = [] //TableView's datasource var bookArray = [Book]() @@ -98,6 +99,9 @@ class ListBooksViewController: UIViewController, UIGestureRecognizerDelegate { //register for book end notifications NotificationCenter.default.addObserver(self, selector: #selector(self.bookEnd(_:)), name: Notification.Name.AudiobookPlayer.bookEnd, object: nil) + //register for book change notifications + NotificationCenter.default.addObserver(self, selector: #selector(self.bookChange(_:)), name: Notification.Name.AudiobookPlayer.bookChange, object: nil) + //register for remote events self.registerRemoteEvents() @@ -188,11 +192,10 @@ class ListBooksViewController: UIViewController, UIGestureRecognizerDelegate { } @IBAction func didPressShowDetail(_ sender: UIButton) { - guard let indexPath = self.tableView.indexPathForSelectedRow else { + guard currentBooks.count > 0 else { return } - - self.tableView(self.tableView, didSelectRowAt: indexPath) + play(books: currentBooks) } //percentage callback @@ -223,6 +226,17 @@ class ListBooksViewController: UIViewController, UIGestureRecognizerDelegate { @objc func bookEnd(_ notification:Notification) { self.footerPlayButton.setImage(self.miniPlayImage, for: UIControlState()) } + + @objc func bookChange(_ notification:Notification) { + guard let userInfo = notification.userInfo, + let books = userInfo["books"] as? [Book], + books.count > 0 else { + return + } + currentBooks = books + setupFooter(book: books.first!) + } + } extension ListBooksViewController: UITableViewDataSource { @@ -304,26 +318,34 @@ extension ListBooksViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let books = Array(self.bookArray.suffix(from: indexPath.row)) - let storyboard = UIStoryboard(name: "Main", bundle: nil) - let playerVC = storyboard.instantiateViewController(withIdentifier: "PlayerViewController") as! PlayerViewController - playerVC.currentBooks = books - - guard let cell = tableView.cellForRow(at: indexPath) as? BookCellView else { - self.presentModal(playerVC, animated: true) + play(books: books) + } + + func play(books: [Book]) { + guard books.count > 0 else { return } - let title = cell.titleLabel.text ?? books.first!.fileURL.lastPathComponent - let author = cell.authorLabel.text ?? "Unknown Author" + let storyboard = UIStoryboard(name: "Main", bundle: nil) + let playerVC = storyboard.instantiateViewController(withIdentifier: "PlayerViewController") as! PlayerViewController + playerVC.currentBooks = books + self.currentBooks = books //show the current player self.presentModal(playerVC, animated: true) { - self.footerView.isHidden = false - self.footerTitleLabel.text = title + " - " + author - self.footerImageView.image = cell.artworkImageView.image - self.footerHeightConstraint.constant = 55 + self.setupFooter(book: books.first!) } } + + func setupFooter(book: Book) { + let title = book.fileURL.lastPathComponent + let author = book.author + + self.footerView.isHidden = false + self.footerTitleLabel.text = title + " - " + author + self.footerImageView.image = book.artwork + self.footerHeightConstraint.constant = 55 + } } extension ListBooksViewController:UIDocumentMenuDelegate { diff --git a/Audiobook Player/PlayerManager.swift b/Audiobook Player/PlayerManager.swift index a3a4be9f6..30a2cf654 100644 --- a/Audiobook Player/PlayerManager.swift +++ b/Audiobook Player/PlayerManager.swift @@ -403,6 +403,17 @@ extension PlayerManager: AVAudioPlayerDelegate { if flag { player.currentTime = player.duration self.updateTimer() + + if (UserDefaults.standard.bool(forKey: UserDefaultsConstants.autoplayEnabled)) { + if self.currentBooks.count > 1 { + let currentBooks = Array(PlayerManager.sharedInstance.currentBooks.dropFirst()) + load(currentBooks, completion: { (audioPlayer) in + let userInfo = ["books": currentBooks] + NotificationCenter.default.post(name: Notification.Name.AudiobookPlayer.bookChange, object: nil, userInfo: userInfo) + }) + } + } + } } diff --git a/Audiobook Player/PlayerViewController.swift b/Audiobook Player/PlayerViewController.swift index 042484877..27f475482 100644 --- a/Audiobook Player/PlayerViewController.swift +++ b/Audiobook Player/PlayerViewController.swift @@ -53,7 +53,27 @@ class PlayerViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - let currentBook = self.currentBooks.first! + let currentBook = currentBooks.first! + setupView(book: currentBook) + setupPlayer(book: currentBook) + } + + //Resize sleep button on orientation transition + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + coordinator.animate(alongsideTransition: { (context) in + let orientation = UIApplication.shared.statusBarOrientation + + if orientation.isLandscape { + self.sleepTimerWidthConstraint.constant = 20 + } else { + self.sleepTimerWidthConstraint.constant = 30 + } + + }) + } + + func setupView(book currentBook: Book) { let averageArtworkColor = UIColor(averageColorFrom: currentBook.artwork) ?? UIColor.flatSkyBlueColorDark() //set UI colors @@ -98,22 +118,23 @@ class PlayerViewController: UIViewController { NotificationCenter.default.addObserver(self, selector: #selector(self.bookPlayed), name: Notification.Name.AudiobookPlayer.bookPlayed, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.bookPaused), name: Notification.Name.AudiobookPlayer.bookPaused, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.bookEnd), name: Notification.Name.AudiobookPlayer.bookEnd, object: nil) - + NotificationCenter.default.addObserver(self, selector: #selector(self.bookChange(_:)), name: Notification.Name.AudiobookPlayer.bookChange, object: nil) + //set initial state for slider self.sliderView.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged) self.sliderView.tintColor = UIColor.flatLimeColorDark() self.sliderView.maximumValue = 100 self.sliderView.value = 0 - self.titleLabel.text = self.currentBooks.first!.title - self.authorLabel.text = self.currentBooks.first!.author + self.titleLabel.text = currentBook.title + self.authorLabel.text = currentBook.author //set percentage label to stored value - let currentPercentage = UserDefaults.standard.string(forKey: self.currentBooks.first!.identifier+"_percentage") ?? "0%" + let currentPercentage = UserDefaults.standard.string(forKey: currentBook.identifier+"_percentage") ?? "0%" self.percentageLabel.text = currentPercentage //get stored value for current time of book - let currentTime = UserDefaults.standard.integer(forKey: self.currentBooks.first!.identifier) + let currentTime = UserDefaults.standard.integer(forKey: currentBook.identifier) //update UI if needed and set player to stored time if currentTime > 0 { @@ -122,9 +143,14 @@ class PlayerViewController: UIViewController { } //update max duration label of book - let maxDuration = self.currentBooks.first!.duration + let maxDuration = currentBook.duration self.maxTimeLabel.text = self.formatTime(maxDuration) - + } + + func setupPlayer(book currentBook: Book) { + //get stored value for current time of book + let currentTime = UserDefaults.standard.integer(forKey: currentBook.identifier) + //make sure player is for a different book guard PlayerManager.sharedInstance.fileURL != self.currentBooks.first!.fileURL else { if PlayerManager.sharedInstance.isPlaying() { @@ -168,21 +194,6 @@ class PlayerViewController: UIViewController { } } - //Resize sleep button on orientation transition - override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { - super.viewWillTransition(to: size, with: coordinator) - coordinator.animate(alongsideTransition: { (context) in - let orientation = UIApplication.shared.statusBarOrientation - - if orientation.isLandscape { - self.sleepTimerWidthConstraint.constant = 20 - } else { - self.sleepTimerWidthConstraint.constant = 30 - } - - }) - } - @objc func sliderChanged(_ sender: UISlider) { let percentage = sender.value / sender.maximumValue @@ -445,7 +456,16 @@ extension PlayerViewController: AVAudioPlayerDelegate { @objc func bookEnd() { self.playButton.setImage(self.playImage, for: UIControlState()) - self.requestReview() + self.requestReview() + } + + @objc func bookChange(_ notification:Notification) { + guard let userInfo = notification.userInfo, + let books = userInfo["books"] as? [Book], + let book = books.first else { + return + } + setupView(book: book) } @objc func updateCurrentChapter(_ notification:Notification) { From 994203823e5a4ec659ea3bd5da1d69af7924461c Mon Sep 17 00:00:00 2001 From: Mayank Sanganeria Date: Sun, 18 Mar 2018 15:41:58 -0700 Subject: [PATCH 4/7] address simple review comments --- Audiobook Player/DataManager.swift | 3 ++ .../ListBooksViewController.swift | 16 ++++------- Audiobook Player/PlayerViewController.swift | 28 ++++++++++--------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Audiobook Player/DataManager.swift b/Audiobook Player/DataManager.swift index 9df91093f..453165c83 100644 --- a/Audiobook Player/DataManager.swift +++ b/Audiobook Player/DataManager.swift @@ -23,6 +23,9 @@ struct Book { } } + var displayTitle: String { + return title + " - " + author + } var title: String var author: String var artwork: UIImage diff --git a/Audiobook Player/ListBooksViewController.swift b/Audiobook Player/ListBooksViewController.swift index bf21c8e48..8705b45f2 100644 --- a/Audiobook Player/ListBooksViewController.swift +++ b/Audiobook Player/ListBooksViewController.swift @@ -192,7 +192,7 @@ class ListBooksViewController: UIViewController, UIGestureRecognizerDelegate { } @IBAction func didPressShowDetail(_ sender: UIButton) { - guard currentBooks.count > 0 else { + guard !currentBooks.isEmpty else { return } play(books: currentBooks) @@ -230,11 +230,10 @@ class ListBooksViewController: UIViewController, UIGestureRecognizerDelegate { @objc func bookChange(_ notification:Notification) { guard let userInfo = notification.userInfo, let books = userInfo["books"] as? [Book], - books.count > 0 else { + let currentBook = books.first else { return } - currentBooks = books - setupFooter(book: books.first!) + setupFooter(book: currentBook) } } @@ -322,7 +321,7 @@ extension ListBooksViewController: UITableViewDelegate { } func play(books: [Book]) { - guard books.count > 0 else { + guard !books.isEmpty else { return } @@ -338,14 +337,11 @@ extension ListBooksViewController: UITableViewDelegate { } func setupFooter(book: Book) { - let title = book.fileURL.lastPathComponent - let author = book.author - self.footerView.isHidden = false - self.footerTitleLabel.text = title + " - " + author + self.footerTitleLabel.text = book.displayTitle self.footerImageView.image = book.artwork self.footerHeightConstraint.constant = 55 - } + } } extension ListBooksViewController:UIDocumentMenuDelegate { diff --git a/Audiobook Player/PlayerViewController.swift b/Audiobook Player/PlayerViewController.swift index 27f475482..b52753965 100644 --- a/Audiobook Player/PlayerViewController.swift +++ b/Audiobook Player/PlayerViewController.swift @@ -52,6 +52,7 @@ class PlayerViewController: UIViewController { //MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() + registerObservers() let currentBook = currentBooks.first! setupView(book: currentBook) @@ -108,18 +109,6 @@ class PlayerViewController: UIViewController { self.setStatusBarStyle(UIStatusBarStyleContrast) - //register for appDelegate requestReview notifications - NotificationCenter.default.addObserver(self, selector: #selector(self.requestReview), name: Notification.Name.AudiobookPlayer.requestReview, object: nil) - //register for timer update - NotificationCenter.default.addObserver(self, selector: #selector(self.updateTimer(_:)), name: Notification.Name.AudiobookPlayer.updateTimer, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(self.updatePercentage(_:)), name: Notification.Name.AudiobookPlayer.updatePercentage, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(self.updateCurrentChapter(_:)), name: Notification.Name.AudiobookPlayer.updateChapter, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(self.bookReady), name: Notification.Name.AudiobookPlayer.bookReady, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(self.bookPlayed), name: Notification.Name.AudiobookPlayer.bookPlayed, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(self.bookPaused), name: Notification.Name.AudiobookPlayer.bookPaused, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(self.bookEnd), name: Notification.Name.AudiobookPlayer.bookEnd, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(self.bookChange(_:)), name: Notification.Name.AudiobookPlayer.bookChange, object: nil) - //set initial state for slider self.sliderView.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged) self.sliderView.tintColor = UIColor.flatLimeColorDark() @@ -193,7 +182,20 @@ class PlayerViewController: UIViewController { } } } - + func registerObservers() { + //register for appDelegate requestReview notifications + NotificationCenter.default.addObserver(self, selector: #selector(self.requestReview), name: Notification.Name.AudiobookPlayer.requestReview, object: nil) + //register for timer update + NotificationCenter.default.addObserver(self, selector: #selector(self.updateTimer(_:)), name: Notification.Name.AudiobookPlayer.updateTimer, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.updatePercentage(_:)), name: Notification.Name.AudiobookPlayer.updatePercentage, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.updateCurrentChapter(_:)), name: Notification.Name.AudiobookPlayer.updateChapter, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.bookReady), name: Notification.Name.AudiobookPlayer.bookReady, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.bookPlayed), name: Notification.Name.AudiobookPlayer.bookPlayed, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.bookPaused), name: Notification.Name.AudiobookPlayer.bookPaused, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.bookEnd), name: Notification.Name.AudiobookPlayer.bookEnd, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.bookChange(_:)), name: Notification.Name.AudiobookPlayer.bookChange, object: nil) + } + @objc func sliderChanged(_ sender: UISlider) { let percentage = sender.value / sender.maximumValue From 8e080fbbd7f9eedf934ed80bdafd5ee8b50e596f Mon Sep 17 00:00:00 2001 From: Mayank Sanganeria Date: Sun, 18 Mar 2018 16:07:30 -0700 Subject: [PATCH 5/7] change player loading from PlayerView to ListBooksVC --- .../ListBooksViewController.swift | 55 +++++++++---- Audiobook Player/PlayerViewController.swift | 78 ++++++------------- 2 files changed, 64 insertions(+), 69 deletions(-) diff --git a/Audiobook Player/ListBooksViewController.swift b/Audiobook Player/ListBooksViewController.swift index 8705b45f2..2114f2b70 100644 --- a/Audiobook Player/ListBooksViewController.swift +++ b/Audiobook Player/ListBooksViewController.swift @@ -36,7 +36,7 @@ class ListBooksViewController: UIViewController, UIGestureRecognizerDelegate { override func viewDidLoad() { super.viewDidLoad() - + //pull-down-to-refresh support self.refreshControl.attributedTitle = NSAttributedString(string: "Pull down to reload books") self.refreshControl.addTarget(self, action: #selector(loadFiles), for: .valueChanged) @@ -90,9 +90,12 @@ class ListBooksViewController: UIViewController, UIGestureRecognizerDelegate { //register for percentage change notifications NotificationCenter.default.addObserver(self, selector: #selector(self.updatePercentage(_:)), name: Notification.Name.AudiobookPlayer.updatePercentage, object: nil) + //register notifications when the book is ready + NotificationCenter.default.addObserver(self, selector: #selector(self.bookReady), name: Notification.Name.AudiobookPlayer.bookReady, object: nil) + //register notifications when the book is played NotificationCenter.default.addObserver(self, selector: #selector(self.bookPlayed), name: Notification.Name.AudiobookPlayer.bookPlayed, object: nil) - + //register notifications when the book is paused NotificationCenter.default.addObserver(self, selector: #selector(self.bookPaused), name: Notification.Name.AudiobookPlayer.bookPaused, object: nil) @@ -214,7 +217,12 @@ class ListBooksViewController: UIViewController, UIGestureRecognizerDelegate { cell.completionLabel.text = percentageString } - + + @objc func bookReady(){ + MBProgressHUD.hideAllHUDs(for: self.view, animated: true) + PlayerManager.sharedInstance.playPressed() + } + @objc func bookPlayed(){ self.footerPlayButton.setImage(self.miniPauseButton, for: UIControlState()) } @@ -249,9 +257,10 @@ extension ListBooksViewController: UITableViewDataSource { let book = self.bookArray[indexPath.row] cell.titleLabel.text = book.title - cell.titleLabel.highlightedTextColor = UIColor.black cell.authorLabel.text = book.author + cell.selectionStyle = .none + //NOTE: we should have a default image for artwork cell.artworkImageView.image = book.artwork @@ -316,7 +325,6 @@ extension ListBooksViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let books = Array(self.bookArray.suffix(from: indexPath.row)) - play(books: books) } @@ -324,24 +332,43 @@ extension ListBooksViewController: UITableViewDelegate { guard !books.isEmpty else { return } - - let storyboard = UIStoryboard(name: "Main", bundle: nil) - let playerVC = storyboard.instantiateViewController(withIdentifier: "PlayerViewController") as! PlayerViewController - playerVC.currentBooks = books self.currentBooks = books + let book = currentBooks.first! + setupPlayer(book: book) + showPlayerView(book: book) + setupFooter(book: book) + } + + func setupPlayer(book: Book) { + //make sure player is for a different book + guard PlayerManager.sharedInstance.fileURL != book.fileURL else { + showPlayerView(book: book) + return + } - //show the current player - self.presentModal(playerVC, animated: true) { - self.setupFooter(book: books.first!) + MBProgressHUD.showAdded(to: self.view, animated: true) + //replace player with new one + PlayerManager.sharedInstance.load(self.currentBooks) { (audioPlayer) in + self.showPlayerView(book: book) } } - + func setupFooter(book: Book) { self.footerView.isHidden = false self.footerTitleLabel.text = book.displayTitle self.footerImageView.image = book.artwork self.footerHeightConstraint.constant = 55 - } + } + + func showPlayerView(book: Book) { + let storyboard = UIStoryboard(name: "Main", bundle: nil) + let playerVC = storyboard.instantiateViewController(withIdentifier: "PlayerViewController") as! PlayerViewController + playerVC.currentBook = book + + //show the current player + self.presentModal(playerVC, animated: true) { + } + } } extension ListBooksViewController:UIDocumentMenuDelegate { diff --git a/Audiobook Player/PlayerViewController.swift b/Audiobook Player/PlayerViewController.swift index b52753965..3950fb2b7 100644 --- a/Audiobook Player/PlayerViewController.swift +++ b/Audiobook Player/PlayerViewController.swift @@ -10,7 +10,6 @@ import UIKit import AVFoundation import MediaPlayer import Chameleon -import MBProgressHUD import StoreKit class PlayerViewController: UIViewController { @@ -44,19 +43,18 @@ class PlayerViewController: UIViewController { let playImage = UIImage(named: "playButton") let pauseImage = UIImage(named: "pauseButton") - var currentBooks: [Book]! + var currentBook: Book! //timer to update sleep time - var sleepTimer:Timer! + var sleepTimer: Timer! //MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() registerObservers() - let currentBook = currentBooks.first! - setupView(book: currentBook) - setupPlayer(book: currentBook) + setupView(book: currentBook!) + playPlayer() } //Resize sleep button on orientation transition @@ -136,52 +134,28 @@ class PlayerViewController: UIViewController { self.maxTimeLabel.text = self.formatTime(maxDuration) } - func setupPlayer(book currentBook: Book) { + func playPlayer() { //get stored value for current time of book let currentTime = UserDefaults.standard.integer(forKey: currentBook.identifier) - //make sure player is for a different book - guard PlayerManager.sharedInstance.fileURL != self.currentBooks.first!.fileURL else { - if PlayerManager.sharedInstance.isPlaying() { - self.playButton.setImage(self.pauseImage, for: UIControlState()) - } else { - self.playButton.setImage(self.playImage, for: UIControlState()) - } - - //set smart speed - self.speedButton.setTitle("Speed \(String(PlayerManager.sharedInstance.currentSpeed))x", for: UIControlState()) - - //enable/disable chapters button - self.chaptersButton.isEnabled = !PlayerManager.sharedInstance.chapterArray.isEmpty - - if let audioPlayer = PlayerManager.sharedInstance.audioPlayer { - let percentage = (Float(currentTime) / Float(audioPlayer.duration)) * 100 - self.sliderView.value = percentage - } - - return + if PlayerManager.sharedInstance.isPlaying() { + self.playButton.setImage(self.pauseImage, for: UIControlState()) + } else { + self.playButton.setImage(self.playImage, for: UIControlState()) } - - MBProgressHUD.showAdded(to: self.view, animated: true) - //replace player with new one - PlayerManager.sharedInstance.load(self.currentBooks) { (audioPlayer) in - //currentChapter is not reliable because of currentTime is not ready, set to blank - if !PlayerManager.sharedInstance.chapterArray.isEmpty { - self.percentageLabel.text = "" - } - - //set smart speed - self.speedButton.setTitle("Speed \(String(PlayerManager.sharedInstance.currentSpeed))x", for: UIControlState()) - - //enable/disable chapters button - self.chaptersButton.isEnabled = !PlayerManager.sharedInstance.chapterArray.isEmpty - - if let audioPlayer = audioPlayer { - let percentage = (Float(currentTime) / Float(audioPlayer.duration)) * 100 - self.sliderView.value = percentage - } + if !PlayerManager.sharedInstance.chapterArray.isEmpty { + self.percentageLabel.text = "" + } + self.speedButton.setTitle("Speed \(String(PlayerManager.sharedInstance.currentSpeed))x", for: UIControlState()) + + self.chaptersButton.isEnabled = !PlayerManager.sharedInstance.chapterArray.isEmpty + + if let audioPlayer = PlayerManager.sharedInstance.audioPlayer { + let percentage = (Float(currentTime) / Float(audioPlayer.duration)) * 100 + self.sliderView.value = percentage } } + func registerObservers() { //register for appDelegate requestReview notifications NotificationCenter.default.addObserver(self, selector: #selector(self.requestReview), name: Notification.Name.AudiobookPlayer.requestReview, object: nil) @@ -189,7 +163,6 @@ class PlayerViewController: UIViewController { NotificationCenter.default.addObserver(self, selector: #selector(self.updateTimer(_:)), name: Notification.Name.AudiobookPlayer.updateTimer, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.updatePercentage(_:)), name: Notification.Name.AudiobookPlayer.updatePercentage, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.updateCurrentChapter(_:)), name: Notification.Name.AudiobookPlayer.updateChapter, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(self.bookReady), name: Notification.Name.AudiobookPlayer.bookReady, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.bookPlayed), name: Notification.Name.AudiobookPlayer.bookPlayed, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.bookPaused), name: Notification.Name.AudiobookPlayer.bookPaused, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.bookEnd), name: Notification.Name.AudiobookPlayer.bookEnd, object: nil) @@ -402,7 +375,7 @@ extension PlayerViewController: AVAudioPlayerDelegate { let fileURL = userInfo["fileURL"] as? URL, let timeText = userInfo["timeString"] as? String, let percentage = userInfo["percentage"] as? Float, - fileURL == self.currentBooks.first!.fileURL else { + fileURL == self.currentBook.fileURL else { return } @@ -417,7 +390,7 @@ extension PlayerViewController: AVAudioPlayerDelegate { @objc func updatePercentage(_ notification:Notification) { guard let userInfo = notification.userInfo, let fileURL = userInfo["fileURL"] as? URL, - fileURL == self.currentBooks.first!.fileURL, + fileURL == self.currentBook.fileURL, let percentageString = userInfo["percentageString"] as? String, let hasChapters = userInfo["hasChapters"] as? Bool, !hasChapters else { @@ -443,11 +416,6 @@ extension PlayerViewController: AVAudioPlayerDelegate { } } - @objc func bookReady(){ - MBProgressHUD.hideAllHUDs(for: self.view, animated: true) - PlayerManager.sharedInstance.playPressed() - } - @objc func bookPlayed(){ self.playButton.setImage(self.pauseImage, for: UIControlState()) } @@ -474,7 +442,7 @@ extension PlayerViewController: AVAudioPlayerDelegate { guard let userInfo = notification.userInfo, let fileURL = userInfo["fileURL"] as? URL, let chapterString = userInfo["chapterString"] as? String, - fileURL == self.currentBooks.first!.fileURL else { + fileURL == self.currentBook.fileURL else { return } From 99c7b371f5abe78fc698938e8fa70c751e75e000 Mon Sep 17 00:00:00 2001 From: Mayank Sanganeria Date: Mon, 26 Mar 2018 10:25:50 -0700 Subject: [PATCH 6/7] set current book --- Audiobook Player/PlayerViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Audiobook Player/PlayerViewController.swift b/Audiobook Player/PlayerViewController.swift index 3950fb2b7..2690019c3 100644 --- a/Audiobook Player/PlayerViewController.swift +++ b/Audiobook Player/PlayerViewController.swift @@ -432,10 +432,10 @@ extension PlayerViewController: AVAudioPlayerDelegate { @objc func bookChange(_ notification:Notification) { guard let userInfo = notification.userInfo, let books = userInfo["books"] as? [Book], - let book = books.first else { + let currentBook = books.first else { return } - setupView(book: book) + setupView(book: currentBook) } @objc func updateCurrentChapter(_ notification:Notification) { From 850c7ffd0672623581ae41e2710d7a5ad69018f6 Mon Sep 17 00:00:00 2001 From: Mayank Sanganeria Date: Tue, 27 Mar 2018 21:17:06 -0700 Subject: [PATCH 7/7] dont autoplay completed books --- Audiobook Player/ListBooksViewController.swift | 2 +- Audiobook Player/PlayerManager.swift | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Audiobook Player/ListBooksViewController.swift b/Audiobook Player/ListBooksViewController.swift index 2114f2b70..5c53296b6 100644 --- a/Audiobook Player/ListBooksViewController.swift +++ b/Audiobook Player/ListBooksViewController.swift @@ -220,7 +220,7 @@ class ListBooksViewController: UIViewController, UIGestureRecognizerDelegate { @objc func bookReady(){ MBProgressHUD.hideAllHUDs(for: self.view, animated: true) - PlayerManager.sharedInstance.playPressed() + PlayerManager.sharedInstance.playPressed(autoplayed: true) } @objc func bookPlayed(){ diff --git a/Audiobook Player/PlayerManager.swift b/Audiobook Player/PlayerManager.swift index 30a2cf654..057752818 100644 --- a/Audiobook Player/PlayerManager.swift +++ b/Audiobook Player/PlayerManager.swift @@ -268,7 +268,7 @@ extension PlayerManager: AVAudioPlayerDelegate { } //toggle play/pause of book - func playPressed() { + func playPressed(autoplayed: Bool = false) { guard let audioplayer = self.audioPlayer else { return } @@ -301,8 +301,14 @@ extension PlayerManager: AVAudioPlayerDelegate { try! AVAudioSession.sharedInstance().setActive(true) + let completed = Int(audioplayer.duration) == Int(audioplayer.currentTime) + + if autoplayed && completed { + return + } + //if book is completed, reset to start - if Int(audioplayer.duration) == Int(audioplayer.currentTime) { + if completed { audioplayer.currentTime = 0 }