From a947b20be4d4a2d5507a048b57d29abf6f5b2428 Mon Sep 17 00:00:00 2001 From: zjx Date: Fri, 6 Jun 2025 17:50:28 +0800 Subject: [PATCH] =?UTF-8?q?adjust=EF=BC=8C=E8=AF=A6=E6=83=85=E6=8E=A8?= =?UTF-8?q?=E8=8D=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Podfile | 3 +- Podfile.lock | 16 +- Veloria.xcodeproj/project.pbxproj | 16 + Veloria/AppDelegate/AppDelegate+Open.swift | 66 +++- .../AppDelegate/AppDelegate+Thirdparty.swift | 7 +- Veloria/AppDelegate/SceneDelegate.swift | 39 ++- Veloria/Base/Extension/String+VPAdd.swift | 20 ++ Veloria/Base/Model/VPOpenAppModel.swift | 25 ++ Veloria/Base/Networking/API/VPStatAPI.swift | 52 ++++ Veloria/Base/Networking/API/VPVideoAPI.swift | 11 + .../Me/Controller/VPMeViewController.swift | 2 +- .../VPDetailPlayerViewController.swift | 50 ++- .../View/VPDetailRecommandBannerCell.swift | 45 +++ .../Player/View/VPDetailRecommandView.swift | 287 ++++++++++++++++++ Veloria/Source/Info.plist | 8 + Veloria/Source/en.lproj/Localizable.strings | 2 + Veloria/Veloria.entitlements | 4 + 资料/app账号信息.txt | 4 +- 18 files changed, 633 insertions(+), 24 deletions(-) create mode 100644 Veloria/Base/Model/VPOpenAppModel.swift create mode 100644 Veloria/Base/Networking/API/VPStatAPI.swift create mode 100644 Veloria/Class/Player/View/VPDetailRecommandBannerCell.swift create mode 100644 Veloria/Class/Player/View/VPDetailRecommandView.swift diff --git a/Podfile b/Podfile index 9c77f8a..174a576 100644 --- a/Podfile +++ b/Podfile @@ -30,6 +30,7 @@ target 'Veloria' do pod 'ZFPlayer/AVPlayer' #播放器 pod 'EmptyDataSet-Swift' #空数据页面 pod 'ZLPhotoBrowser' #相册 - + pod 'Adjust' # Adjust + pod 'FSPagerView' #banner end diff --git a/Podfile.lock b/Podfile.lock index 7a544a9..451e3c1 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,7 +1,13 @@ PODS: + - Adjust (5.4.0): + - Adjust/Adjust (= 5.4.0) + - Adjust/Adjust (5.4.0): + - AdjustSignature (= 3.35.2) + - AdjustSignature (3.35.2) - Alamofire (5.10.2) - CocoaAsyncSocket (7.6.5) - EmptyDataSet-Swift (5.0.0) + - FSPagerView (0.8.3) - HWPanModal (0.9.9) - Kingfisher (8.3.2) - KTVHTTPCache (3.0.2): @@ -31,7 +37,9 @@ PODS: - ZLPhotoBrowser/Core (4.6.0.1) DEPENDENCIES: + - Adjust - EmptyDataSet-Swift + - FSPagerView - HWPanModal - Kingfisher - KTVHTTPCache @@ -48,9 +56,12 @@ DEPENDENCIES: SPEC REPOS: trunk: + - Adjust + - AdjustSignature - Alamofire - CocoaAsyncSocket - EmptyDataSet-Swift + - FSPagerView - HWPanModal - Kingfisher - KTVHTTPCache @@ -66,9 +77,12 @@ SPEC REPOS: - ZLPhotoBrowser SPEC CHECKSUMS: + Adjust: a5f881d0cbfe9a6df979b076dc7116fe19ece797 + AdjustSignature: 23b9e5d4adcadffc303bb6b410fde617dd88504f Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 EmptyDataSet-Swift: eb382c0c87a2d9c678077385a595cec52da38171 + FSPagerView: 670405b2f18e2a87fa37f20b00de783e562c25a8 HWPanModal: b57a6717d3cdcd666bff44f9dd2a5be9f4d6f5d2 Kingfisher: 0621d0ac0c78fecb19f6dc5303bde2b52abaf2f5 KTVHTTPCache: 5711692cdf9a5ecfe829b1e16577deb3ffe3dc86 @@ -83,6 +97,6 @@ SPEC CHECKSUMS: ZFPlayer: 5cf39e8d9f0c2394a014b0db4767b5b5a6bffe13 ZLPhotoBrowser: 20f32e6429448cc1c008795a1b55472d5772939c -PODFILE CHECKSUM: 4ec05325e0f82746c022a9c8ea6b1af5d1e5092f +PODFILE CHECKSUM: a53439eb21a3498b8f423fddbdb2bfb48f960ebf COCOAPODS: 1.16.2 diff --git a/Veloria.xcodeproj/project.pbxproj b/Veloria.xcodeproj/project.pbxproj index c02672f..b65550c 100644 --- a/Veloria.xcodeproj/project.pbxproj +++ b/Veloria.xcodeproj/project.pbxproj @@ -213,6 +213,10 @@ BFF5B2662DF16CF60044227A /* VPWaitRestoreModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5B2652DF16CF60044227A /* VPWaitRestoreModel.swift */; }; BFF5B2682DF16EA30044227A /* VPIAPOrderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5B2672DF16EA30044227A /* VPIAPOrderModel.swift */; }; BFF5B26A2DF170DD0044227A /* VPIAPVerifyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5B2692DF170DD0044227A /* VPIAPVerifyModel.swift */; }; + BFF5B26C2DF28FD50044227A /* VPStatAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5B26B2DF28FD50044227A /* VPStatAPI.swift */; }; + BFF5B26E2DF297680044227A /* VPOpenAppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5B26D2DF297680044227A /* VPOpenAppModel.swift */; }; + BFF5B2752DF2C3750044227A /* VPDetailRecommandView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5B2742DF2C3750044227A /* VPDetailRecommandView.swift */; }; + BFF5B2772DF2CA4B0044227A /* VPDetailRecommandBannerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5B2762DF2CA4B0044227A /* VPDetailRecommandBannerCell.swift */; }; F939C04AD4003BA127F15C28 /* Pods_Veloria.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34F57E87E765BF8D72A43DCA /* Pods_Veloria.framework */; }; /* End PBXBuildFile section */ @@ -431,6 +435,10 @@ BFF5B2652DF16CF60044227A /* VPWaitRestoreModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPWaitRestoreModel.swift; sourceTree = ""; }; BFF5B2672DF16EA30044227A /* VPIAPOrderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPIAPOrderModel.swift; sourceTree = ""; }; BFF5B2692DF170DD0044227A /* VPIAPVerifyModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPIAPVerifyModel.swift; sourceTree = ""; }; + BFF5B26B2DF28FD50044227A /* VPStatAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStatAPI.swift; sourceTree = ""; }; + BFF5B26D2DF297680044227A /* VPOpenAppModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPOpenAppModel.swift; sourceTree = ""; }; + BFF5B2742DF2C3750044227A /* VPDetailRecommandView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPDetailRecommandView.swift; sourceTree = ""; }; + BFF5B2762DF2CA4B0044227A /* VPDetailRecommandBannerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPDetailRecommandBannerCell.swift; sourceTree = ""; }; E0BDA3570E00C90877E45AA0 /* Pods-VideoPlayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer.debug.xcconfig"; path = "Target Support Files/Pods-VideoPlayer/Pods-VideoPlayer.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -584,6 +592,7 @@ children = ( 1B056E402DDAC30A007EE38D /* VPModel.swift */, BF0FA70F2DDC6CA200C9E5F2 /* VPListModel.swift */, + BFF5B26D2DF297680044227A /* VPOpenAppModel.swift */, ); path = Model; sourceTree = ""; @@ -764,6 +773,7 @@ BF0FA73C2DDED2D000C9E5F2 /* VPVideoAPI.swift */, BF0FA7C22DE45DE300C9E5F2 /* VPUserAPI.swift */, BFF5B2402DF045BF0044227A /* VPRewardsAPI.swift */, + BFF5B26B2DF28FD50044227A /* VPStatAPI.swift */, ); path = API; sourceTree = ""; @@ -897,6 +907,8 @@ BFF5AFDD2DEEBF370044227A /* VPPlayerRechargeView.swift */, BFF5AFDF2DEEC5AA0044227A /* VPPlayerVipBuyView.swift */, BFF5AFE12DEED2960044227A /* VPPlayerCoinBuyView.swift */, + BFF5B2742DF2C3750044227A /* VPDetailRecommandView.swift */, + BFF5B2762DF2CA4B0044227A /* VPDetailRecommandBannerCell.swift */, ); path = View; sourceTree = ""; @@ -1335,6 +1347,7 @@ BF5E75B62DE46DB600DE9DFE /* UIScrollView+Empty.swift in Sources */, 1B056E572DDACC6B007EE38D /* VPHomePageViewController.swift in Sources */, BF0FA7BC2DE4563300C9E5F2 /* VPMeUserInfoCell.swift in Sources */, + BFF5B2772DF2CA4B0044227A /* VPDetailRecommandBannerCell.swift in Sources */, BF0FA7B82DE44FCC00C9E5F2 /* VPWebViewController.swift in Sources */, BF0FA72A2DDC922F00C9E5F2 /* VPHomeRecommandCell.swift in Sources */, BF0FA7322DDEBD6400C9E5F2 /* AppDelegate+Config.swift in Sources */, @@ -1382,6 +1395,7 @@ BF0FA7592DDF1C2800C9E5F2 /* VPPlayerProgressView.swift in Sources */, BFF5AFE02DEEC5AB0044227A /* VPPlayerVipBuyView.swift in Sources */, BFF5AFCA2DE97B7A0044227A /* VPWalletViewController.swift in Sources */, + BFF5B26C2DF28FD50044227A /* VPStatAPI.swift in Sources */, BF0FA71B2DDC7FF200C9E5F2 /* VPImageView.swift in Sources */, BF0FA7522DDF134700C9E5F2 /* VPVideoPlayerControlView.swift in Sources */, BF0FA7852DE1561D00C9E5F2 /* VPSearchRecommendedCell.swift in Sources */, @@ -1398,6 +1412,7 @@ BF5E75CD2DE5692D00DE9DFE /* JXTransitionDelegateBridge.swift in Sources */, BF5E75CE2DE5692D00DE9DFE /* JXPushAnimatedTransition.swift in Sources */, BF5E75CF2DE5692D00DE9DFE /* UIViewController+JXTransition.swift in Sources */, + BFF5B26E2DF297680044227A /* VPOpenAppModel.swift in Sources */, BFF5B23F2DF0443B0044227A /* VPWebScriptModel.swift in Sources */, BFF5B23B2DF018900044227A /* VPAlertView.swift in Sources */, BF5E75D02DE5692D00DE9DFE /* UIGestureRecognizer+JXTransition.swift in Sources */, @@ -1493,6 +1508,7 @@ BFF5AFCC2DE98C7F0044227A /* VPOrderRecordsViewController.swift in Sources */, 1B056E412DDAC30A007EE38D /* VPModel.swift in Sources */, BF0FA70A2DDC69C800C9E5F2 /* VPTableViewCell.swift in Sources */, + BFF5B2752DF2C3750044227A /* VPDetailRecommandView.swift in Sources */, BF0FA7A12DE1AA5100C9E5F2 /* VPTableView.swift in Sources */, 1B056E442DDAC355007EE38D /* UIDevice+VPAdd.swift in Sources */, BF0FA7632DE006E700C9E5F2 /* VPDetailPlayerCell.swift in Sources */, diff --git a/Veloria/AppDelegate/AppDelegate+Open.swift b/Veloria/AppDelegate/AppDelegate+Open.swift index 37b6e73..1e905eb 100644 --- a/Veloria/AppDelegate/AppDelegate+Open.swift +++ b/Veloria/AppDelegate/AppDelegate+Open.swift @@ -16,14 +16,74 @@ extension SceneDelegate { } //facebook - ApplicationDelegate.shared.application(UIApplication.shared, open: url, sourceApplication: nil, annotation: [UIApplication.OpenURLOptionsKey.annotation]) - + var result = ApplicationDelegate.shared.application(UIApplication.shared, open: url, sourceApplication: nil, annotation: [UIApplication.OpenURLOptionsKey.annotation]) + if !result { + vp_handleOpenAppMessage(webpageURL: url) + } } func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { guard let webpageURL = userActivity.webpageURL else { return } - + vp_handleOpenAppMessage(webpageURL: webpageURL) + } + +} + +extension SceneDelegate { + + static var hasOpenMessage = false + + func vp_handleOpenAppMessage(webpageURL: URL?) { + guard VPNetworkReachabilityManager.manager.isReachable == true else { return } + + + + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + self._handleOpenAppMessage(webpageURL: webpageURL) + } + + } + + private func _handleOpenAppMessage(webpageURL: URL?) { + if Self.hasOpenMessage { return } + + Self.hasOpenMessage = true + + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + Self.hasOpenMessage = false + } + + //统计用URL + var statUrlStr: String? = webpageURL?.absoluteString + var data: [String : Any]? = webpageURL?.query?.vp_urlQuryToDictionary() + + if let pasteStr = UIPasteboard.general.string { + UIPasteboard.general.string = nil + let tempArr = pasteStr.components(separatedBy: "?") + let query = tempArr.last + + let tempData = query?.vp_urlQuryToDictionary() + if tempData?["short_play_id"] != nil { + data = tempData + statUrlStr = pasteStr + } + } + + if let urlStr = statUrlStr {//上报结果 + VPStatAPI.requestStatW2a(data: urlStr) + } + + + guard let data = data else { return } + guard let model = VPOpenAppModel.deserialize(from: data) else { return } + guard let shortPlayId = model.short_play_id, shortPlayId.count > 0 else { return } + + + let vc = VPDetailPlayerViewController() + vc.shortPlayId = shortPlayId + VPAppTool.topViewController?.navigationController?.pushViewController(vc, animated: true) + } } diff --git a/Veloria/AppDelegate/AppDelegate+Thirdparty.swift b/Veloria/AppDelegate/AppDelegate+Thirdparty.swift index ab69f80..565f9f9 100644 --- a/Veloria/AppDelegate/AppDelegate+Thirdparty.swift +++ b/Veloria/AppDelegate/AppDelegate+Thirdparty.swift @@ -7,6 +7,7 @@ import UIKit import FacebookCore +import AdjustSdk extension AppDelegate { @@ -14,7 +15,11 @@ extension AppDelegate { //facebook ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions) - ///设置刷新控件的语言 + //设置刷新控件的语言 MJRefreshConfig.default.languageCode = VPLocalizedManager.shared.mjLocalizedKey + + //Adjust + let config = ADJConfig(appToken: "jmtc740fki68", environment: ADJEnvironmentProduction) + Adjust.initSdk(config) } } diff --git a/Veloria/AppDelegate/SceneDelegate.swift b/Veloria/AppDelegate/SceneDelegate.swift index b73a072..0de7150 100644 --- a/Veloria/AppDelegate/SceneDelegate.swift +++ b/Veloria/AppDelegate/SceneDelegate.swift @@ -10,46 +10,57 @@ import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - + + + private var timer: Timer? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } VPAppTool.windowScene = windowScene + timer = Timer.scheduledTimer(timeInterval: 60 * 10, target: YYWeakProxy(target: self), selector: #selector(handleOnLine), userInfo: nil, repeats: true) + + NotificationCenter.default.addObserver(self, selector: #selector(reachabilityDidChangeNotification), name: VPNetworkReachabilityManager.reachabilityDidChangeNotification, object: nil) + window = UIWindow(windowScene: windowScene) window?.rootViewController = VPTabBarController() window?.makeKeyAndVisible() + } func sceneDidDisconnect(_ scene: UIScene) { - // Called as the scene is being released by the system. - // This occurs shortly after the scene enters the background, or when its session is discarded. - // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). } 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. + VPStatAPI.requestEnterApp() + vp_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). + VPStatAPI.requestLeaveApp() } func sceneWillEnterForeground(_ scene: UIScene) { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. + } func sceneDidEnterBackground(_ scene: UIScene) { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. } } +extension SceneDelegate { + @objc private func handleOnLine() { + VPStatAPI.requestStatOnLine() + } + + + + @objc private func reachabilityDidChangeNotification() { + vp_handleOpenAppMessage(webpageURL: nil) + + } +} + diff --git a/Veloria/Base/Extension/String+VPAdd.swift b/Veloria/Base/Extension/String+VPAdd.swift index fc26e2d..4eeee52 100644 --- a/Veloria/Base/Extension/String+VPAdd.swift +++ b/Veloria/Base/Extension/String+VPAdd.swift @@ -31,3 +31,23 @@ extension String { return string.size(for: font, size: size, mode: .byWordWrapping) } } + +extension String { + ///将url中的参数转换成字典 + func vp_urlQuryToDictionary() -> [String : Any] { + let array = self.components(separatedBy: "&") + var tempDic: [String : Any] = [:] + + array.forEach { + if let strRange = $0.range(of: "=") { + var key: String = String($0.prefix(upTo: strRange.upperBound)) + key.removeLast() + var value: String = String($0.suffix(from: strRange.upperBound)) + value = value.removingPercentEncoding ?? value + tempDic[key] = value + } + } + return tempDic + } +} + diff --git a/Veloria/Base/Model/VPOpenAppModel.swift b/Veloria/Base/Model/VPOpenAppModel.swift new file mode 100644 index 0000000..5335fc9 --- /dev/null +++ b/Veloria/Base/Model/VPOpenAppModel.swift @@ -0,0 +1,25 @@ +// +// VPOpenAppModel.swift +// Veloria +// +// Created by 湖南秦九 on 2025/6/6. +// + +import UIKit +import SmartCodable + +class VPOpenAppModel: VPModel, SmartCodable { + + enum PathType: String, SmartCaseDefaultable { + case videoDetail = "detail" + ///反馈列表 + case feedback = "feedback" + ///活动 + case promotion = "promotion" + } + + var id: String? + var message_id: String? + var short_play_id: String? + var path: PathType? +} diff --git a/Veloria/Base/Networking/API/VPStatAPI.swift b/Veloria/Base/Networking/API/VPStatAPI.swift new file mode 100644 index 0000000..807ac97 --- /dev/null +++ b/Veloria/Base/Networking/API/VPStatAPI.swift @@ -0,0 +1,52 @@ +// +// VPStatAPI.swift +// Veloria +// +// Created by 湖南秦九 on 2025/6/6. +// + +import UIKit + +class VPStatAPI: NSObject { + + ///进入APP + static func requestEnterApp() { + + var param = VPNetworkParameters(path: "/customer/enterTheApp") + param.isToast = false + param.isLoding = false + + VPNetwork.request(parameters: param) { (_: VPNetworkResponse) in } + } + + ///离开APP + static func requestLeaveApp() { + var param = VPNetworkParameters(path: "/customer/leaveApp") + param.isToast = false + param.isLoding = false + + VPNetwork.request(parameters: param) { (_: VPNetworkResponse) in } + } + + static func requestStatOnLine() { + var param = VPNetworkParameters(path: "/customer/onLine") + param.isToast = false + param.isLoding = false + + VPNetwork.request(parameters: param) { (_: VPNetworkResponse) in } + } + + ///统计w2a点击 + static func requestStatW2a(data: String) { + var param = VPNetworkParameters(path: "/w2aSelfAttribution") + param.isToast = false + param.isLoding = false + param.parameters = [ + "data" : data + ] + + VPNetwork.request(parameters: param) { (response: VPNetworkResponse) in + + } + } +} diff --git a/Veloria/Base/Networking/API/VPVideoAPI.swift b/Veloria/Base/Networking/API/VPVideoAPI.swift index bf5afdb..ad1823d 100644 --- a/Veloria/Base/Networking/API/VPVideoAPI.swift +++ b/Veloria/Base/Networking/API/VPVideoAPI.swift @@ -139,6 +139,17 @@ class VPVideoAPI: NSObject { completer?(response.data) } } + + ///视频推荐 + static func requestDetailsRecommand(completer: ((_ list: [VPShortModel]?) -> Void)?) { + + var param = VPNetworkParameters(path: "/getDetailsRecommand") + param.method = .get + + VPNetwork.request(parameters: param) { (response: VPNetworkResponse>) in + completer?(response.data?.list) + } + } } extension VPVideoAPI { diff --git a/Veloria/Class/Me/Controller/VPMeViewController.swift b/Veloria/Class/Me/Controller/VPMeViewController.swift index f16152c..a192aa1 100644 --- a/Veloria/Class/Me/Controller/VPMeViewController.swift +++ b/Veloria/Class/Me/Controller/VPMeViewController.swift @@ -202,7 +202,7 @@ extension VPMeViewController { ] var cellArr: [VPMeItem] = [] - cellArr.append(VPMeItem(icon: UIImage(named: "me_item_icon_05"), title: "Language".localized, type: .language, cellKey: .normal)) +// cellArr.append(VPMeItem(icon: UIImage(named: "me_item_icon_05"), title: "Language".localized, type: .language, cellKey: .normal)) cellArr.append(VPMeItem(icon: UIImage(named: "me_item_icon_01"), title: "Privacy Policy".localized, type: .privacyPolicy, cellKey: .normal)) cellArr.append(VPMeItem(icon: UIImage(named: "me_item_icon_02"), title: "User Agreement".localized, type: .userAgreement, cellKey: .normal)) cellArr.append(VPMeItem(icon: UIImage(named: "me_item_icon_04"), title: "Help Center".localized, type: .feedback, cellKey: .normal)) diff --git a/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift b/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift index 670f749..af5cca3 100644 --- a/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift +++ b/Veloria/Class/Player/Controller/VPDetailPlayerViewController.swift @@ -22,11 +22,14 @@ class VPDetailPlayerViewController: VPVideoPlayerViewController { private var detailModel: VPVideoDetailModel? + ///推荐列表 + private lazy var recommandList: [VPShortModel] = [] + //MARK: UI属性 private lazy var backButton: UIButton = { let button = UIButton(type: .custom) button.setImage(UIImage(named: "arrow_left_icon_01"), for: .normal) - button.addTarget(self, action: #selector(handleBack), for: .touchUpInside) + button.addTarget(self, action: #selector(handleBackButton), for: .touchUpInside) return button }() @@ -49,8 +52,10 @@ class VPDetailPlayerViewController: VPVideoPlayerViewController { self.delegate = self self.dataSource = self NotificationCenter.default.addObserver(self, selector: #selector(buyVipFinishNotification), name: VPIAPManager.buyVipFinishNotification, object: nil) + self.jx_popDelegate = self requestDetailData() + requestRecommandDataArr() vp_setupUI() vp_addAction() @@ -78,6 +83,28 @@ class VPDetailPlayerViewController: VPVideoPlayerViewController { super.play() VPVideoAPI.requestCreatePlayHistory(videoId: videoInfo.short_play_video_id, shortPlayId: videoInfo.short_play_id) } + + @objc func handleBackButton() { + guard recommandList.count > 0 else { + self.handleBack() + return + } + + self.pause() + + let view = VPDetailRecommandView() + view.dataArr = recommandList + view.clickCloseButton = { [weak self] in + self?.handleBack() + } + view.clickLookButton = { [weak self] model in + guard let self = self else { return } + self.shortPlayId = model.short_play_id + self.activityId = nil + self.requestDetailData() + } + view.present(in: nil) + } } @@ -221,13 +248,22 @@ extension VPDetailPlayerViewController: VPPlayerListViewControllerDelegate { } +//MARK: -------------- JXViewControllerPopDelegate -------------- +extension VPDetailPlayerViewController: JXViewControllerPopDelegate { + func viewControllerPopShouldScrollBegan() -> Bool { + self.handleBackButton() + return false + } +} + extension VPDetailPlayerViewController { private func requestDetailData() { guard let shortPlayId = shortPlayId else { return } - + VPHUD.show(containerView: self.view) VPVideoAPI.requestVideoDetail(shortPlayId: shortPlayId, activityId: activityId) { [weak self] model in + VPHUD.dismiss() guard let self = self else { return } guard let model = model else { return } self.detailModel = model @@ -255,4 +291,14 @@ extension VPDetailPlayerViewController { } } + private func requestRecommandDataArr() { + + VPVideoAPI.requestDetailsRecommand { [weak self] list in + guard let self = self else { return } + if let list = list { + self.recommandList = list + } + } + } + } diff --git a/Veloria/Class/Player/View/VPDetailRecommandBannerCell.swift b/Veloria/Class/Player/View/VPDetailRecommandBannerCell.swift new file mode 100644 index 0000000..a07e64a --- /dev/null +++ b/Veloria/Class/Player/View/VPDetailRecommandBannerCell.swift @@ -0,0 +1,45 @@ +// +// VPDetailRecommandBannerCell.swift +// Veloria +// +// Created by 湖南秦九 on 2025/6/6. +// + +import UIKit +import FSPagerView + +class VPDetailRecommandBannerCell: FSPagerViewCell { + + var model: VPShortModel? { + didSet { + coverImageView.vp_setImage(url: model?.image_url) + } + } + +// private lazy var player: VPPlayer = { +// +// }() + + + private lazy var coverImageView: VPImageView = { + let imageView = VPImageView() + imageView.layer.cornerRadius = 14 + imageView.layer.masksToBounds = true + return imageView + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + + contentView.addSubview(coverImageView) + + coverImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + + @MainActor required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/Veloria/Class/Player/View/VPDetailRecommandView.swift b/Veloria/Class/Player/View/VPDetailRecommandView.swift new file mode 100644 index 0000000..0179f58 --- /dev/null +++ b/Veloria/Class/Player/View/VPDetailRecommandView.swift @@ -0,0 +1,287 @@ +// +// VPDetailRecommandView.swift +// Veloria +// +// Created by 湖南秦九 on 2025/6/6. +// + +import UIKit +import FSPagerView + +class VPDetailRecommandView: HWPanModalContentView { + + + var dataArr: [VPShortModel] = [] { + didSet { + + CATransaction.setCompletionBlock { [weak self] in + self?.setVideoTitle() + } + + CATransaction.begin() + bannerView.reloadData() + CATransaction.commit() + + } + } + + var clickCloseButton: (() -> Void)? + var clickLookButton: ((_ model: VPShortModel) -> Void)? + + + + //MARK: UI属性 + private lazy var bgView: UIView = { + let view = VPGradientView() + view.locations = [0, 0.5, 1] + view.colors = [UIColor.color045241().cgColor, UIColor.color000000().cgColor, UIColor.color000000().cgColor] + view.startPoint = .init(x: 0, y: 0) + view.endPoint = .init(x: 1, y: 1) + return view + }() + + private lazy var closeButton: UIButton = { + let button = UIButton(type: .custom) + button.setImage(UIImage(named: "close_icon_01"), for: .normal) + button.addTarget(self, action: #selector(handleCloseButton), for: .touchUpInside) + return button + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 16) + label.textColor = .colorFFFFFF() + label.text = "kDetailRecommandTitle".localized + return label + }() + + private lazy var bannerView: FSPagerView = { + let view = FSPagerView() + view.transformer = FSPagerViewTransformer(type: .overlap) + view.transformer?.minimumScale = 0.8 + view.decelerationDistance = FSPagerView.automaticDistance + view.itemSize = .init(width: 194, height: 258) + view.isInfinite = true + view.delegate = self + view.dataSource = self + view.register(VPDetailRecommandBannerCell.self, forCellWithReuseIdentifier: "cell") + return view + }() + + private lazy var videoTitleLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 14) + label.textColor = .colorFFFFFF() + label.textAlignment = .center + return label + }() + + private lazy var videoCategoryView: UIButton = { + var config = UIButton.Configuration.plain() + config.contentInsets = .init(top: 0, leading: 5, bottom: 0, trailing: 5) + + let view = UIButton(configuration: config) + view.isUserInteractionEnabled = false + view.backgroundColor = .colorFFFFFF(alpha: 0.1) + view.layer.cornerRadius = 3 + view.layer.masksToBounds = true + view.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + if self.dataArr.count <= self.bannerView.currentIndex { return } + + let model = self.dataArr[self.bannerView.currentIndex] + let category = model.category?.last ?? "" + + let string = AttributedString.createAttributedString(string: category, color: .colorAFAFAF(), font: .fontRegular(ofSize: 10)) + + button.configuration?.attributedTitle = string + } + + return view + }() + + private lazy var lookButton: UIButton = { + let button = VPGradientButton(type: .custom) + button.locations = [0, 1] + button.colors = [UIColor.color05CEA0(alpha: 0.3).cgColor, UIColor.color7C174F(alpha: 0.3).cgColor] + button.startPoint = .init(x: 0, y: 0.3) + button.endPoint = .init(x: 1, y: 0.8) + button.bt_setGradientBorder() + button.setTitle("Watch Now".localized, for: .normal) + button.setTitleColor(.colorFFFFFF(), for: .normal) + button.titleLabel?.font = .fontMedium(ofSize: 14) + button.layer.cornerRadius = 24 + button.layer.masksToBounds = true + button.addTarget(self, action: #selector(handleLookButton), for: .touchUpInside) + return button + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + + + vp_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + + } + + + //MARK: HWPanModalPresentable + override func longFormHeight() -> PanModalHeight { + return PanModalHeightMake(.content, 473 + UIScreen.tabbarSafeBottomMargin) + } + + override func showDragIndicator() -> Bool { + return false + } + + override func backgroundConfig() -> HWBackgroundConfig { + let config = HWBackgroundConfig() + config.backgroundAlpha = 0.6 + return config + } + + override func cornerRadius() -> CGFloat { + return 24 + } + + override func allowsTapBackgroundToDismiss() -> Bool { + return false + } + + override func allowsDragToDismiss() -> Bool { + return false + } + + override func minVerticalVelocityToTriggerDismiss() -> CGFloat { + return 0 + } + + override func allowsPullDownWhenShortState() -> Bool { + return false + } + +} + +extension VPDetailRecommandView { + + private func setVideoTitle() { + let index = self.bannerView.currentIndex + let cell = self.bannerView.cellForItem(at: index) + vpLog(message: cell) + + let model = self.dataArr[index] + videoTitleLabel.text = model.name + + if let category = model.category?.last, !category.isEmpty { + videoCategoryView.isHidden = false + videoCategoryView.setNeedsUpdateConfiguration() + } else { + videoCategoryView.isHidden = true + } + + } + + @objc private func handleCloseButton() { + self.clickCloseButton?() + + self.dismiss(animated: true) { + + } + } + + @objc private func handleLookButton() { + let index = self.bannerView.currentIndex + self.clickLookButton?(dataArr[index]) + + self.dismiss(animated: true) { } + } + + +} + +extension VPDetailRecommandView { + + private func vp_setupUI() { + addSubview(bgView) + addSubview(titleLabel) + addSubview(closeButton) + addSubview(bannerView) + addSubview(videoTitleLabel) + addSubview(videoCategoryView) + addSubview(lookButton) + + bgView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalToSuperview().offset(39) + } + + closeButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-5) + make.top.equalToSuperview().offset(5) + make.width.height.equalTo(40) + } + + bannerView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview().offset(77) + make.height.equalTo(260) + } + + videoTitleLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(bannerView.snp.bottom).offset(9) + make.width.equalTo(200) + } + + videoCategoryView.snp.makeConstraints { make in +// make.left.equalTo(videoTitleLabel) + make.centerX.equalToSuperview() + make.top.equalTo(bannerView.snp.bottom).offset(38) + make.height.equalTo(16) + } + + lookButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.width.equalTo(240) + make.height.equalTo(48) + make.top.equalTo(bannerView.snp.bottom).offset(74) + } + + } + +} + + +//MARK: -------------- FSPagerViewDelegate FSPagerViewDataSource -------------- +extension VPDetailRecommandView: FSPagerViewDelegate, FSPagerViewDataSource { + + func numberOfItems(in pagerView: FSPagerView) -> Int { + return dataArr.count + } + + func pagerView(_ pagerView: FSPagerView, cellForItemAt index: Int) -> FSPagerViewCell { + let cell = pagerView.dequeueReusableCell(withReuseIdentifier: "cell", at: index) as! VPDetailRecommandBannerCell + cell.model = self.dataArr[index] + return cell + } + + func pagerViewDidEndDecelerating(_ pagerView: FSPagerView) { + setVideoTitle() + } + + +} + diff --git a/Veloria/Source/Info.plist b/Veloria/Source/Info.plist index cadbf6b..dcbd593 100644 --- a/Veloria/Source/Info.plist +++ b/Veloria/Source/Info.plist @@ -14,6 +14,14 @@ fb642299232204955 + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + VeloriaTV + + FacebookAppID 642299232204955 diff --git a/Veloria/Source/en.lproj/Localizable.strings b/Veloria/Source/en.lproj/Localizable.strings index c991761..bc355fe 100644 --- a/Veloria/Source/en.lproj/Localizable.strings +++ b/Veloria/Source/en.lproj/Localizable.strings @@ -86,7 +86,9 @@ "Expired" = "Expired"; "Success" = "Success"; "Restore" = "Restore"; +"Watch Now" = "Watch Now"; +"kDetailRecommandTitle" = "Picked Just for You"; "kHomeTitleText" = "10,000+ addictive shorts await!"; "kSearchPlaceholderText1" = "Search dramas"; "kSearchPlaceholderText2" = "#Recersal of fate"; diff --git a/Veloria/Veloria.entitlements b/Veloria/Veloria.entitlements index d705386..df0f60e 100644 --- a/Veloria/Veloria.entitlements +++ b/Veloria/Veloria.entitlements @@ -6,6 +6,10 @@ Default + com.apple.developer.associated-domains + + webcredentials:example.com + keychain-access-groups diff --git a/资料/app账号信息.txt b/资料/app账号信息.txt index 1ad1975..a55adbf 100644 --- a/资料/app账号信息.txt +++ b/资料/app账号信息.txt @@ -14,4 +14,6 @@ D-U-N-S :616751820 沙盒账号 veloria@test.com -Test@168 \ No newline at end of file +Test@168 +veloria1@test.com +Discover2024 \ No newline at end of file