443 lines
17 KiB
C#
443 lines
17 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using EFSDK;
|
||
using GoogleMobileAds.Api;
|
||
using UnityEngine;
|
||
|
||
namespace WZ
|
||
{
|
||
class ShowNativePosition
|
||
{
|
||
public NativeOverlayAd NativeOverlayAd;
|
||
public NativeAdPosition Position;
|
||
|
||
public ShowNativePosition(NativeOverlayAd nativeOverlayAd, NativeAdPosition position)
|
||
{
|
||
NativeOverlayAd = nativeOverlayAd;
|
||
Position = position;
|
||
}
|
||
}
|
||
|
||
public class AdmobNativeAdManager
|
||
{
|
||
private Dictionary<string, NativeOverlayAd> _nativeAds = new Dictionary<string, NativeOverlayAd>();
|
||
private Dictionary<string, double> _adRevenueCache = new Dictionary<string, double>();
|
||
private Dictionary<string, int> _retryCounters = new Dictionary<string, int>();
|
||
private Dictionary<string, float> _adStartLoadTimes = new Dictionary<string, float>();
|
||
private Dictionary<string, ShowNativePosition> showingNativeAds = new();
|
||
|
||
public void InitializeAdUnits(List<string> adUnitIds)
|
||
{
|
||
foreach (var adUnitId in adUnitIds)
|
||
{
|
||
CreateNativeAd(adUnitId);
|
||
}
|
||
}
|
||
|
||
private void CreateNativeAd(string adUnitId)
|
||
{
|
||
if (string.IsNullOrEmpty(adUnitId)) return;
|
||
if (_nativeAds.ContainsKey(adUnitId))
|
||
{
|
||
LoggerUtils.Debug($"[Admob] Native Ad unit {adUnitId} already exists");
|
||
return;
|
||
}
|
||
|
||
LoadAd(adUnitId);
|
||
_adRevenueCache[adUnitId] = 0;
|
||
}
|
||
|
||
public void LoadAd(string adUnitId, bool timingRefresh = false)
|
||
{
|
||
LoggerUtils.Debug($"[Admob] Native Ad unit {adUnitId} load start , timingRefresh {timingRefresh}");
|
||
//判断在线参数是否包含这个id
|
||
if (!AdmobAdsManager.Instance.FindAdsID(AdsType.Native, adUnitId))
|
||
{
|
||
return;
|
||
}
|
||
|
||
NativeOverlayAd.Load(adUnitId, new AdRequest(), new NativeAdOptions(), (NativeOverlayAd ad, LoadAdError error) =>
|
||
{
|
||
_adStartLoadTimes[adUnitId] = Time.realtimeSinceStartup;
|
||
LoggerUtils.Debug($"[Admob] Native Ad unit {adUnitId} load end, timingRefresh {timingRefresh}. {ad} error {error}");
|
||
if (error != null || ad == null)
|
||
{
|
||
if (!timingRefresh)
|
||
{
|
||
if (!_retryCounters.TryAdd(adUnitId, 0))
|
||
{
|
||
_retryCounters[adUnitId]++;
|
||
}
|
||
}
|
||
|
||
|
||
var adSource = "";
|
||
if (ad != null)
|
||
{
|
||
adSource = ad.GetResponseInfo()?.GetLoadedAdapterResponseInfo()?.AdSourceName ?? "";
|
||
}
|
||
|
||
var reason = "";
|
||
|
||
if (error != null)
|
||
{
|
||
reason = error.GetMessage();
|
||
}
|
||
|
||
AdsActionEvents.TrackAdFailToLoad(PlatformType.Admob,
|
||
adSource,
|
||
adUnitId,
|
||
AdsType.Native,
|
||
Time.realtimeSinceStartup - _adStartLoadTimes[adUnitId],
|
||
reason);
|
||
|
||
// 定时任务的刷新,由定时任务自己去刷新
|
||
if (timingRefresh)
|
||
{
|
||
if (!showingNativeAds.ContainsKey(adUnitId))
|
||
{
|
||
LoggerUtils.Debug($"[Admob] Native Ad unit {adUnitId}, 已经隐藏了,失败了也不需要在重新load了");
|
||
return;
|
||
}
|
||
|
||
TimingRefresh(adUnitId);
|
||
return;
|
||
}
|
||
|
||
var retryDelay = Math.Pow(2, Math.Min(6, _retryCounters[adUnitId]));
|
||
TimerUtils.Instance.DelayExecute((float)retryDelay, () => { LoadAd(adUnitId); });
|
||
LoggerUtils.Error($"[Admob] Native Ad unit {adUnitId}, timingRefresh {timingRefresh} ad failed to load an ad with error : " + error + " \n retryDelay :" + retryDelay);
|
||
return;
|
||
}
|
||
|
||
if (!AdmobAdsManager.Instance.FindAdsID(AdsType.Native, adUnitId))
|
||
{
|
||
return;
|
||
}
|
||
|
||
AdsActionEvents.TrackAdLoaded(PlatformType.Admob,
|
||
ad?.GetResponseInfo()?.GetLoadedAdapterResponseInfo()?.AdSourceName ?? "",
|
||
adUnitId,
|
||
AdsType.Native,
|
||
Time.realtimeSinceStartup - _adStartLoadTimes[adUnitId]);
|
||
_retryCounters[adUnitId] = 0;
|
||
|
||
|
||
// 临时缓存上一次的native ad,以便于刷新的时候,显示了新的,隐藏老的。
|
||
var tempAd = _nativeAds.GetValueOrDefault(adUnitId, null);
|
||
var nativeEcpm = AdmobUtils.GetNativeEcpm(ad);
|
||
_nativeAds[adUnitId] = ad;
|
||
_adRevenueCache[adUnitId] = nativeEcpm;
|
||
LoggerUtils.Debug($"Admob Native ad loaded with, timingRefresh {timingRefresh} nativeEcpm = {nativeEcpm} response : " + ad.GetResponseInfo().ToString());
|
||
AdsKeyEvents.Instance.LogAdFPUEvents(AdsType.Native);
|
||
|
||
ad.OnAdPaid += (AdValue adValue) =>
|
||
{
|
||
LoggerUtils.Debug($"[Admob] Native ad paid {adValue.Value} {adValue.CurrencyCode}.");
|
||
AdmobAdsManager.Instance.TrackAdImpression(ad?.GetResponseInfo()?.GetLoadedAdapterResponseInfo().AdSourceName??"",
|
||
ad?.GetResponseInfo()?.GetLoadedAdapterResponseInfo().AdSourceInstanceId??"",
|
||
adValue,
|
||
AdsType.Native,
|
||
adUnitId);
|
||
};
|
||
|
||
ad.OnAdImpressionRecorded += () => { LoggerUtils.Debug("[Admob] Native ad recorded an impression."); };
|
||
|
||
ad.OnAdClicked += () =>
|
||
{
|
||
AdsActionEvents.TrackAdClicked(PlatformType.Admob,
|
||
ad?.GetResponseInfo()?.GetLoadedAdapterResponseInfo()?.AdSourceName ?? "",
|
||
adUnitId,
|
||
AdsType.Native,
|
||
"",
|
||
AdmobUtils.GetNativeEcpm(ad));
|
||
LoggerUtils.Debug("[Admob] Native ad was clicked.");
|
||
};
|
||
|
||
ad.OnAdFullScreenContentOpened += () => { LoggerUtils.Debug("[Admob] Native ad full screen content opened."); };
|
||
|
||
ad.OnAdFullScreenContentClosed += () =>
|
||
{
|
||
AdsActionEvents.TrackAdClicked(PlatformType.Admob,
|
||
ad?.GetResponseInfo()?.GetLoadedAdapterResponseInfo()?.AdSourceName ?? "",
|
||
adUnitId,
|
||
AdsType.Native,
|
||
"",
|
||
AdmobUtils.GetNativeEcpm(ad));
|
||
LoggerUtils.Debug("[Admob] Native ad full screen content closed.");
|
||
};
|
||
|
||
if (timingRefresh && tempAd != null)
|
||
{
|
||
if (showingNativeAds.TryGetValue(adUnitId, out var showing))
|
||
{
|
||
var showingNativeOverlayAd = showing.NativeOverlayAd;
|
||
if (showingNativeOverlayAd != tempAd)
|
||
{
|
||
LoggerUtils.Error("[Admob] Native ad Error!!!! showing.NativeOverlayAd != tempAd");
|
||
}
|
||
|
||
LoggerUtils.Warning("[Admob] Native ad timing refresh , show ad");
|
||
ShowAd(showing.Position, adUnitId, tempAd, showingNativeOverlayAd);
|
||
}
|
||
else
|
||
{
|
||
LoggerUtils.Warning($"[Admob] Native ad timing refresh , show fail , showing native ads not ad unit id , {adUnitId} , 广告已经隐藏了,刷新成功,不需要再去显示出来");
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// 显示价格最高的广告
|
||
public void ShowHighestPayingAd(NativeAdPosition position)
|
||
{
|
||
LoggerUtils.Debug($"[Admob] Native ad ShowHighestPayingAd {position}");
|
||
var highestPayingAdUnit = GetHighestPayingAdUnit();
|
||
|
||
LoggerUtils.Debug($"[Admob] Native ad ShowHighestPayingAd {position} , highestPayingAdUnit = {highestPayingAdUnit}");
|
||
if (!string.IsNullOrEmpty(highestPayingAdUnit))
|
||
{
|
||
ShowAd(position, highestPayingAdUnit);
|
||
}
|
||
}
|
||
|
||
// 显示特定广告位的广告
|
||
public void ShowAd(NativeAdPosition position, string adUnitId, NativeOverlayAd lastAd = null, NativeOverlayAd lastShowAd = null)
|
||
{
|
||
if (!AdmobAdsManager.Instance.FindAdsID(AdsType.Native, adUnitId))
|
||
{
|
||
return;
|
||
}
|
||
|
||
LoggerUtils.Debug($"[Admob] Native ad ShowAd start {adUnitId} , {position}");
|
||
|
||
if (_nativeAds.TryGetValue(adUnitId, out var ad))
|
||
{
|
||
LoggerUtils.Debug($"[Admob] Native ad ShowAd end {adUnitId} , {position}");
|
||
|
||
// Define a native template style with a custom style.
|
||
var style = position.NativeTemplateStyle ?? new NativeTemplateStyle()
|
||
{
|
||
TemplateId = NativeTemplateId.Medium,
|
||
// TemplateId = "big"
|
||
// MainBackgroundColor = Color.green,
|
||
CallToActionText = new NativeTemplateTextStyle()
|
||
{
|
||
FontSize = 9,
|
||
Style = NativeTemplateFontStyle.Bold
|
||
}
|
||
};
|
||
|
||
// Renders a native overlay ad at the default size
|
||
// and anchored to the bottom of the screne.
|
||
ad.RenderTemplate(style, new AdSize(position.Width, position.Height), position.X, position.Y);
|
||
|
||
|
||
var showNativePosition = showingNativeAds.GetValueOrDefault(adUnitId, null);
|
||
|
||
|
||
var lastAdResponseId = lastAd?.GetResponseInfo()?.GetResponseId();
|
||
var lastShowAdResponseId = lastShowAd?.GetResponseInfo()?.GetResponseId();
|
||
var lastShowAdResponseId2 = showNativePosition?.NativeOverlayAd?.GetResponseInfo()?.GetResponseId();
|
||
|
||
var showAdResponseId = ad.GetResponseInfo()?.GetResponseId();
|
||
|
||
LoggerUtils.Debug(
|
||
$"[Admob] Native ad ShowAd [showingNativeAds] {adUnitId} , showAdResponseId = {showAdResponseId} , lastAdResponseId = {lastAdResponseId} , lastShowAdResponseId = {lastShowAdResponseId} , lastShowAdResponseId2 = {lastShowAdResponseId2}");
|
||
|
||
showingNativeAds[adUnitId] = new ShowNativePosition(ad, position);
|
||
ad.Show();
|
||
|
||
try
|
||
{
|
||
lastAd?.Hide();
|
||
lastShowAd?.Hide();
|
||
showNativePosition?.NativeOverlayAd?.Hide();
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
LoggerUtils.Error($"[Admob] Native ad Hide fail {adUnitId} , e : {e.Message}");
|
||
}
|
||
|
||
|
||
TimingRefresh(adUnitId);
|
||
}
|
||
}
|
||
|
||
private void TimingRefresh(string adUnitId)
|
||
{
|
||
var nativeReflashGap = int.Parse(FireBaseRemoteConfigManager.Instance.GetRemoteConfigString("Native_Reflash_Gap", "0"));
|
||
|
||
if (nativeReflashGap <= 0)
|
||
{
|
||
LoggerUtils.Debug($"[Admob] Native ad ({adUnitId}) timing refresh failed. nativeReflashGap = 0");
|
||
return;
|
||
}
|
||
|
||
if (adUnitId == StaticValue.AdmobFullNativeId)
|
||
{
|
||
LoggerUtils.Debug($"[Admob] Native ad ({adUnitId}) timing refresh finished. ad unit id is full native id.");
|
||
return;
|
||
}
|
||
|
||
if (!showingNativeAds.ContainsKey(adUnitId))
|
||
{
|
||
LoggerUtils.Debug($"[Admob] Native ad ({adUnitId}) timing refresh finished. ad unit is not show.");
|
||
return;
|
||
}
|
||
|
||
WLoom.QueueOnMainThread(o =>
|
||
{
|
||
var refreshAdUnitId = (string)o;
|
||
LoggerUtils.Debug($"[Admob] Native ad ({adUnitId}) timing refresh load start refreshAdUnitId : {refreshAdUnitId}.");
|
||
if (showingNativeAds.ContainsKey(refreshAdUnitId))
|
||
{
|
||
LoadAd(refreshAdUnitId, true);
|
||
}
|
||
else
|
||
{
|
||
LoggerUtils.Warning($"[Admob] Native ad ({adUnitId}) timing refresh finished. ad unit is not show. refreshAdUnitId : {refreshAdUnitId}.");
|
||
}
|
||
}, adUnitId, nativeReflashGap);
|
||
}
|
||
|
||
|
||
private float GetLoadedTime(string adUnitId)
|
||
{
|
||
return _adStartLoadTimes.TryGetValue(adUnitId, out var time) ? time : 0;
|
||
}
|
||
|
||
// 检查特定广告位是否可用
|
||
public bool IsAdAvailable(string adUnitId)
|
||
{
|
||
return _nativeAds.TryGetValue(adUnitId, out _);
|
||
}
|
||
|
||
// 获取所有可用的广告位
|
||
public List<string> GetAvailableAdUnits()
|
||
{
|
||
return _nativeAds.Select(kvp => kvp.Key).ToList();
|
||
}
|
||
|
||
// 获取价格最高的广告位ID
|
||
public string GetHighestPayingAdUnit()
|
||
{
|
||
string highestPayingAdUnit = null;
|
||
double highestRevenue = -1;
|
||
LoggerUtils.Debug($"[Admob] Native ad GetHighestPayingAdUnit {_adRevenueCache.Count}");
|
||
|
||
foreach (var kvp in _adRevenueCache)
|
||
{
|
||
var adUnitId = kvp.Key;
|
||
var revenue = kvp.Value;
|
||
|
||
// 确保广告确实已加载并且价格更高
|
||
if (IsAdAvailable(adUnitId) && revenue > highestRevenue)
|
||
{
|
||
highestRevenue = revenue;
|
||
highestPayingAdUnit = adUnitId;
|
||
}
|
||
}
|
||
|
||
return highestPayingAdUnit;
|
||
}
|
||
|
||
// 获取价格最高的广告收益信息
|
||
public double GetHighestPayingAdRevenue()
|
||
{
|
||
var highestPayingAdUnit = GetHighestPayingAdUnit();
|
||
if (!string.IsNullOrEmpty(highestPayingAdUnit) &&
|
||
_adRevenueCache.TryGetValue(highestPayingAdUnit, out var revenue))
|
||
{
|
||
return revenue;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
// 获取广告收益信息
|
||
public double GetAdRevenue(string adUnit)
|
||
{
|
||
if (!string.IsNullOrEmpty(adUnit) &&
|
||
_adRevenueCache.TryGetValue(adUnit, out var revenue))
|
||
{
|
||
return revenue;
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
|
||
// 清理资源
|
||
public void Destroy()
|
||
{
|
||
foreach (var ad in _nativeAds.Values)
|
||
{
|
||
ad.Destroy();
|
||
}
|
||
|
||
_nativeAds.Clear();
|
||
_adRevenueCache.Clear();
|
||
}
|
||
|
||
public IEnumerator RemoveNative(string adUnitId)
|
||
{
|
||
// 不需要等待了
|
||
// yield return new WaitForSeconds(0.2f);
|
||
if (adUnitId == null || string.IsNullOrEmpty(adUnitId))
|
||
{
|
||
foreach (var key in showingNativeAds.Keys.ToList())
|
||
{
|
||
var ad = showingNativeAds[key].NativeOverlayAd;
|
||
LoggerUtils.Debug($"[Admob] Native ad removing [showingNativeAds] {adUnitId} , ad {ad.GetResponseInfo()?.GetResponseId()}");
|
||
ad.Hide();
|
||
// 从字典中删除元素
|
||
showingNativeAds.Remove(key);
|
||
LoadAd(key);
|
||
}
|
||
|
||
yield break;
|
||
}
|
||
|
||
if (showingNativeAds.TryGetValue(adUnitId, out var tempAd))
|
||
{
|
||
var ad = tempAd.NativeOverlayAd;
|
||
LoggerUtils.Debug($"[Admob] Native ad removing [showingNativeAds] {adUnitId} , ad {ad.GetResponseInfo()?.GetResponseId()}");
|
||
ad.Hide();
|
||
showingNativeAds.Remove(adUnitId);
|
||
LoadAd(adUnitId);
|
||
}
|
||
else
|
||
{
|
||
LoggerUtils.Debug($"[Admob] Native ad removing NativeAd {adUnitId} , failed to remove NativeAd");
|
||
}
|
||
}
|
||
|
||
public void ClearAds(string[] adUnitIds)
|
||
{
|
||
// 将数组转换为HashSet以提高查找性能
|
||
HashSet<string> validKeys = new HashSet<string>(adUnitIds);
|
||
// 收集需要移除的key
|
||
List<string> keysToRemove = new List<string>();
|
||
|
||
foreach (var key in _nativeAds.Keys)
|
||
{
|
||
if (!validKeys.Contains(key))
|
||
{
|
||
keysToRemove.Add(key);
|
||
}
|
||
}
|
||
|
||
// 移除不在数组中的key
|
||
foreach (string key in keysToRemove)
|
||
{
|
||
_nativeAds.Remove(key);
|
||
_retryCounters.Remove(key);
|
||
_adStartLoadTimes.Remove(key);
|
||
_adRevenueCache.Remove(key);
|
||
}
|
||
}
|
||
}
|
||
} |