- Tc - 69 fixed - Tc - 70 fixed - TC 71 fixed - Added local ads to fm and more section - Added local ads to mylist - Fixed a bug for sync * Fixed the crashing by temporary updating the wokastaging with raw data
288 lines
11 KiB
Swift
288 lines
11 KiB
Swift
//
|
|
// WokaFMVC.swift
|
|
// WOKA
|
|
//
|
|
// Created by MacBook Pro on 01/08/24.
|
|
//
|
|
|
|
import UIKit
|
|
import AVFoundation
|
|
import Alamofire
|
|
import GoogleMobileAds
|
|
|
|
class WokaFMVC: UIViewController {
|
|
|
|
@IBOutlet weak var roundView: UIView!
|
|
@IBOutlet weak var mainView: UIView!
|
|
@IBOutlet weak var playBtn: UIButton!
|
|
@IBOutlet weak var volPlusBtn: UIButton!
|
|
@IBOutlet weak var volMinusBtnn: UIButton!
|
|
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
|
|
@IBOutlet weak var blackView: UIView!
|
|
@IBOutlet weak var imageAdView: UIImageView!
|
|
@IBOutlet weak var imageAdHeight: NSLayoutConstraint!
|
|
|
|
var vm = WokaFMVM()
|
|
|
|
deinit {
|
|
vm.playerItem.removeObserver(self, forKeyPath: "status")
|
|
vm.player.removeObserver(self, forKeyPath: "timeControlStatus")
|
|
vm.playerItem.removeObserver(self, forKeyPath: "isPlaybackBufferEmpty")
|
|
vm.playerItem.removeObserver(self, forKeyPath: "isPlaybackLikelyToKeepUp")
|
|
|
|
vm.player?.pause()
|
|
NotificationCenter.default.removeObserver(self,name: UIApplication.didEnterBackgroundNotification, object: nil)
|
|
NotificationCenter.default.removeObserver(self,name: UIApplication.didBecomeActiveNotification, object: nil)
|
|
|
|
// Deactivate the audio session if needed
|
|
do {
|
|
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
|
} catch {
|
|
print("Failed to deactivate audio session: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
vm.vc = self
|
|
vm.initView()
|
|
NotificationCenter.default.addObserver(self,selector: #selector(appDidEnterBackground),name: UIApplication.didEnterBackgroundNotification,object: nil)
|
|
NotificationCenter.default.addObserver(self,selector: #selector(appWillEnterForeground),name: UIApplication.didBecomeActiveNotification,object: nil)
|
|
}
|
|
|
|
// MARK: - App LifeCycle HAndler
|
|
|
|
@objc func appDidEnterBackground() {
|
|
vm.setupNowPlayingInfo()
|
|
vm.setupRemoteCommandCenter()
|
|
if let postID = AuthFunc.shareInstance.staticURLs?.liveFmData?.id {
|
|
let duration = DateFormatterLib.dateDifferenceINT(date1: vm.startTimeStamp, date2: Date())
|
|
AuthFunc.shareInstance.userVideoView(postID: postID, postType: PostType.FM.rawValue, duration: duration, catID: 0) { [weak self] isDone in
|
|
guard let self else{return}
|
|
if isDone{
|
|
vm.startTimeStamp = Date()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc func appWillEnterForeground(){
|
|
vm.stopMPNowPlayin()
|
|
}
|
|
|
|
@IBAction func playBtnTapped(_ sender: UIButton) {
|
|
PersistentStorage.shared.addRadioCount()
|
|
if sender == playBtn{
|
|
if sender.image(for: .normal)?.pngData() == UIImage(named: "Reload")?.pngData(){
|
|
// when user reloads check for internet connection
|
|
if !(NetworkReachabilityManager()?.isReachable ?? false){
|
|
return
|
|
}
|
|
vm.retryCount = 0
|
|
vm.retryConnect()
|
|
} else if sender.image(for: .normal)?.pngData() == UIImage(named: "PlayButton")?.pngData(){
|
|
vm.player.play()
|
|
}else{
|
|
vm.player.pause()
|
|
}
|
|
}
|
|
}
|
|
|
|
@IBAction func volumeBtnTapped(_ sender: UIButton) {
|
|
PersistentStorage.shared.addRadioCount()
|
|
switch sender{
|
|
case volPlusBtn:
|
|
if vm.player.volume == 1 {
|
|
sender.isEnabled = false
|
|
return
|
|
}
|
|
|
|
volMinusBtnn.isEnabled = true
|
|
let newVolume = min(vm.player.volume + 0.2, 1.0)
|
|
vm.player.volume = newVolume
|
|
case volMinusBtnn:
|
|
if vm.player.volume == 0.0 {
|
|
sender.isEnabled = false
|
|
return
|
|
}
|
|
|
|
volPlusBtn.isEnabled = true
|
|
let newVolume = max(vm.player.volume - 0.2, 0.0)
|
|
vm.player.volume = newVolume
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
@IBAction func closeBtnTapped(_ sender: UIButton) {
|
|
PersistentStorage.shared.addRadioCount()
|
|
if let postID = AuthFunc.shareInstance.staticURLs?.liveFmData?.id {
|
|
let duration = DateFormatterLib.dateDifferenceINT(date1: vm.startTimeStamp, date2: Date())
|
|
AuthFunc.shareInstance.userVideoView(postID: postID, postType: PostType.FM.rawValue, duration: duration, catID: 0) { _ in}
|
|
}
|
|
self.dismiss(animated: true)
|
|
}
|
|
|
|
func startMonitoring(){
|
|
NetworkReachibility.shared.startMonitoring { [weak self] isConnected in
|
|
guard let self else{return}
|
|
switch isConnected{
|
|
case true: // if network is connected than stop
|
|
// when internet is back we have to setup player again
|
|
NetworkReachibility.shared.stopMonitoring()
|
|
vm.setupPlayer()
|
|
addObservers()
|
|
vm.setupAudioSession()
|
|
case false:
|
|
// if there is no internet connection prompt user and show reload icon
|
|
self.toast(msg: "Lost Connection.", time: 1.4)
|
|
activityIndicator.stopAnimating()
|
|
playBtn.setImage(UIImage(named: "Reload"), for: .normal)
|
|
playBtn.isEnabled = true
|
|
}
|
|
}
|
|
}
|
|
|
|
func addObservers() {
|
|
// Observe the player's status
|
|
vm.playerItem.addObserver(self, forKeyPath: "status", options: [.new, .old], context: nil)
|
|
|
|
// Observe the player's time control status
|
|
vm.player.addObserver(self, forKeyPath: "timeControlStatus", options: [.new, .old], context: nil)
|
|
|
|
// Observe buffering status
|
|
vm.playerItem.addObserver(self, forKeyPath: "isPlaybackBufferEmpty", options: [.new, .old], context: nil)
|
|
vm.playerItem.addObserver(self, forKeyPath: "isPlaybackLikelyToKeepUp", options: [.new, .old], context: nil)
|
|
}
|
|
|
|
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
|
if keyPath == "status" {
|
|
if vm.playerItem.status == .readyToPlay {
|
|
print("FM is ready to play")
|
|
vm.player.play()
|
|
} else if vm.playerItem.status == .failed {
|
|
if vm.retryCount < 4{
|
|
vm.retryCount += 1
|
|
vm.retryConnect()
|
|
print("retry fm connect :-", vm.retryCount)
|
|
return
|
|
}
|
|
print("FM failed to load")
|
|
activityIndicator.stopAnimating()
|
|
playBtn.setImage(UIImage(named: "Reload"), for: .normal)
|
|
playBtn.isEnabled = true
|
|
}
|
|
} else if keyPath == "timeControlStatus" {
|
|
switch vm.player.timeControlStatus {
|
|
case .playing:
|
|
print("FM is playing")
|
|
activityIndicator.stopAnimating()
|
|
playBtn.isEnabled = true
|
|
playBtn.setImage(UIImage(named: "PauseButton"), for: .normal)
|
|
case .paused:
|
|
print("FM is paused")
|
|
playBtn.setImage(UIImage(named: "PlayButton"), for: .normal)
|
|
case .waitingToPlayAtSpecifiedRate:
|
|
activityIndicator.startAnimating()
|
|
playBtn.isEnabled = false
|
|
|
|
if !(NetworkReachabilityManager()?.isReachable ?? false){
|
|
startMonitoring()
|
|
}
|
|
print("FM is buffering 1 ")
|
|
@unknown default:
|
|
break
|
|
}
|
|
} else if keyPath == "isPlaybackBufferEmpty" {
|
|
if vm.playerItem.isPlaybackBufferEmpty {
|
|
print("FM is buffering 2")
|
|
playBtn.isEnabled = false
|
|
activityIndicator.startAnimating()
|
|
startMonitoring()
|
|
}
|
|
} else if keyPath == "isPlaybackLikelyToKeepUp" {
|
|
if vm.playerItem.isPlaybackLikelyToKeepUp {
|
|
print("FM is likely to keep up with the playback")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
extension UIButton {
|
|
func applyGradientBtn(colors: [UIColor], startPoint: CGPoint, endPoint: CGPoint) {
|
|
// Remove existing gradient layer if any
|
|
self.layer.sublayers?.filter { $0 is CAGradientLayer }.forEach { $0.removeFromSuperlayer() }
|
|
|
|
// Create a new CAGradientLayer instance
|
|
let gradientLayer = CAGradientLayer()
|
|
|
|
// Set the frame of the gradient layer to match the bounds of the button
|
|
gradientLayer.frame = self.bounds
|
|
|
|
// Convert the array of UIColor objects to an array of CGColor objects
|
|
gradientLayer.colors = colors.map { $0.cgColor }
|
|
|
|
// Set the start and end points of the gradient
|
|
gradientLayer.startPoint = startPoint
|
|
gradientLayer.endPoint = endPoint
|
|
|
|
// Insert the gradient layer at the bottom of the button's layer hierarchy
|
|
self.layer.insertSublayer(gradientLayer, at: 0)
|
|
|
|
// Bring image and title layers to front
|
|
self.bringSubviewToFront(self.imageView!)
|
|
self.bringSubviewToFront(self.titleLabel!)
|
|
}
|
|
}
|
|
|
|
// MARK: - Google Ad Banner Delegate
|
|
|
|
extension WokaFMVC : GADBannerViewDelegate{
|
|
func bannerViewDidReceiveAd(_ bannerView: GADBannerView) {
|
|
print("bannerViewDidReceiveAd")
|
|
imageAdView.isHidden = false
|
|
bannerView.alpha = 0
|
|
UIView.animate(withDuration: 0.2, animations: {
|
|
bannerView.alpha = 1
|
|
})
|
|
}
|
|
|
|
func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: Error) {
|
|
UIView.animate(withDuration: 0.2, animations: { [weak self] in
|
|
guard let self else{return}
|
|
bannerView.alpha = 0
|
|
imageAdView.isHidden = true
|
|
})
|
|
print("bannerView:didFailToReceiveAdWithError: \(error.localizedDescription)")
|
|
}
|
|
|
|
func bannerViewDidRecordImpression(_ bannerView: GADBannerView) {
|
|
print("bannerViewDidRecordImpression")
|
|
}
|
|
|
|
func bannerViewWillPresentScreen(_ bannerView: GADBannerView) {
|
|
print("bannerViewWillPresentScreen")
|
|
}
|
|
|
|
func bannerViewWillDismissScreen(_ bannerView: GADBannerView) {
|
|
print("bannerViewWillDIsmissScreen")
|
|
}
|
|
|
|
func bannerViewDidDismissScreen(_ bannerView: GADBannerView) {
|
|
print("bannerViewDidDismissScreen")
|
|
}
|
|
}
|
|
|
|
////Update system volume
|
|
//extension MPVolumeView {
|
|
// static func setVolume(_ volume: Float) {
|
|
// let volumeView = MPVolumeView()
|
|
// let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider
|
|
//
|
|
// DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
|
|
// slider?.value = volume
|
|
// }
|
|
// }
|
|
//}
|