MoodCanvas/lib/core/app_ads_tools.dart
fengshengxiong 91b7eebbf2 接入TopON
2026-01-22 16:34:55 +08:00

257 lines
8.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<AdPlacement, String> _adUnitIds = {
AdPlacement.interstitial1: "n6971892e19211",
AdPlacement.interstitial2: "n6971892f2da1f",
AdPlacement.interstitial3: "n697189366ab4f",
};
// --- 内部状态变量 ---
bool _isSdkInitialized = false;
Completer<void>? _initCompleter;
final Set<String> _loadingAds = <String>{};
bool _isAdShowing = false;
final Map<String, VoidCallback> _adClosedCallbacks = {};
// --- 开屏广告专用回调和状态 ---
Function(AdPlacement adPlacement)? _onSplashAdReadyCallback;
Function()? _onSplashAdAllFailedCallback;
Set<AdPlacement> _splashAdsToLoad = {};
int _splashAdsFailedCount = 0;
bool _isHandlingSplashAd = false;
// 广告冷却逻辑
final bool _useGlobalCooldown = true;
static const String _globalCooldownKey = 'any_interstitial_ad_was_shown';
final Map<String, DateTime> _lastShownTimestamps = {};
final int _adCooldownSeconds = 15;
/// 初始化TopOn SDK和广告监听
Future<void> init() {
if (_initCompleter != null) {
return _initCompleter!.future;
}
_initCompleter = Completer<void>();
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<bool> 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';
}
}
}