// // 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 adView: UIView! 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) // 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) } // MARK: - App LifeCycle HAndler @objc func appDidEnterBackground() { 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() } } } } @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.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 { 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") bannerView.alpha = 0 bannerView.backgroundColor = #colorLiteral(red: 0.01960784314, green: 0, blue: 0.2196078431, alpha: 1) UIView.animate(withDuration: 1, animations: { bannerView.alpha = 1 }) } func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: Error) { 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 // } // } //}