diff --git a/MoviaBox/AppDelegate/AppDelegate+APNS.swift b/MoviaBox/AppDelegate/AppDelegate+APNS.swift index 1902ed5..6709329 100644 --- a/MoviaBox/AppDelegate/AppDelegate+APNS.swift +++ b/MoviaBox/AppDelegate/AppDelegate+APNS.swift @@ -19,12 +19,35 @@ extension AppDelegate { center.delegate = self center.requestAuthorization(options: [.badge, .sound, .alert]) { grant, error in - if grant { -// SPRewardsAPI.requestUploadOpenNotify(completer: nil) + if !grant { + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + if let date = UserDefaults.standard.object(forKey: kSPApnsAlertDefaultsKey) as? Date { +#if DEBUG + self.showApnsAlert() +#else + if date.sp_isToday { + self.showApnsAlert() + } +#endif + } + UserDefaults.standard.set(Date(), forKey: kSPApnsAlertDefaultsKey) + } } } UIApplication.shared.registerForRemoteNotifications() } + + private func showApnsAlert() { + let alert = UIAlertController(title: nil, message: "kAlertMessage_03".localized, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "Affirm".localized, style: .default, handler: { _ in + SPAPPTool.openApnsSetting() + })) + alert.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel)) + + + SPAPPTool.topViewController()?.present(alert, animated: true) + } + } extension AppDelegate: UNUserNotificationCenterDelegate { diff --git a/MoviaBox/AppDelegate/AppDelegate+OpenApp.swift b/MoviaBox/AppDelegate/AppDelegate+OpenApp.swift index b6464b8..943165a 100644 --- a/MoviaBox/AppDelegate/AppDelegate+OpenApp.swift +++ b/MoviaBox/AppDelegate/AppDelegate+OpenApp.swift @@ -40,7 +40,12 @@ extension SceneDelegate { static var hasOpenMessage = false func handleOpenAppMessage(webpageURL: URL?) { - + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + self._handleOpenAppMessage(webpageURL: webpageURL) + } + } + + private func _handleOpenAppMessage(webpageURL: URL?) { //统计用URL var statUrlStr: String? var data: [String : Any]? @@ -85,6 +90,7 @@ extension SceneDelegate { let vc = SPPlayerDetailViewController() vc.shortPlayId = shortPlayId SPAPPTool.topViewController()?.navigationController?.pushViewController(vc, animated: true) + } } diff --git a/MoviaBox/AppDelegate/AppDelegate.swift b/MoviaBox/AppDelegate/AppDelegate.swift index a35dcd6..472103b 100644 --- a/MoviaBox/AppDelegate/AppDelegate.swift +++ b/MoviaBox/AppDelegate/AppDelegate.swift @@ -18,20 +18,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate { self.registThirdparty(application, didFinishLaunchingWithOptions: launchOptions) self.appConfig() - ///注册消息通知 - registerAPNS() + ///开启网络监控 SPNetworkReachabilityManager.manager.startMonitoring() NotificationCenter.default.addObserver(self, selector: #selector(reachabilityDidChangeNotification), name: SPNetworkReachabilityManager.reachabilityDidChangeNotification, object: nil) - let _ = JXIAPManager.manager // SPLoginManager.manager.requestVisitorLogin(completer: nil) SPLoginManager.manager.updateUserInfo(completer: nil) + ///注册消息通知 + registerAPNS() + return true } diff --git a/MoviaBox/AppDelegate/SceneDelegate.swift b/MoviaBox/AppDelegate/SceneDelegate.swift index 3e5b33a..6d26696 100644 --- a/MoviaBox/AppDelegate/SceneDelegate.swift +++ b/MoviaBox/AppDelegate/SceneDelegate.swift @@ -22,9 +22,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window?.rootViewController = tabBarController window?.makeKeyAndVisible() - if let webpageURL = session.stateRestorationActivity?.webpageURL { - handleOpenAppMessage(webpageURL: webpageURL) - } +// if let webpageURL = session.stateRestorationActivity?.webpageURL { +// handleOpenAppMessage(webpageURL: webpageURL) +// } //在线状态统计 timer = Timer.scheduledTimer(timeInterval: 60 * 10, target: self, selector: #selector(handleOnLine), userInfo: nil, repeats: true) @@ -40,13 +40,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func sceneDidBecomeActive(_ scene: UIScene) { // Called when the scene has moved from an inactive state to an active state. // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. - SPStatAPI.requestEnterApp() handleOpenAppMessage(webpageURL: nil) } func sceneWillResignActive(_ scene: UIScene) { // Called when the scene will move from an active state to an inactive state. // This may occur due to temporary interruptions (ex. an incoming phone call). + SPStatAPI.requestEnterApp() } func sceneWillEnterForeground(_ scene: UIScene) { diff --git a/MoviaBox/Base/Define/SPUserDefaultsKey.swift b/MoviaBox/Base/Define/SPUserDefaultsKey.swift index ce47d3d..ef8d39c 100644 --- a/MoviaBox/Base/Define/SPUserDefaultsKey.swift +++ b/MoviaBox/Base/Define/SPUserDefaultsKey.swift @@ -17,3 +17,6 @@ let kSPHomeSearchHistoryDefaultsKey = "kSPHomeSearchHistoryDefaultsKey" ///待恢复数据 let kSPWaitRestoreIAPDefaultsKey = "kSPWaitRestoreIAPDefaultsKey" + +///推送提示 +let kSPApnsAlertDefaultsKey = "kSPApnsAlertDefaultsKey" diff --git a/MoviaBox/Base/Extension/Date+SPAdd.swift b/MoviaBox/Base/Extension/Date+SPAdd.swift index 1dea5b4..e9c4862 100644 --- a/MoviaBox/Base/Extension/Date+SPAdd.swift +++ b/MoviaBox/Base/Extension/Date+SPAdd.swift @@ -19,6 +19,21 @@ extension Date { let dateComponents = Calendar.current.dateComponents([.day], from: self, to: date) return dateComponents.day ?? 0 } + + ///是否是今天 + var sp_isToday: Bool { + get { + return Calendar.current.isDateInToday(self) + } + } + ///是否是昨天 + var sp_isYesterday: Bool { + return Calendar.current.isDateInYesterday(self) + } + ///是否是明天 + var sp_isTomorrow: Bool { + return Calendar.current.isDateInTomorrow(self) + } } diff --git a/MoviaBox/Base/Networking/API/SPVideoAPI.swift b/MoviaBox/Base/Networking/API/SPVideoAPI.swift index 7ceb976..11f3c41 100644 --- a/MoviaBox/Base/Networking/API/SPVideoAPI.swift +++ b/MoviaBox/Base/Networking/API/SPVideoAPI.swift @@ -32,7 +32,7 @@ class SPVideoAPI: NSObject { } ///创建播放记录 - static func requestRequestVideoPlayHistory(videoId: String, shortPlayId: String) { + static func requestCreateVideoPlayHistory(videoId: String, shortPlayId: String) { var param = SPNetworkParameters(path: "/createHistory") param.isLoding = false param.isToast = false @@ -46,6 +46,39 @@ class SPVideoAPI: NSObject { } } + ///视频观看结束 + static func requestViewingFinish(shortPlayId: String, videoId: String, activityId: String) { + var param = SPNetworkParameters(path: "/activeAfterWatchingVideo") + param.isLoding = false + param.isToast = false + param.parameters = [ + "short_play_video_id" : videoId, + "short_play_id" : shortPlayId, + "activity_id" : activityId + ] + + SPNetwork.request(parameters: param) { (response: SPNetworkResponse) in + + } + } + + ///上报播放时长 + static func requestUploadPlayTime(shortPlayId: String, videoId: String, seconds: Int) { + + var param = SPNetworkParameters(path: "/uploadHistorySeconds") + param.isLoding = false + param.isToast = false + param.parameters = [ + "video_id" : videoId, + "short_play_id" : shortPlayId, + "play_seconds" : seconds + ] + + SPNetwork.request(parameters: param) { (response: SPNetworkResponse) in + + } + } + ///收藏短剧 static func requestCollectShort(isCollect: Bool, shortPlayId: String, videoId: String?, success: (() -> Void)?, failure: (() -> Void)? = nil) { let path: String diff --git a/MoviaBox/Base/WebView/SPWebViewController+ScriptMessage.swift b/MoviaBox/Base/WebView/SPWebViewController+ScriptMessage.swift index 8c389ad..ce1ad0d 100644 --- a/MoviaBox/Base/WebView/SPWebViewController+ScriptMessage.swift +++ b/MoviaBox/Base/WebView/SPWebViewController+ScriptMessage.swift @@ -89,15 +89,7 @@ extension SPWebViewController { DispatchQueue.main.async { [weak self] in guard let self = self else { return } if settings.authorizationStatus != .authorized {//未开启通知打开设置 - if #available(iOS 16.0, *) { - if let url = URL(string: UIApplication.openNotificationSettingsURLString) { - UIApplication.shared.open(url) - } - } else { - if let url = URL(string: UIApplication.openSettingsURLString) { - UIApplication.shared.open(url) - } - } + SPAPPTool.openApnsSetting() } else {//开启通知上报结果 SPRewardsAPI.requestUploadOpenNotify(completer: nil) } diff --git a/MoviaBox/Class/Mine/View/SPMineWalletView.swift b/MoviaBox/Class/Mine/View/SPMineWalletView.swift index f0b8d5b..1ea1107 100644 --- a/MoviaBox/Class/Mine/View/SPMineWalletView.swift +++ b/MoviaBox/Class/Mine/View/SPMineWalletView.swift @@ -26,6 +26,8 @@ class SPMineWalletView: UIView { view.backgroundColor = .colorFFFFFF(alpha: 0.06) view.layer.cornerRadius = 8 view.layer.masksToBounds = true + view.layer.borderWidth = 1 + view.layer.borderColor = UIColor.colorFFFFFF(alpha: 0.14).cgColor return view }() diff --git a/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift b/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift index 9300174..6aa1c4f 100644 --- a/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift +++ b/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift @@ -25,6 +25,9 @@ class SPPlayerDetailViewController: SPPlayerListViewController { private var detailModel: SPVideoDetailModel? + ///上一次上报播放时长的节点 + private var lastUploadTime: Int = 0 + //MARK: UI属性 ///选集视图 private weak var episodeView: SPEpisodeView? @@ -63,6 +66,7 @@ class SPPlayerDetailViewController: SPPlayerListViewController { override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(buyVipFinishNotification), name: SPIAPManager.buyVipFinishNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(reachabilityDidChangeNotification), name: SPNetworkReachabilityManager.reachabilityDidChangeNotification, object: nil) self.autoNextEpisode = true self.dataSource = self @@ -102,7 +106,31 @@ class SPPlayerDetailViewController: SPPlayerListViewController { super.play() - SPVideoAPI.requestRequestVideoPlayHistory(videoId: videoInfo.short_play_video_id ?? "", shortPlayId: videoInfo.short_play_id ?? "") + SPVideoAPI.requestCreateVideoPlayHistory(videoId: videoInfo.short_play_video_id ?? "", shortPlayId: videoInfo.short_play_id ?? "") + } + + override func currentPlayFinish() { + if let videoInfo = self.viewModel.currentPlayer?.videoInfo, let activityId = self.activityId { + //播放完成统计 + SPVideoAPI.requestViewingFinish(shortPlayId: videoInfo.short_play_id ?? "", videoId: videoInfo.short_play_video_id ?? "", activityId: activityId) + } + super.currentPlayFinish() + } + + override func currentPlayTimeDidChange(time: Int) { + super.currentPlayTimeDidChange(time: time) + let videoInfo = self.viewModel.currentPlayer?.videoInfo + + + //每播放5秒上报一次 播放时长小于上次上报时间也需要上报 + if (time >= lastUploadTime + 5 || time < lastUploadTime) && time >= 5 { + lastUploadTime = time + + guard let shortPlayId = videoInfo?.short_play_id, let videoId = videoInfo?.short_play_video_id else { return } + //上报播放时长 + SPVideoAPI.requestUploadPlayTime(shortPlayId: shortPlayId, videoId: videoId, seconds: time) + } + } } @@ -220,6 +248,12 @@ extension SPPlayerDetailViewController { self?.play() } } + ///网络切换通知 + @objc private func reachabilityDidChangeNotification() { + if SPNetworkReachabilityManager.manager.isReachable == true && self.detailModel == nil { + self.requestDetailData() + } + } } //MARK: -------------- SPPlayerListViewControllerDataSource -------------- diff --git a/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift b/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift index c14b555..3e2ebf0 100644 --- a/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift +++ b/MoviaBox/Class/Player/Controller/SPPlayerListViewController.swift @@ -217,6 +217,18 @@ class SPPlayerListViewController: SPViewController { self.collectionView.scrollToItem(at: indexPath, at: .top, animated: animated); CATransaction.commit() } + + ///当前播放完成 子类可重写 + func currentPlayFinish() { + if self.autoNextEpisode { + scrollToNextEpisode() + } + } + + ///当前播放进度变更 子类可重写 + func currentPlayTimeDidChange(time: Int) { + + } } extension SPPlayerListViewController { @@ -242,6 +254,11 @@ extension SPPlayerListViewController { self?.currentPlayFinish() } + self.viewModel.handlePlayTimeDidChange = { [weak self] time in + + self?.currentPlayTimeDidChange(time: time) + } + } } @@ -267,12 +284,6 @@ extension SPPlayerListViewController { } } - ///当前播放完成 - private func currentPlayFinish() { - guard self.autoNextEpisode else { return } - - scrollToNextEpisode() - } ///滑动至下一级 private func scrollToNextEpisode() { diff --git a/MoviaBox/Class/Player/View/SPPlayerListCell.swift b/MoviaBox/Class/Player/View/SPPlayerListCell.swift index c7ce1ad..fdf5839 100644 --- a/MoviaBox/Class/Player/View/SPPlayerListCell.swift +++ b/MoviaBox/Class/Player/View/SPPlayerListCell.swift @@ -197,6 +197,7 @@ extension SPPlayerListCell: SPPlayerDelegate { controlView.progress = CGFloat(currentTime) / CGFloat(duration) controlView.currentTime = currentTime controlView.durationTime = duration + self.viewModel?.handlePlayTimeDidChange?(currentTime) } func sp_firstRenderedStart(_ player: SPPlayer) { diff --git a/MoviaBox/Class/Player/ViewModel/SPPlayerListViewModel.swift b/MoviaBox/Class/Player/ViewModel/SPPlayerListViewModel.swift index 7fa13bd..ba04b0d 100644 --- a/MoviaBox/Class/Player/ViewModel/SPPlayerListViewModel.swift +++ b/MoviaBox/Class/Player/ViewModel/SPPlayerListViewModel.swift @@ -46,6 +46,8 @@ class SPPlayerListViewModel: NSObject { var handlePauseOrPlay: (() -> Void)? ///播放完成 var handlePlayFinish: (() -> Void)? + ///播放进度变更 + var handlePlayTimeDidChange: ((_ time: Int) -> Void)? ///选集 var handleEpisode: (() -> Void)? diff --git a/MoviaBox/Libs/APPTool/SPAPPTool.swift b/MoviaBox/Libs/APPTool/SPAPPTool.swift index 07a8a4a..3112408 100644 --- a/MoviaBox/Libs/APPTool/SPAPPTool.swift +++ b/MoviaBox/Libs/APPTool/SPAPPTool.swift @@ -94,3 +94,20 @@ extension SPAPPTool { } } } + +extension SPAPPTool { + + ///打开消息通知设置页面 + static func openApnsSetting() { + if #available(iOS 16.0, *) { + if let url = URL(string: UIApplication.openNotificationSettingsURLString) { + UIApplication.shared.open(url) + } + } else { + if let url = URL(string: UIApplication.openSettingsURLString) { + UIApplication.shared.open(url) + } + } + } + +} diff --git a/MoviaBox/Source/en.lproj/Localizable.strings b/MoviaBox/Source/en.lproj/Localizable.strings index 67ada5a..2b8c978 100644 --- a/MoviaBox/Source/en.lproj/Localizable.strings +++ b/MoviaBox/Source/en.lproj/Localizable.strings @@ -103,6 +103,8 @@ "kAlertMessage_01" = "The previous episode of this series has not been unlocked yet. Please unlock the previous episode first."; ///没有找到视频提示 "kAlertMessage_02" = "Purchase failed, please try again later!"; +///推送消息提示 +"kAlertMessage_03" = "Go and enable push messages?"; "kLoginAgreementText" = "By continuing, you agree to the User Agreement and Privacy Policy";