import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:secmtp_sdk/at_init.dart'; import 'package:secmtp_sdk/at_interstitial.dart'; import 'package:secmtp_sdk/at_interstitial_response.dart'; import 'package:secmtp_sdk/at_listener.dart'; // --- 1. 使用枚举定义广告位,更安全、清晰 --- enum AdPlacement { interstitial1, interstitial2, interstitial3 } /// 广告管理器 class AppAdsTools { // --- 单例实现 --- static final AppAdsTools _instance = AppAdsTools._internal(); AppAdsTools._internal(); static AppAdsTools get instance => _instance; // --- 广告 ID 配置 --- static const String _appId = 'h69718907a25ad'; static const String _appKey = 'a5a35545d00d0e9c5471c354dd24edd03'; static final Map _adUnitIds = { AdPlacement.interstitial1: "n6971892e19211", AdPlacement.interstitial2: "n6971892f2da1f", AdPlacement.interstitial3: "n697189366ab4f", }; // --- 内部状态变量 --- bool _isSdkInitialized = false; Completer? _initCompleter; final Set _loadingAds = {}; bool _isAdShowing = false; final Map _adClosedCallbacks = {}; // --- 开屏广告专用回调和状态 --- Function(AdPlacement adPlacement)? _onSplashAdReadyCallback; Function()? _onSplashAdAllFailedCallback; Set _splashAdsToLoad = {}; int _splashAdsFailedCount = 0; bool _isHandlingSplashAd = false; // 广告冷却逻辑 final bool _useGlobalCooldown = true; static const String _globalCooldownKey = 'any_interstitial_ad_was_shown'; final Map _lastShownTimestamps = {}; final int _adCooldownSeconds = 15; /// 初始化TopOn SDK和广告监听 Future init() { if (_initCompleter != null) { return _initCompleter!.future; } _initCompleter = Completer(); debugPrint("【TopOnAdManager】SDK 开始初始化..."); ATInitManger.setLogEnabled(logEnabled: false); ATInitManger.initAnyThinkSDK(appidStr: _appId, appidkeyStr: _appKey).then(( _, ) { debugPrint("【TopOnAdManager】SDK 初始化成功。"); _isSdkInitialized = true; _setupEventListeners(); _initCompleter!.complete(); }); return _initCompleter!.future; } /// 统一配置插屏广告监听器 void _setupEventListeners() { ATListenerManager.interstitialEventHandler.listen((event) { final placement = _getPlacementForAdUnitId(event.placementID); if (placement == null) return; switch (event.interstatus) { case InterstitialStatus.interstitialAdDidFinishLoading: _loadingAds.remove(event.placementID); debugPrint("【TopOnAdManager】✅ 广告加载成功: ${placement.name}"); if (_isHandlingSplashAd && _splashAdsToLoad.contains(placement) && _onSplashAdReadyCallback != null) { _onSplashAdReadyCallback!(placement); _clearSplashCallbacks(); } break; case InterstitialStatus.interstitialAdFailToLoadAD: _loadingAds.remove(event.placementID); debugPrint( "【TopOnAdManager】❌ 广告加载失败: ${placement.name}, 原因: ${event.requestMessage}", ); if (_isHandlingSplashAd && _splashAdsToLoad.contains(placement) && _onSplashAdAllFailedCallback != null) { _splashAdsFailedCount++; if (_splashAdsFailedCount >= _splashAdsToLoad.length) { _onSplashAdAllFailedCallback!(); _clearSplashCallbacks(); } } break; case InterstitialStatus.interstitialAdDidClose: case InterstitialStatus.interstitialFailedToShow: debugPrint("【TopOnAdManager】广告已关闭或展示失败: ${placement.name}"); _isAdShowing = false; _adClosedCallbacks[event.placementID]?.call(); _adClosedCallbacks.remove(event.placementID); preloadAd(placement); break; case InterstitialStatus.interstitialDidShowSucceed: _isAdShowing = true; debugPrint("【TopOnAdManager】广告开始显示: ${placement.name}"); final keyToUpdate = _useGlobalCooldown ? _globalCooldownKey : event.placementID; _lastShownTimestamps[keyToUpdate] = DateTime.now(); debugPrint("【TopOnAdManager】冷却计时器已更新 (Key: $keyToUpdate)"); break; default: break; } }); } /// 加载开屏广告的专用方法 void loadInitialSplashAd({ required Function(AdPlacement adPlacement) onAdReady, required Function() onAllAdsFailed, }) { if (!_isSdkInitialized) { debugPrint("【TopOnAdManager】SDK 未初始化,无法加载开屏广告。"); onAllAdsFailed(); return; } _onSplashAdReadyCallback = onAdReady; _onSplashAdAllFailedCallback = onAllAdsFailed; _splashAdsFailedCount = 0; _isHandlingSplashAd = true; _splashAdsToLoad = { AdPlacement.interstitial1, AdPlacement.interstitial2, AdPlacement.interstitial3, }; debugPrint("【TopOnAdManager】开始并行加载初始开屏广告..."); for (final placement in _splashAdsToLoad) { _loadInterstitialAd(placement); } } /// 清除开屏广告回调的私有方法 void _clearSplashCallbacks() { _onSplashAdReadyCallback = null; _onSplashAdAllFailedCallback = null; _splashAdsToLoad.clear(); _splashAdsFailedCount = 0; _isHandlingSplashAd = false; } /// 公共方法:展示插屏广告 Future showAd(AdPlacement placement, {VoidCallback? onAdClosed}) async { if (_isAdShowing) { debugPrint("【TopOnAdManager】已有广告正在显示,无法展示新广告: ${placement.name}"); return false; } if (!_isSdkInitialized) { debugPrint("【TopOnAdManager】SDK 未初始化,无法展示广告。"); return false; } final adUnitId = _adUnitIds[placement]!; final keyToCheck = _useGlobalCooldown ? _globalCooldownKey : adUnitId; final lastShown = _lastShownTimestamps[keyToCheck]; if (lastShown != null && DateTime.now().difference(lastShown) < Duration(seconds: _adCooldownSeconds)) { debugPrint("【TopOnAdManager】❌ 广告冷却中 (${_adCooldownSeconds}s)。放弃展示。"); return false; } bool isReady = await ATInterstitialManager.hasInterstitialAdReady( placementID: adUnitId, ); debugPrint("【TopOnAdManager】检查广告就绪状态 ${placement.name}: $isReady"); if (isReady) { if (onAdClosed != null) { _adClosedCallbacks[adUnitId] = onAdClosed; } ATInterstitialManager.showSceneInterstitialAd( placementID: adUnitId, sceneID: _getSceneIdForPlacement(placement), ); return true; } else { debugPrint("【TopOnAdManager】广告 ${placement.name} 尚未准备好展示。"); preloadAd(placement); // 第一次调用show时,如果没准备好,会自动触发加载 return false; } } /// 预加载广告 void preloadAd(AdPlacement placement) { _loadInterstitialAd(placement); } /// 内部方法:加载单个插屏广告 void _loadInterstitialAd(AdPlacement placement) { final adUnitId = _adUnitIds[placement]!; if (!_isSdkInitialized) { debugPrint("【TopOnAdManager】SDK 未初始化,无法加载广告: ${placement.name}"); return; } if (_loadingAds.contains(adUnitId)) { debugPrint("【TopOnAdManager】广告 ${placement.name} 正在加载中,跳过本次请求"); return; } _loadingAds.add(adUnitId); debugPrint("【TopOnAdManager】请求加载插屏广告: ${placement.name}"); ATInterstitialManager.loadInterstitialAd( placementID: adUnitId, extraMap: const {}, ); } // --- 辅助方法 --- AdPlacement? _getPlacementForAdUnitId(String adUnitId) { for (var entry in _adUnitIds.entries) { if (entry.value == adUnitId) return entry.key; } return null; } String _getSceneIdForPlacement(AdPlacement placement) { switch (placement) { case AdPlacement.interstitial1: return 'interstitial1'; case AdPlacement.interstitial2: return 'interstitial2'; case AdPlacement.interstitial3: return 'interstitial3'; } } }