Переглянути джерело

feat:新版本修复bug,广告,倒计时,气泡

kailen 2 місяців тому
батько
коміт
5e99d12ad3
36 змінених файлів з 675 додано та 135 видалено
  1. 3 3
      Podfile.lock
  2. 16 0
      TSLiveWallpaper.xcodeproj/project.pbxproj
  3. 14 15
      TSLiveWallpaper/AppDelegate.swift
  4. 65 0
      TSLiveWallpaper/Assets.xcassets/Music/ic_bubble_bg.imageset/Contents.json
  5. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_bubble_bg.imageset/ic_bubble_bg@1x.png
  6. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_bubble_bg.imageset/ic_bubble_bg@2x.png
  7. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_bubble_bg.imageset/ic_bubble_bg@3x.png
  8. 23 0
      TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time.imageset/Contents.json
  9. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time.imageset/ic_clock_time@1x.png
  10. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time.imageset/ic_clock_time@2x.png
  11. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time.imageset/ic_clock_time@3x.png
  12. 23 0
      TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time_selected.imageset/Contents.json
  13. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time_selected.imageset/ic_clock_time_selected@1x.png
  14. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time_selected.imageset/ic_clock_time_selected@2x.png
  15. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time_selected.imageset/ic_clock_time_selected@3x.png
  16. 23 0
      TSLiveWallpaper/Assets.xcassets/Music/ic_tag.imageset/Contents.json
  17. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_tag.imageset/ic_tag@1x.png
  18. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_tag.imageset/ic_tag@2x.png
  19. BIN
      TSLiveWallpaper/Assets.xcassets/Music/ic_tag.imageset/ic_tag@3x.png
  20. 22 0
      TSLiveWallpaper/Assets.xcassets/Music/img-pre-ad-emo-error.imageset/Contents.json
  21. BIN
      TSLiveWallpaper/Assets.xcassets/Music/img-pre-ad-emo-error.imageset/img-pre-ad-emo-error@2x.png
  22. BIN
      TSLiveWallpaper/Assets.xcassets/Music/img-pre-ad-emo-error.imageset/img-pre-ad-emo-error@3x.png
  23. 138 0
      TSLiveWallpaper/Business/AdMob/ADLoadingViewController.swift
  24. 50 0
      TSLiveWallpaper/Business/AdMob/AppConfig.swift
  25. 62 0
      TSLiveWallpaper/Business/AdMob/GoogleMobileAdsConsentManager.swift
  26. 18 26
      TSLiveWallpaper/Business/TSMusic/Detail/Controller/PlayDetailViewController.swift
  27. 24 11
      TSLiveWallpaper/Business/TSMusic/Detail/PlayerManager.swift
  28. 0 1
      TSLiveWallpaper/Business/TSMusic/Detail/View/PlayDetailTopView.swift
  29. 57 0
      TSLiveWallpaper/Business/TSMusic/Helper/GuideBubbleView.swift
  30. 55 25
      TSLiveWallpaper/Business/TSMusic/List/Controller/MusicContainerViewController.swift
  31. 1 0
      TSLiveWallpaper/Business/TSMusic/List/Controller/SongListViewController+Target.swift
  32. 2 40
      TSLiveWallpaper/Business/TSMusic/List/Controller/SongListViewController.swift
  33. 7 3
      TSLiveWallpaper/Business/TSMusic/PlayList/Controller/PlaylistDetailViewController.swift
  34. 12 4
      TSLiveWallpaper/Business/TSMusic/SearchResult/SearchResultViewController.swift
  35. 6 0
      TSLiveWallpaper/Common/Ex/UIDevice+Extension.swift
  36. 54 7
      TSLiveWallpaper/LaunchVC/TSLaunchVC.swift

+ 3 - 3
Podfile.lock

@@ -178,7 +178,7 @@ CHECKOUT OPTIONS:
     :git: https://gitee.com/WanlanNeel/tsvideo-kit.git
 
 SPEC CHECKSUMS:
-  ADManager: 82b2c255aadfb314141275088e13287ccc3dcd14
+  ADManager: 69cd1b8805b2e64a72315ed2abfa1f1c02f8f879
   AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
   Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
   BetterSegmentedControl: 09607b27861d49cbce48b7673b74f9150a3d371a
@@ -198,11 +198,11 @@ SPEC CHECKSUMS:
   SJVideoPlayer: 4f09814f58522e0975cb2dccfda925f6c8643467
   SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
   SVProgressHUD: 4837c74bdfe2e51e8821c397825996a8d7de6e22
-  TSVideoKit: 922402ea051d72e78224f22d37c10fdb2e378f60
+  TSVideoKit: 16761d4bf8bb9e8af192459ae9eb01a213fc1531
   TYCyclePagerView: 2b051dade0615c70784aa34f40c646feeddb7344
   TZImagePickerController: d084a7b97c82d387e7669dd86dc9a9057500aacf
   YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
 
 PODFILE CHECKSUM: 436b32429cb708cca90f47b70a0a376c1a6e0417
 
-COCOAPODS: 1.16.2
+COCOAPODS: 1.15.2

+ 16 - 0
TSLiveWallpaper.xcodeproj/project.pbxproj

@@ -104,6 +104,10 @@
 		606372D82D545E6C005C82CF /* Example Music.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 606372D72D545E6C005C82CF /* Example Music.mp3 */; };
 		606372DA2D545F0D005C82CF /* ExampleIniter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606372D92D545F0D005C82CF /* ExampleIniter.swift */; };
 		606372DD2D54999C005C82CF /* ADScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606372DC2D54999C005C82CF /* ADScene.swift */; };
