diff --git a/WOKA/Info.plist b/WOKA/Info.plist
index 1b62a00..d08457d 100644
--- a/WOKA/Info.plist
+++ b/WOKA/Info.plist
@@ -38,6 +38,7 @@
UIBackgroundModes
+ audio
remote-notification
diff --git a/WOKA/Main/Delegate/AppDelegate.swift b/WOKA/Main/Delegate/AppDelegate.swift
index 954da9c..7baa4ca 100644
--- a/WOKA/Main/Delegate/AppDelegate.swift
+++ b/WOKA/Main/Delegate/AppDelegate.swift
@@ -9,6 +9,7 @@ import UIKit
import Lottie
import IQKeyboardManagerSwift
import JWPlayerKit
+import AVFAudio
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
@@ -40,10 +41,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
configureSideBar()
JWPlayerKitLicense.setLicenseKey("Lgok1t7H4PKY+M8FZqmCx54ibUF+NeCTn+xgd+/LVTaRdc+L")
-
+ setupAudioSession()
return true
}
+
+ func setupAudioSession() {
+ let session = AVAudioSession.sharedInstance()
+ do {
+ try session.setCategory(.playback, mode: .default)
+ try session.setActive(true)
+ } catch {
+ print("Failed to set up audio session")
+ }
+ }
+
// var myOrientation: UIInterfaceOrientationMask = .portrait
//
// func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
diff --git a/WOKA/SideBarNav/SideBarNav.storyboard b/WOKA/SideBarNav/SideBarNav.storyboard
index b3e8f0f..ebd1c3c 100644
--- a/WOKA/SideBarNav/SideBarNav.storyboard
+++ b/WOKA/SideBarNav/SideBarNav.storyboard
@@ -131,7 +131,7 @@
-
+
@@ -225,16 +225,16 @@
-
+
-
+
-
+
-
+
@@ -378,10 +378,10 @@
-
+
-
+
@@ -403,7 +403,7 @@
-
+
-
+
@@ -439,7 +439,7 @@
-
+
-
+
@@ -475,7 +475,7 @@
-
+
-
+
-
+
-
+
-
+
-
+
@@ -500,7 +500,7 @@
-
+
@@ -509,10 +509,10 @@
-
+
-
+
@@ -757,7 +757,7 @@
-
+
diff --git a/WOKA/Theme/Controller/MoreVC.swift b/WOKA/Theme/Controller/MoreVC.swift
index 1f4c121..2a70a17 100644
--- a/WOKA/Theme/Controller/MoreVC.swift
+++ b/WOKA/Theme/Controller/MoreVC.swift
@@ -16,7 +16,8 @@ class MoreVC: UIViewController {
@IBOutlet weak var songTableView: UITableView!
var vm = MoreVM()
-
+ var timeObserverToken: Any?
+
override func viewDidLoad() {
super.viewDidLoad()
vm.vc = self
@@ -24,7 +25,7 @@ class MoreVC: UIViewController {
}
override func viewWillDisappear(_ animated: Bool) {
- vm.player.pause()
+ vm.player?.pause()
}
}
@@ -36,22 +37,16 @@ extension MoreVC : TableViewSRC{
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let cell = tableView.dequeueReusableCell(withIdentifier: K.CellIdentifier.Home.songListCell) as! SongListCell
- let data = vm.songData[indexPath.row]
- cell.setData(data: data,playerStatus: vm.playerStatus)
-// if let playingIndex = vm.currentIndexPlayingSong {
-// if indexPath.row == playingIndex{
-// if vm.onGoingTime != nil{ // if index is nil that means its just paused
-// cell.setData(data: data,playerStatus: vm.playerStatus,onGoingTime: vm.onGoingTime)
-// }else{ // if index is there it means song is playing and needs to be paused
-// cell.setData(data: data,playerStatus: .resume,onGoingTime: vm.onGoingTime)
-// }
-// }else{
-// cell.setData(data: data,playerStatus: vm.playerStatus,onGoingTime: nil)
-// }
-// }else{
-// cell.setData(data: data,playerStatus: .stopped ,onGoingTime: nil)
-// }
+
+ let cell = tableView.dequeueReusableCell(withIdentifier: "SongListCell", for: indexPath) as! SongListCell
+ let songData = vm.songData[indexPath.row]
+ let isActive = (indexPath.row == vm.currentIndexPlayingSong)
+ if let index = vm.currentIndexPlayingSong , index == indexPath.row{
+ cell.setData(data: songData,playerStatus: vm.playerStatus, active: true, currentTime: currentTimePlayer)
+ }else{
+ cell.setData(data: songData,playerStatus: .stopped , active: false, currentTime: 0)
+ }
+
return cell
}
@@ -85,11 +80,15 @@ extension MoreVC : TableViewSRC{
if indexPath.row == playingIndex{ // if same row is selected pause the row
print(vm.playerStatus)
switch vm.playerStatus{
- case .play:
+ case .play: // if player is playing pause it.
print("Player is playing")
- vm.player.pause()
+ vm.player?.pause()
+ vm.playerStatus = .pause
+ tableView.reloadRows(at: [IndexPath(row: vm.currentIndexPlayingSong!, section: 0)], with: .none)
case .pause:
- print("Player is pause")
+ vm.player?.play()
+ vm.playerStatus = .resume
+ tableView.reloadRows(at: [IndexPath(row: vm.currentIndexPlayingSong!, section: 0)], with: .none)
case .loading:
print("Player is loading")
case .resume:
@@ -102,16 +101,27 @@ extension MoreVC : TableViewSRC{
this means other cell was playing now stop it and play this new cell
first reload the playing cell and pause the audio
*/
+ //Reset Old Cell
+ vm.playerStatus = .stopped
+ tableView.reloadRows(at: [IndexPath(row: vm.currentIndexPlayingSong!, section: 0)], with: .none)
+ //Update new cell
+ vm.currentIndexPlayingSong = indexPath.row
+ vm.playerStatus = .loading
+ currentTimePlayer = 0
+ tableView.reloadRows(at: [indexPath], with: .none)
+ let data = vm.songData[indexPath.row]
+ startPlaying(song: data, at: indexPath)
print("Other cell playing")
}
}else{
// if there is no playing audio before
- vm.playerStatus = .play
+ vm.playerStatus = .loading
vm.currentIndexPlayingSong = indexPath.row
+ currentTimePlayer = 0
tableView.reloadRows(at: [indexPath], with: .none)
let data = vm.songData[indexPath.row]
- playSong(url: data.contentMoreDetails?.first?.url)
+ startPlaying(song: data, at: indexPath)
print("First Play")
}
}
@@ -130,11 +140,46 @@ extension MoreVC : TableViewSRC{
})
}
- func playSong(url : String?){
- if let url {
- vm.player = AVPlayer(url: URL(string: url)!)
+ func startPlaying(song: SongBlogDM.PaintDatum, at indexPath: IndexPath) {
+ // Set up and start the player
+ stopPlaying()
+ if let urlString = song.contentMoreDetails?.first?.url , let url = URL(string: urlString){
+ vm.player = AVPlayer(url: url)
+ vm.player?.play()
+ vm.playerStatus = .play
+ vm.currentIndexPlayingSong = indexPath.row
+ addPeriodicTimeObserver()
vm.observePlayer()
- vm.player.play()
+ }
+ }
+
+ func stopPlaying() {
+ // Remove the periodic time observer
+ removePeriodicTimeObserver()
+
+ // Stop the player
+ vm.player?.pause()
+// vm.player = nil
+ vm.playerStatus = .stopped
+ vm.currentIndexPlayingSong = nil
+ }
+
+ func removePeriodicTimeObserver() {
+ if let token = timeObserverToken {
+ vm.player?.removeTimeObserver(token)
+ timeObserverToken = nil
+ }
+ }
+
+ func addPeriodicTimeObserver() {
+ let interval = CMTime(seconds: 1, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
+ timeObserverToken = vm.player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in
+ guard let self = self else { return }
+ currentTimePlayer = Int(CMTimeGetSeconds(time))
+ if let currentPlayingIndex = self.vm.currentIndexPlayingSong,
+ let cell = self.songTableView.cellForRow(at: IndexPath(row: vm.currentIndexPlayingSong ?? 0, section: 0)) as? SongListCell {
+ cell.updateCurrentTime()
+ }
}
}
diff --git a/WOKA/Theme/View/BlogsCell.xib b/WOKA/Theme/View/BlogsCell.xib
index 30dcd79..7288a4a 100644
--- a/WOKA/Theme/View/BlogsCell.xib
+++ b/WOKA/Theme/View/BlogsCell.xib
@@ -29,14 +29,14 @@
-
+
-
+
-
+
diff --git a/WOKA/Theme/View/SongListCell.swift b/WOKA/Theme/View/SongListCell.swift
index 74f3b79..0d06dfd 100644
--- a/WOKA/Theme/View/SongListCell.swift
+++ b/WOKA/Theme/View/SongListCell.swift
@@ -16,27 +16,11 @@ class SongListCell: UITableViewCell {
@IBOutlet weak var playPauseImage: UIImageView!
@IBOutlet weak var indicatorView: UIView!
-// typealias btnTappedBlock = (String?) -> Void
-// var btnTapped : btnTappedBlock!
-
- var timer: Timer?
- private var currentTime: Int = 0
- var totalTime: Int = 0
+// var timer: Timer?
override func awakeFromNib() {
super.awakeFromNib()
-// self.addTapGesture { [weak self] in
-// guard let self else{return}
-// UIView.animate(withDuration: 0.1, animations: {
-// self.outerStack.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)
-// }) { _ in
-// UIView.animate(withDuration: 0.1) {
-// self.outerStack.transform = .identity
-// self.btnTapped(self.ongoingTimeLabel.text)
-// }
-// }
-// }
}
override func setSelected(_ selected: Bool, animated: Bool) {
@@ -47,89 +31,57 @@ class SongListCell: UITableViewCell {
override func prepareForReuse() {
super.prepareForReuse()
// Invalidate the timer when the cell is reused
- timer?.invalidate()
- timer = nil
- ongoingTimeLabel.text = "0:00"
+ currentTimePlayer = 0
+// self.timer?.invalidate()
+// self.timer = nil
}
- func setData(data: SongBlogDM.PaintDatum, playerStatus: PlayerStatus) {
- ongoingTimeLabel.text = "00:00"
- totalTime = 0
- currentTime = 0
+ func setData(data: SongBlogDM.PaintDatum, playerStatus: PlayerStatus, active: Bool, currentTime: Int) {
+ totalTime = data.songDuration?.timeStringToSeconds() ?? 0
+ ongoingTimeLabel.text = formatTime(currentTime)
+ playPauseImage.image = UIImage(systemName: "play.fill")
-
- switch playerStatus{
- case .stopped:
- playPauseImage.image = UIImage(systemName: "play.fill")
- case .pause:
- playPauseImage.image = UIImage(systemName: "play.fill")
- case .play:
- playPauseImage.image = UIImage(systemName: "pause.fill")
- totalTime = data.songDuration?.timeStringToSeconds() ?? 0
-
-// if let onGoingTime{
-// currentTime = onGoingTime.timeStringToSeconds() ?? 0
-// ongoingTimeLabel.text = onGoingTime
-// }else{
-// currentTime = 0
-// }
- startCountdown()
-
- case .resume:
- playPauseImage.image = UIImage(systemName: "pause.fill")
- totalTime = data.songDuration?.timeStringToSeconds() ?? 0
-// if let onGoingTime{
-// currentTime = onGoingTime.timeStringToSeconds() ?? 0
-// ongoingTimeLabel.text = onGoingTime
-// }else{
-// currentTime = 0
-// }
- startCountdown()
- case .loading:
- break
+ if active {
+ ongoingTimeLabel.text = formatTime(currentTime)
+ switch playerStatus {
+ case .stopped:
+ showHideLoading(show: false)
+ playPauseImage.image = UIImage(systemName: "play.fill")
+ case .pause:
+ showHideLoading(show: false)
+ playPauseImage.image = UIImage(systemName: "play.fill")
+ case .play, .resume:
+ showHideLoading(show: false)
+ playPauseImage.image = UIImage(systemName: "pause.fill")
+ case .loading:
+ showHideLoading(show: true)
+ break
+ }
+ } else {
+ showHideLoading(show: false)
}
- // Set total time label and song title
- if let totalDuration = data.songDuration{
- //Check of the hour is zero removing it
+ if let totalDuration = data.songDuration {
let totalTimeFiltered = totalDuration.checkHourZero()
totalTimeLabel.text = "/" + totalTimeFiltered
- }else{
+ } else {
totalTimeLabel.text = "/" + (data.songDuration ?? "00:00")
}
songTitle.text = data.title ?? "Unknown Title"
}
- private func startCountdown() {
- // Invalidate any existing timer
- timer?.invalidate()
-
- // Schedule a new timer
- timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCountdown), userInfo: nil, repeats: true)
+ func showHideLoading(show : Bool){
+ show ? self.indicatorView.showLoading() : self.indicatorView.hideLoading()
+ self.indicatorView.isHidden = !show
+ self.playPauseImage.isHidden = show
}
- @objc private func updateCountdown() {
- // Update the timer label
- if currentTime < totalTime {
- currentTime += 1
- ongoingTimeLabel.text = formatTime(currentTime)
- } else {
- timer?.invalidate()
- timer = nil
- }
+ func updateCurrentTime() {
+ let timeInSeconds = Int(currentTimePlayer)
+ currentTimePlayer = timeInSeconds
+ ongoingTimeLabel.text = formatTime(currentTimePlayer)
}
-// @objc private func updateCountdown() {
-// // Update countdown label
-// if totalTime > 0 {
-// totalTime -= 1
-// ongoingTimeLabel.text = formatTime(totalTime)
-// } else {
-// timer?.invalidate()
-// timer = nil
-// ongoingTimeLabel.text = "0:00"
-// }
-// }
private func formatTime(_ seconds: Int) -> String {
// Format time as MM:SS
diff --git a/WOKA/Theme/ViewModel/MoreVM.swift b/WOKA/Theme/ViewModel/MoreVM.swift
index e04098e..9b923fb 100644
--- a/WOKA/Theme/ViewModel/MoreVM.swift
+++ b/WOKA/Theme/ViewModel/MoreVM.swift
@@ -15,6 +15,10 @@ enum PlayerStatus{
case resume
case stopped
}
+
+var currentTimePlayer = Int()
+var totalTime = Int()
+
class MoreVM{
weak var vc : MoreVC!
@@ -23,7 +27,7 @@ class MoreVM{
var songData = [SongBlogDM.PaintDatum]()
- var player = AVPlayer()
+ var player : AVPlayer?
var playerObserver: NSKeyValueObservation?
var playerStatus = PlayerStatus.loading
@@ -38,28 +42,37 @@ class MoreVM{
vc.homeBtn.addTapGesture {
self.vc.dismiss(animated: true)
}
-
-
}
func observePlayer() {
- playerObserver = self.player.observe(\.timeControlStatus, options: [.new, .old], changeHandler: { [weak self] player, change in
+ playerObserver = self.player?.observe(\.timeControlStatus, options: [.new, .old], changeHandler: { [weak self] player, change in
guard let self = self else { return }
switch player.timeControlStatus {
case .playing:
- print("Player is playing")
+ print("Player is playing OOO")
self.playerStatus = .play
+ vc.songTableView.reloadRows(at: [IndexPath(row: currentIndexPlayingSong ?? 0, section: 0)], with: .none)
case .paused:
- print("Player is paused")
- self.playerStatus = .pause
+ print("Player is paused OOO")
+// self.playerStatus = .pause
+ if self.player?.currentTime() == self.player?.currentItem?.duration {
+ print("Player finished playing")
+ self.playerStatus = .stopped
+ self.vc.songTableView.reloadRows(at: [IndexPath(row: currentIndexPlayingSong!, section: 0)], with: .none)
+ currentIndexPlayingSong = nil
+ } else {
+ print("Player is paused")
+ self.playerStatus = .pause
+ }
case .waitingToPlayAtSpecifiedRate:
- print("Player is waiting to play at specified rate")
+ print("Player is waiting to play at specified rate OOO")
self.playerStatus = .loading
@unknown default:
- print("Unknown player status")
+ print("Unknown player status OOO")
}
})
}
+
//
// func playSong(urlString: String) {
// guard let url = URL(string: urlString) else {
@@ -141,3 +154,83 @@ class MoreVM{
}
}
}
+
+
+import UIKit
+
+extension UIView {
+
+ private struct AssociatedKeys {
+ static var loadingView = "loadingView"
+ }
+
+ private var loadingView: LoadingView? {
+ get {
+ return objc_getAssociatedObject(self, AssociatedKeys.loadingView) as? LoadingView
+ }
+ set {
+ objc_setAssociatedObject(self, AssociatedKeys.loadingView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
+ }
+ }
+
+ func showLoading() {
+ if loadingView == nil {
+ let newLoadingView = LoadingView(frame: self.bounds)
+ newLoadingView.translatesAutoresizingMaskIntoConstraints = false
+ self.addSubview(newLoadingView)
+ NSLayoutConstraint.activate([
+ newLoadingView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
+ newLoadingView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
+ newLoadingView.topAnchor.constraint(equalTo: self.topAnchor),
+ newLoadingView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
+ ])
+ loadingView = newLoadingView
+ }
+ loadingView?.startLoading()
+ }
+
+ func hideLoading() {
+ loadingView?.stopLoading()
+ }
+}
+
+class LoadingView: UIView {
+
+ private var activityIndicator: UIActivityIndicatorView!
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ setupView()
+ }
+
+ required init?(coder: NSCoder) {
+ super.init(coder: coder)
+ setupView()
+ }
+
+ private func setupView() {
+ self.backgroundColor = .clear
+
+ activityIndicator = UIActivityIndicatorView(style: .medium)
+ activityIndicator.translatesAutoresizingMaskIntoConstraints = false
+ activityIndicator.color = .white
+ self.addSubview(activityIndicator)
+
+ NSLayoutConstraint.activate([
+ activityIndicator.centerXAnchor.constraint(equalTo: self.centerXAnchor),
+ activityIndicator.centerYAnchor.constraint(equalTo: self.centerYAnchor),
+ activityIndicator.heightAnchor.constraint(equalToConstant: self.frame.height - 10),
+ activityIndicator.widthAnchor.constraint(equalToConstant: self.frame.width - 10)
+ ])
+ }
+
+ func startLoading() {
+ activityIndicator.startAnimating()
+ self.isHidden = false
+ }
+
+ func stopLoading() {
+ activityIndicator.stopAnimating()
+ self.isHidden = true
+ }
+}