com.guru.unity.sdk/Runtime/Code/SDK/GuruSDK.Ads.cs

556 lines
19 KiB
C#

namespace Guru
{
using UnityEngine;
using System;
using Guru.Notification;
public partial class GuruSDK
{
private const float CONSENT_FLOW_TIMEOUT = 10; // Consent Flow 超时时间(秒)
private static AdsInitSpec _adInitSpec;
/// <summary>
/// 启动广告服务
/// </summary>
public static void StartAds(AdsInitSpec spec = null)
{
_adInitSpec = spec;
if (InitConfig.UseCustomConsent)
{
UnityEngine.Debug.Log($"{Tag} --- Call <color=orange>StartAdsWithCustomConsent</color> when you use custom consent, and pass the result (boolean) to the method.");
return;
}
// 默认的启动顺序是先启动Consent后, 根据用户回调的结果来启动广告
Instance.StartConsentFlow();
}
/// <summary>
/// 是否已经购买了去广告
/// </summary>
/// <param name="buyNoAds"></param>
public static void StartAds(bool buyNoAds = false)
{
StartAds(AdsInitSpec.BuildWithNoAds()); // 按照默认的去广告来生成广告启动配置
}
/// <summary>
/// 使用自定义的Consent, 获取用户授权后, 调用此方法
/// </summary>
/// <param name="userAllow"></param>
/// <param name="consentName">Consent 引导的类型, 如果使用了 MAX 的 consent 请填写 max </param>
/// <param name="spec">广告启动配置</param>
public static void StartAdsWithCustomConsent(bool userAllow = true,
string consentName = "custom", AdsInitSpec spec = null)
{
#if UNITY_IOS
_attType = consentName;
InitAttStatus();
#endif
if (userAllow)
{
#if UNITY_IOS
Instance.CheckAttStatus();
#else
StartAdService(spec);
#endif
}
else
{
UnityEngine.Debug.Log($"{Tag} --- User refuse to provide ads Id, Ads Service will be cancelled");
}
}
/// <summary>
/// 使用自定义的Consent, 获取用户授权后, 调用此方法
/// </summary>
/// <param name="userAllow">自定义 Consent 的用户授权结果</param>
/// <param name="consentName">Consent引导名称</param>
/// <param name="buyNoAds">是否已经购买了去广告</param>
public static void StartAdsWithCustomConsent(bool userAllow = true, string consentName = "custom",
bool buyNoAds = false)
{
StartAdsWithCustomConsent(userAllow, consentName, AdsInitSpec.BuildWithNoAds());
}
#region Guru Consent
private bool _hasConsentCalled = false;
private bool _adServiceHasStarted = false;
private string _notiStatue = "";
private bool _hasNotiGranted = false;
/// <summary>
/// 启动Consent流程
/// 因为之后规划广告流程会放在 Consent 初始化之后, 因此请求广告的时候会需要先请求 Consent
/// </summary>
private void StartConsentFlow()
{
LogI($"#4.5 --- StartConsentFlow ---");
float time = 1;
if (!_adServiceHasStarted && _appServicesConfig != null)
{
time = _appServicesConfig.IsAdsCompliance() ? CONSENT_FLOW_TIMEOUT : 1f; // 启动合规判定后, 延迟最多 10 秒后启动广告
}
Delay(time, AdServiceHandler); // 广告延迟启动
if (_hasConsentCalled) return;
_hasConsentCalled = true;
bool enableCountryCheck = false;
string dmaMapRule = "";
if (_appServicesConfig != null && _appServicesConfig.parameters != null)
{
enableCountryCheck = _appServicesConfig.DMACountryCheck();
dmaMapRule = _appServicesConfig.DMAMapRule();
}
#if UNITY_IOS
InitAttStatus(); // Consent 启动前记录 ATT 初始值
#endif
UnityEngine.Debug.Log($"{Tag} --- Call:StartConsentFlow ---");
GuruConsent.StartConsent(OnGuruConsentOver, dmaMapRule:dmaMapRule, enableCountryCheck:enableCountryCheck);
}
/// <summary>
/// Guru Consent flow is Over
/// </summary>
/// <param name="code"></param>
private void OnGuruConsentOver(int code)
{
// 无论状态如何, 都在回调内启动广告初始化
AdServiceHandler();
// 调用回调
Callbacks.ConsentFlow._onConsentResult?.Invoke(code);
#if UNITY_IOS
CheckAttStatus(); // [iOS] Consent 启动后检查 ATT 初始值
#elif UNITY_ANDROID
CheckNotiPermission(); // Consent 回调后检查 Notification 权限
#endif
// 内部处理后继逻辑
switch(code)
{
case GuruConsent.StatusCode.OBTAINED:
case GuruConsent.StatusCode.NOT_AVAILABLE:
// 已获取授权, 或者地区不可用, ATT 尚未启动
// TODO: 添加后继处理逻辑
break;
}
}
/// <summary>
/// 启动广告服务
/// </summary>
private void AdServiceHandler()
{
if (_adServiceHasStarted) return;
_adServiceHasStarted = true;
StartAdService(_adInitSpec);
}
#endregion
#region IOS ATT 广告授权流程
#if UNITY_IOS
private static string _initialAttStatus;
private static String _attType = "admob";
private static bool _autoReCallAtt = false;
/// <summary>
/// 显示系统的 ATT 弹窗
/// </summary>
public void RequestAttDialog()
{
LogI($"RequestATTDialog");
ATTManager.RequestATTDailog(ReportAttStatus);
}
/// <summary>
/// 初始化 ATT 状态
/// </summary>
public static void InitAttStatus()
{
_attType = InitConfig.UseCustomConsent ? ATTManager.GUIDE_TYPE_CUSTOM : ATTManager.GUIDE_TYPE_ADMOB; // 点位属性确定
_initialAttStatus = ATTManager.GetStatus();
SetUserProperty(Analytics.ParameterATTStatus, _initialAttStatus); // 上报一个初始的状态
}
/// <summary>
/// iOS 平台检查 ATT 状态
/// </summary>
private void CheckAttStatus(bool autoReCall = false)
{
_autoReCallAtt = autoReCall;
// Delay 1s to get the user choice
Delay(1, () => ATTManager.CheckStatus(ReportAttStatus));
}
private void ReportAttStatus(string status)
{
LogI($"{Tag} --- Get Att status:{status} att Type:{_attType} recall:{_autoReCallAtt}");
SetUserProperty(Analytics.ParameterATTStatus, status); // 当前的状态
if (!string.IsNullOrEmpty(status)
&& status != _initialAttStatus
&& status != ATTManager.ATT_STATUS_NOT_DETERMINED)
{
// 上报点位:
Analytics.AttResult(status, _attType);
}
switch(status)
{
case ATTManager.ATT_STATUS_NOT_DETERMINED:
// ATT 状态未知, 请求弹窗
if(_autoReCallAtt) RequestAttDialog();
break;
case ATTManager.ATT_STATUS_RESTRICTED:
case ATTManager.ATT_STATUS_DENIED:
// ATT 状态受限, 或者被拒绝
break;
case ATTManager.ATT_STATUS_AUTHORIZED:
// ATT 状态已授权
break;
}
CheckNotiPermission(); // Consent 回调后检查 Notification 权限
}
#endif
#endregion
#region Notification Permission Check
/// <summary>
/// 初始化 Noti Service
/// </summary>
private void InitNotiPermission()
{
bool hasNotiGranted = false;
_notiStatue = "no_determined";
NotificationService.Initialize(); // 初始化 Noti 服务
SetNotiPerm(NotificationService.GetStatus());
}
/// <summary>
/// 检查 Noti 状态
/// </summary>
private void CheckNotiPermission()
{
var status = NotificationService.GetStatus();
// 如果未启用自动 Noti 授权,则直接上报状态
if (!_initConfig.AutoNotificationPermission)
{
UnityEngine.Debug.LogWarning($"[SDK] ---- AutoNotificationPermission is OFF, Project should request permission own.");
return;
}
bool isGranted = NotificationService.IsPermissionGranted();
UnityEngine.Debug.Log($"[SDK] ---- Check Noti Permission: {isGranted}");
if (isGranted)
{
UnityEngine.Debug.Log($"[SDK] ---- Set Notification Permission: {status}");
SetNotiPerm(status);
return;
}
RequestNotificationPermission(); // 请求授权
}
/// <summary>
/// 请求推送授权
/// </summary>
/// <param name="callback"></param>
public static void RequestNotificationPermission(Action<string> callback = null)
{
FirebaseUtil.StartFetchFcmToken();
UnityEngine.Debug.Log($"[SDK] ---- RequestNotificationPermission");
NotificationService.RequestPermission(status =>
{
UnityEngine.Debug.Log($"[SDK] ---- Set Notification Permission: {status}");
if(!string.IsNullOrEmpty(status)) SetNotiPerm(status);
callback?.Invoke(status);
});
}
/// <summary>
/// 获取 Notification 状态值
/// </summary>
/// <returns></returns>
public static string GetNotificationStatus()
{
return NotificationService.GetStatus();
}
/// <summary>
/// 用户是否已经获取了 Notification 授权了
/// </summary>
/// <returns></returns>
public static bool IsNotificationPermissionGranted()
{
return NotificationService.IsPermissionGranted();
}
#endregion
#region Ad Services
private static bool _initAdsCompleted = false;
private static bool _isBannerVisible = false;
public static bool IsAdsReady => _initAdsCompleted;
private static int _preBannerAction = 0;
public static AdsInitSpec GetDefaultAdsSpec()
{
return AdsInitSpec.BuildDefault(InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
}
/// <summary>
/// 启动广告服务
/// </summary>
internal static void StartAdService(AdsInitSpec spec = null)
{
//---------- Using InitConfig ----------
if (InitConfig is { IsBuyNoAds: true }) SetBuyNoAds(true);
LogI($"StartAdService");
if (spec == null)
{
spec = AdsInitSpec.BuildDefault(InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
if (ADService.Instance.IsBuyNoAds)
{
spec = AdsInitSpec.BuildWithNoAds(InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
}
}
if(InitConfig != null && !string.IsNullOrEmpty(InitConfig.BannerBackgroundColor))
spec.bannerColorHex = InitConfig.BannerBackgroundColor;
//--------- Add Callbacks -----------
// BADS
ADService.OnBannerStartLoad = OnBannerStartLoad;
ADService.OnBannerLoaded = OnBannerLoaded;
// IADS
ADService.OnInterstitialStartLoad = OnInterstitialStartLoad;
ADService.OnInterstitialLoaded = OnInterstitialLoaded;
ADService.OnInterstitialFailed = OnInterstitialFailed;
ADService.OnInterstitialClosed = OnInterstitialClosed;
// RADS
ADService.OnRewardedStartLoad = OnRewardStartLoad;
ADService.OnRewardLoaded = OnRewardLoaded;
ADService.OnRewardFailed = OnRewardFailed;
ADService.OnRewardClosed = OnRewardClosed;
// ---------- Start Services ----------
ADService.Instance.StartService(OnAdsInitComplete, spec);
// ---------- Life Cycle ----------
Callbacks.App.OnAppPaused += OnAppPaused;
}
/// <summary>
/// 生命周期回调
/// </summary>
/// <param name="paused"></param>
private static void OnAppPaused(bool paused)
{
if(ADService.Instance.IsInitialized)
ADService.Instance.OnAppPaused(paused);
}
private static void OnBannerStartLoad(string adUnitId)
=> Callbacks.Ads._onBannerADStartLoad?.Invoke(adUnitId);
private static void OnBannerLoaded()
=> Callbacks.Ads._onBannerADLoaded?.Invoke();
private static void OnInterstitialStartLoad(string adUnitId)
=> Callbacks.Ads._onInterstitialADStartLoad?.Invoke(adUnitId);
private static void OnInterstitialLoaded()
=> Callbacks.Ads._onInterstitialADLoaded?.Invoke();
private static void OnInterstitialFailed()
=> Callbacks.Ads._onInterstitialADFailed?.Invoke();
private static void OnInterstitialClosed()
=> Callbacks.Ads._onInterstitialADClosed?.Invoke();
private static void OnRewardStartLoad(string adUnitId)
=> Callbacks.Ads._onRewardedADStartLoad?.Invoke(adUnitId);
private static void OnRewardLoaded()
=> Callbacks.Ads._onRewardedADLoaded?.Invoke();
private static void OnRewardFailed()
=> Callbacks.Ads._onRewardADFailed?.Invoke();
private static void OnRewardClosed()
=> Callbacks.Ads._onRewardADClosed?.Invoke();
private static void OnAdsInitComplete()
{
_initAdsCompleted = true;
if (_adInitSpec != null && _adInitSpec.loadBanner)
{
// 预制动作处理
if (_preBannerAction == 1)
{
_preBannerAction = 0;
ShowBanner();
}
else if (_preBannerAction == -1)
{
_preBannerAction = 0;
HideBanner();
}
}
Callbacks.Ads._onAdsInitComplete?.Invoke();
}
private static bool CheckAdsReady()
{
if (!IsAdsReady)
{
LogW("[SDK] Ads is not ready. Call <GuruSDk.StartAdService> first.");
return false;
}
return true;
}
/// <summary>
/// 显示Banner广告
/// </summary>
/// <param name="placement"></param>
public static void ShowBanner(string placement = "")
{
if (!CheckAdsReady())
{
_preBannerAction = 1;
return;
}
ADService.Instance.ShowBanner(placement);
}
/// <summary>
/// 设置 Banner 背景颜色
/// </summary>
/// <param name="color"></param>
public static void SetBannerBackgroundColor(Color color)
{
// if (!CheckAdsReady()) return;
ADService.Instance.SetBannerBackgroundColor(color);
}
public static void SetBannerAutoRefresh(bool value = true)
{
if (!CheckAdsReady()) return;
ADService.Instance.SetBannerAutoRefresh(value);
}
/// <summary>
/// 隐藏Banner广告
/// </summary>
public static void HideBanner()
{
if (!CheckAdsReady())
{
_preBannerAction = -1;
return;
}
ADService.Instance.HideBanner();
}
public static void LoadInterstitialAd()
{
if (!CheckAdsReady()) return;
ADService.Instance.RequestInterstitialAD();
}
public static bool IsInterstitialAdReady => ADService.Instance.IsInterstitialADReady();
/// <summary>
/// 显示插屏广告
/// </summary>
/// <param name="placement"></param>
/// <param name="onDismissed"></param>
public static void ShowInterstitialAd(string placement = "", Action onDismissed = null)
{
if (!CheckAdsReady()) return;
if (!ADService.Instance.IsInterstitialADReady())
{
LogE("Interstitial is not ready. Call <GuruSDk.ShowInterstitialAd> again.");
LoadInterstitialAd();
return;
}
ADService.Instance.ShowInterstitialAD(placement, onDismissed);
}
public static void LoadRewardAd()
{
if (!CheckAdsReady()) return;
ADService.Instance.RequestRewardedAD();
}
public static bool IsRewardAdReady => ADService.Instance.IsRewardedADReady();
/// <summary>
/// 显示激励视频广告
/// </summary>
/// <param name="placement"></param>
/// <param name="onRewarded"></param>
/// <param name="onFailed"></param>
public static void ShowRewardAd(string placement = "", Action onRewarded = null, Action<string> onFailed = null)
{
if (!CheckAdsReady()) return;
if (!ADService.Instance.IsRewardedADReady())
{
LogE("RewardAd is not ready. Call <GuruSDk.LoadRewardAd> again.");
LoadRewardAd();
return;
}
ADService.Instance.ShowRewardedAD(placement, onRewarded, onFailed);
}
#endregion
#region MaxServices
/// <summary>
/// 显示Max调试菜单
/// </summary>
public static void ShowMaxDebugPanel()
{
#if UNITY_EDITOR
LogI($"Can not show Max Debug Panel in Editor, skipped.");
return;
#endif
if (!ADService.Instance.IsInitialized)
{
LogI($"ADService is not initialized, call <GuruSDK.StartAds> first.");
return;
}
ADService.Instance.ShowMaxDebugPanel();
}
#endregion
}
}