import 'dart:io' show Platform; import 'package:flutter/services.dart'; import 'package:unity_levelplay_mediation/unity_levelplay_mediation.dart'; const String TAG = 'InterstitialAdManager'; const String APP_KEY_IOS = '24e816d5d'; enum InterstitialAdType { first, second, third; String get displayName { switch (this) { case InterstitialAdType.first: return 'First Interstitial'; case InterstitialAdType.second: return 'Second Interstitial'; case InterstitialAdType.third: return 'Third Interstitial'; } } /// 获取广告的 adUnitId(广告位 ID) String get adUnitId { switch (this) { case InterstitialAdType.first: return 'ipgwvc8d6vg3bbf6'; case InterstitialAdType.second: return '5b53pus0enyp87xw'; case InterstitialAdType.third: return 'mnhvu9lj9vrdb4fz'; } } /// 获取广告的 placement 名称(返回广告 ID) String get placementName => adUnitId; } /// Interstitial 广告管理器(单例) class InterstitialAdManager with LevelPlayInitListener { // 单例实例 static final InterstitialAdManager _instance = InterstitialAdManager._internal(); // 工厂构造函数返回单例 factory InterstitialAdManager() { return _instance; } // 私有构造函数 InterstitialAdManager._internal(); // 三个 Interstitial 广告实例 final Map _interstitialAds = {}; // adId 到 adType 的映射,用于在监听器中识别广告 final Map _adIdToTypeMap = {}; // 初始化状态 bool _isInitialized = false; bool _isInitializing = false; // 当前正在展示的广告(避免同时展示多个) InterstitialAdType? _currentShowingAd; // 广告完成回调映射 final Map _adCompletedCallbacks = {}; // --- 开屏广告专用回调和状态 --- Function(InterstitialAdType adType)? _onSplashAdReadyCallback; Function()? _onSplashAdAllFailedCallback; Set _splashAdsToLoad = {}; int _splashAdsFailedCount = 0; bool _isHandlingSplashAd = false; /// 获取单例实例 static InterstitialAdManager get instance => _instance; /// 获取广告实例 LevelPlayInterstitialAd? getAd(InterstitialAdType adType) { return _interstitialAds[adType]; } /// 初始化 LevelPlay SDK 和广告实例 Future initialize() async { if (_isInitialized || _isInitializing) { logMethodName( 'Initialize', 'Info', 'Already initialized or initializing', ); return; } _isInitializing = true; try { // 检查平台,只支持 iOS if (!Platform.isIOS) { logMethodName( 'Platform Check', 'Error', 'This app only supports iOS platform', ); _isInitializing = false; return; } // 初始化三个 Interstitial 广告实例,每个使用不同的 adUnitId for (var adType in InterstitialAdType.values) { final ad = LevelPlayInterstitialAd(adUnitId: adType.adUnitId); _interstitialAds[adType] = ad; ad.setListener(_InterstitialAdListenerWrapper(adType, this)); } // 启用调试模式 // await enableDebug(); // 初始化 LevelPlay SDK final initRequest = LevelPlayInitRequest.builder(APP_KEY_IOS).build(); await LevelPlay.init(initRequest: initRequest, initListener: this); } on PlatformException catch (e) { _isInitializing = false; logMethodName('Initialize', 'PlatformException', e); } catch (e) { _isInitializing = false; logMethodName('Initialize', 'Error', e); } } /// iOS14 IDFA 访问权限检查 Future checkATT() async { try { final currentStatus = await ATTrackingManager.getTrackingAuthorizationStatus(); logMethodName( 'getTrackingAuthorizationStatus', 'ATTStatus:', currentStatus, ); if (currentStatus == ATTStatus.NotDetermined) { final returnedStatus = await ATTrackingManager.requestTrackingAuthorization(); logMethodName( 'requestTrackingAuthorizationATTStatus', 'ATTStatus returned:', returnedStatus, ); } } catch (e) { logMethodName('checkATT', 'Error', e); } } /// 启用调试模式 Future enableDebug() async { try { await LevelPlay.setAdaptersDebug(true); LevelPlay.validateIntegration(); } catch (e) { logMethodName('enableDebug', 'Error', e); } } /// 加载开屏广告的专用方法 - 并行加载三个广告,任意一个成功就立即回调并跳转 /// 如果所有广告都失败,则调用 onAllAdsFailed void loadInitialSplashAd({ required Function(InterstitialAdType adType) onAdReady, required Function() onAllAdsFailed, }) { if (!_isInitialized) { logMethodName( 'LoadInitialSplashAd', 'Warning', 'Manager not initialized yet. Call initialize() first.', ); onAllAdsFailed(); return; } _onSplashAdReadyCallback = onAdReady; _onSplashAdAllFailedCallback = onAllAdsFailed; _splashAdsFailedCount = 0; _isHandlingSplashAd = true; _splashAdsToLoad = { InterstitialAdType.first, InterstitialAdType.second, InterstitialAdType.third, }; logMethodName('LoadInitialSplashAd', 'Info', '开始并行加载初始开屏广告...'); for (final adType in _splashAdsToLoad) { loadAd(adType); } } /// 清除开屏广告回调的私有方法 void _clearSplashCallbacks() { _onSplashAdReadyCallback = null; _onSplashAdAllFailedCallback = null; _splashAdsToLoad.clear(); _splashAdsFailedCount = 0; _isHandlingSplashAd = false; } /// 加载指定类型的广告 Future loadAd(InterstitialAdType adType) async { if (!_isInitialized) { logMethodName( 'LoadAd', 'Warning', 'Manager not initialized yet. Call initialize() first.', ); return; } final ad = _interstitialAds[adType]; if (ad != null) { try { await ad.loadAd(); // 加载后更新 adId 映射 if (ad.adId.isNotEmpty) { _adIdToTypeMap[ad.adId] = adType; } logMethodName('LoadAd', 'Success', 'Loading ${adType.displayName}'); } catch (e) { logMethodName( 'LoadAd', 'Error', 'Failed to load ${adType.displayName}: $e', ); } } else { logMethodName( 'LoadAd', 'Error', 'Ad instance not found for ${adType.displayName}', ); } } /// 显示指定类型的广告 /// [onAdCompleted] 广告完成回调(广告关闭或展示失败时调用) Future showAd( InterstitialAdType adType, { VoidCallback? onAdCompleted, }) async { if (!_isInitialized) { logMethodName( 'ShowAd', 'Warning', 'Manager not initialized yet. Call initialize() first.', ); return; } // 如果已有广告在展示,直接忽略新的展示请求,避免串台 if (_currentShowingAd != null) { logMethodName( 'ShowAd', 'Info', 'Another interstitial is showing (${_currentShowingAd!.displayName}), skip ${adType.displayName}', ); return; } final ad = _interstitialAds[adType]; if (ad != null) { try { final isReady = await ad.isAdReady(); if (isReady) { _currentShowingAd = adType; // 保存完成回调 _adCompletedCallbacks[adType] = onAdCompleted; await ad.showAd(placementName: adType.placementName); logMethodName('ShowAd', 'Success', 'Showing ${adType.displayName}'); } else { logMethodName( 'ShowAd', 'Warning', '${adType.displayName} is not ready yet', ); // 如果未就绪,尝试重新加载 await loadAd(adType); // 广告未就绪时也调用完成回调 onAdCompleted?.call(); } } catch (e) { logMethodName( 'ShowAd', 'Error', 'Failed to show ${adType.displayName}: $e', ); // 避免异常导致状态悬挂 if (_currentShowingAd == adType) { _currentShowingAd = null; } // 异常时调用完成回调 onAdCompleted?.call(); } } else { logMethodName( 'ShowAd', 'Error', 'Ad instance not found for ${adType.displayName}, try to load it.', ); // 理论上初始化已创建实例;若缺失则尝试加载 await loadAd(adType); // 广告实例不存在时也调用完成回调 onAdCompleted?.call(); } } /// 检查指定类型的广告是否已准备好 Future isAdReady(InterstitialAdType adType) async { if (!_isInitialized) { return false; } final ad = _interstitialAds[adType]; if (ad != null) { try { return await ad.isAdReady(); } catch (e) { logMethodName( 'isAdReady', 'Error', 'Failed to check ${adType.displayName}: $e', ); return false; } } return false; } /// 检查是否已初始化 bool get isInitialized => _isInitialized; /// LevelPlay Init listener @override void onInitFailed(LevelPlayInitError error) { ////初始化失败 logMethodName('❌InitListener', 'onInitFailed', error); _isInitialized = false; _isInitializing = false; } @override void onInitSuccess(LevelPlayConfiguration configuration) { //初始化成功 logMethodName('✅InitListener', 'onInitSuccess', configuration); _isInitialized = true; _isInitializing = false; } /// 内部方法:处理广告事件(由监听器包装类调用) void _handleAdEvent( InterstitialAdType adType, String eventName, dynamic data, ) { logMethodName('${adType.displayName}', eventName, data); } /// 工具函数 void logMethodName(String adFormat, String methodName, dynamic data) { print('$TAG: $adFormat - $methodName $data'); } } /// Interstitial 广告监听器包装类 /// 用于为每个广告实例创建独立的监听器,以便准确识别事件来源 class _InterstitialAdListenerWrapper with LevelPlayInterstitialAdListener { final InterstitialAdType adType; final InterstitialAdManager manager; _InterstitialAdListenerWrapper(this.adType, this.manager); @override void onAdClicked(LevelPlayAdInfo adInfo) { manager._handleAdEvent(adType, 'onAdClicked', adInfo); } @override void onAdClosed(LevelPlayAdInfo adInfo) { manager._handleAdEvent(adType, 'onAdClosed', adInfo); // 调用完成回调 final callback = manager._adCompletedCallbacks[adType]; callback?.call(); manager._adCompletedCallbacks.remove(adType); // 广告关闭后,自动重新加载该广告以便下次使用 manager.logMethodName( 'onAdClosed', 'Info', '广告已关闭,开始重新加载: ${adType.displayName}', ); // 清理当前展示状态 if (manager._currentShowingAd == adType) { manager._currentShowingAd = null; } manager.loadAd(adType); } @override void onAdDisplayFailed(LevelPlayAdError error, LevelPlayAdInfo adInfo) { manager._handleAdEvent(adType, 'onAdDisplayFailed', '$error | $adInfo'); // 调用完成回调 final callback = manager._adCompletedCallbacks[adType]; callback?.call(); manager._adCompletedCallbacks.remove(adType); // 广告展示失败后,也重新加载该广告 manager.logMethodName( 'onAdDisplayFailed', 'Info', '广告展示失败,开始重新加载: ${adType.displayName}', ); // 展示失败时清理当前展示状态 if (manager._currentShowingAd == adType) { manager._currentShowingAd = null; } manager.loadAd(adType); } @override void onAdDisplayed(LevelPlayAdInfo adInfo) { manager._handleAdEvent(adType, 'onAdDisplayed', adInfo); } @override void onAdInfoChanged(LevelPlayAdInfo adInfo) { manager._handleAdEvent(adType, 'onAdInfoChanged', adInfo); } @override void onAdLoadFailed(LevelPlayAdError error) { manager._handleAdEvent(adType, 'onAdLoadFailed', error); // 处理开屏广告失败逻辑 if (manager._isHandlingSplashAd && manager._splashAdsToLoad.contains(adType) && manager._onSplashAdAllFailedCallback != null) { manager.logMethodName( 'LoadInitialSplashAd', 'Failed', '❌ 广告加载失败: ${adType.displayName}, 原因: $error', ); manager._splashAdsFailedCount++; if (manager._splashAdsFailedCount >= manager._splashAdsToLoad.length) { manager._onSplashAdAllFailedCallback!(); manager._clearSplashCallbacks(); } } } @override void onAdLoaded(LevelPlayAdInfo adInfo) { manager._handleAdEvent(adType, 'onAdLoaded', adInfo); // 处理开屏广告逻辑:任意一个加载成功就立即回调(只触发一次) if (manager._isHandlingSplashAd && manager._splashAdsToLoad.contains(adType) && manager._onSplashAdReadyCallback != null) { manager.logMethodName( 'LoadInitialSplashAd', 'Success', '✅ 广告加载成功: ${adType.displayName}', ); manager._onSplashAdReadyCallback!(adType); // 清除回调状态,确保后续广告加载成功不会再次触发回调 manager._clearSplashCallbacks(); } } }