// Author: fengshengxiong // Date: 2024/6/25 // Description: 插页广告 import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:get/get.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:tone_snap/data/enum/ad_scenes.dart'; import 'package:tone_snap/data/models/ad_config_model.dart'; import 'package:tone_snap/data/storage/music_box.dart'; import 'package:tone_snap/firebase/firebase_analytics_manager.dart'; import 'package:tone_snap/modules/launch/launch_controller.dart'; import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart'; import 'package:tone_snap/utils/log_util.dart'; import 'package:tone_snap/utils/obj_util.dart'; class InterstitialAdManager { InterstitialAdManager._(); static final InterstitialAdManager _instance = InterstitialAdManager._(); factory InterstitialAdManager() => _instance; /// 广告对象Map final Map _interstitialAdMap = {}; /// 加载和显示广告之间允许的最长持续时间 final Duration _maxCacheDuration = const Duration(minutes: 50); /// 跟踪加载时间,这样我们就不会显示过期的广告 final Map _appOpenLoadTimeMap = {}; /// 记录关闭时的时间,用于下一次展示时计算时间差 DateTime? _closeDateTime; /// 是否显示 bool isShowingAd = false; /// 广告显示前的,音乐是否在播放 bool showAdFrontIsPlaying = false; /// 加载所有插页广告位 void loadAdAll() { AdConfigModel adConfigModel = MusicBox().getAdConfig(); _loadSingleAd(adConfigModel.coldLoading, AdScenes.coldLoading.name); _loadSingleAd(adConfigModel.hotLoading, AdScenes.hotLoading.name); _loadSingleAd(adConfigModel.play, AdScenes.play.name); _loadSingleAd(adConfigModel.download, AdScenes.download.name); _loadSingleAd(adConfigModel.list, AdScenes.list.name); _loadSingleAd(adConfigModel.playCut, AdScenes.playCut.name); _loadSingleAd(adConfigModel.search, AdScenes.search.name); } /// 加载单个广告位 void _loadSingleAd(List? adList, String adScenes) { if (adList != null && adList.isNotEmpty) { var adModel = adList.reduce((value, element) => element.level! < value.level! ? element : value); loadAd(adModel.identifier, adScenes); } } /// 加载广告 void loadAd(String? adUnitId, String adScenes) async { final List connectivityResult = await (Connectivity().checkConnectivity()); if (connectivityResult.contains(ConnectivityResult.none)) { LogUtil.d('当前无网络,不加载广告'); return; } if (ObjUtil.isEmpty(adUnitId)) { return; } InterstitialAd.load( adUnitId: adUnitId!, request: const AdRequest(), adLoadCallback: InterstitialAdLoadCallback( onAdLoaded: (ad) { LogUtil.d('$adScenes:${ad.adUnitId}插页广告加载完成'); _interstitialAdMap[adScenes] = ad; _appOpenLoadTimeMap[adScenes] = DateTime.now(); if (adScenes == AdScenes.coldLoading.name) { if (Get.isRegistered()) { LaunchController.to.editChangeValue(); } } }, onAdFailedToLoad: (LoadAdError error) { LogUtil.d('$adScenes:$adUnitId插页广告加载失败: $error'); _loadFailHandle(adUnitId, adScenes); FirebaseAnalyticsManager.logPlayAdsLoadFailure(error.message); }, ), ); } /// 加载失败处理 void _loadFailHandle(String adUnitId, String adScenes) { int interval = 0; if (adScenes != AdScenes.coldLoading.name && adScenes != AdScenes.hotLoading.name) { interval = 15; } Future.delayed(Duration(seconds: interval), () { AdConfigModel adConfigModel = MusicBox().getAdConfig(); var adList = _getAdList(adScenes); if (adList != null) { var currentItem = adList.firstWhereOrNull((e) => e.identifier == adUnitId); if (currentItem != null) { var nextItem = adList.firstWhereOrNull((e) => e.level == currentItem.level! + 1); if (nextItem != null) { // 当前id加载失败,加载下一个id loadAd(nextItem.identifier, adScenes); } else { // 对应场景id都加载失败,加载全局id _loadSingleAd(adConfigModel.backup, adScenes); } } else { var currentItem = adConfigModel.backup?.firstWhereOrNull((e) => e.identifier == adUnitId); if (currentItem != null) { var nextItem = adConfigModel.backup?.firstWhereOrNull((e) => e.level == currentItem.level! + 1); if (nextItem != null) { loadAd(nextItem.identifier, adScenes); } else { if (adList.isNotEmpty) { _loadSingleAd(adList, adScenes); } } } } } }); } List? _getAdList(String adScenes) { AdConfigModel adConfigModel = MusicBox().getAdConfig(); if (adScenes == AdScenes.coldLoading.name) { return adConfigModel.coldLoading; } else if (adScenes == AdScenes.hotLoading.name) { return adConfigModel.hotLoading; } else if (adScenes == AdScenes.play.name) { return adConfigModel.play; } else if (adScenes == AdScenes.download.name) { return adConfigModel.download; } else if (adScenes == AdScenes.list.name) { return adConfigModel.list; } else if (adScenes == AdScenes.playCut.name) { return adConfigModel.playCut; } else if (adScenes == AdScenes.search.name) { return adConfigModel.search; } else if (adScenes == AdScenes.backup.name) { return adConfigModel.backup; } return null; } /// 是否可以播放广告 bool isAdAvailable(String adScenes) { return _interstitialAdMap[adScenes] != null; } /// 显示广告(如果存在且尚未显示) /// 如果先前缓存的广告已过期,则只加载并缓存新广告 void showAdIfAvailable(String adScenes, {Function()? onTap}) { if (!isAdAvailable(adScenes)) { LogUtil.d('尝试在可用之前显示广告'); if(onTap != null) onTap(); return; } if (isShowingAd) { LogUtil.d('尝试在已显示广告的情况下显示广告'); if(onTap != null) onTap(); return; } if (DateTime.now().subtract(_maxCacheDuration).isAfter(_appOpenLoadTimeMap[adScenes]!)) { LogUtil.d('超过了最大缓存持续时间。正在加载另一个广告'); _interstitialAdMap[adScenes]!.dispose(); loadAd(_interstitialAdMap[adScenes]?.adUnitId, adScenes); _interstitialAdMap[adScenes] = null; _interstitialAdMap.remove(adScenes); if(onTap != null) onTap(); return; } if (_closeDateTime != null) { // 计算时间差 Duration timeDifference = DateTime.now().difference(_closeDateTime!); int seconds; if (adScenes == AdScenes.hotLoading.name) { seconds = MusicBox().getOpenAppEventDuration(); } else { seconds = MusicBox().getInterstitialEventDuration(); } if (timeDifference < Duration(seconds: seconds)) { LogUtil.d('距离上次广告展示未达到插页广告事件间隔时长'); if(onTap != null) onTap(); return; } } FirebaseAnalyticsManager.logPlayAdsChanceAction(); // 设置 fullScreenContentCallback 并显示广告 _interstitialAdMap[adScenes]!.fullScreenContentCallback = FullScreenContentCallback( // 暂停应用程序中的活动或记录广告展示的时间 onAdShowedFullScreenContent: (ad) { LogUtil.d('$ad onAdShowedFullScreenContent'); isShowingAd = true; showAdFrontIsPlaying = MusicPlayerController.to.isPlaying.value; if (showAdFrontIsPlaying) { MusicPlayerController.to.pause(); } }, // 更适合用于跟踪广告展示的次数和效果,以及执行与广告展示相关的操作 onAdImpression: (ad) { LogUtil.d('$ad onAdImpression'); FirebaseAnalyticsManager.logPlayAdsShowSuccess(ad); }, onAdFailedToShowFullScreenContent: (ad, error) { LogUtil.d('$ad onAdFailedToShowFullScreenContent: $error'); isShowingAd = false; ad.dispose(); loadAd(_interstitialAdMap[adScenes]?.adUnitId, adScenes); _interstitialAdMap[adScenes] = null; _interstitialAdMap.remove(adScenes); FirebaseAnalyticsManager.logPlayAdsLoadFailure(error.message); if(onTap != null) onTap(); }, onAdDismissedFullScreenContent: (ad) { LogUtil.d('$ad onAdDismissedFullScreenContent'); isShowingAd = false; if (showAdFrontIsPlaying) { MusicPlayerController.to.play(); } _closeDateTime = DateTime.now(); ad.dispose(); int interval = 0; if (adScenes != AdScenes.coldLoading.name && adScenes != AdScenes.hotLoading.name) { interval = 15; } Future.delayed(Duration(seconds: interval), () { loadAd(_interstitialAdMap[adScenes]?.adUnitId, adScenes); _interstitialAdMap[adScenes] = null; _interstitialAdMap.remove(adScenes); }); if(onTap != null) onTap(); }, onAdClicked: (ad) { LogUtil.d('$ad onAdClicked'); }, ); _interstitialAdMap[adScenes]!.show(); } }