播放完成统计,播放时长上报,推动提示
This commit is contained in:
parent
94e047a0e9
commit
b78bd95996
@ -19,12 +19,35 @@ extension AppDelegate {
|
|||||||
center.delegate = self
|
center.delegate = self
|
||||||
|
|
||||||
center.requestAuthorization(options: [.badge, .sound, .alert]) { grant, error in
|
center.requestAuthorization(options: [.badge, .sound, .alert]) { grant, error in
|
||||||
if grant {
|
if !grant {
|
||||||
// SPRewardsAPI.requestUploadOpenNotify(completer: nil)
|
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()
|
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 {
|
extension AppDelegate: UNUserNotificationCenterDelegate {
|
||||||
|
@ -40,7 +40,12 @@ extension SceneDelegate {
|
|||||||
static var hasOpenMessage = false
|
static var hasOpenMessage = false
|
||||||
|
|
||||||
func handleOpenAppMessage(webpageURL: URL?) {
|
func handleOpenAppMessage(webpageURL: URL?) {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
|
self._handleOpenAppMessage(webpageURL: webpageURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _handleOpenAppMessage(webpageURL: URL?) {
|
||||||
//统计用URL
|
//统计用URL
|
||||||
var statUrlStr: String?
|
var statUrlStr: String?
|
||||||
var data: [String : Any]?
|
var data: [String : Any]?
|
||||||
@ -85,6 +90,7 @@ extension SceneDelegate {
|
|||||||
let vc = SPPlayerDetailViewController()
|
let vc = SPPlayerDetailViewController()
|
||||||
vc.shortPlayId = shortPlayId
|
vc.shortPlayId = shortPlayId
|
||||||
SPAPPTool.topViewController()?.navigationController?.pushViewController(vc, animated: true)
|
SPAPPTool.topViewController()?.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,20 +18,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
self.registThirdparty(application, didFinishLaunchingWithOptions: launchOptions)
|
self.registThirdparty(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
|
|
||||||
self.appConfig()
|
self.appConfig()
|
||||||
///注册消息通知
|
|
||||||
registerAPNS()
|
|
||||||
|
|
||||||
///开启网络监控
|
///开启网络监控
|
||||||
SPNetworkReachabilityManager.manager.startMonitoring()
|
SPNetworkReachabilityManager.manager.startMonitoring()
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityDidChangeNotification), name: SPNetworkReachabilityManager.reachabilityDidChangeNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityDidChangeNotification), name: SPNetworkReachabilityManager.reachabilityDidChangeNotification, object: nil)
|
||||||
|
|
||||||
|
|
||||||
let _ = JXIAPManager.manager
|
let _ = JXIAPManager.manager
|
||||||
|
|
||||||
// SPLoginManager.manager.requestVisitorLogin(completer: nil)
|
// SPLoginManager.manager.requestVisitorLogin(completer: nil)
|
||||||
SPLoginManager.manager.updateUserInfo(completer: nil)
|
SPLoginManager.manager.updateUserInfo(completer: nil)
|
||||||
|
|
||||||
|
///注册消息通知
|
||||||
|
registerAPNS()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
window?.rootViewController = tabBarController
|
window?.rootViewController = tabBarController
|
||||||
window?.makeKeyAndVisible()
|
window?.makeKeyAndVisible()
|
||||||
|
|
||||||
if let webpageURL = session.stateRestorationActivity?.webpageURL {
|
// if let webpageURL = session.stateRestorationActivity?.webpageURL {
|
||||||
handleOpenAppMessage(webpageURL: webpageURL)
|
// handleOpenAppMessage(webpageURL: webpageURL)
|
||||||
}
|
// }
|
||||||
|
|
||||||
//在线状态统计
|
//在线状态统计
|
||||||
timer = Timer.scheduledTimer(timeInterval: 60 * 10, target: self, selector: #selector(handleOnLine), userInfo: nil, repeats: true)
|
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) {
|
func sceneDidBecomeActive(_ scene: UIScene) {
|
||||||
// Called when the scene has moved from an inactive state to an active state.
|
// 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.
|
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||||
SPStatAPI.requestEnterApp()
|
|
||||||
handleOpenAppMessage(webpageURL: nil)
|
handleOpenAppMessage(webpageURL: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sceneWillResignActive(_ scene: UIScene) {
|
func sceneWillResignActive(_ scene: UIScene) {
|
||||||
// Called when the scene will move from an active state to an inactive state.
|
// 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).
|
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||||
|
SPStatAPI.requestEnterApp()
|
||||||
}
|
}
|
||||||
|
|
||||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||||
|
@ -17,3 +17,6 @@ let kSPHomeSearchHistoryDefaultsKey = "kSPHomeSearchHistoryDefaultsKey"
|
|||||||
|
|
||||||
///待恢复数据
|
///待恢复数据
|
||||||
let kSPWaitRestoreIAPDefaultsKey = "kSPWaitRestoreIAPDefaultsKey"
|
let kSPWaitRestoreIAPDefaultsKey = "kSPWaitRestoreIAPDefaultsKey"
|
||||||
|
|
||||||
|
///推送提示
|
||||||
|
let kSPApnsAlertDefaultsKey = "kSPApnsAlertDefaultsKey"
|
||||||
|
@ -19,6 +19,21 @@ extension Date {
|
|||||||
let dateComponents = Calendar.current.dateComponents([.day], from: self, to: date)
|
let dateComponents = Calendar.current.dateComponents([.day], from: self, to: date)
|
||||||
return dateComponents.day ?? 0
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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")
|
var param = SPNetworkParameters(path: "/createHistory")
|
||||||
param.isLoding = false
|
param.isLoding = false
|
||||||
param.isToast = 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<String>) 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<String>) in
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///收藏短剧
|
///收藏短剧
|
||||||
static func requestCollectShort(isCollect: Bool, shortPlayId: String, videoId: String?, success: (() -> Void)?, failure: (() -> Void)? = nil) {
|
static func requestCollectShort(isCollect: Bool, shortPlayId: String, videoId: String?, success: (() -> Void)?, failure: (() -> Void)? = nil) {
|
||||||
let path: String
|
let path: String
|
||||||
|
@ -89,15 +89,7 @@ extension SPWebViewController {
|
|||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
if settings.authorizationStatus != .authorized {//未开启通知打开设置
|
if settings.authorizationStatus != .authorized {//未开启通知打开设置
|
||||||
if #available(iOS 16.0, *) {
|
SPAPPTool.openApnsSetting()
|
||||||
if let url = URL(string: UIApplication.openNotificationSettingsURLString) {
|
|
||||||
UIApplication.shared.open(url)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let url = URL(string: UIApplication.openSettingsURLString) {
|
|
||||||
UIApplication.shared.open(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {//开启通知上报结果
|
} else {//开启通知上报结果
|
||||||
SPRewardsAPI.requestUploadOpenNotify(completer: nil)
|
SPRewardsAPI.requestUploadOpenNotify(completer: nil)
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ class SPMineWalletView: UIView {
|
|||||||
view.backgroundColor = .colorFFFFFF(alpha: 0.06)
|
view.backgroundColor = .colorFFFFFF(alpha: 0.06)
|
||||||
view.layer.cornerRadius = 8
|
view.layer.cornerRadius = 8
|
||||||
view.layer.masksToBounds = true
|
view.layer.masksToBounds = true
|
||||||
|
view.layer.borderWidth = 1
|
||||||
|
view.layer.borderColor = UIColor.colorFFFFFF(alpha: 0.14).cgColor
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -25,6 +25,9 @@ class SPPlayerDetailViewController: SPPlayerListViewController {
|
|||||||
|
|
||||||
private var detailModel: SPVideoDetailModel?
|
private var detailModel: SPVideoDetailModel?
|
||||||
|
|
||||||
|
///上一次上报播放时长的节点
|
||||||
|
private var lastUploadTime: Int = 0
|
||||||
|
|
||||||
//MARK: UI属性
|
//MARK: UI属性
|
||||||
///选集视图
|
///选集视图
|
||||||
private weak var episodeView: SPEpisodeView?
|
private weak var episodeView: SPEpisodeView?
|
||||||
@ -63,6 +66,7 @@ class SPPlayerDetailViewController: SPPlayerListViewController {
|
|||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(buyVipFinishNotification), name: SPIAPManager.buyVipFinishNotification, object: nil)
|
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.autoNextEpisode = true
|
||||||
self.dataSource = self
|
self.dataSource = self
|
||||||
@ -102,7 +106,31 @@ class SPPlayerDetailViewController: SPPlayerListViewController {
|
|||||||
|
|
||||||
super.play()
|
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()
|
self?.play()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
///网络切换通知
|
||||||
|
@objc private func reachabilityDidChangeNotification() {
|
||||||
|
if SPNetworkReachabilityManager.manager.isReachable == true && self.detailModel == nil {
|
||||||
|
self.requestDetailData()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: -------------- SPPlayerListViewControllerDataSource --------------
|
//MARK: -------------- SPPlayerListViewControllerDataSource --------------
|
||||||
|
@ -217,6 +217,18 @@ class SPPlayerListViewController: SPViewController {
|
|||||||
self.collectionView.scrollToItem(at: indexPath, at: .top, animated: animated);
|
self.collectionView.scrollToItem(at: indexPath, at: .top, animated: animated);
|
||||||
CATransaction.commit()
|
CATransaction.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///当前播放完成 子类可重写
|
||||||
|
func currentPlayFinish() {
|
||||||
|
if self.autoNextEpisode {
|
||||||
|
scrollToNextEpisode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///当前播放进度变更 子类可重写
|
||||||
|
func currentPlayTimeDidChange(time: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SPPlayerListViewController {
|
extension SPPlayerListViewController {
|
||||||
@ -242,6 +254,11 @@ extension SPPlayerListViewController {
|
|||||||
self?.currentPlayFinish()
|
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() {
|
private func scrollToNextEpisode() {
|
||||||
|
@ -197,6 +197,7 @@ extension SPPlayerListCell: SPPlayerDelegate {
|
|||||||
controlView.progress = CGFloat(currentTime) / CGFloat(duration)
|
controlView.progress = CGFloat(currentTime) / CGFloat(duration)
|
||||||
controlView.currentTime = currentTime
|
controlView.currentTime = currentTime
|
||||||
controlView.durationTime = duration
|
controlView.durationTime = duration
|
||||||
|
self.viewModel?.handlePlayTimeDidChange?(currentTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sp_firstRenderedStart(_ player: SPPlayer) {
|
func sp_firstRenderedStart(_ player: SPPlayer) {
|
||||||
|
@ -46,6 +46,8 @@ class SPPlayerListViewModel: NSObject {
|
|||||||
var handlePauseOrPlay: (() -> Void)?
|
var handlePauseOrPlay: (() -> Void)?
|
||||||
///播放完成
|
///播放完成
|
||||||
var handlePlayFinish: (() -> Void)?
|
var handlePlayFinish: (() -> Void)?
|
||||||
|
///播放进度变更
|
||||||
|
var handlePlayTimeDidChange: ((_ time: Int) -> Void)?
|
||||||
///选集
|
///选集
|
||||||
var handleEpisode: (() -> Void)?
|
var handleEpisode: (() -> Void)?
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -103,6 +103,8 @@
|
|||||||
"kAlertMessage_01" = "The previous episode of this series has not been unlocked yet. Please unlock the previous episode first.";
|
"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_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";
|
"kLoginAgreementText" = "By continuing, you agree to the User Agreement and Privacy Policy";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user