+		606372DF2D54BBB5005C82CF /* AppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606372DE2D54BBB5005C82CF /* AppConfig.swift */; };
+		606372E12D54BC37005C82CF /* GoogleMobileAdsConsentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606372E02D54BC37005C82CF /* GoogleMobileAdsConsentManager.swift */; };
+		606372E32D55A995005C82CF /* ADLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606372E22D55A995005C82CF /* ADLoadingViewController.swift */; };
+		606372E52D55BAB8005C82CF /* GuideBubbleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 606372E42D55BAB8005C82CF /* GuideBubbleView.swift */; };
 		60F82C0F2D43295100FFB08D /* MusicContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F82C0E2D43295100FFB08D /* MusicContainerViewController.swift */; };
 		60F82C112D43298800FFB08D /* MusicContainerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F82C102D43298800FFB08D /* MusicContainerViewModel.swift */; };
 		A81CA4652D15685F00A3AAC8 /* TSLaunchVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81CA4642D15685D00A3AAC8 /* TSLaunchVC.swift */; };
@@ -307,6 +311,10 @@
 		606372D72D545E6C005C82CF /* Example Music.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "Example Music.mp3"; sourceTree = "<group>"; };
 		606372D92D545F0D005C82CF /* ExampleIniter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleIniter.swift; sourceTree = "<group>"; };
 		606372DC2D54999C005C82CF /* ADScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ADScene.swift; sourceTree = "<group>"; };
+		606372DE2D54BBB5005C82CF /* AppConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfig.swift; sourceTree = "<group>"; };
+		606372E02D54BC37005C82CF /* GoogleMobileAdsConsentManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleMobileAdsConsentManager.swift; sourceTree = "<group>"; };
+		606372E22D55A995005C82CF /* ADLoadingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ADLoadingViewController.swift; sourceTree = "<group>"; };
+		606372E42D55BAB8005C82CF /* GuideBubbleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuideBubbleView.swift; sourceTree = "<group>"; };
 		60F82C0E2D43295100FFB08D /* MusicContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicContainerViewController.swift; sourceTree = "<group>"; };
 		60F82C102D43298800FFB08D /* MusicContainerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicContainerViewModel.swift; sourceTree = "<group>"; };
 		71E5F623537702A8306DF3C8 /* Pods-TSLiveWallpaper.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TSLiveWallpaper.release.xcconfig"; path = "Target Support Files/Pods-TSLiveWallpaper/Pods-TSLiveWallpaper.release.xcconfig"; sourceTree = "<group>"; };
@@ -566,6 +574,7 @@
 				60553F2C2D3B528A00BAAD7F /* FilterBarViewController.swift */,
 				60553F2D2D3B528A00BAAD7F /* FilterBarViewModel.swift */,
 				60553F2E2D3B528A00BAAD7F /* FilterBarView.swift */,
+				606372E42D55BAB8005C82CF /* GuideBubbleView.swift */,
 				60553F2F2D3B528A00BAAD7F /* SortMenuViewController.swift */,
 				60553F302D3B528A00BAAD7F /* DownloadButton.swift */,
 				60553F312D3B528A00BAAD7F /* CWCustomProgressView.swift */,
