Files
Woka_Native_iOS/WOKA/Theme/ViewModel/ThemeOneVM.swift
2024-09-10 19:43:14 +05:30

697 lines
28 KiB
Swift

//
// ThemeOneVM.swift
// WOKA
//
// Created by MacBook Pro on 23/05/24.
//
import UIKit
import AVFoundation
import JWPlayerKit
import Alamofire
import AppTrackingTransparency
import AdSupport
class ThemeOneVM{
weak var vc : ThemeOneVC!
var cloudMovingRight = false // Flag to track the direction of movement
var isMovingRight = false // Flag to track the direction of movement
// var liveStreamURL = "https://d3volyx7jx7oal.cloudfront.net/master.m3u8"
var avPlayer : AVPlayer!
var playerItem: AVPlayerItem!
var playerLayer: AVPlayerLayer!
var shouldAnimate = true
let reachability = NetworkReachabilityManager()
let monitor = NWPathMonitor()
let queue = DispatchQueue.global(qos: .background)
var isNetworkMonitored = false
func requestIDFA() {
ATTrackingManager.requestTrackingAuthorization { status in
switch status {
case .authorized:
// User granted permission, now you can access IDFA
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
print("IDFA: \(idfa)")
case .denied:
print("User denied tracking permission")
case .restricted:
print("Tracking is restricted")
case .notDetermined:
print("Tracking authorization dialog has not been shown")
@unknown default:
print("Unknown authorization status")
}
}
}
func initView(){
requestIDFA()
AuthFunc.shareInstance.initTimePeriods()
startInitialTimer()
// moveCloudView()
handleTaps()
setUserData()
handleNotificationCenter()
addTapGestureToMovingView()
vc.nameLabel.setContentHuggingPriority(.fittingSizeLevel, for: .horizontal)
vc.nameLabel.setContentCompressionResistancePriority(.fittingSizeLevel, for: .horizontal)
handleBackground()
// Set initial small scale in viewDidLoad
self.vc.allIconView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) // Set initial scale to 50% of original size
DispatchQueue.main.async {
// Perform the animation after the view has been laid out
UIView.animate(withDuration: 0.7, delay: 0, options: [], animations: {
self.vc.allIconView.transform = CGAffineTransform.identity // Reset the transform to original size
}, completion: {_ in
})
}
if let adsData = AuthFunc.shareInstance.adsData{
// check if ads data contains ad for webseries
if let themeOneAd = adsData.result?.filter({$0.forPage == AdsEnum.themeOne.rawValue}).first, let bannerImage = themeOneAd.bannerImage{
vc.adBanner.imageURL(bannerImage, color: .textDarkBlue)
vc.adBanner.isHidden = false
}
}
}
private func handleNotificationCenter(){
NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.themeOnePush(notification:)), name: NSNotification.Name(rawValue: K.NotificationCenterReloads.themeOnePush), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.reloadTheme), name: NSNotification.Name(rawValue: K.NotificationCenterReloads.reloadTheme), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleRouteChange(_:)), name: AVAudioSession.routeChangeNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(languageDidChange), name: .languageDidChange, object: nil)
// NotificationCenter.default.addObserver(self, selector: #selector(showOfflineDeviceUI(notification:)), name: NSNotification.Name.connectivityStatus, object: nil)
}
@objc private func languageDidChange() {
print("Language Change")
}
// @objc func showOfflineDeviceUI(notification: Notification) {
// if NetworkMonitor.shared.isConnected {
// NetworkMonitor.shared.stopMonitoring()
// guard let avPlayer else{return}
// DispatchQueue.main.async { [weak self] in
// guard let self else{return}
// if avPlayer.currentItem == nil || avPlayer.currentItem?.status == .failed {
// print("Player was Nil")
// setupAvPlayer()
// } else {
// print("Player is not Nil")
// setupAvPlayer()
// }
// }
// print("Connected")
// } else {
// print("Not connected")
// }
// }
@objc func handleRouteChange(_ notification: Notification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
return
}
switch reason {
case .oldDeviceUnavailable:
// Headphones unplugged, avoid pausing if desired
avPlayer?.play() // Resume playing if paused
case .newDeviceAvailable:
// Headphones plugged in, you may want to take specific actions if needed
avPlayer?.play() // Resume playing if paused
default:
break
}
}
// MARK: - start stop activity Indicator
func startStopActivity(isStart : Bool){
DispatchQueue.main.async { [weak self] in
guard let self else{return}
if isStart{
vc.liveTVActivityIndicator.startAnimating()
}else{
vc.liveTVActivityIndicator.hidesWhenStopped = true
vc.liveTVActivityIndicator.stopAnimating()
}
}
}
func setUserData(){
guard let data = AuthFunc.shareInstance.userData else{return}
//set the first name as the name
/*
Check User Type,
Dont show username if the user type is guest
*/
switch data.userType{
case "1": // child
vc.nameLabel.text = data.fullname?.components(separatedBy: " ").first
vc.notificationBtnn.isHidden = false
case "2" : // adult
vc.nameLabel.text = data.fullname?.components(separatedBy: " ").first
vc.notificationBtnn.isHidden = false
case "3": // Guest
vc.nameLabel.text = ""
vc.notificationBtnn.isHidden = true
break
default:
break
}
if let avatar = data.avtarURL{
vc.avatarImage.imageURL(avatar)
// vc.avatarImage.imageURL("https://wokaland.com/secret-panel-10102023/hidden-admin-portal-20092023/storage/app/public/uploads/avtar/avatar2.png?d=1716889852")
}else{
vc.avatarImage.image = UIImage(named: "DefaultAvatar")
}
}
// MARK: - Notification Center Handlers
@objc func reloadTheme(){
self.vc.delegate?.didPressSwitchButton(from: self.vc)
}
@objc func appDidEnterBackground() {
// Code to execute when the app enters the background
print("App entered background")
if let rootViewController = UIApplication.shared.mainKeyWindow?.rootViewController {
if let topVC = topVC(in: rootViewController) {
if topVC is HomeVC{
shouldAnimate = false
avPlayer.pause()
handleBackground()
} else {
print("The top view controller is not HomeVC")
}
} else {
print("No top view controller found")
}
}
}
@objc func appWillEnterForeground() {
// Code to execute when the app enters the foreground
print("App will enter foreground")
if let rootViewController = UIApplication.shared.mainKeyWindow?.rootViewController {
if let topVC = topVC(in: rootViewController) {
if topVC is HomeVC{
shouldAnimate = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
guard let self else{return}
moveLiveTVView()
moveCloudView()
}
if let player = avPlayer{
player.play()
}
handleBackground()
} else {
print("The top view controller is not HomeVC")
}
} else {
print("No top view controller found")
}
}
}
// MARK: - Handle Tap Gesture
func handleTaps(){
vc.bottomArrow.addTapGesture {
PersistentStorage.shared.addOthersCount()
let sb = UIStoryboard(name: K.StoryBoard.theme, bundle: nil)
let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.Theme.moreVC) as! MoreVC
vcPush.modalPresentationStyle = .fullScreen
vcPush.modalTransitionStyle = .crossDissolve
self.vc.present(vcPush, animated: true)
}
/*
1 = series, 2 = season, 3= episode, 4 = video, 5 = paint, 6 = game, 7 = audio, 8 = kareoke video, 9 = shop product, 10 = parental video, 11 = article, 12 = live TV, 13 = FM, 14 = teaser, 15 others, 16 = Home
FM & Live TV - post_id , post_type
rest all will be in others post_type = 15
*/
//WebSeries
vc.webSeriesView.addTapGesture {
PersistentStorage.shared.addOthersCount()
ViewButtonAnimation.sharedInstance.btnTapped(in: self.vc, view: self.vc.webSeriesView) { [weak self] in
guard let self else{return}
self.checkType(action: .webseries)
}
}
vc.audioBooksView.addTapGesture {
PersistentStorage.shared.addOthersCount()
ViewButtonAnimation.sharedInstance.btnTapped(in: self.vc, view: self.vc.audioBooksView) { [weak self] in
guard let self else{return}
self.checkType(action: .audioBooks)
}
}
vc.gamesView.addTapGesture {
PersistentStorage.shared.addOthersCount()
ViewButtonAnimation.sharedInstance.btnTapped(in: self.vc, view: self.vc.gamesView) { [weak self] in
guard let self else{return}
self.checkType(action: .games)
}
}
vc.karaokeView.addTapGesture { [self] in
PersistentStorage.shared.addOthersCount()
ViewButtonAnimation.sharedInstance.btnTapped(in: self.vc, view: self.vc.karaokeView) { [weak self] in
guard let self else{return}
self.checkType(action: .karaoke)
}
}
vc.shopView.addTapGesture {
PersistentStorage.shared.addOthersCount()
ViewButtonAnimation.sharedInstance.btnTapped(in: self.vc, view: self.vc.shopView) { [weak self] in
guard let self else{return}
self.checkType(action: .shop)
}
}
vc.liveTVIcon.addTapGesture {
PersistentStorage.shared.addLiveTVCount()
ViewButtonAnimation.sharedInstance.btnTapped(in: self.vc, view: self.vc.liveTVIcon) { [weak self] in
guard let self else{return}
self.checkType(action: .liveTV)
}
}
/*
If ad banner is set then show the banner
*/
vc.adBanner.addTapGesture {
ViewButtonAnimation.sharedInstance.btnTapped(in: self.vc, view: self.vc.adBanner) {
//check url from api
if let adsData = AuthFunc.shareInstance.adsData{
// check if ads data contains ad for webseries
if let themeOneAd = adsData.result?.filter({$0.forPage == AdsEnum.themeOne.rawValue}).first, let adLink = themeOneAd.adLink, let adID = themeOneAd.id{
PersistentStorage.shared.addAdsCount(adID: adID,clicks: 1)
if let url = URL(string: adLink), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
}
}
}
}
//Get the notification from observer
@objc func themeOnePush(notification: Notification){
if let userInfo = notification.userInfo, let action = userInfo["action"] as? TopViewPush {
checkType(action: action)
}
}
// Made a common func to check which module to push
func checkType(action : TopViewPush){
switch action {
case .webseries:
let sb = UIStoryboard(name: K.StoryBoard.webSeries, bundle: nil)
let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.WebSeries.webSeriesVC) as! WebSeriesVC
vc.navigationController?.pushViewController(vcPush, animated: true)
case .audioBooks:
let sb = UIStoryboard(name: K.StoryBoard.audioBooks, bundle: nil)
let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.AudioBooks.audioBookHomeVC) as! AudioBookHomeVC
vc.navigationController?.pushViewController(vcPush, animated: true)
case .games:
let sb = UIStoryboard(name: K.StoryBoard.Games, bundle: nil)
let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.Games.gamesListVC) as! GamesListVC
vc.navigationController?.pushViewController(vcPush, animated: true)
case .karaoke:
let sb = UIStoryboard(name: K.StoryBoard.Karaoke, bundle: nil)
let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.Karaoke.karaokeListingVC) as! KaraokeListingVC
vc.navigationController?.pushViewController(vcPush, animated: true)
case .shop:
let sb = UIStoryboard(name: K.StoryBoard.shop, bundle: nil)
let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.Shop.shopListingVC) as! ShopListingVC
vc.navigationController?.pushViewController(vcPush, animated: true)
case .liveTV:
handleTap(UITapGestureRecognizer())
case .blogs:
let sb = UIStoryboard(name: K.StoryBoard.theme, bundle: nil)
let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.Theme.blogsVC) as! BlogsVC
vc.navigationController?.pushViewController(vcPush, animated: true)
case .radio:
let sb = UIStoryboard(name: K.StoryBoard.wokaFM, bundle: nil)
let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.WokaFM.wokaFMVC) as! WokaFMVC
vcPush.modalPresentationStyle = .overCurrentContext
vcPush.modalTransitionStyle = .crossDissolve
vc.present(vcPush, animated: true)
}
}
// MARK: - Animate Clouds and LiveTV and setupAv Player
func setupAvPlayer(){
/*
Av Player Setup
*/
guard let data = AuthFunc.shareInstance.staticURLs , let liveStreamData = data.liveData?.first else{
self.vc.toast(msg: "Issue with live streaming", time: 2)
return
}
var url = String()
// var title = String()
if AuthFunc.shareInstance.languageSelected == .english{
url = liveStreamData.liveURL?.hdURLEn ?? ""
// title = liveStreamData.name?.titleEn ?? ""
}else{
url = liveStreamData.liveURL?.hdURLHin ?? ""
// title = liveStreamData.name?.titleHin ?? ""
}
guard let streamURL = URL(string: url) else{return}
playerItem = AVPlayerItem(url: streamURL)
// Create AVPlayer with the stream URL
avPlayer = AVPlayer(playerItem: playerItem)
// avPlayer.isMuted = true
// Create AVPlayerLayer
playerLayer = AVPlayerLayer(player: avPlayer)
playerLayer.videoGravity = .resizeAspectFill // You can set different videoGravity as per your need
// Remove any existing sublayers to avoid duplicates
self.vc.liveTvPlayer.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
self.vc.liveTvPlayer.layer.addSublayer(playerLayer)
playerLayer.frame = vc.liveTvPlayer.bounds
avPlayer.volume = 0
vc.addObservers()
}
func centerLiveTVViewHorizontally() {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.vc.liveTVView.center.x = self.vc.view.center.x // Center the liveTVView horizontally
self.vc.liveTVView.layoutIfNeeded()
}
}
func centerCloudsHorizontally() {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.vc.cloud1.center.x = self.vc.view.center.x // Center the liveTVView horizontally
self.vc.cloud2.center.x = self.vc.view.center.x // Center the liveTVView horizontally
self.vc.cloud1.layoutIfNeeded()
self.vc.cloud2.layoutIfNeeded()
}
}
func moveLiveTVView() {
guard shouldAnimate else {
self.centerLiveTVViewHorizontally()
return
}
// Stop animating if shouldAnimate is false
guard let vc = self.vc else { return } // Ensure the view controller is available
UIView.animate(withDuration: 8, delay: 0, options: [.allowUserInteraction], animations: { [weak self] in
guard let self = self else { return }
let margin: CGFloat = 30
let screenWidth = vc.view.frame.width
let viewWidth = vc.liveTVView.frame.width
let maxX = screenWidth - margin - viewWidth / 2
let minX = margin + viewWidth / 2
if self.isMovingRight {
vc.liveTVView.center.x = maxX // Move to the right
} else {
vc.liveTVView.center.x = minX // Move to the left
}
}, completion: { [weak self] _ in
guard let self = self else { return }
self.isMovingRight.toggle() // Toggle the direction for the next iteration
DispatchQueue.main.async { // Ensure the recursive call is made on the main thread
self.moveLiveTVView() // Recursively call moveLiveTVView to create a continuous animation
}
})
}
func moveCloudView() {
guard shouldAnimate else {
centerCloudsHorizontally()
return
}
UIView.animate(withDuration: 23, delay: 0, options: [], animations: { [weak self] in
guard let self else{return}
if cloudMovingRight {
// print("right")
vc.cloud2.center.x += 140 // Move to the right
vc.cloud1.center.x -= 140 // Move to the right
} else {
// print("left")
vc.cloud2.center.x -= 140 // Move to the left
vc.cloud1.center.x += 140 // Move to the left
}
}, completion: { [weak self] _ in
guard let self else{return}
cloudMovingRight.toggle() // Toggle the direction for the next iteration
moveCloudView() // Recursively call moveView to create a continuous animation
})
}
// MARK: - LiveTV Tap Handling
private func addTapGestureToMovingView(){
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
// tap.numberOfTouchesRequired = 1
tap.numberOfTapsRequired = 1
vc.liveTVView.addGestureRecognizer(tap)
vc.liveTVView.isUserInteractionEnabled = true
}
// function which is triggered when handleTap on livetv is called
@objc func handleTap(_ sender: UITapGestureRecognizer) {
PersistentStorage.shared.addLiveTVCount()
DispatchQueue.main.async {
Utilities.startProgressHUD(msg: "Loading...")
}
print("tapped")
let vc = self.vc.storyboard?.instantiateViewController(withIdentifier: K.StoryBoardID.Theme.playerVC) as! PlayerVC
guard let data = AuthFunc.shareInstance.staticURLs , let liveStreamData = data.liveData?.first else{
self.vc.toast(msg: "Issue with live streaming", time: 2)
return
}
var url = String()
var title = String()
if AuthFunc.shareInstance.languageSelected == .english{
url = liveStreamData.liveURL?.hdURLEn ?? ""
title = liveStreamData.name?.titleEn ?? ""
}else{
url = liveStreamData.liveURL?.hdURLHin ?? ""
title = liveStreamData.name?.titleHin ?? ""
}
do {
// Ensure the liveStreamURL is valid
guard let liveStreamURL = URL(string: url) else {
print("Invalid live stream URL")
Utilities.dismissProgressHUD()
return
}
let videoSourceBuilder = try JWVideoSourceBuilder()
// .defaultVideo(true)
.file(liveStreamURL)
.label(title)
.build()
// Create a JWPlayerItem
let item = try JWPlayerItemBuilder()
// .file(liveStreamURL)
.videoSources([videoSourceBuilder])
.title(title)
.build()
// Create a JWPlayerConfiguration
let config = try JWPlayerConfigurationBuilder()
.playlist(items: [item])
.preload(JWPreload(rawValue: 20) ?? .none)
.autostart(true)
.build()
vc.config = config
vc.dismissTapped = self.tapped
vc.contentType = .liveStream
if let streamID = AuthFunc.shareInstance.staticURLs?.liveData?.first?.id{
vc.vm.videoIDs = [streamID]
}
vc.modalPresentationStyle = .fullScreen
vc.modalTransitionStyle = .crossDissolve
// Present the PlayerVC
self.vc.present(vc, animated: true) { [weak self] in
guard let self else{return}
stopLiveStream()
}
} catch {
print("Error creating JWPlayer configuration: \(error)")
Utilities.dismissProgressHUD()
}
// Dismiss the progress HUD after the view controller presentation
Utilities.dismissProgressHUD()
}
func tapped(){
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
guard let self else{return}
isMovingRight = false
self.avPlayer.play()
// self.moveLiveTVView()
// self.moveCloudView()
self.vc.liveTvPlayer.layoutIfNeeded()
}
print("Player Will Exit")
}
func startLiveStream(){
avPlayer.play()
avPlayer.volume = 0
}
func stopLiveStream(){
avPlayer.pause()
}
// MARK: - Handle Time Change
@objc func handleBackground(){
let currentTime = Date()
for period in AuthFunc.shareInstance.timePeriods {
if period.contains(currentTime: currentTime) {
let details = period.details
// Update view's background color with a gradient or single color
DispatchQueue.main.async { [weak self] in
guard let self else{return}
if period.details.dayCycle == .night{
startStarGlowAnimation()
}else{
//just hide the moon and star if its not night
!vc.moonImage.isHidden ? vc.moonImage.isHidden = true : nil
vc.star.forEach { image in
!image.isHidden ? image.isHidden = true : nil
}
}
vc.gradientView.applyGradient(colors: [details.color1, details.color2], startPoint: CGPoint(x: 0, y: 0), endPoint: CGPoint(x: 0, y: 0.8))
vc.nameLabel.textColor = details.textColor
vc.HelloLabel.textColor = details.textColor
vc.welcomeLabel.textColor = details.textColor
vc.homeGrass.image = UIImage(named: details.grass)
}
break
}
}
}
func startStarGlowAnimation() {
vc.moonImage.isHidden ? vc.moonImage.isHidden = false : nil
for (index, imageView) in vc.star.enumerated() {
//If star is hidden just unhide it for the night time
(vc.star[index].isHidden) ? (vc.star[index].isHidden = false) : nil
let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 1.0
animation.toValue = 0.07
animation.duration = 3.0
animation.autoreverses = true
animation.repeatCount = .infinity
// Calculate delay for each animation based on index
let delay = Double(index) * 1 // Adjust the delay as needed
// Apply the delay to the animation
animation.beginTime = CACurrentMediaTime() + delay
imageView.layer.add(animation, forKey: "glowAnimation")
}
}
func startInitialTimer() {
self.handleBackground()
let now = Date()
let nextMinute = Calendar.current.nextDate(after: now, matching: DateComponents(second: 0), matchingPolicy: .nextTime)!
let timeInterval = nextMinute.timeIntervalSince(now)
// Schedule the initial timer to fire at the start of the next minute
vc.timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(startRepeatingTimer), userInfo: nil, repeats: false)
}
@objc func startRepeatingTimer() {
// Handle the minute change when the initial timer fires
self.handleBackground()
// Schedule the repeating timer to fire every minute
vc.timer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(handleBackground), userInfo: nil, repeats: true)
}
func updateClicks(){
var totalClicks = [ClicksAnalytics]()
totalClicks.append(ClicksAnalytics(postID: 0, postType: 15, numberOfClicks: 12, deviceType: 2, categoryID: 0))
// NetworkManager.shareInstance.nwCallRawJSON(clicksData: totalClicks)
}
}
//class CustomLiveTVView: UIView {
// override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// let hitView = super.hitTest(point, with: event)
// // Debug statement to check when hitTest is called
// print("hitTest called with point: \(point)")
//
// // Check if the touch is within the bounds of this view
// if hitView == self {
// return hitView
// }
// // If the touch is outside the bounds, return nil
// return nil
// }
//}