// // AppDelegate.swift // relax.offline.mp3.music // // Created by Mr.Zhou on 2024/5/22. // import UIKit import CoreData import AVFoundation import Alamofire import Tiercel import Firebase import GoogleMobileAds import FacebookCore import StoreKit import UserMessagingPlatform @_exported import IQKeyboardManagerSwift @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? //当前页面状态 var showStatus:Bool! // 用来保存由系统提供的完成处理器 var backgroundSessionCompletionHandler: (() -> Void)? //退到后台的时间值 private var backgroundEntryTime:Date? //B面 private var positiceVC:MPPositive_TabBarController! //推送要求 private let notificationHandler = NotificationHandler() var backgroundTask: UIBackgroundTaskIdentifier = .invalid func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { //请求通知权限 UNUserNotificationCenter.current() .requestAuthorization(options: [.alert, .sound, .badge]) { (accepted, error) in if !accepted { print("Users are not allowed to be notified of messages.") } } //切换通知代理 UNUserNotificationCenter.current().delegate = notificationHandler //广告默认ID coreDefaultValues() ActiveDaysCalculation() //FireBase初始化 FirebaseApp.configure() //AdMob广告初始化 MP_ADSimpleManager.shared.start() //启动前销毁所有的下载任务 MP_DownloadManager.shared.cancelAllTasksIfNeeded() setAudioSupport() MP_NetWorkManager.shared.requestStatusToYouTube() IQKeyboardManager.shared.enable = true IQKeyboardManager.shared.shouldResignOnTouchOutside = true window = UIWindow(frame: UIScreen.main.bounds) window?.backgroundColor = .init(hex: "#161616") //关联faceBook ApplicationDelegate.shared.application(application,didFinishLaunchingWithOptions: launchOptions) switch_lunch() //执行用户启动事件日志 MP_AnalyticsManager.shared.user_launchAction() return true } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:] ) -> Bool { ApplicationDelegate.shared.application(app, open: url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, annotation: options[UIApplication.OpenURLOptionsKey.annotation]) } func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) { print("后台任务处理中-\(identifier)") if identifier == MP_DownloadManager.shared.session.identifier { MP_DownloadManager.shared.session.completionHandler = completionHandler } } ///设置播放器会话 func setAudioSupport(){ //设置会话 let session = AVAudioSession.sharedInstance() do { //允许后台活跃,播放以及记录 try session.setCategory(.playAndRecord, mode: .default, options: [ .allowAirPlay, .allowBluetoothA2DP,.defaultToSpeaker]) //激活会话 try session.setActive(true) print("成功激活会话") } catch { print("Failed to set type:\(error.localizedDescription)") } //响应后台控制 UIApplication.shared.beginReceivingRemoteControlEvents() } ///终止播放器会话 func setAudioStop() { //设置会话 let session = AVAudioSession.sharedInstance() do { //中止会话 try session.setActive(false) print("中止会话") } catch { print("Failed to set type:\(error.localizedDescription)") } } ///重新激活播放器会话 func setAudioActive() { //设置会话 let session = AVAudioSession.sharedInstance() do { if session.category != .playAndRecord || session.categoryOptions != [ .allowAirPlay, .allowBluetoothA2DP,.defaultToSpeaker] { if session.category != .playAndRecord { print("音频会话状态发生变动") }else { print("音频会话类型发生变动") } try session.setCategory(.playAndRecord, mode: .default, options: [ .allowAirPlay, .allowBluetoothA2DP,.defaultToSpeaker]) try session.setActive(true) print("重新设置音频会话模型") }else { try session.setActive(true) print("恢复会话") } } catch { print("Failed to set type:\(error.localizedDescription)") } //发送一个通知,询问当前音乐是否播放中 NotificationCenter.notificationKey.post(notificationName: .player_is_continua) } //活跃天数计算 private func ActiveDaysCalculation() { //判断是否存在活跃天数组 guard var actives = UserDefaults.standard.object(forKey: "ActiveDays") as? [Date] else { //首次打开,没有存入数据 let now = Date().timeZone() UserDefaults.standard.setValue([now], forKey: "ActiveDays") return } //追加一次时间值 let now = Date().timeZone() actives.append(now) UserDefaults.standard.setValue(actives, forKey: "ActiveDays") } //MARK: - 页面跳转 ///跳转启动页 func switch_lunch() { let lunchVC = MP_LunchViewController() //动画设置 let transtition = CATransition() transtition.duration = 0.8 transtition.timingFunction = .init(name: .easeOut)//外层模糊化 window?.layer.add(transtition, forKey: "lunch.easeOut") window?.rootViewController = lunchVC window?.makeKeyAndVisible() } ///跳转A面 func switch_aSide() { guard showStatus != false else { return } let tabBarVC = MPSideA_TabBarController() //动画设置 let transtition = CATransition() transtition.duration = 0.8 transtition.timingFunction = .init(name: .easeOut)//外层模糊化 window?.layer.add(transtition, forKey: "aSide.easeOut") window?.rootViewController = tabBarVC window?.makeKeyAndVisible() showStatus = false } //跳转b面 func switch_positive() { guard showStatus != true else { return } //销毁A面所有内容 MPSideA_MediaCenterManager.shared.destroySideA() MPSideA_VolumeManager.shared.destroySideA() positiceVC = MPPositive_TabBarController() //动画设置 let transtition = CATransition() transtition.duration = 0.8 transtition.timingFunction = .init(name: .easeOut)//外层模糊化 window?.layer.add(transtition, forKey: "positive.easeOut") window?.rootViewController = positiceVC window?.makeKeyAndVisible() showStatus = true } //MARK: - 前后台检索设置 //退往后台 func applicationDidEnterBackground(_ application: UIApplication) { //更新当前后台时间节点 backgroundEntryTime = Date() //保持会话激活 setAudioActive() } //将要进入前台 func applicationWillEnterForeground(_ application: UIApplication) { } //进入前台 func applicationDidBecomeActive(_ application: UIApplication) { guard let backDate = backgroundEntryTime else { return } backgroundEntryTime = nil //当前时间值 let currentDate = Date() //算出与后台时间节点的差值 let duration = currentDate.timeIntervalSince(backDate) //获取插页间隔时长 let times = MP_ADSimpleManager.shared.platform ? MP_AppLovinManager.shared.getOpenAppDuration():MP_AdMobManager.shared.getOpenAppDuration() if duration >= times { MP_ADSimpleManager.shared.showOpenAdIfAvailable(.HOST, completion: nil) //超过间隔时长,进行热启动广告展示 print("返回前台,并展示了热启动广告") }else { //未超过,无事发生 print("返回前台") } } //MARK: - 当应用被用户关闭时 func applicationWillTerminate(_ application: UIApplication) { print("应用关闭了") //检索应用是否播放歌曲 guard let load = MP_PlayerManager.shared.loadPlayer, let videoId = load.currentVideoId, let songs = load.songVideos else {return} //将songs转为数据 guard let data = coreSongsforJson(songs) else {return} //当前音乐情况是否合理加载了数据 let last:[String:Any] = ["currentVideoId":videoId, "Songs":data] UserDefaults.standard.set(last, forKey: "Last_Play_Songs") } // MARK: - Core Data stack lazy var persistentContainer: NSPersistentContainer = { /* The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. */ let container = NSPersistentContainer(name: "relax.offline.mp3") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. /* Typical reasons for an error here include: * The parent directory does not exist, cannot be created, or disallows writing. * The persistent store is not accessible, due to permissions or data protection when the device is locked. * The device is out of space. * The store could not be migrated to the current model version. Check the error message to determine what the actual problem was. */ fatalError("Unresolved error \(error), \(error.userInfo)") } }) return container }() // MARK: - Core Data Saving support func saveContext () { let context = persistentContainer.viewContext if context.hasChanges { do { try context.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nserror = error as NSError fatalError("Unresolved error \(nserror), \(nserror.userInfo)") } } } func adManagerDidShowInterstitial() { window?.rootViewController?.setNeedsStatusBarAppearanceUpdate() } } ///访问appDelegate let accessAppdelegate = ( UIApplication.shared.delegate as! AppDelegate) ///在应用内展示通知 class NotificationHandler: NSObject, UNUserNotificationCenterDelegate { func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.alert, .sound, .badge]) // 如果不想显示某个通知,可以直接用空 options 调用 completionHandler: // completionHandler([]) } }