@@ -745,7 +754,10 @@
 		606372DB2D549998005C82CF /* AdMob */ = {
 			isa = PBXGroup;
 			children = (
+				606372E02D54BC37005C82CF /* GoogleMobileAdsConsentManager.swift */,
+				606372DE2D54BBB5005C82CF /* AppConfig.swift */,
 				606372DC2D54999C005C82CF /* ADScene.swift */,
+				606372E22D55A995005C82CF /* ADLoadingViewController.swift */,
 			);
 			path = AdMob;
 			sourceTree = "<group>";
@@ -1358,6 +1370,7 @@
 				A8F76C3C2D35026200AA6E93 /* TSPurchaseMembershipVC.swift in Sources */,
 				A81CA46E2D156C7000A3AAC8 /* GlobalImports.swift in Sources */,
 				A87833202D293EEC00E47F2C /* TSSimpleTableView.swift in Sources */,
+				606372E52D55BAB8005C82CF /* GuideBubbleView.swift in Sources */,
 				A8F774522D3757E700AA6E93 /* Color+Ex.swift in Sources */,
 				A81F5B4D2D1965F800740085 /* UIImage+Ex.swift in Sources */,
 				A81CA4832D157F5C00A3AAC8 /* UIImageView+Ex.swift in Sources */,
@@ -1438,6 +1451,7 @@
 				60553F852D3B528A00BAAD7F /* SongDownloadCellViewModel.swift in Sources */,
 				60553F862D3B528A00BAAD7F /* PlayDetailControlView.swift in Sources */,
 				60553F872D3B528A00BAAD7F /* PlayDetailListViewModel.swift in Sources */,
+				606372E32D55A995005C82CF /* ADLoadingViewController.swift in Sources */,
 				60553F882D3B528A00BAAD7F /* SearchViewModel.swift in Sources */,
 				60553F892D3B528A00BAAD7F /* CWOperateViewController.swift in Sources */,
 				60553F8A2D3B528A00BAAD7F /* THUDProtocol.swift in Sources */,
@@ -1458,6 +1472,7 @@
 				60553F992D3B528A00BAAD7F /* FilterBarViewModel.swift in Sources */,
 				60F82C0F2D43295100FFB08D /* MusicContainerViewController.swift in Sources */,
 				60553F9A2D3B528A00BAAD7F /* PlayListManageView.swift in Sources */,
+				606372DF2D54BBB5005C82CF /* AppConfig.swift in Sources */,
 				60553F9B2D3B528A00BAAD7F /* LWSearchBar.swift in Sources */,
 				60553F9C2D3B528A00BAAD7F /* BubbleMenuView.swift in Sources */,
 				60553F9D2D3B528A00BAAD7F /* SongListViewModel.swift in Sources */,
@@ -1508,6 +1523,7 @@
 				A81CA4AE2D16944B00A3AAC8 /* TSBaseCollectionCell.swift in Sources */,
 				A81CA4772D15779E00A3AAC8 /* UIColor+Ex.swift in Sources */,
 				A81CA4952D1652B500A3AAC8 /* TSEditLiveVC.swift in Sources */,
+				606372E12D54BC37005C82CF /* GoogleMobileAdsConsentManager.swift in Sources */,
 				A83946332D1D66A900ABFF0D /* TSPrivacyPolicyVC.swift in Sources */,
 				A81CA47B2D15784800A3AAC8 /* Int+Ex.swift in Sources */,
 				A839463A2D1D6E3000ABFF0D /* TSRandomWallpaperTutorialsVC.swift in Sources */,

+ 14 - 15
TSLiveWallpaper/AppDelegate.swift

@@ -9,6 +9,7 @@ import AppTrackingTransparency
 import GoogleMobileAds
 import TSVideoKit
 import UIKit
+import ADManager
 
 @main
 class AppDelegate: UIResponder, UIApplicationDelegate {
@@ -36,20 +37,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
         var config = TSConfiguration.default
         config.configurePath = "http://p.100yearslater.com/live/config"
         TSVideoOperator.shared.loadWithConfiguration(config: config, needJs: true)
-//        if UserDefaults.standard.string(forKey: "InitExampleData") == nil {
-//            if let path = Bundle.main.path(forResource: "CandyTown", ofType: ".mp3") {
-//                let fileUrl = URL(fileURLWithPath: path)
-//                ImportFilesManager.shared.copyFileToUrl(url: fileUrl)
-//                UserDefaults.standard.set("1", forKey: "InitExampleData")
-//            }
-//
-//            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
-//                if let firstVideo = TSVideoOperator.shared.dataManager.fetchAllVideos().first {
-//                    TSVideoOperator.shared.playerViewModel.currentVideo = firstVideo
-//                    PlayerManager.shared.miniBar.updateVideoInfo(video: firstVideo, state: .pause)
-//                }
-//            }
-//        }
 
         if let videoId = UserDefaults.standard.string(forKey: "lastedVideoId"),
            !videoId.isEmpty,
@@ -88,11 +75,23 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
     func applicationDidBecomeActive(_ application: UIApplication) {
         AppDelegate.requestAdTrack()
     }
-    
+
     func applicationWillTerminate(_ application: UIApplication) {
         UserDefaults.standard.setValue(PlayerManager.shared.currentVideo?.videoId ?? "", forKey: "lastedVideoId")
         UserDefaults.standard.setValue(PlayerManager.shared.currentLoopMode.rawValue, forKey: "lastedPlayMode")
     }
+
+    func applicationWillEnterForeground(_ application: UIApplication) {
+        // 热启动显示开屏广告
+        if let topVC = UIApplication.defaultViewController?.topVc {
+            if PurchaseManager.default.isVip {
+                return
+            }
+            DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
+                ADManager.shared.showLaunchAdWhenActive(scene: ADScene.launch, from: topVC)
+            }
+        }
+    }
 }
 
 extension AppDelegate {

+ 65 - 0
TSLiveWallpaper/Assets.xcassets/Music/ic_bubble_bg.imageset/Contents.json

@@ -0,0 +1,65 @@
+{
+  "images" : [
+    {
+      "filename" : "ic_bubble_bg@1x.png",
+      "idiom" : "universal",
+      "resizing" : {
+        "cap-insets" : {
+          "bottom" : 12,
+          "left" : 124,
+          "right" : 71,
+          "top" : 20
+        },
+        "center" : {
+          "height" : 11,
+          "mode" : "tile",
+          "width" : 10
+        },
+        "mode" : "9-part"
+      },
+      "scale" : "1x"
+    },
+    {
+      "filename" : "ic_bubble_bg@2x.png",
+      "idiom" : "universal",
+      "resizing" : {
+        "cap-insets" : {
+          "bottom" : 27,
+          "left" : 254,
+          "right" : 132,
+          "top" : 49
+        },
+        "center" : {
+          "height" : 15,
+          "mode" : "tile",
+          "width" : 21
+        },
+        "mode" : "9-part"
+      },
+      "scale" : "2x"
+    },
+    {
+      "filename" : "ic_bubble_bg@3x.png",
+      "idiom" : "universal",
+      "resizing" : {
+        "cap-insets" : {
+          "bottom" : 41,
+          "left" : 356,
+          "right" : 232,
+          "top" : 72
+        },
+        "center" : {
+          "height" : 19,
+          "mode" : "tile",
+          "width" : 26
+        },
+        "mode" : "9-part"
+      },
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_bubble_bg.imageset/ic_bubble_bg@1x.png


BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_bubble_bg.imageset/ic_bubble_bg@2x.png


BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_bubble_bg.imageset/ic_bubble_bg@3x.png


+ 23 - 0
TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "ic_clock_time@1x.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "ic_clock_time@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "ic_clock_time@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time.imageset/ic_clock_time@1x.png


BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time.imageset/ic_clock_time@2x.png


BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time.imageset/ic_clock_time@3x.png


+ 23 - 0
TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time_selected.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "ic_clock_time_selected@1x.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "ic_clock_time_selected@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "ic_clock_time_selected@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time_selected.imageset/ic_clock_time_selected@1x.png


BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time_selected.imageset/ic_clock_time_selected@2x.png


BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_clock_time_selected.imageset/ic_clock_time_selected@3x.png


+ 23 - 0
TSLiveWallpaper/Assets.xcassets/Music/ic_tag.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "ic_tag@1x.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "ic_tag@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "ic_tag@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_tag.imageset/ic_tag@1x.png


BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_tag.imageset/ic_tag@2x.png


BIN
TSLiveWallpaper/Assets.xcassets/Music/ic_tag.imageset/ic_tag@3x.png


+ 22 - 0
TSLiveWallpaper/Assets.xcassets/Music/img-pre-ad-emo-error.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "img-pre-ad-emo-error@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "img-pre-ad-emo-error@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
TSLiveWallpaper/Assets.xcassets/Music/img-pre-ad-emo-error.imageset/img-pre-ad-emo-error@2x.png


BIN
TSLiveWallpaper/Assets.xcassets/Music/img-pre-ad-emo-error.imageset/img-pre-ad-emo-error@3x.png


+ 138 - 0
TSLiveWallpaper/Business/AdMob/ADLoadingViewController.swift

@@ -0,0 +1,138 @@
+//
+//  ADLoadingViewController.swift
+//  ColorfulWallpaper
+//
+//  Created by nkl on 2024/9/25.
+//
+
+import UIKit
+import ADManager
+
+class ADLoadingViewController: BasePresentViewController {
+    var adScene: ADScene = .unknow
+    var finishedHandler: ((Bool) -> Void)?
+
+    private let totalDuration: TimeInterval = 10
+    private var remindTimeInterval: TimeInterval = 0
+    private var loadCompletionHandler: ADCompletionHandler?
+
+    // 定时器
+    private var timer: DispatchSourceTimer?
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        loadCompletionHandler = { [weak self] state in
+            guard let self = self else { return }
+
+            switch state {
+            case .willPresent:
+                THUD.hide()
+            case .finished:
+                let finishedHandler = self.finishedHandler
+                finishedHandler?(true)
+            case .fail:
+                self.stopTimer()
+                self.updateLoadingState(.failure)
+            case .loadSuccess:
+                self.stopTimer()
+                // 加载成功,且未超过计时,显示,超时则本次不展示
+                if remindTimeInterval > 0 {
+                    self.view.backgroundColor = .clear
+                    self.dismiss(animated: false) {
+                        if let vc = PlayerManager.shared.rootVc {
+                            vc.presentingViewController?.dismiss(animated: false)
+                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+                                ADManager.shared.showAd(scene: self.adScene, from: vc) { state in
+                                    switch state {
+                                    case .willPresent:
+                                        THUD.hide()
+                                    case .finished:
+                                        let finishedHandler = self.finishedHandler
+                                        finishedHandler?(true)
+                                    case .fail:
+                                        self.stopTimer()
+                                        self.updateLoadingState(.failure)
+                                    case .loadSuccess:
+                                        self.stopTimer()
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
+            self.startLoadAD()
+        }
+    }
+
+    override var shouldDismissWhenTapBackground: Bool {
+        return false
+    }
+
+    override var alertBackgroundColor: UIColor {
+        return .clear
+    }
+
+    func startLoadAD() {
+        updateLoadingState(.loading)
+        startTimer()
+
+        ADManager.shared.requestAd(for: adScene, completionHandler: loadCompletionHandler)
+    }
+
+    private func startTimer() {
+        remindTimeInterval = totalDuration
+        if timer == nil {
+            print("===剩余:\(remindTimeInterval)")
+            timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
+            timer?.schedule(deadline: .now(), repeating: .milliseconds(200))
+            timer?.setEventHandler { [weak self] in
+                guard let self = self else { return }
+                self.remindTimeInterval -= 0.2
+                print("===剩余:\(self.remindTimeInterval)")
+                if self.remindTimeInterval <= 0 {
+                    self.stopTimer()
+                    DispatchQueue.main.async {
+                        self.updateLoadingState(.failure)
+                    }
+                }
+            }
+            timer?.resume()
+        }
+    }
+
+    func stopTimer() {
+        timer?.cancel()
+        timer = nil
+    }
+
+    enum State {
+        case loading
+        case failure
+    }
+
+    func updateLoadingState(_ state: State) {
+        switch state {
+        case .loading:
+            THUD.showLoading("Unlocking...".localized(), message: "It will be unlocked after an ad.".localized())
+        case .failure:
+            THUD.showLoading(UIImage(named: "img-pre-ad-emo-error"), message: "Loading Failed".localized()) { [weak self] isClose in
+                THUD.hide()
+                if isClose {
+                    self?.dismiss(animated: false) {
+                        self?.finishedHandler?(false)
+                    }
+                } else {
+                    self?.startLoadAD()
+                }
+            }
+        }
+    }
+
+    deinit {
+        print("===deinit: \(Self.description())")
+    }
+}

+ 50 - 0
TSLiveWallpaper/Business/AdMob/AppConfig.swift

@@ -0,0 +1,50 @@
+//
+//  AppConfig.swift
+//  PhysicalWallPaper
+//
+//  Created by nkl on 2024/12/16.
+//
+
+import Foundation
+
+class AppConfig {
+    static var appName: String = {
+        let appName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String ?? "Dmanager"
+        return appName
+    }()
+    
+    static var appVersion: String = {
+        return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
+    }()
+    
+    static let appLanguage: String = {
+        let systemLanguages = UserDefaults.standard.value(forKey: "AppleLanguages")
+        var currentLanguage: String?
+        if let arr = systemLanguages as? [String] {
+            currentLanguage = arr.first
+        } else if let str = systemLanguages as? String {
+            currentLanguage = str
+        }
+        return currentLanguage ?? "en"
+    }()
+    
+    static var isChinaRegion: Bool {
+        let localeId = Locale.current.identifier
+        return localeId.contains("_CN")
+    }
+    
+    static var isEURegion : Bool {
+        let EURegions: Set<String> = [
+            "BE", "FR", "DE", "IT", "LU", "NL", "DK", "IE", "GB", "GR",
+            "PT", "ES", "AT", "FI", "SE", "CY", "CZ", "EE", "HU", "LV",
+            "LT", "MT", "PL", "SK", "SI", "RO", "BG",
+        ]
+
+        guard let currentRegionCode = Locale.current.regionCode else {
+            print("无法确定当前地区")
+            return false
+        }
+
+        return EURegions.contains(currentRegionCode)
+    }
+}

+ 62 - 0
TSLiveWallpaper/Business/AdMob/GoogleMobileAdsConsentManager.swift

@@ -0,0 +1,62 @@
+//
+//  GoogleMobileAdsConsentManager.swift
+//  PhysicalWallPaper
+//
+//  Created by nkl on 2024/12/16.
+//
+
+import Foundation
+import GoogleMobileAds
+import UserMessagingPlatform
+
+/// Google Mobile Ads SDK 提供了用户消息平台(Google 的 IAB 认证的同意管理平台)作为在受 GDPR 影响的国家获取用户同意的解决方案之一。这是一个示例,你可以选择另一个同意管理平台来获取同意。
+
+class GoogleMobileAdsConsentManager: NSObject {
+    static let shared = GoogleMobileAdsConsentManager()
+
+    var canRequestAds: Bool {
+        return UMPConsentInformation.sharedInstance.canRequestAds
+    }
+
+    var isPrivacyOptionsRequired: Bool {
+        return UMPConsentInformation.sharedInstance.privacyOptionsRequirementStatus == .required
+    }
+
+    /// 辅助方法,调用 UMP SDK 方法请求同意信息并加载/显示同意表单(如果需要)。
+    func gatherConsent(
+        from consentFormPresentationviewController: UIViewController,
+        consentGatheringComplete: @escaping (Error?) -> Void
+    ) {
+        let parameters = UMPRequestParameters()
+
+        // 用于测试目的,你可以强制设置 UMPDebugGeography 为 EEA 或非 EEA。
+        let debugSettings = UMPDebugSettings()
+        #if DEBUG
+        debugSettings.geography = UMPDebugGeography.EEA
+        #endif
+        parameters.debugSettings = debugSettings
+
+        
+        // 应该在每次应用启动时调用请求同意信息的更新。
+        UMPConsentInformation.sharedInstance.requestConsentInfoUpdate(with: parameters) {
+            requestConsentError in
+            guard requestConsentError == nil else {
+                return consentGatheringComplete(requestConsentError)
+            }
+            UMPConsentForm.loadAndPresentIfRequired(from: consentFormPresentationviewController) {
+                loadAndPresentError in
+
+                // 已获取同意。
+                consentGatheringComplete(loadAndPresentError)
+            }
+        }
+    }
+
+    /// 辅助方法,调用 UMP SDK 方法来呈现隐私选项表单。
+    func presentPrivacyOptionsForm(
+        from viewController: UIViewController, completionHandler: @escaping (Error?) -> Void
+    ) {
+        UMPConsentForm.presentPrivacyOptionsForm(
+            from: viewController, completionHandler: completionHandler)
+    }
+}

+ 18 - 26
TSLiveWallpaper/Business/TSMusic/Detail/Controller/PlayDetailViewController.swift

@@ -5,7 +5,7 @@
 //  Created by nkl on 2024/9/13.
 //
 
-//import ADManager
+// import ADManager
 import Combine
 import Foundation
 import KLTips
@@ -461,32 +461,24 @@ extension PlayDetailViewController {
            let videoId = video.videoId {
             switch sender.downloadState {
             case .idle(isAnimate: false), .idle(isAnimate: true):
-
-//                if PurchaseManager.default.isVip {
-                TSNewDownloadManager.shared.downloadVideo(videoId: videoId, isAudio: false) { [weak self] downloader in
-                    self?.downloader = downloader
-                    self?.updateDownloader(id: videoId)
-                }
-                if let rootVc = PlayerManager.shared.rootVc {
-                    rootVc.presentingViewController?.dismiss(animated: false)
-//                    if !PurchaseManager.default.isVip {
-//                        ADManager.shared.showAd(scene: ADScene.downloadInsert, from: rootVc)
-//                    }
+                if PurchaseManager.default.isVip {
+                    TSNewDownloadManager.shared.downloadVideo(videoId: videoId, isAudio: false) { [weak self] downloader in
+                        self?.downloader = downloader
+                        self?.updateDownloader(id: videoId)
+                    }
+                } else {
+                    let loading = ADLoadingViewController()
+                    loading.adScene = .downloadReward
+                    loading.finishedHandler = { [weak self] finished in
+                        if finished {
+                            TSNewDownloadManager.shared.downloadVideo(videoId: videoId, isAudio: false) { [weak self] downloader in
+                                self?.downloader = downloader
+                                self?.updateDownloader(id: videoId)
+                            }
+                        }
+                    }
+                    PlayerManager.shared.rootVc?.present(loading, animated: false)
                 }
-
-//                } else {
-//                    let loading = ADLoadingViewController()
-//                    loading.adScene = .downloadReward
-//                    loading.finishedHandler = { [weak self] finished in
-//                        if finished {
-//                    TSNewDownloadManager.shared.downloadVideo(videoId: videoId, isAudio: false) { [weak self] downloader in
-//                        self?.downloader = downloader
-//                        self?.updateDownloader(id: videoId)
-//                    }
-//                        }
-//                    }
-//                    PlayerManager.shared.rootVc?.present(loading, animated: false)
-//                }
                 break
             case .pause:
                 TSNewDownloadManager.shared.resumeTask(id: videoId) { [weak self] downloader in

+ 24 - 11
TSLiveWallpaper/Business/TSMusic/Detail/PlayerManager.swift

@@ -5,7 +5,8 @@
 //  Created by nkl on 2024/9/13.
 //
 
-//import ADManager
+import ADManager
+// import ADManager
 import Foundation
 import KLTips
 import TSVideoKit
@@ -35,6 +36,8 @@ class PlayerManager {
 
     var selectedVideos: [TSVideo] = []
     var removePlaylist: TSPlayList?
+    /// 广告池
+    var adPool: [String] = []
 
     var currentLoopMode: LoopMode {
         player?.playControl.viewModel.loopMode ?? .cyclic
@@ -120,17 +123,20 @@ class PlayerManager {
     }
 
     func playVideo(video: TSVideo, list: [TSVideo], scene: TSPlayScene, onceAdKey: String) {
-//        if let vc = rootVc, !PurchaseManager.default.isVip {
-//            ADManager.shared.showAd(scene: ADScene.playInsert, from: vc) { state in
-//                if state == .finished || state == .fail {
-//                    self.showPlayerViewController()
-//                    self.player?.playControl.playVideo(video: video, list: list, scene: scene)
-//                }
-//            }
-//        } else {
+        if needShowOnceAd(key: onceAdKey),
+           let vc = rootVc,
+           !PurchaseManager.default.isVip {
+            ADManager.shared.showAd(scene: ADScene.playInsert, from: vc) { state in
+                if state == .finished || state == .fail {
+                    self.adPool.append(onceAdKey)
+                    self.showPlayerViewController()
+                    self.player?.playControl.playVideo(video: video, list: list, scene: scene)
+                }
+            }
+        } else {
             showPlayerViewController()
             player?.playControl.playVideo(video: video, list: list, scene: scene)
-//        }
+        }
     }
 
     func playVideo(onlineVideo: VideoOnlineModel, recommendDatas: [VideoOnlineModel], scene: TSPlayScene) {
@@ -146,6 +152,13 @@ class PlayerManager {
         rootVc?.hideMultiSelectView()
     }
 
+    func needShowOnceAd(key: String) -> Bool {
+        if key.isEmpty {
+            return false
+        }
+        return !adPool.contains(key)
+    }
+
     @objc func addVideosToPlaylist() {
         if selectedVideos.isEmpty {
             return
@@ -188,7 +201,7 @@ class PlayerManager {
             let needRestart = videoIds.contains { id in
                 id == TSVideoOperator.shared.playerViewModel.currentVideo?.videoId
             }
-            
+
             TSVideoOperator.shared.dataManager.deleteVideos(videos: self.selectedVideos) { _ in
 
                 PlayerManager.shared.hideMutiOperateView()

+ 0 - 1
TSLiveWallpaper/Business/TSMusic/Detail/View/PlayDetailTopView.swift

@@ -60,7 +60,6 @@ class PlayDetailTopView: UIView {
         
         addSubview(backButton)
         addSubview(hStack)
-        timeButton.isHidden = true
         moreButton.isHidden = true
         hStack.addArrangedSubview(timeButton)
         hStack.addArrangedSubview(moreButton)

+ 57 - 0
TSLiveWallpaper/Business/TSMusic/Helper/GuideBubbleView.swift

@@ -0,0 +1,57 @@
+//
+//  GuideBubbleView.swift
+//  PhysicalWallPaper
+//
+//  Created by nkl on 2024/12/17.
+//
+
+import Foundation
+import UIKit
+
+class GuideBubbleView: UIControl {
+    
+    lazy var titleLabel: UILabel = {
+        let lab = UILabel()
+        lab.textColor = .white
+        lab.font = .systemFont(ofSize: 16)
+        lab.text = "Find your favorite songs".localized()
+        return lab
+    }()
+    
+    lazy var bgView: UIImageView = .init(image: UIImage.init(named: "ic_bubble_bg"))
+    
+    lazy var tapArea : UIControl = UIControl()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        addChildren()
+        makeConstraints()
+    }
+    
+    func addChildren(){
+        addSubview(bgView)
+        bgView.addSubview(titleLabel)
+        addSubview(tapArea)
+    }
+    
+    func makeConstraints(){
+        bgView.snp.makeConstraints { make in
+            make.centerX.equalToSuperview()
+            make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(84)
+        }
+        
+        titleLabel.snp.makeConstraints { make in
+            make.horizontalEdges.equalToSuperview().inset(12)
+            make.centerY.equalToSuperview()
+        }
+        
+        tapArea.snp.makeConstraints { make in
+            make.top.leading.trailing.equalToSuperview()
+            make.bottom.equalTo(bgView)
+        }
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+}

+ 55 - 25
TSLiveWallpaper/Business/TSMusic/List/Controller/MusicContainerViewController.swift

@@ -8,6 +8,7 @@
 import BetterSegmentedControl
 import Foundation
 import TSVideoKit
+import ADManager
 
 class MusicContainerViewController: LWBGViewController {
     lazy var navBar: LWRightNavigationBar = LWRightNavigationBar()
@@ -52,6 +53,14 @@ class MusicContainerViewController: LWBGViewController {
     var childVcs: [LWBaseViewController] {
         [songlistVc, playlistVc]
     }
+    
+    
+    lazy var guideBubble: GuideBubbleView = {
+        let guide = GuideBubbleView()
+        guide.addTarget(self, action: #selector(dismissGuideBubble), for: .touchUpInside)
+        guide.tapArea.addTarget(self, action: #selector(bubbleClick), for: .touchUpInside)
+        return guide
+    }()
 
     // 添加 PageViewController
     private lazy var pageViewController: UIPageViewController = {
@@ -78,6 +87,18 @@ class MusicContainerViewController: LWBGViewController {
         super.viewWillAppear(animated)
         PlayerManager.shared.rootVc?.moveUpMiniBar()
     }
+    
+    override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        print("TSConfiguration.isYs ===", TSConfiguration.isYs)
+        if UserDefaults.standard.string(forKey: "GuideKey") == nil, TSConfiguration.isYs {
+            view.addSubview(guideBubble)
+            view.bringSubviewToFront(guideBubble)
+            guideBubble.snp.makeConstraints { make in
+                make.edges.equalToSuperview()
+            }
+        }
+    }
 
     func addTargets() {
         searchBar.addTarget(self, action: #selector(showSearchViewController), for: .touchUpInside)
@@ -98,7 +119,7 @@ class MusicContainerViewController: LWBGViewController {
         // 切换到目标视图控制器
         let targetViewController = childVcs[index]
         pageViewController.setViewControllers([targetViewController], direction: direction, animated: true, completion: nil)
-        
+
         songlistVc.filterVc.viewModel?.doneMutiSelect()
     }
 
@@ -124,34 +145,43 @@ class MusicContainerViewController: LWBGViewController {
             ImportFilesManager.shared.openFileDocument(parent: self, completion: nil)
         }
     }
+    
+    @objc func bubbleClick() {
+        showSearchViewController()
+        dismissGuideBubble()
+    }
+
+    @objc func dismissGuideBubble() {
+        guideBubble.isHidden = true
+        UserDefaults.standard.setValue("Guide", forKey: "GuideKey")
+    }
 
     @objc func showSearchViewController() {
-//        if PurchaseManager.default.isVip {
-        if TSConfiguration.isYs {
-            let vc = SearchOnlineViewController()
-            vc.hidesBottomBarWhenPushed = true
-            navigationController?.pushViewController(vc, animated: true)
+        if PurchaseManager.default.isVip {
+            if TSConfiguration.isYs {
+                let vc = SearchOnlineViewController()
+                vc.hidesBottomBarWhenPushed = true
+                navigationController?.pushViewController(vc, animated: true)
+            } else {
+                let vc = LocalSearchViewController()
+                vc.hidesBottomBarWhenPushed = true
+                navigationController?.pushViewController(vc, animated: true)
+            }
         } else {
-            let vc = LocalSearchViewController()
-            vc.hidesBottomBarWhenPushed = true
-            navigationController?.pushViewController(vc, animated: true)
+            ADManager.shared.showAd(scene: ADScene.searchInsert, from: self) { state in
+                if state == .finished || state == .fail {
+                    if TSConfiguration.isYs {
+                        let vc = SearchOnlineViewController()
+                        vc.hidesBottomBarWhenPushed = true
+                        self.navigationController?.pushViewController(vc, animated: true)
+                    } else {
+                        let vc = LocalSearchViewController()
+                        vc.hidesBottomBarWhenPushed = true
+                        self.navigationController?.pushViewController(vc, animated: true)
+                    }
+                }
+            }
         }
-//        } else {
-//            ADManager.shared.showAd(scene: ADScene.searchInsert, from: self) { state in
-//                if state == .finished || state == .fail {
-//                    if TSConfiguration.isYs {
-//                        ADManager.shared.prepareAd(scenes: [ADScene.downloadInsert])
-//                        let vc = SearchOnlineViewController()
-//                        vc.hidesBottomBarWhenPushed = true
-//                        self.navigationController?.pushViewController(vc, animated: true)
-//                    } else {
-//                        let vc = LocalSearchViewController()
-//                        vc.hidesBottomBarWhenPushed = true
-//                        self.navigationController?.pushViewController(vc, animated: true)
-//                    }
-//                }
-//            }
-//        }
     }
 
     override func addChildren() {

+ 1 - 0
TSLiveWallpaper/Business/TSMusic/List/Controller/SongListViewController+Target.swift

@@ -8,6 +8,7 @@
 //import ADManager
 import Foundation
 import TSVideoKit
+import ADManager
 
 extension SongListViewController {
    

+ 2 - 40
TSLiveWallpaper/Business/TSMusic/List/Controller/SongListViewController.swift

@@ -64,12 +64,6 @@ class SongListViewController: LWBGViewController {
         return tabView
     }()
 
-    //    lazy var guideBubble: GuideBubbleView = {
-    //        let guide = GuideBubbleView()
-    //        guide.addTarget(self, action: #selector(dismissGuideBubble), for: .touchUpInside)
-    //        guide.tapArea.addTarget(self, action: #selector(bubbleClick), for: .touchUpInside)
-    //        return guide
-    //    }()
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -103,16 +97,6 @@ class SongListViewController: LWBGViewController {
         NotificationCenter.default.addObserver(self, selector: #selector(reloadDatas), name: .K_RefreshNotifaction, object: nil)
     }
 
-    //    @objc func bubbleClick() {
-    //        showSearchViewController()
-    //        dismissGuideBubble()
-    //    }
-    //
-    //    @objc func dismissGuideBubble() {
-    //        guideBubble.isHidden = true
-    //        UserDefaults.standard.setValue("Guide", forKey: "GuideKey")
-    //    }
-
     @objc func showMenuAlert() {
         if !view.contains(menuView) {
             view.addSubview(menuView)
@@ -145,18 +129,6 @@ class SongListViewController: LWBGViewController {
         view.addSubview(emptyView)
     }
 
-    @objc func showFavourtie() {
-//        ADManager.shared.showAd(scene: ADScene.playlist, from: self) { state in
-//            if state == .finished || state == .fail {
-        let vModel: PlayListDetaiViewModel = .init()
-        vModel.listType = .favourite
-        let vc = PlaylistDetailViewController(viewModel: vModel)
-        vc.hidesBottomBarWhenPushed = true
-        navigationController?.pushViewController(vc, animated: true)
-//            }
-//        }
-    }
-
     @objc func showPlaylist() {
         let vc = PlaylistViewController()
         navigationController?.pushViewController(vc, animated: true)
@@ -194,18 +166,6 @@ class SongListViewController: LWBGViewController {
         }
     }
 
-    override func viewDidLayoutSubviews() {
-        super.viewDidLayoutSubviews()
-//        print("TSConfiguration.isYs ===" , TSConfiguration.isYs)
-//        if UserDefaults.standard.string(forKey: "GuideKey") == nil, TSConfiguration.isYs {
-//            view.addSubview(guideBubble)
-//            view.bringSubviewToFront(guideBubble)
-//            guideBubble.snp.makeConstraints { make in
-//                make.edges.equalToSuperview()
-//            }
-//        }
-    }
-
     @objc func showSortOperateView() {
         menuView.removeFromSuperview()
         let vc = SortMenuViewController(type: viewModel.sortType)
@@ -216,6 +176,8 @@ class SongListViewController: LWBGViewController {
         }
         present(vc, animated: true)
     }
+
+
 }
 
 extension SongListViewController: UITableViewDelegate {

+ 7 - 3
TSLiveWallpaper/Business/TSMusic/PlayList/Controller/PlaylistDetailViewController.swift

@@ -185,7 +185,7 @@ class PlaylistDetailViewController: LWBGViewController, UITableViewDelegate {
         }
         if let firstVideo = mViewModel.videos.first {
             PlayerManager.shared.player?.playControl.viewModel.loopMode = .cyclic
-            PlayerManager.shared.playVideo(video: firstVideo, list: mViewModel.videos, scene: .local, onceAdKey: "")
+            PlayerManager.shared.playVideo(video: firstVideo, list: mViewModel.videos, scene: .local, onceAdKey: "PlaylistDetail_PlayAll")
             listView.reloadData()
         }
     }
@@ -196,7 +196,7 @@ class PlaylistDetailViewController: LWBGViewController, UITableViewDelegate {
         }
         if let firstVideo = mViewModel.videos.randomElement() {
             PlayerManager.shared.player?.playControl.viewModel.loopMode = .random
-            PlayerManager.shared.playVideo(video: firstVideo, list: mViewModel.videos, scene: .local, onceAdKey: "")
+            PlayerManager.shared.playVideo(video: firstVideo, list: mViewModel.videos, scene: .local, onceAdKey: "PlaylistDetail_Shuffle")
             listView.reloadData()
         }
     }
@@ -284,7 +284,11 @@ class PlaylistDetailViewController: LWBGViewController, UITableViewDelegate {
                 filterVc.viewModel?.selectedVideos = vM.selectedVideos
                 listView.reloadData()
             } else {
-                PlayerManager.shared.playVideo(video: model, list: vM.videos, scene: .local, onceAdKey: viewModel?.playlist?.id?.uuidString ?? "DefaultPlaylist")
+                var key = viewModel?.playlist?.id?.uuidString ?? "DefaultPlaylist"
+                if viewModel?.listType == .favourite {
+                    key = "FavouritePlaylist"
+                }
+                PlayerManager.shared.playVideo(video: model, list: vM.videos, scene: .local, onceAdKey: key)
             }
         }
     }

+ 12 - 4
TSLiveWallpaper/Business/TSMusic/SearchResult/SearchResultViewController.swift

@@ -238,10 +238,18 @@ extension SearchResultViewController: UITableViewDelegate, UIScrollViewDelegate,
             }
             switch state {
             case .idle(isAnimate: false), .idle(isAnimate: true):
-                viewModel.startDownload()
-//                if !PurchaseManager.default.isVip {
-//                    ADManager.shared.showAd(scene: ADScene.downloadInsert, from: self)
-//                }
+                if PurchaseManager.default.isVip {
+                    viewModel.startDownload()
+                } else {
+                    let loading = ADLoadingViewController()
+                    loading.adScene = .downloadReward
+                    loading.finishedHandler = { finished in
+                        if finished {
+                            viewModel.startDownload()
+                        }
+                    }
+                    present(loading, animated: false)
+                }
                 break
             case .pause:
                 viewModel.resumeDownload()

+ 6 - 0
TSLiveWallpaper/Common/Ex/UIDevice+Extension.swift

@@ -115,6 +115,12 @@ extension UIApplication {
     static var topViewController: UIViewController? {
         return rootViewController?.topVc
     }
+    
+    static var defaultViewController: UIViewController? {
+        let window = (UIApplication.shared.delegate as? AppDelegate)?.window
+        return window?.rootViewController
+    }
+
 }
 
 extension UIViewController {

+ 54 - 7
TSLiveWallpaper/LaunchVC/TSLaunchVC.swift

@@ -5,9 +5,11 @@
 //  Created by 100Years on 2024/12/20.
 //
 
+import ADManager
 import Alamofire
-import UIKit
 import GoogleMobileAds
+import TSVideoKit
+import UIKit
 
 class TSLaunchVC: UIViewController {
     var dismissHandler: (() -> Void)?
@@ -38,23 +40,45 @@ class TSLaunchVC: UIViewController {
         TSNetworkShard.startListenNetStatus { status, manager in
             switch status {
             case .reachable:
-                PurchaseManager.default.requestProducts()
-                AppDelegate.requestAdTrack()
+                /// 一旦有网直接重新拉取配置
+                TSVideoOperator.shared.fetchConfiguration()
+                if !PurchaseManager.default.isVip {
+                    ADManager.shared.showLaunchAd(scene: ADScene.launch, in: self)
+                }
+                self.initUmpProtocal()
                 manager?.stopListening()
-                self.initAdMob()
             default:
-                AppDelegate.requestAdTrack()
-                PurchaseManager.default.requestProducts()
-                self.initAdMob()
                 break
             }
         }
     }
 
+    /// 欧盟
+    func initUmpProtocal() {
+        if AppConfig.isEURegion {
+            /// 请求弹窗
+            pauseTimer()
+            GoogleMobileAdsConsentManager.shared.gatherConsent(from: self) { _ in
+                self.continueTimer()
+                if GoogleMobileAdsConsentManager.shared.canRequestAds {
+                    self.initAdMob()
+                }
+            }
+            /// 这步优先执行,然后等回调回来是做更新的
+            initAdMob()
+        } else {
+            initAdMob()
+        }
+    }
+
     func initAdMob() {
         GADMobileAds.sharedInstance().start { status in
             print("启动状态 == status === \(status.adapterStatusesByClassName)")
         }
+        GADMobileAds.sharedInstance().audioVideoManager.audioSessionIsApplicationManaged = true
+        ADManager.shared.isForbittenRegion = false
+        ADManager.shared.isVipUser = PurchaseManager.default.isVip
+        ADManager.shared.prepareAd(scenes: ADScene.prepareScenes)
     }
 
     func enterApp() {
@@ -70,6 +94,7 @@ class TSLaunchVC: UIViewController {
             self.dismissHandler?()
         }
     }
+    
 
     private func startTimer() {
         if timer == nil {
@@ -118,3 +143,25 @@ class TSLaunchVC: UIViewController {
     }
 }
 
+extension TSLaunchVC: ADManagerLaunchDelegate {
+    func adWillPresent(_ manager: ADManager) {
+        // 暂停计时
+        pauseTimer()
+    }
+
+    func adDidDismiss(_ manager: ADManager) {
+        // 关闭广告,继续计时后关闭开屏页
+        continueTimer()
+    }
+
+    func adDidFailToPresent(_ manager: ADManager) {
+    }
+
+    private func pauseTimer() {
+        timer?.suspend()
+    }
+
+    private func continueTimer() {
+        timer?.resume()
+    }
+}