added iOS source code
[wl-app.git] / iOS / WolneLektury / Screens / Player / PlayerViewController.swift
1 //
2 //  PlayerViewController.swift
3 //  WolneLektury
4 //
5 //  Created by Pawel Dabrowski on 07/09/2018.
6 //  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
7 //
8
9 import UIKit
10 import AVFoundation
11 import MediaPlayer
12 import Kingfisher
13
14 class PlayerViewController: WLViewController {
15
16     @IBOutlet weak var titleLabel: UILabel!
17     @IBOutlet weak var bgImageView: UIImageView!
18     @IBOutlet weak var bgOverlayView: UIView!
19     @IBOutlet weak var miniatureImageView: UIImageView!
20     @IBOutlet weak var tableView: UITableView!
21     @IBOutlet weak var backButton: UIButton!
22     @IBOutlet weak var toggleListButton: UIButton!
23     @IBOutlet weak var nextButton: UIButton!
24     @IBOutlet weak var prevButton: UIButton!
25     @IBOutlet weak var forwardButton: UIButton!
26     @IBOutlet weak var rewindButton: UIButton!
27     @IBOutlet weak var playPauseButton: UIButton!
28     @IBOutlet weak var trackNumberLabel: UILabel!
29     @IBOutlet weak var trackTitleLabel: UILabel!
30     @IBOutlet weak var trackCurrentTimeLabel: UILabel!
31     @IBOutlet weak var trackTimeLabel: UILabel!
32     @IBOutlet weak var progressSlider: UISlider!
33     @IBOutlet weak var miniatureImageWidthConstraint: NSLayoutConstraint!
34     @IBOutlet weak var trackTitleLabelTopConstraint: NSLayoutConstraint!
35
36     var bookDetailsModel: BookDetailsModel!
37     var mediaModels: [MediaModel]!
38     var selectedRow: Int?
39     
40     static func instance(bookDetailsModel: BookDetailsModel) -> PlayerViewController{
41         let controller = PlayerViewController.instance()
42         controller.bookDetailsModel = bookDetailsModel
43         controller.mediaModels = bookDetailsModel.getAudiobookMediaModels()
44         return controller
45     }
46
47     open override var preferredStatusBarStyle : UIStatusBarStyle {
48         return .lightContent
49     }
50
51     override func viewDidLoad() {
52         super.viewDidLoad()
53         tableView.separatorStyle = .none
54         tableView.isHidden = true
55         
56         if UIScreen.main.bounds.size.width == 320 || UIScreen.main.bounds.size.height == 320 {
57             miniatureImageWidthConstraint.constant = 100
58         }
59         
60         bgOverlayView.alpha = 0.7
61         bgOverlayView.backgroundColor = bookDetailsModel.bgColor
62         let titleAttributedText = bookDetailsModel.getAttributedAuthorAndTitle(titleFont: UIFont.systemFont(ofSize: 21, weight: .light), descFont: UIFont.systemFont(ofSize: 28, weight: .light))
63         titleLabel.attributedText = titleAttributedText
64         
65         if let bookDetails = PlayerController.shared.currentBookDetails, bookDetails.slug == bookDetailsModel.slug {
66             PlayerController.shared.startOrContinuePlaying(bookDetailsModel: bookDetailsModel, delegate: self)
67         }
68         else {
69             PlayerController.shared.stopAndClear()
70             PlayerController.shared.preparePlayer(bookDetailsModel: bookDetailsModel, delegate: self, trackIndex: 0)
71         }
72         
73         if mediaModels.count < 2 {
74             trackNumberLabel.isHidden = true
75             trackNumberLabel.text = ""
76             toggleListButton.isHidden = true
77             trackTitleLabelTopConstraint.constant = 10
78         }
79
80         refreshButtonsVisibility()
81         
82         if let url = bookDetailsModel.getCoverThumbUrl(){
83             ImageDownloader.default.downloadImage(with: url, options: [], progressBlock: nil) {
84                 [weak self] (image, error, url, data) in
85                 if let image = image{
86                     self?.bgImageView.image = image
87                     self?.miniatureImageView.image = image
88                     PlayerController.shared.setCoverImage(image: image)
89                 }
90             }
91         }
92     }
93     
94     // ############################################
95     // MARK: - Private
96     // ############################################
97
98     private func toggleList() {
99         tableView.isHidden = !tableView.isHidden
100     }
101     
102     private func startNextTrack() {
103         PlayerController.shared.playNextAudio()
104     }
105
106     private func startPrevTrack() {
107         PlayerController.shared.playPreviousAudio()
108     }
109     
110     //This returns song length
111     private func calculateTimeFromNSTimeInterval(_ duration:TimeInterval) ->(hour: String, minute:String, second:String){
112         let hour_   = abs(Int(duration)/3600)
113         let minute_ = abs(Int((duration/60).truncatingRemainder(dividingBy: 60)))
114         let second_ = abs(Int(duration.truncatingRemainder(dividingBy: 60)))
115         
116         var hour = hour_ > 9 ? "\(hour_)" : "0\(hour_)"
117         if hour_ == 0 {
118             hour = ""
119         }
120         let minute = minute_ > 9 ? "\(minute_)" : "0\(minute_)"
121         let second = second_ > 9 ? "\(second_)" : "0\(second_)"
122         return (hour, minute,second)
123     }
124
125     fileprivate func updateCurrentTime(timeInterval: TimeInterval?) {
126         
127         var ti = timeInterval
128         if ti == nil {
129             ti = PlayerController.shared.getCurrentTime()
130         }
131         
132         if let ti = ti {
133             let timeIntervalFloat = Float(ti)
134             progressSlider.value = timeIntervalFloat
135             let time = calculateTimeFromNSTimeInterval(ti)
136             if time.hour.count == 0 {
137                 trackCurrentTimeLabel.text = "\(time.minute):\(time.second)"
138             }
139             else {
140                 trackCurrentTimeLabel.text = "\(time.hour):\(time.minute):\(time.second)"
141             }
142         }
143     }
144
145     func refreshButtonsVisibility(){
146         
147         prevButton.isHidden = true
148         nextButton.isHidden = true
149         
150         let currentAudiobookIndex = PlayerController.shared.currentAudiobookIndex
151         let allMediaCount = mediaModels.count
152         
153         if currentAudiobookIndex > 0{
154             prevButton.isHidden = false
155         }
156         if allMediaCount > currentAudiobookIndex + 1 {
157             nextButton.isHidden = false
158         }
159     }
160
161     // ############################################
162     // MARK: - Action
163     // ############################################
164     
165     @IBAction func backButtonAction(sender: UIButton) {
166         PlayerController.shared.delegate = nil
167         navigationController?.popViewController(animated: true)
168     }
169     
170     @IBAction func toggleListButtonAction(sender: UIButton) {
171         toggleList()
172     }
173     
174     @IBAction func nextButtonAction(sender: UIButton) {
175         PlayerController.shared.playNextAudio()
176     }
177     
178     @IBAction func prevButtonAction(sender: UIButton) {
179         PlayerController.shared.playPreviousAudio()
180     }
181     
182     @IBAction func forwardButtonAction(sender: UIButton) {
183         PlayerController.shared.forward()
184     }
185     
186     @IBAction func rewindButtonAction(sender: UIButton) {
187         PlayerController.shared.rewind()
188     }
189     
190     @IBAction func playPauseButtonAction(sender: UIButton) {
191         let player = PlayerController.shared
192         if player.isPlaying() {
193             player.pauseAudioPlayer()
194         }
195         else {
196             if player.audioPlayer == nil {
197                 player.startOrContinuePlaying(bookDetailsModel: bookDetailsModel, delegate: self)
198             }
199             else {
200                 player.playAudio()
201             }
202         }
203         playPauseButton.setImage(player.isPlaying() ? #imageLiteral(resourceName: "player_controls_pause") : #imageLiteral(resourceName: "player_controls_play"), for: .normal)
204     }
205     
206     @IBAction func progressSliderAction(sender: UISlider) {
207         PlayerController.shared.changeTime(timeInterval: TimeInterval(sender.value))
208     }
209 }
210
211 extension PlayerViewController: UITableViewDataSource {
212     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
213         return mediaModels.count
214     }
215     
216     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
217         let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerItemTableViewCell", for: indexPath) as! PlayerItemTableViewCell
218         let isSelected = PlayerController.shared.currentAudiobookIndex == indexPath.row
219         cell.setup(mediaModel:  mediaModels[indexPath.row], isPlaying: isSelected)
220         return cell
221     }
222 }
223
224 extension PlayerViewController: UITableViewDelegate {
225     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
226         if mediaModels.count > indexPath.row {
227             
228             PlayerController.shared.startPlaying(bookDetailsModel: bookDetailsModel, delegate: self, trackIndex: indexPath.row)
229         }
230     }
231 }
232
233 extension PlayerViewController: PlayerControllerDelegate {
234     
235     func playerControllerDelegateTrackChanged() {
236         
237         let player = PlayerController.shared
238         progressSlider.maximumValue = Float(player.audioLength)
239         
240         updateCurrentTime(timeInterval: player.getCurrentTime())
241         trackNumberLabel.text = String(format: "player_chapter_number".localized, player.currentAudiobookIndex + 1)
242         
243         if let currentModel = player.getCurentAudiobookMediaModel() {
244             trackTitleLabel.text = currentModel.name
245             let time = calculateTimeFromNSTimeInterval(player.audioLength)
246             if time.hour.count == 0 {
247                 trackTimeLabel.text = "\(time.minute):\(time.second)"
248             }
249             else {
250                 trackTimeLabel.text = "\(time.hour):\(time.minute):\(time.second)"
251             }
252         }
253         
254         playPauseButton.setImage(player.isPlaying() ? #imageLiteral(resourceName: "player_controls_pause") : #imageLiteral(resourceName: "player_controls_play"), for: .normal)
255         
256         let newSelectedIndex =  IndexPath(row: PlayerController.shared.currentAudiobookIndex, section: 0)
257         tableView.selectRow(at: newSelectedIndex, animated: true, scrollPosition: .middle)
258         
259         refreshButtonsVisibility()
260     }
261     
262     func playerControllerDelegateUpdatePlayerProgress(timeInterval: TimeInterval) {
263         updateCurrentTime(timeInterval: timeInterval)
264     }
265
266     func playerControllerDelegatePlayStateChanged() {
267         playPauseButton.setImage(PlayerController.shared.isPlaying() ? #imageLiteral(resourceName: "player_controls_pause") : #imageLiteral(resourceName: "player_controls_play"), for: .normal)
268     }
269 }
270