Compare commits

..

No commits in common. "c8b85b0f79765d657994f6fd2c450bbb33cb02b4" and "cf832d88adad02aac276d743ba2ba5587a7b81ac" have entirely different histories.

49 changed files with 3754 additions and 1514 deletions

View File

@ -53,12 +53,9 @@ namespace Guru
/// </summary> /// </summary>
/// <param name="appToken"></param> /// <param name="appToken"></param>
/// <param name="fbAppId">MIR 追踪 AppID</param> /// <param name="fbAppId">MIR 追踪 AppID</param>
/// <param name="deviceId"></param> /// <param name="onInitComplete"></param>
/// <param name="onInitComplete">初始化完成的时候会返回 AdjustId </param>
/// <param name="onDeeplinkCallback"></param> /// <param name="onDeeplinkCallback"></param>
/// <param name="firebaseId"></param> public static void StartService(string appToken, string fbAppId = "", Action<string> onInitComplete = null, Action<string> onDeeplinkCallback = null)
public static void StartService(string appToken, string fbAppId = "", string firebaseId = "", string deviceId = "",
Action<string> onInitComplete = null, Action<string> onDeeplinkCallback = null)
{ {
if (string.IsNullOrEmpty(appToken)) if (string.IsNullOrEmpty(appToken))
{ {
@ -68,7 +65,7 @@ namespace Guru
_onInitComplete = onInitComplete; _onInitComplete = onInitComplete;
InstallEvent(firebaseId, deviceId); // 注入启动参数 InstallEvent(IPMConfig.FIREBASE_ID, IPMConfig.IPM_DEVICE_ID); // 注入启动参数
AdjustEnvironment environment = GetAdjustEnvironment(); AdjustEnvironment environment = GetAdjustEnvironment();
AdjustConfig config = new AdjustConfig(appToken, environment); AdjustConfig config = new AdjustConfig(appToken, environment);

View File

@ -20,19 +20,19 @@ namespace Guru
bool res; bool res;
from = $"{files}/{AndroidLib}.f"; from = $"{files}/{AndroidLib}.f";
to = $"{Application.dataPath}/Plugins/Android/{AndroidLib}"; to = $"{Application.dataPath}/Plugins/Android/{AndroidLib}";
res = CopyFile(from, to); // 无需覆盖 res = CopyFile(from, to);
if (res) Debug.Log($"Copy <color=#88ff00>{AndroidLib} to {to}</color> success..."); if (res) Debug.Log($"Copy <color=#88ff00>{AndroidLib} to {to}</color> success...");
from = $"{files}/{AndroidLib}.f.meta"; from = $"{files}/{AndroidLib}.f.meta";
to = $"{Application.dataPath}/Plugins/Android/{AndroidLib}.meta"; to = $"{Application.dataPath}/Plugins/Android/{AndroidLib}.meta";
CopyFile(from, to); // 无需覆盖 CopyFile(from, to);
from = $"{files}/{iOSLib}.f"; from = $"{files}/{iOSLib}.f";
to = $"{Application.dataPath}/Plugins/iOS/{iOSLib}"; to = $"{Application.dataPath}/Plugins/iOS/{iOSLib}";
res = CopyFile(from, to); // 无需覆盖 res = CopyFile(from, to);
if (res) Debug.Log($"Copy <color=#88ff00>{iOSLib} to {to}</color> success..."); if (res) Debug.Log($"Copy <color=#88ff00>{iOSLib} to {to}</color> success...");
from = $"{files}/{iOSLib}.f.meta"; from = $"{files}/{iOSLib}.f.meta";
to = $"{Application.dataPath}/Plugins/iOS/{iOSLib}.meta"; to = $"{Application.dataPath}/Plugins/iOS/{iOSLib}.meta";
CopyFile(from, to); // 无需覆盖 CopyFile(from, to);
AssetDatabase.Refresh(); AssetDatabase.Refresh();
} }
@ -55,25 +55,25 @@ namespace Guru
return Path.GetFullPath($"{Application.dataPath}/../Packages/com.guru.unity.sdk.core/Runtime/GuruAdjust/Editor/Signature"); return Path.GetFullPath($"{Application.dataPath}/../Packages/com.guru.unity.sdk.core/Runtime/GuruAdjust/Editor/Signature");
} }
private static bool CopyFile(string from, string to, bool overwrite = false) private static bool CopyFile(string source, string dest)
{ {
if (File.Exists(to) && !overwrite) if (File.Exists(source))
{ {
// 如果目标文件存在, 且不允许覆写, 则不进行拷贝 if (!File.Exists(dest))
return false; {
} File.Delete(dest);
}
if (File.Exists(from)) else
{ {
// 确保拷贝目录存在 var destDir = Directory.GetParent(dest);
var destDir = Directory.GetParent(to); if(!destDir.Exists) destDir.Create();
if(destDir != null && !destDir.Exists) destDir.Create(); }
File.Copy(from, to, overwrite); File.Copy(source, dest, true);
return true; return true;
} }
Debug.Log($"<colo=red>File not found: {from}...</color>"); Debug.Log($"<colo=red>File not found: {source}...</color>");
return false; return false;
} }

View File

@ -8,20 +8,18 @@ Sample Dependencies.xml:
<androidPackage spec="androidx.core:core:1.7.0" /> <androidPackage spec="androidx.core:core:1.7.0" />
<!-- <androidPackage spec="androidx.appcompat:appcompat:1.5.1" />--> <!-- <androidPackage spec="androidx.appcompat:appcompat:1.5.1" />-->
<androidPackage spec="androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" /> <androidPackage spec="androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" />
<androidPackage spec="androidx.work:work-runtime:2.8.1" /> <androidPackage spec="androidx.work:work-runtime:2.7.1" />
<androidPackage spec="androidx.work:work-runtime-ktx:2.8.1" /> <androidPackage spec="androidx.work:work-runtime-ktx:2.7.1" />
<androidPackage spec="androidx.work:work-rxjava2:2.8.1" /> <androidPackage spec="androidx.work:work-rxjava2:2.7.1" />
<androidPackage spec="androidx.lifecycle:lifecycle-process:2.4.0" /> <androidPackage spec="androidx.lifecycle:lifecycle-process:2.4.0" />
<androidPackage spec="com.jakewharton.timber:timber:4.7.1" /> <androidPackage spec="com.jakewharton.timber:timber:4.7.1" />
<androidPackage spec="com.google.code.gson:gson:2.8.5" /> <androidPackage spec="com.google.code.gson:gson:2.8.5" />
<androidPackage spec="androidx.room:room-runtime:2.6.1" /> <androidPackage spec="androidx.room:room-runtime:2.4.3" />
<androidPackage spec="androidx.room:room-rxjava2:2.6.1" /> <androidPackage spec="androidx.room:room-rxjava2:2.4.3" />
<androidPackage spec="com.squareup.retrofit2:retrofit:2.7.1" /> <androidPackage spec="com.squareup.retrofit2:retrofit:2.7.1" />
<androidPackage spec="com.squareup.retrofit2:converter-gson:2.7.1" /> <androidPackage spec="com.squareup.retrofit2:converter-gson:2.7.1" />
<androidPackage spec="com.squareup.retrofit2:adapter-rxjava2:2.7.1" /> <androidPackage spec="com.squareup.retrofit2:adapter-rxjava2:2.7.1" />
<androidPackage spec="com.squareup.okhttp3:okhttp:4.12.0" /> <androidPackage spec="com.squareup.okhttp3:okhttp:4.9.3" />
<androidPackage spec="com.squareup.okhttp3:okhttp-dnsoverhttps:4.12.0" />
<androidPackage spec="com.google.net.cronet:cronet-okhttp:0.1.0" />
<!-- <androidPackage spec="com.mapzen:on-the-road:0.8.1" />--> <!-- <androidPackage spec="com.mapzen:on-the-road:0.8.1" />-->
<!-- <androidPackage spec="com.squareup.retrofit2:retrofit:2.7.1" />--> <!-- <androidPackage spec="com.squareup.retrofit2:retrofit:2.7.1" />-->
</androidPackages> </androidPackages>

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 8009d3bf70bb4438599a32f6ea601f9d guid: 32eda01e213614348899eefe856392d3
PluginImporter: PluginImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -1,3 +1,6 @@
namespace Guru namespace Guru
{ {
using System; using System;
@ -11,7 +14,7 @@ namespace Guru
public class GuruAnalytics public class GuruAnalytics
{ {
// Plugin Version // Plugin Version
private const string Version = "1.12.0"; private const string Version = "1.11.1";
public static readonly string Tag = "[ANU]"; public static readonly string Tag = "[ANU]";
private static readonly string ActionName = "logger_error"; private static readonly string ActionName = "logger_error";
@ -82,9 +85,6 @@ namespace Guru
/// </summary> /// </summary>
private readonly List<int> _errorCodeList = new List<int>(); private readonly List<int> _errorCodeList = new List<int>();
private bool _enableErrorLog; private bool _enableErrorLog;
private string _experimentGroupId;
public string ExperimentGroupId => _experimentGroupId;
/// <summary> /// <summary>
/// 启动日志错误上报 /// 启动日志错误上报
@ -105,46 +105,20 @@ namespace Guru
/// <summary> /// <summary>
/// 初始化接口 /// 初始化接口
/// </summary> /// </summary>
public static void Init(string appId, string deviceInfo, Action onInitComplete, bool isDebug = false) public static void Init(string appId, string deviceInfo, Action onInitComplete, bool isDebug = false,
bool enableErrorLog = false)
{ {
Debug.Log($"{Tag} --- Guru Analytics [{Version}] initialing..."); Debug.Log($"{Tag} --- Guru Analytics [{Version}] initialing...");
if (_instance == null) if (_instance == null)
{ {
_instance = new GuruAnalytics(); _instance = new GuruAnalytics();
bool enableErrorLog = false;
string groupId = "not_set";
#if UNITY_ANDROID
enableErrorLog = true;
// 获取云控参数
// TODO: 针对 GuruSDK 整体的云控值做一个分组的解决方案
var guruInitParams = GuruAnalyticsConfigManager.GetInitParams();
// 记录分组数据
groupId = guruInitParams.groupId;
if (guruInitParams.enabled && Instance.Agent is AnalyticsAgentAndroid androidAgent)
{
// 强制转换为 Android 的自打点初始化接口
androidAgent.InitAndroidConfig(appId, deviceInfo,
guruInitParams.baseUrl, guruInitParams.uploadIpAddress, // <--- Android 附加参数
onInitComplete, isDebug);
}
else
{
// 外部(云控)如果关闭使用 Android 自打点附加参数, 则使用正常的启动接口
_instance.Agent.Init(appId, deviceInfo, onInitComplete, isDebug);
}
#else
// iOS 使用正常的启动接口
_instance.Agent.Init(appId, deviceInfo, onInitComplete, isDebug); _instance.Agent.Init(appId, deviceInfo, onInitComplete, isDebug);
#endif
_instance.EnableErrorLog = enableErrorLog; _instance.EnableErrorLog = enableErrorLog;
_instance._isReady = true; _instance._isReady = true;
_instance._experimentGroupId = groupId;
} }
} }
/// <summary> /// <summary>
/// 设置视图名称 /// 设置视图名称
/// </summary> /// </summary>
@ -476,6 +450,92 @@ namespace Guru
} }
} }
/**
private void ParseWithRaw(string raw)
{
var code = (int)AnalyticsCode.Unknown;
string info;
//------- message send to unity ----------
Debug.Log($"{Tag} get callback errorInfo:\n{raw}");
var patten = "msg\":\"";
if (raw.Contains(patten))
{
info = raw.Substring(raw.IndexOf(patten, StringComparison.Ordinal) + patten.Length);
if (!string.IsNullOrEmpty(info))
{
if (info.StartsWith("\"")) info = info.Substring(1, info.Length - 1);
if (info.EndsWith("\"}}")) info = info.Replace("\"}}", "");
}
else
{
info = "msg is null";
}
}
else
{
info = "no msg property";
}
try
{
var idx = raw.IndexOf(patten, StringComparison.Ordinal) + patten.Length;
string act = raw.Substring(idx, ActionName.Length);
if (act == ActionName)
{
patten = "code\":";
var patten2 = ",\"msg";
idx = raw.IndexOf(patten, StringComparison.Ordinal);
var idx2 = raw.IndexOf(patten2, StringComparison.Ordinal);
var len = idx2 - (idx + patten.Length);
if (len > 0)
{
string c = raw.Substring(idx + patten.Length, len);
int.TryParse(c, out code);
}
// Catch target code to report errors
switch ((AnalyticsCode)code)
{
case AnalyticsCode.Network_Lost:
case AnalyticsCode.ERROR_API:
case AnalyticsCode.ERROR_RESPONSE:
case AnalyticsCode.ERROR_CACHE_CONTROL:
case AnalyticsCode.ERROR_DELETE_EXPIRED:
case AnalyticsCode.ERROR_LOAD_MARK:
case AnalyticsCode.ERROR_DNS:
case AnalyticsCode.ERROR_ZIP:
OnLoggerErrorEvent(code, info);
return;
}
return;
}
}
catch (Exception ex)
{
Debug.LogError($"{Tag} Catch ex: {ex}\tJson:{raw}");
Analytics.LogCrashlytics(raw, false);
Analytics.LogCrashlytics($"{Tag} --- Json:{raw} Ex:{ex}");
OnLoggerErrorEvent(code, info);
return;
}
if (raw.Contains("msg"))
{
Analytics.LogCrashlytics(raw, false);
Analytics.LogCrashlytics($"{Tag} --- format error:{raw}");
OnLoggerErrorEvent(code, raw.Substring(raw.IndexOf("msg\":", StringComparison.Ordinal) + 5));
}
}
**/
private void ReportCodeInfo(int code, string info) private void ReportCodeInfo(int code, string info)
{ {
var ac = (AnalyticsCode)code; var ac = (AnalyticsCode)code;
@ -498,13 +558,11 @@ namespace Guru
case AnalyticsCode.ERROR_ZIP: case AnalyticsCode.ERROR_ZIP:
case AnalyticsCode.ERROR_DNS_CACHE: case AnalyticsCode.ERROR_DNS_CACHE:
case AnalyticsCode.CRONET_INTERCEPTOR: case AnalyticsCode.CRONET_INTERCEPTOR:
case AnalyticsCode.EVENT_LOOKUP:
case AnalyticsCode.EVENT_SESSION_ACTIVE:
canCatch = true; canCatch = true;
break; break;
} }
if (!canCatch && code is > 100 and <= 200) if (code > 100 && code <= 200)
{ {
// 100 < code <= 200 // 100 < code <= 200
canCatch = true; canCatch = true;
@ -543,8 +601,6 @@ namespace Guru
} }
/// <summary> /// <summary>
/// 网络状态枚举 /// 网络状态枚举
/// </summary> /// </summary>
@ -552,24 +608,21 @@ namespace Guru
{ {
Unknown = -1, Unknown = -1,
DELETE_EXPIRED = 12, // 删除过期事件 DELETE_EXPIRED = 12,
UPLOAD_FAIL = 14, // 上报事件失败 UPLOAD_FAIL = 14,
NETWORK_LOST = 22, // 网络状态不可用 NETWORK_LOST = 22,
CRONET_INIT_FAIL = 26, // 开启Cronet失败 CRONET_INIT_FAIL = 26,
CRONET_INIT_EXCEPTION = 27, // 开启Cronet报错 CRONET_INIT_EXCEPTION = 27,
ERROR_API = 101, // 调用api出错 ERROR_API = 101,
ERROR_RESPONSE = 102, // api返回结果错误 ERROR_RESPONSE = 102,
ERROR_CACHE_CONTROL = 103, // 设置cacheControl出错 ERROR_CACHE_CONTROL = 103,
ERROR_DELETE_EXPIRED = 104, // 删除过期事件出错 ERROR_DELETE_EXPIRED = 104,
ERROR_LOAD_MARK = 105, // 从数据库取事件以及更改事件状态为正在上报出错 ERROR_LOAD_MARK = 105,
ERROR_DNS = 106, // dns 错误 ERROR_DNS = 106,
ERROR_ZIP = 107, // zip 错误 ERROR_ZIP = 107,
ERROR_DNS_CACHE = 108, // zip 错误 ERROR_DNS_CACHE = 108,
CRONET_INTERCEPTOR = 109, // cronet拦截器 CRONET_INTERCEPTOR = 109,
EVENT_LOOKUP = 1003,
EVENT_SESSION_ACTIVE = 1004,
} }
} }

View File

@ -1,274 +0,0 @@
namespace Guru
{
using System;
using UnityEngine;
using Random = UnityEngine.Random;
using Firebase.RemoteConfig;
using System.Linq;
public class GuruAnalyticsConfigManager
{
private const string Tag = "[SDK][ANU][EXP]";
private static bool IsDebug
{
get
{
#if UNITY_EDITOR || DEBUG
return true;
#endif
return false;
}
}
private static string _localExperimentGroupId = "";
private static string LocalExperimentGroupId
{
get
{
if (string.IsNullOrEmpty(_localExperimentGroupId))
{
_localExperimentGroupId = PlayerPrefs.GetString(nameof(LocalExperimentGroupId), "");
}
return _localExperimentGroupId;
}
set
{
_localExperimentGroupId = value;
PlayerPrefs.SetString(nameof(LocalExperimentGroupId), value);
PlayerPrefs.Save();
}
}
/**
*
private const string JSON_GROUP_B =
"{\"cap\":\"firebase|facebook|guru\",\"init_delay_s\":10,\"experiment\":\"B\",\"guru_upload_ip_address\":[\"13.248.248.135\", \"3.33.195.44\"]}";
private const string JSON_GROUP_C =
"{\"cap\":\"firebase|facebook|guru\",\"init_delay_s\":10,\"experiment\":\"C\",\"guru_upload_ip_address\":[\"34.107.185.54\"],\"guru_event_url\":\"https://collect3.saas.castbox.fm\"}";
**/
/// <summary>
/// 解析 JSON 字符串
/// </summary>
/// <param name="json"></param>
/// <returns></returns>
private static GuruAnalyticsExperimentData Parse(string json)
{
if (string.IsNullOrEmpty(json)) return null;
return JsonParser.ToObject<GuruAnalyticsExperimentData>(json);
}
/// <summary>
/// 云控数据参数
/// </summary>
public const string KEY_GURU_ANALYTICS_EXP = "guru_analytics_exp";
/// <summary>
/// 默认的本地配置
/// </summary>
private const string DEFAULT_GURU_ANALYTICS_EXP = @"{
""enable"": true,
""exps"": [{
""groupId"": ""B"",
""baseUrl"": ""https://collect.saas.castbox.fm"",
""uploadIpAddress"": [""13.248.248.135"", ""3.33.195.44""]
}, {
""groupId"": ""C"",
""baseUrl"": ""https://collect3.saas.castbox.fm"",
""uploadIpAddress"": [""34.107.185.54""]
}]
}";
/// <summary>
/// 获取默认数据
/// </summary>
private static GuruAnalyticsExperimentData DefaultData => Parse(DEFAULT_GURU_ANALYTICS_EXP);
/// <summary>
/// 在当前版本中,随机获取线上配置的值
/// 若无法获取线上配置,则默认是 B 分组
/// </summary>
/// <param name="groupId"></param>
/// <param name="baseUrl"></param>
/// <param name="uploadIpAddress"></param>
/// <param name="isEnable"></param>
internal static GuruInitParams GetInitParams()
{
var groupId = "";
var baseUrl = "";
string[] uploadIpAddress = null;
var isEnabled = true;
GuruAnalyticsExperimentConfig config;
if(IsDebug) Debug.LogWarning($"{Tag} --- #0 Analytics EXP saved groupId :{LocalExperimentGroupId}");
// 拉取云控数据
var json = "";
if(FirebaseUtil.IsFirebaseInitialized && FirebaseRemoteConfig.DefaultInstance.Keys.Contains(KEY_GURU_ANALYTICS_EXP))
json = FirebaseRemoteConfig.DefaultInstance.GetValue(GuruAnalyticsConfigManager.KEY_GURU_ANALYTICS_EXP).StringValue;
if (string.IsNullOrEmpty(json))
{
// 没有云控值,走本地的数据配置,随机取值
if(IsDebug) Debug.LogWarning($"{Tag} --- #1 Analytics EXP json is Null -> using DefaultData");
config = GetDefaultGuruAnalyticsExpConfig();
}
else
{
// 有云控值,则直接使用云控的数据
if(IsDebug) Debug.LogWarning($"{Tag} --- #2 Analytics EXP Try to get remote json -> {json}");
var expData = Parse(json);
if (expData == null)
{
// 如果云控值为空,则使用本地分组
if(IsDebug) Debug.LogWarning($"{Tag} --- #2.1 Analytics EXP Parse failed -> using DefaultData");
config = GetDefaultGuruAnalyticsExpConfig();
}
else
{
// 如果云控值不为空,但不可用,则直接使用默认分组
if (!expData.enable)
{
Debug.LogWarning($"{Tag} --- #2.2 Analytics EXP Disabled -> using DefaultData");
expData = DefaultData;
}
config = expData.GetFirstConfig();
}
}
// 最后取不到的话只能默认分组了
if (config == null) {
config = DefaultData.GetFirstConfig(); // 默认是 B 组
if(IsDebug) Debug.LogWarning($"{Tag} --- #3 Try get config is Null -> using Default config");
}
if (config != null)
{
baseUrl = config.baseUrl;
groupId = config.groupId;
uploadIpAddress = config.uploadIpAddress;
LocalExperimentGroupId = groupId;
}
else
{
isEnabled = false;
}
Debug.Log($"{Tag} --- Analytics EXP params:: groupId:{groupId} baseUrl:{baseUrl} uploadIpAddress:[{ (uploadIpAddress != null ? string.Join(",", uploadIpAddress) : "null")}]");
return new GuruInitParams()
{
groupId = groupId,
baseUrl = baseUrl,
uploadIpAddress = uploadIpAddress,
enabled = isEnabled
};
}
private static GuruAnalyticsExperimentConfig GetDefaultGuruAnalyticsExpConfig()
{
GuruAnalyticsExperimentConfig config = null;
if (!string.IsNullOrEmpty(LocalExperimentGroupId))
{
config = DefaultData.GetConfig(LocalExperimentGroupId); // 非空则取值
}
else
{
config = DefaultData.GetRandomConfig(); // 随机获取本地的 Config
}
if(IsDebug) Debug.LogWarning($"{Tag} --- #1.1 using Default GroupId: {config.groupId}");
return config;
}
}
/// <summary>
/// 实验数据主题
/// </summary>
[Serializable]
internal class GuruAnalyticsExperimentData
{
public readonly bool enable = true; // 默认是打开的状态
public GuruAnalyticsExperimentConfig[] experiments; // 实验列表
public string ToJson() => JsonParser.ToJson(this); // 转换成 JSON 字符串
/// <summary>
/// 获取随机分组
/// </summary>
/// <returns></returns>
public GuruAnalyticsExperimentConfig GetRandomConfig()
{
if (experiments == null || experiments.Length == 0) return null;
return experiments[Random.Range(0, experiments.Length)];
}
/// <summary>
/// 根据分组名称获取分组
/// </summary>
/// <param name="groupId"></param>
/// <returns></returns>
public GuruAnalyticsExperimentConfig GetConfig(string groupId)
{
foreach (var g in experiments)
{
if (g.groupId == groupId) return g;
}
return null;
}
/// <summary>
/// 获取首个配置
/// </summary>
/// <returns></returns>
public GuruAnalyticsExperimentConfig GetFirstConfig()
{
if (experiments != null && experiments.Length > 0) return experiments[0];
return null;
}
/// <summary>
/// 分组是否存在
/// </summary>
/// <param name="groupId"></param>
/// <returns></returns>
public bool IsGroupExists(string groupId)
{
foreach (var g in experiments)
{
if (g.groupId == groupId) return true;
}
return false;
}
}
/// <summary>
/// 实验配置
/// </summary>
[Serializable]
internal class GuruAnalyticsExperimentConfig
{
public string groupId;
public string baseUrl;
public string[] uploadIpAddress;
}
[Serializable]
internal class GuruInitParams
{
public string groupId;
public string baseUrl;
public string[] uploadIpAddress;
public bool enabled;
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: a840d9218d324971b528c639ddfea270
timeCreated: 1721722549

View File

@ -17,7 +17,7 @@ namespace Guru
void SetUid(string uid); void SetUid(string uid);
bool IsDebug { get; } bool IsDebug { get; }
bool EnableErrorLog { get; set; } bool EnableErrorLog { get; set; }
void LogEvent(string eventName, string parameters, int priority = 0); void LogEvent(string eventName, string parameters, int priority = -1);
void ReportEventSuccessRate(); // 上报任务成功率 void ReportEventSuccessRate(); // 上报任务成功率
void SetTch02Value(double value); // 设置太极02数值 void SetTch02Value(double value); // 设置太极02数值
void InitCallback(string objName, string method); // 设置回调对象参数 void InitCallback(string objName, string method); // 设置回调对象参数

View File

@ -1,3 +1,7 @@
using System.Threading.Tasks;
namespace Guru namespace Guru
{ {
using System; using System;
@ -8,16 +12,20 @@ namespace Guru
#if UNITY_ANDROID #if UNITY_ANDROID
private static readonly string AnalyticsClassName = "com.guru.unity.analytics.Analytics"; public static readonly string AnalyticsClassName = "com.guru.unity.analytics.Analytics";
private static AndroidJavaClass _classAnalytics; private static AndroidJavaClass _classAnalytics;
private static AndroidJavaClass ClassAnalytics => _classAnalytics ??= new AndroidJavaClass(AnalyticsClassName); private static AndroidJavaClass ClassAnalytics => _classAnalytics ??= new AndroidJavaClass(AnalyticsClassName);
#endif #endif
private static bool _isDebug = false; private static bool _isDebug = false;
public static bool UseWorker = true;
public static bool UseCronet = false;
public static string BaseUrl = ""; public static string BaseUrl = "";
#region 工具方法 #region 工具方法
/// <summary> /// <summary>
/// 调用静态方法 /// 调用静态方法
/// </summary> /// </summary>
@ -56,7 +64,7 @@ namespace Guru
{ {
if (ClassAnalytics != null) if (ClassAnalytics != null)
{ {
if(_isDebug) Debug.Log($"{GuruAnalytics.Tag} Android call static <{typeof(T)}> :: {methodName}"); if(_isDebug) Debug.Log($"{GuruAnalytics.Tag} Android call static <{typeof(T).ToString()}> :: {methodName}");
return ClassAnalytics.CallStatic<T>(methodName, args); return ClassAnalytics.CallStatic<T>(methodName, args);
} }
} }
@ -72,60 +80,17 @@ namespace Guru
#region 接口实现 #region 接口实现
/// <summary> public async void Init(string appId, string deviceInfo, Action onInitComplete, bool isDebug = false)
/// 默认的启动参数
/// </summary>
/// <param name="appId"></param>
/// <param name="deviceInfo"></param>
/// <param name="onInitComplete"></param>
/// <param name="isDebug"></param>
public void Init(string appId, string deviceInfo, Action onInitComplete, bool isDebug = false)
{
InitAndroidConfig(appId, deviceInfo, "", null, onInitComplete, isDebug); // 调用接口
}
/// <summary>
/// 面向 Android 启动专用的 API
/// </summary>
/// <param name="appId"></param>
/// <param name="deviceInfo"></param>
/// <param name="baseUrl"></param>
/// <param name="uploadIpAddress"></param>
/// <param name="onInitComplete"></param>
/// <param name="isDebug"></param>
public void InitAndroidConfig(string appId, string deviceInfo, string baseUrl, string[]uploadIpAddress,Action onInitComplete = null, bool isDebug = false)
{ {
_isDebug = isDebug; _isDebug = isDebug;
string bundleId = Application.identifier; string bundleId = Application.identifier;
CallSDKInit(appId, deviceInfo, bundleId, baseUrl, uploadIpAddress , true, false, _isDebug); // 调用接口 // public static void init(String appId, String deviceInfo, String bundleId, boolean isDebug, boolean useWorker, boolean useCronet, String baseUrl)
// TODO: 将来把 CallStatic 转为异步实现
CallStatic("init", appId, deviceInfo, bundleId, isDebug, UseWorker, UseCronet, BaseUrl); // 调用接口
onInitComplete?.Invoke(); onInitComplete?.Invoke();
} }
/********* Android API **********
public static void init(String appId,
String deviceInfo,
String bundleId,
boolean debug,
boolean useWorker,
boolean enabledCronet,
String baseUrl,
List<String> uploadIpAddress)
*/
private void CallSDKInit(string appId,
string deviceInfo,
string bundleId,
string baseUrl = "",
string[] uploadIpAddress = null,
bool useWorker = true,
bool useCronet = false,
bool isDebug = false)
{
CallStatic("init", appId, deviceInfo, bundleId, isDebug, useWorker, useCronet, baseUrl, string.Join(",", uploadIpAddress ?? Array.Empty<string>())); // 调用接口
}
public void SetScreen(string screenName) public void SetScreen(string screenName)
{ {
@ -169,7 +134,10 @@ namespace Guru
} }
public bool IsDebug => CallStatic<bool>("isDebug"); public bool IsDebug => CallStatic<bool>("isDebug");
public void LogEvent(string eventName, string parameters, int priority = 0) => CallStatic("logEvent", eventName, parameters, priority); public void LogEvent(string eventName, string parameters, int priority = -1)
{
CallStatic("logEvent", eventName, parameters, priority);
}
public void ReportEventSuccessRate() => CallStatic("reportEventRate"); public void ReportEventSuccessRate() => CallStatic("reportEventRate");
public void SetTch02Value(double value) => CallStatic("setTch02Value", value); public void SetTch02Value(double value) => CallStatic("setTch02Value", value);
public void InitCallback(string objName, string method) => CallStatic("initCallback", objName, method); public void InitCallback(string objName, string method) => CallStatic("initCallback", objName, method);

View File

@ -125,7 +125,7 @@ namespace Guru
public bool IsDebug => _isDebug; public bool IsDebug => _isDebug;
public void LogEvent(string eventName, string data, int priority = 0) public void LogEvent(string eventName, string data, int priority = -1)
{ {
#if UNITY_IOS #if UNITY_IOS
unityLogEvent(eventName, data); unityLogEvent(eventName, data);

View File

@ -92,7 +92,7 @@ namespace Guru
public bool IsDebug => _isDebug; public bool IsDebug => _isDebug;
public void LogEvent(string eventName, string parameters, int priority = 0) public void LogEvent(string eventName, string parameters, int priority = -1)
{ {
if (_isShowLog) if (_isShowLog)
{ {

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 82693d012b64748c8ac997389b0426a7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Guru
{
public class GuruAnalyticsDemo: MonoBehaviour
{
[SerializeField] private bool _isDebug = true;
[SerializeField] private Button _btnInitSDK;
[SerializeField] private Button _btnStatus;
[SerializeField] private Button _btnUserProperties;
[SerializeField] private Button _btnEvents;
[SerializeField] private Button _btnEvents2;
[SerializeField] private Button _btnReport;
[SerializeField] private Button _btnTestCrash;
// ----------- All Status IDs -----------
private static readonly string AdjustID = "e35b41522140fa2db9089ef3c78eb8f9";
private static readonly string FirebaseID = "b7ab5fc399a7bc8725c004943fa82837";
private static readonly string UID = "BS-YYYYF";
private static readonly string AdID = "dda3cc2b-5a5e-44cb-8a59-4a0b1b3780fd";
private static readonly string DeviceID = "e2fb3c5a4c36473648c989bd86a41153";
private static readonly string AppID = "";
private static readonly string DeviceInfo = "";
private static readonly string ScreenName = "MainMenu";
private void Awake()
{
_btnInitSDK.onClick.AddListener(OnClickInit);
_btnStatus.onClick.AddListener(OnClickStatus);
_btnUserProperties.onClick.AddListener(OnClickUserProperties);
_btnEvents.onClick.AddListener(OnClickEvents);
_btnEvents2.onClick.AddListener(OnClickEvents2);
_btnReport.onClick.AddListener(OnClickReport);
_btnTestCrash.onClick.AddListener(OnClickTestCrash);
#if !UNITY_IOS
_btnTestCrash.gameObject.SetActive(false);
#endif
}
#region Button Callbacks
private void OnClickInit()
{
Debug.Log($"---- [DEMO] Call Analytics init");
GuruAnalytics.Init(AppID, DeviceInfo, OnGuruAnalyticsInitComplete, _isDebug);
}
private void OnGuruAnalyticsInitComplete()
{
}
private void OnClickStatus()
{
Debug.Log($"---- [DEMO] Report Stats IDs: UID:{UID} DeviceID:{DeviceID} FirebaseID:{FirebaseID} AdID:{AdID} AdjustID:{AdjustID}");
GuruAnalytics.Instance.SetUid(UID);
GuruAnalytics.Instance.SetDeviceId(DeviceID);
GuruAnalytics.Instance.SetFirebaseId(FirebaseID);
GuruAnalytics.Instance.SetAdId(AdID);
GuruAnalytics.Instance.SetAdjustId(AdjustID);
}
private void OnClickUserProperties()
{
string item_category = "main";
int level = 7;
Debug.Log($"---- [DEMO] Call SetUserProperty: item_category:{item_category} level:{level}");
GuruAnalytics.Instance.SetUserProperty("item_category", item_category);
GuruAnalytics.Instance.SetUserProperty("level", level.ToString());
}
private void OnClickEvents()
{
Debug.Log($"---- [DEMO] Report Screen: {ScreenName}");
GuruAnalytics.Instance.SetScreen(ScreenName);
string eventName = "user_get_coin";
Dictionary<string, dynamic> data = new Dictionary<string, dynamic>()
{
{ "level", 7 },
{ "user_coin", 105L },
{ "win_rate", 21.25f },
{ "b_level", 7 },
{ "result", "retry" }
};
string s = "---- Data ----\n";
foreach (var k in data.Keys)
{
s += $"-- K:{k} V:{data[k]}\n";
}
Debug.Log(s);
Debug.Log($"---- [DEMO] Call LogEvent");
GuruAnalytics.Instance.LogEvent(eventName, data);
}
private void OnClickEvents2()
{
string eventName = "user_data_loaded";
GuruAnalytics.Instance.LogEvent(eventName);
}
private void OnClickReport()
{
GuruAnalytics.Instance.ReportEventSuccessRate();
}
private void OnClickTestCrash()
{
#if UNITY_IOS
Debug.Log($"--> OnClickTestCrash");
GuruAnalytics.TestCrash();
#endif
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8fde2a18f7d347408a6b869ee03e7de9
timeCreated: 1672712830

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: ea095a004daab4fc096aa297c21fca99
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -116,7 +116,7 @@ namespace Guru
private void OnLoadMaxBanner() private void OnLoadMaxBanner()
{ {
_badsLoadStartTime = DateTime.UtcNow; _badsloadStartTime = DateTime.UtcNow;
_chanelMax.LoadBannerAD(); _chanelMax.LoadBannerAD();
OnBannerStartLoad?.Invoke(_chanelMax.MaxBADSSlotID); OnBannerStartLoad?.Invoke(_chanelMax.MaxBADSSlotID);
} }

View File

@ -1,11 +1,12 @@
using System.Linq;
namespace Guru namespace Guru
{ {
using System; using System;
using UnityEngine; using UnityEngine;
using System.Collections.Generic; using System.Collections.Generic;
using Guru.Ads;
//TODO: 将 BADSIADSRADS 分别放到不同的类中,方便维护
public abstract class ADServiceBase<T> : IADService where T : new() public abstract class ADServiceBase<T> : IADService where T : new()
{ {
// 单利定义 // 单利定义
@ -20,9 +21,9 @@ namespace Guru
} }
} }
private const string Tag = "[SDK][ADS]"; protected static readonly string Tag = "[SDK][ADS]";
public bool IsInitialized => MaxSdk.IsInitialized() || _isServiceStarted; public bool IsInitialized => MaxSdk.IsInitialized() || _isServiceStarted;
private static bool IsNetworkEnabled => Application.internetReachability != NetworkReachability.NotReachable; protected bool IsNetworkEnabled => Application.internetReachability != NetworkReachability.NotReachable;
private const int MAX_ADS_RELOAD_INTERVAL = 6; // 广告加载最高时间为 2 的 6 次方 = 64秒 private const int MAX_ADS_RELOAD_INTERVAL = 6; // 广告加载最高时间为 2 的 6 次方 = 64秒
@ -30,23 +31,28 @@ namespace Guru
protected Action _onSdkInitReady; protected Action _onSdkInitReady;
public Action<string> OnBannerStartLoad; public static Action<string> OnBannerStartLoad;
public Action OnBannerLoaded; public static Action OnBannerLoaded;
public Action<string> OnInterstitialStartLoad; public static Action<string> OnInterstitialStartLoad;
public Action OnInterstitialLoaded; public static Action OnInterstitialLoaded;
public Action OnInterstitialFailed; public static Action OnInterstitialFailed;
public Action OnInterstitialClosed; public static Action OnInterstitialClosed;
public Action<string> OnRewardedStartLoad; public static Action<string> OnRewardedStartLoad;
public Action OnRewardLoaded; public static Action OnRewardLoaded;
public Action OnRewardFailed; public static Action OnRewardFailed;
public Action OnRewardClosed; public static Action OnRewardClosed;
private AdsModel _model; private Dictionary<string, string> _reviewCreativeIds = new Dictionary<string, string>(10); // Creative ID 缓存: Cid : RCid
private Dictionary<string, List<AdImpressionData>> _impressionCache = new Dictionary<string, List<AdImpressionData>>(10);
protected AdsModel _model;
protected AdsInitSpec _initSpec = null; protected AdsInitSpec _initSpec = null;
private AdsModel Model private AdImpressionDriver _impressionDriver;
public AdsModel Model
{ {
get get
{ {
@ -61,7 +67,8 @@ namespace Guru
/// 启动广告服务 /// 启动广告服务
/// </summary> /// </summary>
/// <param name="callback">广告初始化回调</param> /// <param name="callback">广告初始化回调</param>
/// <param name="initSpec">初始化配置参数</param> /// <param name="autoLoadAds">自动启动广告加载</param>
/// <param name="isDebugMode">debug模式</param>
public virtual void StartService(Action callback = null, AdsInitSpec initSpec = null) public virtual void StartService(Action callback = null, AdsInitSpec initSpec = null)
{ {
if (IsInitialized) return; // 已经初始化后, 无需再次初始化 if (IsInitialized) return; // 已经初始化后, 无需再次初始化
@ -75,6 +82,9 @@ namespace Guru
if(_model == null) _model = AdsModel.Create(); if(_model == null) _model = AdsModel.Create();
this.Log("AD SDK Start Init"); this.Log("AD SDK Start Init");
_impressionDriver = new AdImpressionDriver();
_impressionDriver.Init(ReportAdsRevenue); // 初始化 Impression 驱动器
InitMaxCallbacks(); // 初始化 MAX 广告 InitMaxCallbacks(); // 初始化 MAX 广告
InitService(); // 内部继承接口 InitService(); // 内部继承接口
} }
@ -89,14 +99,13 @@ namespace Guru
//--------------- MRec 回调 ----------------- //--------------- MRec 回调 -----------------
// MaxSdkCallbacks.MRec.OnAdRevenuePaidEvent += OnAdRevenuePaidEvent; // MaxSdkCallbacks.MRec.OnAdRevenuePaidEvent += OnAdRevenuePaidEvent;
//--------------- Banner 回调 ----------------- //--------------- Banner 回调 -----------------
MaxSdkCallbacks.Banner.OnAdLoadedEvent += OnBannerLoadedEvent; MaxSdkCallbacks.Banner.OnAdLoadedEvent += OnBannerLoadedEvent;
MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += OnBannerFailedEvent; MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += OnBannerFailedEvent;
MaxSdkCallbacks.Banner.OnAdClickedEvent += OnBannerClickedEvent; MaxSdkCallbacks.Banner.OnAdClickedEvent += OnBannerClickedEvent;
// MaxSdkCallbacks.Banner.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent;
MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent += OnBannerRevenuePaidEvent; MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent += OnBannerRevenuePaidEvent;
MaxSdkCallbacks.Banner.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent;
//--------------- IV 回调 ----------------- //--------------- IV 回调 -----------------
MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnInterstitialLoadedEvent; MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnInterstitialLoadedEvent;
MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnInterstitialFailedEvent; MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnInterstitialFailedEvent;
@ -105,7 +114,7 @@ namespace Guru
MaxSdkCallbacks.Interstitial.OnAdDisplayedEvent += OnInterstitialDisplayEvent; MaxSdkCallbacks.Interstitial.OnAdDisplayedEvent += OnInterstitialDisplayEvent;
MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += OnInterstitialDismissedEvent; MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += OnInterstitialDismissedEvent;
MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent += OnInterstitialPaidEvent; MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent += OnInterstitialPaidEvent;
// MaxSdkCallbacks.Interstitial.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent; MaxSdkCallbacks.Interstitial.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent;
//--------------- RV 回调 ----------------- //--------------- RV 回调 -----------------
MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnRewardedAdLoadedEvent; MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnRewardedAdLoadedEvent;
MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnRewardedAdFailedEvent; MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnRewardedAdFailedEvent;
@ -113,15 +122,20 @@ namespace Guru
MaxSdkCallbacks.Rewarded.OnAdDisplayedEvent += OnRewardedAdDisplayedEvent; MaxSdkCallbacks.Rewarded.OnAdDisplayedEvent += OnRewardedAdDisplayedEvent;
MaxSdkCallbacks.Rewarded.OnAdClickedEvent += OnRewardedAdClickedEvent; MaxSdkCallbacks.Rewarded.OnAdClickedEvent += OnRewardedAdClickedEvent;
MaxSdkCallbacks.Rewarded.OnAdHiddenEvent += OnRewardedAdDismissedEvent; MaxSdkCallbacks.Rewarded.OnAdHiddenEvent += OnRewardedAdDismissedEvent;
MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent += OnRewardedAdPaidEvent;
MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnRewardedAdReceivedRewardEvent; MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnRewardedAdReceivedRewardEvent;
// MaxSdkCallbacks.Rewarded.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent; MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent += OnRewardedAdPaidEvent;
MaxSdkCallbacks.Rewarded.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent;
//--------------- Creative 回调 -----------------
//-------------- SDK 初始化 ------------------- //-------------- SDK 初始化 -------------------
MaxSdk.SetExtraParameter("enable_black_screen_fixes", "true"); // 修复黑屏 MaxSdk.SetExtraParameter("enable_black_screen_fixes", "true"); // 修复黑屏
} }
protected virtual void InitService() protected virtual void InitService()
{ {
} }
@ -149,7 +163,7 @@ namespace Guru
/// 可加载广告 /// 可加载广告
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private bool CanLoadAds() public virtual bool CanLoadAds()
{ {
return IsInitialized && IsNetworkEnabled; return IsInitialized && IsNetworkEnabled;
} }
@ -200,21 +214,26 @@ namespace Guru
/// <summary> /// <summary>
/// 上报广告收益 /// 上报广告收益
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="adInfo"></param>
private void ReportAdsRevenue(AdParams data) /// <param name="reviewedCreativeId"></param>
private void ReportAdsRevenue(AdImpressionData data)
{ {
// #1 ad_impression // #1 ad_impression
Analytics.ADImpression(data.ToAdImpressionData()); Analytics.ADImpression(data);
// #2 tch_001 // #2 tch_001 和 tch_02
double revenue = data.value; double revenue = data.value;
CalcTch001Value(revenue); CalcTch001Value(revenue);
CalcTch02Value(revenue); CalcTch02Value(revenue);
// #3 adjust_ad_revenue // #3 adjust_ad_revenue
AdjustService.TrackADRevenue(data.value, data.currency, data.adSource, data.adUnitId, data.networkPlacement); AdjustService.TrackADRevenue(data.value, data.currency, data.ad_source, data.ad_unit_name, data.ad_placement);
} }
/// <summary> /// <summary>
/// 计算太极001收益 /// 计算太极001收益
/// </summary> /// </summary>
@ -223,10 +242,10 @@ namespace Guru
{ {
TchAD001RevValue += revenue; TchAD001RevValue += revenue;
double revenueValue = TchAD001RevValue; double revenueValue = TchAD001RevValue;
// Debug.Log($"{Tag} --- [Tch] get <TchAD001RevValue> totally: {revenueValue}"); Debug.Log($"[TaichConfig] get <TchAD001RevValue> totally: {revenueValue}");
if (revenueValue >= Analytics.Tch001TargetValue) if (revenueValue >= Analytics.Tch001TargetValue)
{ {
Debug.Log($"{Tag} --- [Tch] call <tch_ad_rev_roas_001> with value: {revenueValue}"); Debug.Log($"[TaichConfig] call <tch_ad_rev_roas_001> with value: {revenueValue}");
Analytics.Tch001ADRev(revenueValue); Analytics.Tch001ADRev(revenueValue);
TchAD001RevValue = 0.0; TchAD001RevValue = 0.0;
} }
@ -255,15 +274,15 @@ namespace Guru
#region Banner Ads #region Banner Ads
private string _backColorStr = "#50A436";
private Color _backColor = new Color(0, 0, 0, 0); private Color _backColor = new Color(0, 0, 0, 0);
private string _badsCategory; private string _badsCategory;
protected DateTime _badsLoadStartTime; protected DateTime _badsloadStartTime;
private bool _bannerVisible = false; private bool _bannerVisible = false;
public bool IsBannerVisible => _bannerVisible; public bool IsBannerVisible => _bannerVisible;
private int _badsLoadedNum = 0; private int _badsloadedNum = 0;
private int _badsLoadFailNum = 0; private int _badsLoadFailNum = 0;
/// <summary> /// <summary>
/// 获取动作间隔之间的毫秒数 /// 获取动作间隔之间的毫秒数
/// </summary> /// </summary>
@ -290,32 +309,32 @@ namespace Guru
/// <summary> /// <summary>
/// Banner MAX 加载方式 /// Banner MAX 加载方式
/// </summary> /// </summary>
private void LoadMaxBannerAd() protected void LoadMaxBannerAd()
{ {
OnLoadBads(); OnLoadBads();
// Banners are automatically sized to 320x50 on phones and 728x90 on tablets // Banners are automatically sized to 320x50 on phones and 728x90 on tablets
// You may use the utility method `MaxSdkUtils.isTablet()` to help with view sizing adjustments // You may use the utility method `MaxSdkUtils.isTablet()` to help with view sizing adjustments
var adUnitId = GetBannerID(); var id = GetBannerID();
MaxSdk.CreateBanner(adUnitId, MaxSdkBase.BannerPosition.BottomCenter); MaxSdk.CreateBanner(id, MaxSdkBase.BannerPosition.BottomCenter);
MaxSdk.SetBannerExtraParameter(adUnitId, "adaptive_banner", "false"); MaxSdk.SetBannerExtraParameter(id, "adaptive_banner", "false");
// Set background or background color for banners to be fully functional // Set background or background color for banners to be fully functional
MaxSdk.SetBannerBackgroundColor(adUnitId, _backColor); MaxSdk.SetBannerBackgroundColor(id, _backColor);
// Analytics.ADBadsLoad(GetBannerID()); // Analytics.ADBadsLoad(GetBannerID());
Analytics.TrackAdBadsLoad(AdParams.Build(adUnitId:adUnitId).ToAdLoadedData()); Analytics.ADBadsLoad(AdParams.Build(id));
} }
private void OnLoadBads() public void OnLoadBads()
{ {
_badsLoadStartTime = DateTime.UtcNow; _badsloadStartTime = DateTime.UtcNow;
} }
private void OnBadsLoaded() protected virtual void OnBadsLoaded()
{ {
_badsLoadStartTime = DateTime.UtcNow; _badsloadStartTime = DateTime.UtcNow;
OnBannerLoaded?.Invoke(); OnBannerLoaded?.Invoke();
} }
public void SetBannerAutoRefresh(bool value = true, string adUnitId = "") public virtual void SetBannerAutoRefresh(bool value = true, string adUnitId = "")
{ {
if(string.IsNullOrEmpty(adUnitId)) adUnitId = GetBannerID(); if(string.IsNullOrEmpty(adUnitId)) adUnitId = GetBannerID();
if (value) if (value)
@ -341,10 +360,9 @@ namespace Guru
SetBannerAutoRefresh(true, adUnitId); SetBannerAutoRefresh(true, adUnitId);
if (!_bannerVisible) if (!_bannerVisible)
{ {
// 从隐藏状态到显示,累计的加载次数和失败次数清零
_bannerVisible = true; _bannerVisible = true;
OnBannerImpEvent(adUnitId); OnBannerImpEvent(adUnitId);
_badsLoadedNum = 0; _badsloadedNum = 0;
_badsLoadFailNum = 0; _badsLoadFailNum = 0;
} }
} }
@ -352,7 +370,7 @@ namespace Guru
/// <summary> /// <summary>
/// 隐藏 Banner /// 隐藏 Banner
/// </summary> /// </summary>
public void HideBanner() public virtual void HideBanner()
{ {
string adUnitId = GetBannerID(); string adUnitId = GetBannerID();
MaxSdk.HideBanner(adUnitId); MaxSdk.HideBanner(adUnitId);
@ -368,44 +386,47 @@ namespace Guru
/// 设置 Banner 背景颜色 /// 设置 Banner 背景颜色
/// </summary> /// </summary>
/// <param name="color"></param> /// <param name="color"></param>
public void SetBannerBackgroundColor(Color color) public virtual void SetBannerBackgroundColor(Color color)
{ {
_backColor = color; _backColor = color;
} }
private void OnBannerLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) private void OnBannerLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{ {
_badsLoadedNum++; // 记录加载次数 _badsloadedNum++;
// --- fixed by Yufei 2024-5-29 为 don't report bads_loaded any more. ---
// Analytics.ADBadsLoaded(AdParams.Build(adUnitId, adInfo,
// duration: GetAdsLoadDuration(ref _badsloadStartTime), category: _badsCategory));
Debug.Log( $"[SDK][Ads][Loaded] --- adUnitId:{adUnitId} Revenue:{adInfo.Revenue} Type:{adInfo.AdFormat} CreativeId:{adInfo.CreativeIdentifier}"); Debug.Log( $"[SDK][Ads][Loaded] --- adUnitId:{adUnitId} Revenue:{adInfo.Revenue} Type:{adInfo.AdFormat} CreativeId:{adInfo.CreativeIdentifier}");
OnBadsLoaded(); OnBadsLoaded();
// BADSLoaded 事件已经不做上报处理了
// --- fixed by YuFei 2024-5-29 为 don't report bads_loaded any more. ---
// Analytics.ADBadsLoaded(AdParams.Build(adUnitId, adInfo,
// duration: GetAdsLoadDuration(ref _badsLoadStartTime), category: _badsCategory));
} }
private void OnBannerFailedEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo) private void OnBannerFailedEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
{ {
_badsLoadFailNum++; // 记录加载失败次数 _badsLoadFailNum ++;
Analytics.TrackAdBadsFailed(BuildBadsFailedData(adUnitId, errorInfo)); // Analytics.ADBadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _badsloadStartTime), _badsCategory);
Analytics.ADBadsFailed(AdParams.Build(adUnitId,
duration: GetActionDuration(_badsloadStartTime), category: _badsCategory,
errorCode: (int)errorInfo.Code,
waterfallName: errorInfo?.WaterfallInfo?.Name ?? ""));
} }
private void OnBannerClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) private void OnBannerClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{ {
// Analytics.ADBadsClick(adUnitId, _badsCategory); // Analytics.ADBadsClick(adUnitId, _badsCategory);
Analytics.TrackAdBadsClick(BuildBadsClickData(adInfo, adUnitId)); Analytics.ADBadsClick(AdParams.Build(adUnitId, adInfo, _badsCategory));
} }
private void OnBannerImpEvent(string adUnitId) private void OnBannerImpEvent(string adUnitId)
{ {
// Analytics.ADBadsClick(adUnitId, _badsCategory); // Analytics.ADBadsClick(adUnitId, _badsCategory);
Analytics.TrackAdBadsImp(BuildBadsImpData(adUnitId)); Analytics.ADBadsImp(AdParams.Build(adUnitId, category: _badsCategory));
} }
private void OnBannerHideEvent() private void OnBannerHideEvent()
{ {
Analytics.TrackAdBadsHide(_badsLoadedNum, _badsLoadFailNum); Analytics.ADBadsHide(_badsloadedNum, _badsLoadFailNum);
} }
/// <summary> /// <summary>
@ -417,7 +438,8 @@ namespace Guru
{ {
if (_bannerVisible) if (_bannerVisible)
{ {
ReportAdsRevenue(AdParams.Build(adInfo, adUnitId)); // Banner 只有显示时才上报收益值
AppendImpressionData(adInfo, eventName:Analytics.EventBadsPaid, itemCategory:_badsCategory);
} }
} }
@ -428,7 +450,7 @@ namespace Guru
private string _iadsCategory = "main"; private string _iadsCategory = "main";
private int _interstitialRetryAttempt; private int _interstitialRetryAttempt;
private Action _interCloseAction; private Action _interCloseAction;
private bool _isIadsLoading = false; protected bool _isIadsLoading = false;
protected DateTime _iadsLoadStartTime; protected DateTime _iadsLoadStartTime;
private DateTime _iadsDisplayStartTime; private DateTime _iadsDisplayStartTime;
@ -446,15 +468,15 @@ namespace Guru
} }
private void LoadMaxInterstitial() protected void LoadMaxInterstitial()
{ {
OnLoadIads(); OnLoadIads();
var adUnitId = GetInterstitialID(); var id = GetInterstitialID();
Analytics.TrackAdIadsLoad(AdParams.Build(adUnitId:adUnitId).ToAdLoadedData()); Analytics.ADIadsLoad(AdParams.Build(id));
MaxSdk.LoadInterstitial(adUnitId); MaxSdk.LoadInterstitial(id);
} }
private void OnLoadIads() public void OnLoadIads()
{ {
_iadsLoadStartTime = DateTime.UtcNow; _iadsLoadStartTime = DateTime.UtcNow;
} }
@ -469,7 +491,8 @@ namespace Guru
/// <summary> /// <summary>
/// 显示插屏广告 /// 显示插屏广告
/// </summary> /// </summary>
/// <param name="category">广告奖励回调</param> /// <param name="rewardAction">广告奖励回调</param>
/// <param name="failAction">广告失败回调</param>
/// <param name="dismissAction">广告界面关闭回调</param> /// <param name="dismissAction">广告界面关闭回调</param>
public virtual void ShowInterstitialAD(string category, Action dismissAction = null) public virtual void ShowInterstitialAD(string category, Action dismissAction = null)
{ {
@ -490,6 +513,7 @@ namespace Guru
MaxSdk.ShowInterstitial(GetInterstitialID()); MaxSdk.ShowInterstitial(GetInterstitialID());
_iadsDisplayStartTime = DateTime.UtcNow; _iadsDisplayStartTime = DateTime.UtcNow;
// RequestInterstitialAD(); // 直接加载下一个广告
} }
protected virtual void OnInterstitialLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) protected virtual void OnInterstitialLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
@ -498,9 +522,12 @@ namespace Guru
// Interstitial ad is ready to be shown. MaxSdk.IsInterstitialReady(interstitialAdUnitId) will now return 'true' // Interstitial ad is ready to be shown. MaxSdk.IsInterstitialReady(interstitialAdUnitId) will now return 'true'
// Reset retry attempt // Reset retry attempt
// Analytics.ADIadsLoaded(adUnitId, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory); // Analytics.ADIadsLoaded(adUnitId, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory);
Analytics.TrackAdIadsLoaded(BuildIadsLoadedData(adInfo, adUnitId)); Analytics.ADIadsLoaded(AdParams.Build(adUnitId,
duration: GetActionDuration(_iadsLoadStartTime), category: _iadsCategory));
_interstitialRetryAttempt = 0; _interstitialRetryAttempt = 0;
Debug.Log( $"[SDK][Ads][Loaded] --- adUnitId:{adUnitId} Revenue:{adInfo.Revenue} Type:{adInfo.AdFormat} CreativeId:{adInfo.CreativeIdentifier}"); Debug.Log( $"[SDK][Ads][Loaded] --- adUnitId:{adUnitId} Revenue:{adInfo.Revenue} Type:{adInfo.AdFormat} CreativeId:{adInfo.CreativeIdentifier}");
OnInterstitialLoaded?.Invoke(); OnInterstitialLoaded?.Invoke();
} }
@ -515,7 +542,11 @@ namespace Guru
float retryDelay = GetRetryDelaySeconds(_interstitialRetryAttempt); float retryDelay = GetRetryDelaySeconds(_interstitialRetryAttempt);
DelayCall(retryDelay, RequestInterstitialAD); DelayCall(retryDelay, RequestInterstitialAD);
// Analytics.ADIadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory); // Analytics.ADIadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory);
Analytics.TrackAdIadsFailed(BuildIadsFailedData(adUnitId, errorInfo)); if(string.IsNullOrEmpty(_iadsCategory)) _iadsCategory = "not_set";
Analytics.ADIadsFailed(AdParams.Build(adUnitId,
duration: GetActionDuration(_iadsLoadStartTime), category: _iadsCategory,
errorCode: (int)errorInfo.Code,
waterfallName: errorInfo?.WaterfallInfo?.Name ?? ""));
OnInterstitialFailed?.Invoke(); OnInterstitialFailed?.Invoke();
} }
@ -527,7 +558,10 @@ namespace Guru
this.LogError( this.LogError(
$"InterstitialFailedToDisplayEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}"); $"InterstitialFailedToDisplayEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}");
// Analytics.ADIadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory); // Analytics.ADIadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory);
Analytics.TrackAdIadsFailed(BuildIadsFailedData(adUnitId, errorInfo)); Analytics.ADIadsFailed(AdParams.Build(adUnitId,
duration: GetActionDuration(_iadsDisplayStartTime), category: _iadsCategory,
errorCode: (int)errorInfo.Code,
waterfallName: errorInfo.WaterfallInfo?.Name ?? ""));
DelayCall(2.0f, RequestInterstitialAD); DelayCall(2.0f, RequestInterstitialAD);
} }
@ -535,13 +569,16 @@ namespace Guru
protected virtual void OnInterstitialDisplayEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) protected virtual void OnInterstitialDisplayEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{ {
// Analytics.ADIadsImp(adUnitId, _iadsCategory); // Analytics.ADIadsImp(adUnitId, _iadsCategory);
Analytics.TrackAdIadsImp(BuildIadsImpData(adInfo, adUnitId)); AppendImpressionData(adInfo, eventName:Analytics.EventIadsImp, itemCategory:_iadsCategory);
} }
protected virtual void OnInterstitialClickEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) protected virtual void OnInterstitialClickEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{ {
// Analytics.ADIadsClick(adUnitId, _iadsCategory); // Analytics.ADIadsClick(adUnitId, _iadsCategory);
Analytics.TrackAdIadsClick(BuildIadsClickData(adInfo, adUnitId)); if(string.IsNullOrEmpty(_iadsCategory)) _iadsCategory = "not_set";
AppendImpressionData(adInfo,
eventName:Analytics.EventIadsClick,
itemCategory:_iadsCategory);
} }
// Close // Close
@ -551,7 +588,13 @@ namespace Guru
_interCloseAction?.Invoke(); _interCloseAction?.Invoke();
OnInterstitialClosed?.Invoke(); OnInterstitialClosed?.Invoke();
// Analytics.ADIadsClose(adUnitId, _iadsCategory); // Analytics.ADIadsClose(adUnitId, _iadsCategory);
Analytics.TrackAdIadsClose(BuildIadsCloseData(adInfo, adUnitId)); Analytics.ADIadsClose(new Dictionary<string, object>()
{
[Analytics.ParameterItemCategory] = _iadsCategory,
[Analytics.ParameterDuration] = GetActionDuration(_iadsDisplayStartTime),
});
_impressionDriver.CleanCreativeId(adInfo.CreativeIdentifier);
//延时加载下一个广告 //延时加载下一个广告
DelayCall(2.0f, RequestInterstitialAD); DelayCall(2.0f, RequestInterstitialAD);
@ -559,9 +602,7 @@ namespace Guru
private void OnInterstitialPaidEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) private void OnInterstitialPaidEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{ {
var p = AdParams.Build(adInfo, adUnitId, category:_iadsCategory); AppendImpressionData(adInfo, eventName:Analytics.EventIadsPaid, itemCategory: _iadsCategory);
Analytics.TrackAdIadsPaid(p.ToAdPaidData());
ReportAdsRevenue(p);
} }
@ -569,14 +610,14 @@ namespace Guru
#region Rewarded Ads #region Rewarded Ads
private string _radsCategory = "main"; private string _rewardCategory = "main";
private int _rewardRetryAttempt; private int _rewardRetryAttempt;
protected DateTime _radsLoadStartTime; protected DateTime _radsLoadStartTime;
private DateTime _radsDisplayStartTime; protected DateTime _radsShowStartTime;
private Action _rvRewardAction; private Action _rvRewardAction;
private Action<string> _rvFailAction; private Action<string> _rvFailAction;
private Action _rvDismissAction; private Action _rvDismissAction;
private bool _isRadsLoading = false; protected bool _isRadsLoading = false;
public bool IsRadsLoading => _isRadsLoading; public bool IsRadsLoading => _isRadsLoading;
public virtual void RequestRewardedAD() public virtual void RequestRewardedAD()
@ -593,17 +634,17 @@ namespace Guru
/// <summary> /// <summary>
/// 默认加载 MAX 广告逻辑 /// 默认加载 MAX 广告逻辑
/// </summary> /// </summary>
private void LoadMaxRewardAd() protected void LoadMaxRewardAd(string unitId = "")
{ {
if (IsRadsLoading) return; if (IsRadsLoading) return;
OnLoadRads(); OnLoadRads();
var adUnitId = GetRewardedID(); var id = GetRewardedID();
Analytics.TrackAdRadsLoad(AdParams.Build(adUnitId:adUnitId).ToAdLoadData()); // 上报打点 Analytics.ADRadsLoad(AdParams.Build(id)); // 上报打点
MaxSdk.LoadRewardedAd(adUnitId); MaxSdk.LoadRewardedAd(id);
} }
private void OnLoadRads() public void OnLoadRads()
{ {
_radsLoadStartTime = DateTime.UtcNow; _radsLoadStartTime = DateTime.UtcNow;
} }
@ -620,8 +661,7 @@ namespace Guru
/// <summary> /// <summary>
/// 显示激励视频广告 /// 显示激励视频广告
/// </summary> /// </summary>
/// <param name="category">广告奖励回调</param> /// <param name="rewardAction">广告奖励回调</param>
/// <param name="rewardAction">广告失败回调</param>
/// <param name="failAction">广告失败回调</param> /// <param name="failAction">广告失败回调</param>
/// <param name="dismissAction">广告界面关闭回调</param> /// <param name="dismissAction">广告界面关闭回调</param>
public virtual void ShowRewardedAD(string category, Action rewardAction = null, public virtual void ShowRewardedAD(string category, Action rewardAction = null,
@ -639,13 +679,13 @@ namespace Guru
return; return;
} }
_radsCategory = category; _rewardCategory = category;
_rvRewardAction = rewardAction; _rvRewardAction = rewardAction;
_rvFailAction = failAction; _rvFailAction = failAction;
_rvDismissAction = dismissAction; _rvDismissAction = dismissAction;
MaxSdk.ShowRewardedAd(GetRewardedID()); MaxSdk.ShowRewardedAd(GetRewardedID());
_radsDisplayStartTime = DateTime.UtcNow; _radsShowStartTime = DateTime.UtcNow;
// RequestRewardedAD(); // RequestRewardedAD();
} }
@ -658,7 +698,8 @@ namespace Guru
// Reset retry attempt // Reset retry attempt
// this.Log("OnRewardedAdLoadedEvent"); // this.Log("OnRewardedAdLoadedEvent");
// Analytics.ADRadsLoaded(adUnitId, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory); // Analytics.ADRadsLoaded(adUnitId, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory);
Analytics.TrackAdRadsLoaded(BuildRadsLoadedData(adInfo, adUnitId)); Analytics.ADRadsLoaded(AdParams.Build(adUnitId,
duration: GetActionDuration(_radsLoadStartTime), category: _iadsCategory));
_rewardRetryAttempt = 0; _rewardRetryAttempt = 0;
Debug.Log( $"[SDK][Ads][Loaded] --- adUnitId:{adUnitId} Revenue:{adInfo.Revenue} Type:{adInfo.AdFormat} CreativeId:{adInfo.CreativeIdentifier}"); Debug.Log( $"[SDK][Ads][Loaded] --- adUnitId:{adUnitId} Revenue:{adInfo.Revenue} Type:{adInfo.AdFormat} CreativeId:{adInfo.CreativeIdentifier}");
@ -675,7 +716,10 @@ namespace Guru
this.LogError( this.LogError(
$"OnRewardedAdFailedEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}"); $"OnRewardedAdFailedEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}");
// Analytics.ADRadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory); // Analytics.ADRadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory);
Analytics.TrackAdRadsFailed(BuildRadsFailedData(adUnitId, errorInfo)); Analytics.ADRadsFailed(AdParams.Build(adUnitId,
duration: GetActionDuration(_radsLoadStartTime), category: _rewardCategory,
errorCode: (int)errorInfo.Code,
waterfallName: errorInfo?.WaterfallInfo?.Name ?? ""));
_rewardRetryAttempt++; _rewardRetryAttempt++;
float retryDelay = GetRetryDelaySeconds(_rewardRetryAttempt); float retryDelay = GetRetryDelaySeconds(_rewardRetryAttempt);
DelayCall(retryDelay, RequestRewardedAD); DelayCall(retryDelay, RequestRewardedAD);
@ -690,7 +734,10 @@ namespace Guru
this.LogError( this.LogError(
$"OnRewardedAdFailedToDisplayEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}"); $"OnRewardedAdFailedToDisplayEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}");
// Analytics.ADRadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory); // Analytics.ADRadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory);
Analytics.TrackAdRadsFailed(BuildRadsFailedData(adUnitId, errorInfo)); Analytics.ADRadsFailed(AdParams.Build(adUnitId,
duration: GetActionDuration(_radsShowStartTime), category: _rewardCategory,
errorCode: (int)errorInfo.Code,
waterfallName: errorInfo?.WaterfallInfo?.Name ?? ""));
_rvFailAction?.Invoke("OnRewardedAdFailedToDisplayEvent"); _rvFailAction?.Invoke("OnRewardedAdFailedToDisplayEvent");
DelayCall(2.0f, RequestRewardedAD); DelayCall(2.0f, RequestRewardedAD);
@ -701,17 +748,16 @@ namespace Guru
{ {
this.Log("OnRewardedAdDisplayedEvent"); this.Log("OnRewardedAdDisplayedEvent");
// Analytics.ADRadsImp(adUnitId, _rewardCategory); // Analytics.ADRadsImp(adUnitId, _rewardCategory);
Analytics.TrackAdRadsImp(BuildRadsImpData(adInfo, adUnitId)); AppendImpressionData(adInfo, eventName:Analytics.EventRadsImp, itemCategory: _rewardCategory);
} }
protected virtual void OnRewardedAdClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) protected virtual void OnRewardedAdClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{ {
this.Log("OnRewardedAdClickedEvent"); this.Log("OnRewardedAdClickedEvent");
// Analytics.ADRadsClick(adUnitId, _rewardCategory); // Analytics.ADRadsClick(adUnitId, _rewardCategory);
Analytics.TrackAdRadsClick(BuildRadsClickData(adInfo, adUnitId)); AppendImpressionData(adInfo, eventName:Analytics.EventRadsClick, itemCategory: _rewardCategory);
} }
// rads_close // rads_close
protected virtual void OnRewardedAdDismissedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) protected virtual void OnRewardedAdDismissedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{ {
@ -721,7 +767,13 @@ namespace Guru
OnRewardClosed?.Invoke(); OnRewardClosed?.Invoke();
// Analytics.ADRadsClose(adUnitId, _rewardCategory); // Analytics.ADRadsClose(adUnitId, _rewardCategory);
Analytics.TrackAdRadsClose(BuildRadsCloseData(adInfo, adUnitId)); Analytics.ADRadsClose(new Dictionary<string, object>()
{
[Analytics.ParameterItemCategory] = _rewardCategory,
[Analytics.ParameterDuration] = GetActionDuration(_radsShowStartTime),
});
_impressionDriver.CleanCreativeId(adInfo.CreativeIdentifier);
//延时加载下一个广告 //延时加载下一个广告
DelayCall(2.0f, RequestRewardedAD); DelayCall(2.0f, RequestRewardedAD);
@ -732,18 +784,19 @@ namespace Guru
MaxSdkBase.AdInfo adInfo) MaxSdkBase.AdInfo adInfo)
{ {
this.Log("OnRewardedAdReceivedRewardEvent"); this.Log("OnRewardedAdReceivedRewardEvent");
Analytics.TrackAdRadsRewarded(BuildRadsRewardedData(adInfo, adUnitId)); // Analytics.ADRadsRewarded(adUnitId, _rewardCategory);
// Rewarded ad was displayed and user should receive the reward AppendImpressionData(adInfo, eventName:Analytics.EventRadsRewarded, itemCategory: _rewardCategory);
_rvRewardAction?.Invoke(); _rvRewardAction?.Invoke();
} }
// rads_paid // rads_paid
private void OnRewardedAdPaidEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) private void OnRewardedAdPaidEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{ {
var p = AdParams.Build(adInfo, adUnitId, category:_iadsCategory); this.Log("OnRewardedAdReceivedRewardEvent");
Analytics.TrackAdRadsPaid(p.ToAdPaidData()); AppendImpressionData(adInfo, eventName:Analytics.EventRadsPaid, itemCategory: _rewardCategory);
ReportAdsRevenue(p);
} }
#endregion #endregion
#region Ad Settings #region Ad Settings
@ -769,8 +822,8 @@ namespace Guru
public void ShowMaxDebugPanel() public void ShowMaxDebugPanel()
{ {
#if !UNITY_EDITOR
if (!IsInitialized) return; if (!IsInitialized) return;
#if !UNITY_EDITOR
MaxSdk.ShowMediationDebugger(); MaxSdk.ShowMediationDebugger();
#endif #endif
} }
@ -786,107 +839,86 @@ namespace Guru
#endregion #endregion
#region Build AdParams #region ImpressionData
const int MAX_BADS_CID_NUMBER = 6;
private List<string> _badsCreativeIds = new List<string>(MAX_BADS_CID_NUMBER);
/// <summary>
/// 构建 Impression 数据
/// </summary>
/// <param name="adInfo"></param>
/// <param name="reviewCreativeId"></param>
/// <param name="platform"></param>
/// <param name="eventName"></param>
/// <param name="itemCategory"></param>
/// <param name="duration"></param>
/// <returns></returns>
private AdImpressionData CreateImpressionData(MaxSdkBase.AdInfo adInfo,
string reviewCreativeId = "", string platform = "",
string eventName = "", string itemCategory = "", int duration = 0)
{
var impression = AdImpressionDriver.Build(adInfo.Revenue, Analytics.USD, platform,
adInfo.NetworkName,
adInfo.AdFormat,
adInfo.AdUnitIdentifier,
adInfo.NetworkPlacement,
adInfo.CreativeIdentifier, reviewCreativeId,
eventName, itemCategory, duration);
return impression;
}
/// <summary>
/// 添加一条ImpressionData 数据
/// </summary>
/// <param name="adInfo"></param>
/// <param name="reviewCreativeId"></param>
/// <param name="platform"></param>
/// <param name="eventName"></param>
/// <param name="itemCategory"></param>
/// <param name="duration"></param>
/// <returns></returns>
private void AppendImpressionData(MaxSdkBase.AdInfo adInfo, string reviewCreativeId = "", string platform = "", string eventName = "", string itemCategory = "", int duration = 0)
{
var data = CreateImpressionData(adInfo, reviewCreativeId, platform, eventName, itemCategory, duration);
_impressionDriver.Append(data);
}
/// <summary>
/// 获取 AdReviewCreativeId 后的回调
/// </summary>
/// <param name="adUnitId"></param>
/// <param name="reviewCreativeId"></param>
/// <param name="adInfo"></param>
private void OnAdReviewCreativeIdGeneratedEvent(string adUnitId, string reviewCreativeId, MaxSdkBase.AdInfo adInfo)
{
Debug.Log($"{Tag} --- ReviewCreativeId:{reviewCreativeId} adUnitId: {adUnitId} Type:{adInfo?.AdFormat ?? "NULL"} CreativeId: {adInfo?.CreativeIdentifier ?? "NULL"} Revenue:{adInfo.Revenue}");
if (string.IsNullOrEmpty(adInfo.CreativeIdentifier))
{
Debug.LogError($"{Tag} --- Get ReviewCreativeId:{reviewCreativeId} but CreativeIdentifier is null");
return;
}
//---------- BADS Params -------------- _impressionDriver.SetReviewCreativeId(adInfo.CreativeIdentifier, reviewCreativeId); // 缓存 ReviewCreateId
private Dictionary<string, object> BuildBadsImpData(string adUnitId)
{
var p = AdParams.Build(adUnitId:adUnitId, category:_badsCategory);
return p.ToBadsImpData();
}
private Dictionary<string, object> BuildBadsFailedData(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
{
int duration = GetActionDuration(_badsLoadStartTime);
int errorCode = (int)errorInfo.Code;
var p = AdParams.Build(adUnitId:adUnitId, errorCode:errorCode, duration:duration);
return p.ToAdFailedData();
}
private Dictionary<string, object> BuildBadsClickData(MaxSdk.AdInfo adInfo, string adUnitId)
{
var p = AdParams.Build(adInfo, adUnitId, _badsCategory);
return p.ToAdClickData();
}
if (adUnitId == GetBannerID() || adInfo.AdFormat.ToUpper().Contains("BANNER"))
//---------- IADS Params -------------- {
private Dictionary<string, object> BuildIadsLoadedData(MaxSdk.AdInfo adInfo, string adUnitId) // 清理 BADS CID 缓存
{ _badsCreativeIds.Add(adInfo.CreativeIdentifier);
int duration = GetActionDuration(_iadsLoadStartTime); if (_badsCreativeIds.Count > MAX_BADS_CID_NUMBER)
var p = AdParams.Build(adInfo, adUnitId, duration:duration); {
return p.ToAdLoadedData(); var cid = _badsCreativeIds[0];
_badsCreativeIds.Remove(cid);
_impressionDriver.CleanCreativeId(cid);
}
}
} }
private Dictionary<string, object> BuildIadsFailedData(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
{
int duration = GetActionDuration(_iadsLoadStartTime);
int errorCode = (int)errorInfo.Code;
var p = AdParams.Build(adUnitId:adUnitId, errorCode:errorCode, duration:duration);
return p.ToAdFailedData();
}
private Dictionary<string, object> BuildIadsImpData(MaxSdk.AdInfo adInfo, string adUnitId)
{
var p = AdParams.Build(adInfo, adUnitId:adUnitId, category:_iadsCategory);
return p.ToAdImpData();
}
private Dictionary<string, object> BuildIadsClickData(MaxSdk.AdInfo adInfo, string adUnitId)
{
var p = AdParams.Build(adInfo, adUnitId, category:_iadsCategory);
return p.ToAdClickData();
}
private Dictionary<string, object> BuildIadsCloseData(MaxSdk.AdInfo adInfo, string adUnitId)
{
int duration = GetActionDuration(_iadsDisplayStartTime);
var p = AdParams.Build(adInfo, adUnitId, category:_iadsCategory, duration:duration);
return p.ToAdCloseData();
}
//---------- RADS Params --------------
private Dictionary<string, object> BuildRadsLoadedData(MaxSdk.AdInfo adInfo, string adUnitId)
{
int duration = GetActionDuration(_radsLoadStartTime);
var p = AdParams.Build(adInfo, adUnitId, duration:duration);
return p.ToAdLoadedData();
}
private Dictionary<string, object> BuildRadsFailedData(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
{
int duration = GetActionDuration(_radsLoadStartTime);
int errorCode = (int)errorInfo.Code;
var p = AdParams.Build(adUnitId:adUnitId, errorCode:errorCode, duration:duration);
return p.ToAdFailedData();
}
private Dictionary<string, object> BuildRadsImpData(MaxSdk.AdInfo adInfo, string adUnitId)
{
var p = AdParams.Build(adInfo, adUnitId:adUnitId, category:_radsCategory);
return p.ToAdLoadedData();
}
private Dictionary<string, object> BuildRadsClickData(MaxSdk.AdInfo adInfo, string adUnitId)
{
var p = AdParams.Build(adInfo, adUnitId, category:_radsCategory);
return p.ToAdClickData();
}
private Dictionary<string, object> BuildRadsCloseData(MaxSdk.AdInfo adInfo, string adUnitId)
{
int duration = GetActionDuration(_radsDisplayStartTime);
var p = AdParams.Build(adInfo, adUnitId, category:_iadsCategory, duration:duration);
return p.ToAdCloseData();
}
private Dictionary<string, object> BuildRadsRewardedData(MaxSdk.AdInfo adInfo, string adUnitId)
{
var p = AdParams.Build(adInfo, adUnitId, category:_iadsCategory);
return p.ToAdRewardedData();
}
#endregion #endregion
} }
} }

View File

@ -15,7 +15,7 @@ namespace Guru
public static AdsInitSpec Build(bool loadBanner = true, bool loadInterstitial = true, bool loadReward = true, public static AdsInitSpec Build(bool loadBanner = true, bool loadInterstitial = true, bool loadReward = true,
bool autoLoad = true, bool isDebug = false, string bannerColorHex = "#00000040") bool autoLoad = true, bool isDebug = false, string bannerColorHex = "#00000000")
{ {
return new AdsInitSpec return new AdsInitSpec
{ {

View File

@ -53,7 +53,7 @@ namespace Guru
MaxSdk.SetBannerExtraParameter(MaxBADSSlotID, "adaptive_banner", "false"); MaxSdk.SetBannerExtraParameter(MaxBADSSlotID, "adaptive_banner", "false");
// Set background or background color for banners to be fully functional // Set background or background color for banners to be fully functional
MaxSdk.SetBannerBackgroundColor(MaxBADSSlotID, _backColor); MaxSdk.SetBannerBackgroundColor(MaxBADSSlotID, _backColor);
Analytics.TrackAdBadsLoad(AdParams.Build(adUnitId:MaxBADSSlotID).ToAdLoadData()); Analytics.ADBadsLoad(AdParams.Build(MaxBADSSlotID));
} }
public void SetBannerBackColor(string hex) public void SetBannerBackColor(string hex)
@ -69,7 +69,7 @@ namespace Guru
public bool IsInterstitialRequestOver => true; public bool IsInterstitialRequestOver => true;
public void LoadInterstitialAD() public void LoadInterstitialAD()
{ {
Analytics.TrackAdIadsLoad(AdParams.Build(adUnitId:MaxIADSSlotID).ToAdLoadData()); // 上报打点 Analytics.ADIadsLoad(AdParams.Build(MaxIADSSlotID)); // 上报打点
MaxSdk.LoadInterstitial(MaxIADSSlotID); MaxSdk.LoadInterstitial(MaxIADSSlotID);
} }
@ -80,7 +80,7 @@ namespace Guru
public bool IsRewardRequestOver => true; public bool IsRewardRequestOver => true;
public void LoadRewardAD() public void LoadRewardAD()
{ {
Analytics.TrackAdRadsLoad(AdParams.Build(adUnitId:MaxRADSSlotID).ToAdLoadData()); // 上报打点 Analytics.ADRadsLoad(AdParams.Build(MaxRADSSlotID)); // 上报打点
MaxSdk.LoadRewardedAd(MaxRADSSlotID); MaxSdk.LoadRewardedAd(MaxRADSSlotID);
} }

View File

@ -0,0 +1,185 @@
namespace Guru.Ads
{
using System;
using System.Collections.Generic;
/// <summary>
/// AdImpression 对象
/// </summary>
public class AdImpressionData
{
private const int MAX_VALUE_LENGTH = 96;
public double value;
public string currency = "USD";
public string ad_platform = "MAX";
public string ad_source;
public string ad_format;
public string ad_unit_name;
public string ad_placement;
public string ad_creative_id;
public string review_creative_id;
public string event_name;
public DateTime createTime;
public string item_category;
public int duration;
public AdImpressionData()
{
createTime = DateTime.UtcNow;
}
public override string ToString()
{
return $"[AdImpressionData] ad_unit_name:{ad_unit_name} value:{value} currency:{currency} ad_platform:{ad_platform} ad_source:{ad_source} ad_format:{ad_format} ad_placement:{ad_placement} ad_creative_id:{ad_creative_id} review_creative_id:{review_creative_id} event_name:{event_name} duration:{duration} item_category:{item_category}";
}
public bool EqualsCreativeId(string cid)
{
if (string.IsNullOrEmpty(cid)) return false;
return ad_creative_id.Equals(cid);
}
public bool EqualsReviewCreativeId(string rcid)
{
if (string.IsNullOrEmpty(rcid)) return false;
return review_creative_id.Equals(rcid);
}
/// <summary>
/// 设置 CreativeId
/// </summary>
/// <param name="creativeId"></param>
public void SetCreativeId(string creativeId)
{
ad_creative_id = FixStringLength(creativeId);
}
/// <summary>
/// 设置 ReviewCreativeId
/// </summary>
/// <param name="reviewCreativeId"></param>
public void SetReviewCreativeId(string reviewCreativeId)
{
if (string.IsNullOrEmpty(reviewCreativeId)) return;
review_creative_id = FixStringLength(reviewCreativeId);
}
/// <summary>
/// 限制字符串长度为 96
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
private string FixStringLength(string source)
{
return source.Length <= MAX_VALUE_LENGTH ? source : source.Substring(0, MAX_VALUE_LENGTH);
}
/// <summary>
/// 获取自创建开始经过的秒数
/// </summary>
/// <returns></returns>
public double GetPassedSecond()
{
return (DateTime.UtcNow - createTime).TotalSeconds;
}
public Dictionary<string, object> BuildEventData()
{
var data = new Dictionary<string, object>()
{
{ "value", value },
{ "currency", currency },
{ "ad_platform", ad_platform },
{ "ad_format", ad_format },
{ "ad_source", ad_source },
{ "ad_unit_name", ad_unit_name },
{ "ad_placement", ad_placement },
{ "ad_creative_id", ad_creative_id },
{ "review_creative_id", review_creative_id },
};
return data;
}
public Dictionary<string, object> BuildEventAdImpData()
{
var data = new Dictionary<string, object>()
{
{ "ad_platform", ad_platform },
{ "ad_source", ad_source },
{ "ad_unit_name", ad_unit_name },
{ "ad_placement", ad_placement },
{ "ad_creative_id", ad_creative_id },
{ "review_creative_id", review_creative_id },
{ "item_category", item_category}
};
return data;
}
public Dictionary<string, object> BuildEventAdClickData()
{
var data = new Dictionary<string, object>()
{
{ "value", value },
{ "currency", currency },
{ "ad_platform", ad_platform },
{ "ad_source", ad_source },
{ "ad_unit_name", ad_unit_name },
{ "ad_placement", ad_placement },
{ "ad_creative_id", ad_creative_id },
{ "review_creative_id", review_creative_id },
{ "item_category", item_category}
};
return data;
}
public Dictionary<string, object> BuildEventAdPaidData()
{
var data = new Dictionary<string, object>()
{
{ "value", value },
{ "currency", currency },
{ "ad_platform", ad_platform },
{ "ad_source", ad_source },
{ "ad_unit_name", ad_unit_name },
{ "ad_placement", ad_placement },
{ "ad_creative_id", ad_creative_id },
{ "review_creative_id", review_creative_id },
{ "item_category", item_category}
};
return data;
}
public Dictionary<string, object> BuildEventAdRewardedData()
{
var data = new Dictionary<string, object>()
{
{ "value", value },
{ "currency", currency },
{ "ad_platform", ad_platform },
{ "ad_source", ad_source },
{ "ad_unit_name", ad_unit_name },
{ "ad_placement", ad_placement },
{ "ad_creative_id", ad_creative_id },
{ "review_creative_id", review_creative_id },
{ "item_category", item_category}
};
return data;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1aca2a4507b94dfe9f88040125708edb
timeCreated: 1721883318

View File

@ -0,0 +1,251 @@
namespace Guru.Ads
{
// using System.Collections.Concurrent;
using System;
using System.Collections.Generic;
using UnityEngine;
public class AdImpressionDriver
{
private const string Tag = "[SDK][ADS]";
private const double NEED_FLUSH_TIME = 40.0; // 单条 Impression data 存在的最大间隔时间(秒)
private List<AdImpressionData> _impressionData; // 所有缓存待上报的 impression data
private Dictionary<string, string> _savedReviewCreativeIds;
private Action<AdImpressionData> _onReportAdsRevenue;
public static AdImpressionData Build(double value, string currency,
string adPlatform, string adSource, string adFormat,
string adUnitName, string adPlacement, string adCreativeID, string reviewCreativeID,
string eventName = "", string itemCategory = "", int duration = 0)
{
if (string.IsNullOrEmpty(adPlatform)) adPlatform = Analytics.AdMAX;
if (string.IsNullOrEmpty(itemCategory)) itemCategory = "not_set";
if (string.IsNullOrEmpty(eventName)) eventName = Analytics.EventAdImpression;
var data = new AdImpressionData()
{
value = value,
currency = currency,
ad_platform = adPlatform,
ad_source = adSource,
ad_format = adFormat,
ad_unit_name = adUnitName,
ad_placement = adPlacement,
ad_creative_id = adCreativeID,
event_name = eventName,
item_category = itemCategory,
duration = duration,
};
data.SetCreativeId(adCreativeID);
data.SetReviewCreativeId(reviewCreativeID);
return data;
}
public void Init(Action<AdImpressionData> onReportRevenue)
{
_impressionData = new List<AdImpressionData>(20);
_savedReviewCreativeIds = new Dictionary<string, string>(100);
_onReportAdsRevenue = onReportRevenue;
}
public bool IsCreativeIdExists(string creativeId) => _savedReviewCreativeIds.ContainsKey(creativeId);
public void SetReviewCreativeId(string creativeId, string reviewCreativeId)
{
bool isCidExists = IsCreativeIdExists(creativeId);
_savedReviewCreativeIds[creativeId] = reviewCreativeId;
if (isCidExists && !string.IsNullOrEmpty(reviewCreativeId))
{
FlushAllImpressionData(creativeId, reviewCreativeId);
}
}
/// <summary>
/// 强制发送相关的数据
/// </summary>
/// <param name="creativeId"></param>
public void CleanCreativeId(string creativeId)
{
if (string.IsNullOrEmpty(creativeId)) return;
if (IsCreativeIdExists(creativeId))
{
FlushAllImpressionData(creativeId, "", true);
}
RemoveCreativeId(creativeId); //清理对应的 createId
}
private string GetReviewCreativeId(string creativeId)
{
if(string.IsNullOrEmpty(creativeId)) return "";
return _savedReviewCreativeIds.GetValueOrDefault(creativeId, "");
}
public void RemoveCreativeId(string creativeId)
{
if (IsCreativeIdExists(creativeId))
{
_savedReviewCreativeIds.Remove(creativeId);
}
}
/// <summary>
/// 添加一条Impression数据
/// </summary>
/// <param name="data"></param>
public void Append(AdImpressionData data)
{
if (CanFlushData(data))
{
// 立即触发数据
FlushImpressionData(data);
}
else
{
if (!IsCreativeIdExists(data.ad_creative_id))
{
SetReviewCreativeId(data.ad_creative_id, "");
}
// 缓存数据
PushImpressionData(data);
}
}
private void PushImpressionData(AdImpressionData data)
{
_impressionData.Add(data);
}
private bool CanFlushData(AdImpressionData data)
{
// #1 如果超时, 强制上报
if (data.GetPassedSecond() > NEED_FLUSH_TIME) return true;
// #2 如果 cid 和 rcid 都赋值了 则上报
if (!string.IsNullOrEmpty(data.review_creative_id)) return true;
// #3 如果 rcid 为空, 但是可以找到,也上报
var rcid = GetReviewCreativeId(data.ad_creative_id);
if (!string.IsNullOrEmpty(rcid))
{
data.SetReviewCreativeId(rcid);
return true;
}
return false;
}
/// <summary>
/// 触发所有相关的 cid 和 reviewCreateId
/// </summary>
/// <param name="creativeId"></param>
/// <param name="reviewCreativeId"></param>
/// <param name="force">强制触发</param>
private void FlushAllImpressionData(string creativeId, string reviewCreativeId = "", bool force = false)
{
List<AdImpressionData> tmp = new List<AdImpressionData>(_impressionData.Count);
if(string.IsNullOrEmpty(reviewCreativeId)) reviewCreativeId = GetReviewCreativeId(creativeId); //获取 reviewCreativeId
foreach(var data in _impressionData)
{
if (data.EqualsCreativeId(creativeId))
{
data.SetReviewCreativeId(reviewCreativeId);
if (CanFlushData(data) || force)
{
FlushImpressionData(data);
}
else
{
tmp.Add(data);
}
}
else if (CanFlushData(data))
{
FlushImpressionData(data);
}
else
{
tmp.Add(data);
}
}
_impressionData = tmp;
}
/// <summary>
/// 输出一个 Impression 事件
/// </summary>
/// <param name="data"></param>
private void FlushImpressionData(AdImpressionData data)
{
Debug.Log($"{Tag} --- FlushImpressionData: {data}");
switch (data.event_name)
{
case Analytics.EventBadsPaid:
// BADS 收入事件
// BADS 尚未统计 bads_paid 打点
_onReportAdsRevenue?.Invoke(data);
break;
case Analytics.EventIadsImp:
Analytics.ADIadsImp(data.BuildEventAdImpData());
break;
case Analytics.EventIadsClick:
Analytics.ADIadsClick(data.BuildEventAdClickData());
break;
case Analytics.EventIadsPaid:
// IADS 收入事件
Analytics.ADIadsPaid(data.BuildEventAdPaidData());
_onReportAdsRevenue?.Invoke(data);
break;
case Analytics.EventRadsImp:
Analytics.ADRadsImp(data.BuildEventAdImpData());
break;
case Analytics.EventRadsClick:
Analytics.ADRadsClick(data.BuildEventAdClickData());
break;
case Analytics.EventRadsRewarded:
Analytics.ADRadsRewarded(data.BuildEventAdRewardedData());
break;
case Analytics.EventRadsPaid:
// RADS 收入事件
Analytics.ADRadsPaid(data.BuildEventAdPaidData());
_onReportAdsRevenue?.Invoke(data);
break;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7194cdd8ed1447508011264371e2c23d
timeCreated: 1722313472

View File

@ -1,254 +0,0 @@
using System.Collections.Generic;
namespace Guru
{
/// <summary>
/// 广告打点上报参数
/// </summary>
public class AdParams
{
public double value;
public string currency;
public string category;
public string networkPlacement;
public string adPlatform;
public string adSource;
public string adFormat;
public string adUnitId;
public int errorCode = 0;
public int duration = 0;
public string creativeId;
public string reviewCreativeId;
/// <summary>
/// 构造AD参数
/// </summary>
/// <param name="adInfo"></param>
/// <param name="adUnitId"></param>
/// <param name="category"></param>
/// <param name="duration"></param>
/// <param name="errorCode"></param>
/// <param name="reviewCreativeId"></param>
/// <param name="adPlatform"></param>
/// <param name="currency"></param>
/// <returns></returns>
public static AdParams Build(MaxSdkBase.AdInfo adInfo = null, string adUnitId = "", string category = "",
int duration = 0, int errorCode = 0, string reviewCreativeId = "", string adPlatform = "", string currency = "")
{
if (string.IsNullOrEmpty(adUnitId) && adInfo != null) adUnitId = adInfo.AdUnitIdentifier;
var networkPlacement = "";
var creativeId = "";
var adSource = "";
var adFormart = "";
double value = 0;
string waterfallName = "";
if (adInfo != null)
{
value = adInfo.Revenue;
networkPlacement = adInfo.NetworkPlacement;
creativeId = adInfo.CreativeIdentifier;
adSource = adInfo.NetworkName;
adFormart = adInfo.AdFormat;
}
if (string.IsNullOrEmpty(adPlatform)) adPlatform = Analytics.AdMAX;
if (string.IsNullOrEmpty(category)) category = "not_set";
if (string.IsNullOrEmpty(currency)) currency = Analytics.USD;
var p = new AdParams()
{
value = value,
currency = currency,
adUnitId = adUnitId,
adPlatform = adPlatform,
adSource = adSource,
adFormat = adFormart,
duration = duration,
networkPlacement = networkPlacement,
category = category,
errorCode = errorCode,
creativeId = creativeId,
reviewCreativeId = reviewCreativeId,
};
return p;
}
/// <summary>
/// 转化为 AdImpression 事件数据
/// </summary>
/// <returns></returns>
public Dictionary<string, object> ToAdImpressionData()
{
var data = new Dictionary<string, object>()
{
[Analytics.ParameterValue] = value,
[Analytics.ParameterCurrency] = currency,
[Analytics.ParameterAdPlatform] = adPlatform,
[Analytics.ParameterAdSource] = adSource,
[Analytics.ParameterAdFormat] = adFormat,
[Analytics.ParameterAdUnitName] = adUnitId,
[Analytics.ParameterAdPlacement] = networkPlacement,
[Analytics.ParameterAdCreativeId] = creativeId,
};
if (!string.IsNullOrEmpty(reviewCreativeId))
data[Analytics.ParameterReviewCreativeId] = reviewCreativeId;
return data;
}
/// <summary>
/// 转化为 ads_imp 事件数据
/// </summary>
/// <returns></returns>
public Dictionary<string, object> ToAdImpData()
{
var data = new Dictionary<string, object>()
{
[Analytics.ParameterAdPlatform] = adPlatform,
[Analytics.ParameterAdSource] = adSource,
[Analytics.ParameterAdUnitName] = adUnitId,
[Analytics.ParameterAdPlacement] = networkPlacement,
[Analytics.ParameterAdCreativeId] = creativeId,
[Analytics.ParameterItemCategory] = category,
};
if (!string.IsNullOrEmpty(reviewCreativeId))
data[Analytics.ParameterReviewCreativeId] = reviewCreativeId;
return data;
}
/// <summary>
/// 转化为 ads_paid 通用事件数据
/// </summary>
/// <returns></returns>
public Dictionary<string, object> ToAdPaidData()
{
var data = new Dictionary<string, object>()
{
[Analytics.ParameterValue] = value,
[Analytics.ParameterCurrency] = currency,
[Analytics.ParameterAdPlatform] = adPlatform,
[Analytics.ParameterAdSource] = adSource,
[Analytics.ParameterAdUnitName] = adUnitId,
[Analytics.ParameterAdPlacement] = networkPlacement,
[Analytics.ParameterAdCreativeId] = creativeId,
[Analytics.ParameterItemCategory] = category,
};
if (!string.IsNullOrEmpty(reviewCreativeId))
data[Analytics.ParameterReviewCreativeId] = reviewCreativeId;
return data;
}
/// <summary>
/// 转化为 ads_paid 通用事件数据
/// </summary>
/// <returns></returns>
public Dictionary<string, object> ToAdRewardedData()
{
var data = new Dictionary<string, object>()
{
[Analytics.ParameterValue] = value,
[Analytics.ParameterCurrency] = currency,
[Analytics.ParameterAdPlatform] = adPlatform,
[Analytics.ParameterAdSource] = adSource,
[Analytics.ParameterAdUnitName] = adUnitId,
[Analytics.ParameterAdPlacement] = networkPlacement,
[Analytics.ParameterAdCreativeId] = creativeId,
[Analytics.ParameterItemCategory] = category,
};
if (!string.IsNullOrEmpty(reviewCreativeId))
data[Analytics.ParameterReviewCreativeId] = reviewCreativeId;
return data;
}
public Dictionary<string, object> ToAdClickData()
{
var data = new Dictionary<string, object>()
{
[Analytics.ParameterValue] = value,
[Analytics.ParameterCurrency] = currency,
[Analytics.ParameterAdPlatform] = adPlatform,
[Analytics.ParameterAdSource] = adSource,
[Analytics.ParameterAdUnitName] = adUnitId,
[Analytics.ParameterAdPlacement] = networkPlacement,
[Analytics.ParameterAdCreativeId] = creativeId,
[Analytics.ParameterItemCategory] = category,
};
if (!string.IsNullOrEmpty(reviewCreativeId))
data[Analytics.ParameterReviewCreativeId] = reviewCreativeId;
return data;
}
public Dictionary<string, object> ToAdCloseData()
{
var data = new Dictionary<string, object>()
{
[Analytics.ParameterItemCategory] = category,
[Analytics.ParameterDuration] = duration,
};
return data;
}
/// <summary>
/// 广告失败事件数据
/// </summary>
/// <returns></returns>
public Dictionary<string, object> ToAdFailedData()
{
var data = new Dictionary<string, object>()
{
[Analytics.ParameterItemCategory] = category,
[Analytics.ParameterItemName] = adUnitId,
[Analytics.ParameterErrorCode] = errorCode,
[Analytics.ParameterDuration] = duration,
};
return data;
}
/// <summary>
/// 广告加载成功事件数据
/// </summary>
/// <returns></returns>
public Dictionary<string, object> ToAdLoadData()
{
var data = new Dictionary<string, object>()
{
[Analytics.ParameterItemCategory] = category,
[Analytics.ParameterItemName] = adUnitId,
};
return data;
}
/// <summary>
/// 广告加载成功事件数据
/// </summary>
/// <returns></returns>
public Dictionary<string, object> ToAdLoadedData()
{
var data = new Dictionary<string, object>()
{
[Analytics.ParameterItemCategory] = category,
[Analytics.ParameterItemName] = adUnitId,
[Analytics.ParameterDuration] = duration,
};
return data;
}
public Dictionary<string, object> ToBadsImpData()
{
var data = new Dictionary<string, object>()
{
[Analytics.ParameterItemCategory] = category,
[Analytics.ParameterItemName] = adUnitId,
};
return data;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 0341a8c153f04535a4193ce32086315a
timeCreated: 1722396384

View File

@ -1,13 +1,17 @@
using System.Collections.Generic;
using UnityEngine;
namespace Guru namespace Guru
{ {
using System.Collections.Generic;
using UnityEngine;
public static partial class Analytics public static partial class Analytics
{ {
#region 广告属性 #region 广告属性
//------ 广告额外的参数 --------
private static readonly string ParameterNetworkName = "network_name"; //NetworkName
private static readonly string ParameterNetworkPlacement = "network_placement"; //NetworkPlacement
private static readonly string ParameterWaterfall = "waterfall"; //Waterfall
//------ 默认值 ------- //------ 默认值 -------
public const string DefaultCategory = "not_set"; public const string DefaultCategory = "not_set";
public const string DefaultWaterfall = "unknown"; public const string DefaultWaterfall = "unknown";
@ -17,32 +21,89 @@ namespace Guru
#region Ads #region Ads
private static Dictionary<string, object> BuildAdEventData(AdParams adParams, Dictionary<string, object> extra = null)
{
if (string.IsNullOrEmpty(adParams.category))
adParams.category = DefaultCategory;
if (string.IsNullOrEmpty(adParams.waterfallName))
adParams.waterfallName = DefaultWaterfall;
var data = new Dictionary<string, object>()
{
{ ParameterItemCategory, adParams.category },
{ ParameterItemName, adParams.adUnitId },
};
if (adParams.duration > 0)
data[ParameterDuration] = adParams.duration;
if (adParams.errorCode != 0)
data[ParameterErrorCode] = adParams.errorCode;
if (!string.IsNullOrEmpty(adParams.networkName))
data[ParameterNetworkName] = adParams.networkName;
if (!string.IsNullOrEmpty(adParams.networkPlacement))
{
data[ParameterNetworkPlacement] = adParams.networkPlacement;
data[ParameterAdPlacement] = adParams.networkPlacement;
}
if (!string.IsNullOrEmpty(adParams.waterfallName))
data[ParameterWaterfall] = adParams.waterfallName;
if (!string.IsNullOrEmpty(adParams.creativeId))
data[ParameterAdCreativeId] = adParams.creativeId;
if (!string.IsNullOrEmpty(adParams.reviewCreativeId))
data[ParameterReviewCreativeId] = adParams.reviewCreativeId;
if (!string.IsNullOrEmpty(adParams.adPlatform))
data[ParameterAdPlatform] = adParams.adPlatform;
if (!string.IsNullOrEmpty(adParams.adSource))
data[ParameterAdSource] = adParams.adSource;
if (!string.IsNullOrEmpty(adParams.adFormat))
data[ParameterAdFormat] = adParams.adFormat;
if (extra != null && extra.Count > 0)
{
foreach (var k in extra.Keys)
{
data[k] = extra[k];
}
}
return data;
}
//---------------------- BANNER ------------------------- //---------------------- BANNER -------------------------
public static void TrackAdBadsLoad(Dictionary<string, object> data) public static void ADBadsLoad(AdParams adParams)
{ {
TrackEvent(EventBadsLoad, data); TrackEvent(EventBadsLoad, BuildAdEventData(adParams));
} }
public static void TrackAdBadsLoaded(Dictionary<string, object> data) public static void ADBadsLoaded(AdParams adParams)
{ {
TrackEvent(EventBadsLoaded, data); TrackEvent(EventBadsLoaded, BuildAdEventData(adParams));
} }
public static void TrackAdBadsFailed(Dictionary<string, object> data) public static void ADBadsFailed(AdParams adParams)
{ {
TrackEvent(EventBadsFailed, data); TrackEvent(EventBadsFailed, BuildAdEventData(adParams));
} }
/// <summary> /// <summary>
/// 广告点击 /// 广告点击
/// </summary> /// </summary>
public static void TrackAdBadsClick(Dictionary<string, object> data) public static void ADBadsClick(AdParams adParams)
{ {
TrackEvent(EventBadsClick, data); TrackEvent(EventBadsClick, BuildAdEventData(adParams));
} }
public static void TrackAdBadsImp(Dictionary<string, object> data) public static void ADBadsImp(AdParams adParams)
{ {
TrackEvent(EventBadsImp, data); TrackEvent(EventBadsImp, BuildAdEventData(adParams));
} }
public static void TrackAdBadsHide( int loadedTimes, int failTimes) public static void ADBadsHide( int loadedTimes, int failTimes)
{ {
var dict = new Dictionary<string, object>() var dict = new Dictionary<string, object>()
{ {
@ -55,31 +116,36 @@ namespace Guru
/// <summary> /// <summary>
/// 广告拉取 /// 广告拉取
/// </summary> /// </summary>
public static void TrackAdIadsLoad(Dictionary<string, object> data) public static void ADIadsLoad(AdParams adParams)
{ {
TrackEvent(EventIadsLoad, data); TrackEvent(EventIadsLoad, BuildAdEventData(adParams));
} }
/// <summary> /// <summary>
/// 广告拉取成功 /// 广告拉取成功
/// </summary> /// </summary>
public static void TrackAdIadsLoaded(Dictionary<string, object> data) public static void ADIadsLoaded(AdParams adParams)
{ {
TrackEvent(EventIadsLoaded, data); TrackEvent(EventIadsLoaded, BuildAdEventData(adParams));
} }
/// <summary> /// <summary>
/// 广告拉取失败 /// 广告拉取失败
/// </summary> /// </summary>
public static void TrackAdIadsFailed(Dictionary<string, object> data) public static void ADIadsFailed(AdParams adParams)
{ {
TrackEvent(EventIadsFailed, data); TrackEvent(EventIadsFailed, BuildAdEventData(adParams));
} }
/// <summary> /// <summary>
/// 广告展示 /// 广告展示
/// </summary> /// </summary>
public static void TrackAdIadsImp(Dictionary<string, dynamic> data) public static void ADIadsImp(AdParams adParams)
{
ADIadsImp(BuildAdEventData(adParams));
}
public static void ADIadsImp(Dictionary<string, dynamic> data)
{ {
TrackEvent(EventIadsImp, data); TrackEvent(EventIadsImp, data);
} }
@ -87,7 +153,12 @@ namespace Guru
/// <summary> /// <summary>
/// 广告点击 /// 广告点击
/// </summary> /// </summary>
public static void TrackAdIadsClick(Dictionary<string, object> data) public static void ADIadsClick(AdParams adParams)
{
ADIadsClick(BuildAdEventData(adParams));
}
public static void ADIadsClick(Dictionary<string, object> data)
{ {
TrackEvent(EventIadsClick, data); TrackEvent(EventIadsClick, data);
} }
@ -95,7 +166,12 @@ namespace Guru
/// <summary> /// <summary>
/// 用户关闭广告 /// 用户关闭广告
/// </summary> /// </summary>
public static void TrackAdIadsClose(Dictionary<string, object> data) public static void ADIadsClose(AdParams adParams)
{
ADIadsClose(BuildAdEventData(adParams));
}
public static void ADIadsClose(Dictionary<string, object> data)
{ {
TrackEvent(EventIadsClose, data); TrackEvent(EventIadsClose, data);
} }
@ -104,7 +180,7 @@ namespace Guru
/// 插屏广告收到奖励 /// 插屏广告收到奖励
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
public static void TrackAdIadsPaid(Dictionary<string, object> data) public static void ADIadsPaid(Dictionary<string, object> data)
{ {
TrackEvent(EventIadsPaid, data); TrackEvent(EventIadsPaid, data);
} }
@ -113,28 +189,33 @@ namespace Guru
/// <summary> /// <summary>
/// 广告开始加载 /// 广告开始加载
/// </summary> /// </summary>
public static void TrackAdRadsLoad(Dictionary<string, object> data) public static void ADRadsLoad(AdParams adParams)
{ {
TrackEvent(EventRadsLoad, data); TrackEvent(EventRadsLoad, BuildAdEventData(adParams));
} }
/// <summary> /// <summary>
/// 广告拉取成功 /// 广告拉取成功
/// </summary> /// </summary>
public static void TrackAdRadsLoaded(Dictionary<string, object> data) public static void ADRadsLoaded(AdParams adParams)
{ {
TrackEvent(EventRadsLoaded, data); TrackEvent(EventRadsLoaded, BuildAdEventData(adParams));
} }
/// <summary> /// <summary>
/// 广告拉取失败 /// 广告拉取失败
/// </summary> /// </summary>
public static void TrackAdRadsFailed(Dictionary<string, object> data) public static void ADRadsFailed(AdParams adParams)
{ {
TrackEvent(EventRadsFailed, data); TrackEvent(EventRadsFailed, BuildAdEventData(adParams));
} }
/// <summary> /// <summary>
/// 广告展示 /// 广告展示
/// </summary> /// </summary>
public static void TrackAdRadsImp(Dictionary<string, dynamic> data) public static void ADRadsImp(AdParams adParams)
{
ADRadsImp(BuildAdEventData(adParams));
}
public static void ADRadsImp(Dictionary<string, dynamic> data)
{ {
TrackEvent(EventRadsImp, data); TrackEvent(EventRadsImp, data);
} }
@ -144,14 +225,19 @@ namespace Guru
/// <summary> /// <summary>
/// 广告完成观看发奖励 /// 广告完成观看发奖励
/// </summary> /// </summary>
public static void TrackAdRadsRewarded(Dictionary<string, object> data) public static void ADRadsRewarded(AdParams adParams)
{
ADRadsRewarded(BuildAdEventData(adParams));
}
public static void ADRadsRewarded(Dictionary<string, object> data)
{ {
TrackEvent(EventRadsRewarded, data); TrackEvent(EventRadsRewarded, data);
if (RadsRewardCount == 0) if (RadsRewardCount == 0)
{ {
RadsRewardCount = 1; RadsRewardCount = 1;
TrackAdRadsFirstRewarded(data); ADRadsFirstRewarded(data);
} }
} }
@ -159,11 +245,12 @@ namespace Guru
/// 插屏广告收到奖励 /// 插屏广告收到奖励
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
public static void TrackAdRadsPaid(Dictionary<string, object> data) public static void ADRadsPaid(Dictionary<string, object> data)
{ {
TrackEvent(EventRadsPaid, data); TrackEvent(EventRadsPaid, data);
} }
private static int RadsRewardCount private static int RadsRewardCount
{ {
get => PlayerPrefs.GetInt(nameof(RadsRewardCount), 0); get => PlayerPrefs.GetInt(nameof(RadsRewardCount), 0);
@ -173,7 +260,7 @@ namespace Guru
/// <summary> /// <summary>
/// 用户首次完成观看 /// 用户首次完成观看
/// </summary> /// </summary>
private static void TrackAdRadsFirstRewarded(Dictionary<string, object> data) public static void ADRadsFirstRewarded(Dictionary<string, object> data)
{ {
TrackEvent(EventFirstRadsRewarded, data); TrackEvent(EventFirstRadsRewarded, data);
} }
@ -181,7 +268,12 @@ namespace Guru
/// <summary> /// <summary>
/// 广告点击 /// 广告点击
/// </summary> /// </summary>
public static void TrackAdRadsClick(Dictionary<string, object> data) public static void ADRadsClick(AdParams adParams)
{
ADRadsClick(BuildAdEventData(adParams));
}
public static void ADRadsClick(Dictionary<string, object> data)
{ {
TrackEvent(EventRadsClick, data); TrackEvent(EventRadsClick, data);
} }
@ -189,7 +281,12 @@ namespace Guru
/// <summary> /// <summary>
/// 用户关闭广告 /// 用户关闭广告
/// </summary> /// </summary>
public static void TrackAdRadsClose(Dictionary<string, dynamic> data) public static void ADRadsClose(AdParams adParams)
{
ADRadsClose( BuildAdEventData(adParams));
}
public static void ADRadsClose(Dictionary<string, dynamic> data)
{ {
TrackEvent(EventRadsClose, data); TrackEvent(EventRadsClose, data);
} }
@ -232,6 +329,78 @@ namespace Guru
#endregion #endregion
} }
/// <summary>
/// 广告打点上报参数
/// </summary>
public class AdParams
{
public string category;
public string networkName;
public string networkPlacement;
public string adPlatform;
public string adSource;
public string adFormat;
public string waterfallName;
public string adUnitId;
public int errorCode = 0;
public int duration = 0;
public string creativeId;
public string reviewCreativeId;
/// <summary>
/// 构造AD参数
/// </summary>
/// <param name="adInfo"></param>
/// <param name="adUnitId"></param>
/// <param name="category"></param>
/// <param name="duration"></param>
/// <param name="errorCode"></param>
/// <param name="waterfallName"></param>
/// <param name="reviewCreativeId"></param>
/// <param name="adPlatform"></param>
/// <returns></returns>
public static AdParams Build(string adUnitId, MaxSdkBase.AdInfo adInfo = null, string category = "",
int duration = 0, int errorCode = 0, string waterfallName = "", string reviewCreativeId = "", string adPlatform = "")
{
if (string.IsNullOrEmpty(adUnitId) && adInfo != null) adUnitId = adInfo.AdUnitIdentifier;
var networkName = "";
var networkPlacement = "";
var creativeId = "";
var adSource = "";
var adFormart = "";
if (string.IsNullOrEmpty(adPlatform)) adPlatform = Analytics.AdMAX;
if (adInfo != null)
{
networkName = adInfo.NetworkName;
networkPlacement = adInfo.NetworkPlacement;
creativeId = adInfo.CreativeIdentifier;
adSource = adInfo.NetworkName;
adFormart = adInfo.AdFormat;
if (string.IsNullOrEmpty(waterfallName))
waterfallName = adInfo.WaterfallInfo?.Name ?? "";
}
var p = new AdParams()
{
adUnitId = adUnitId,
adPlatform = adPlatform,
adSource = adSource,
adFormat = adFormart,
duration = duration,
networkName = networkName,
networkPlacement = networkPlacement,
waterfallName = waterfallName,
category = category,
errorCode = errorCode,
creativeId = creativeId,
reviewCreativeId = reviewCreativeId,
};
return p;
}
}
} }

View File

@ -6,7 +6,6 @@ namespace Guru
fail, fail,
timeout, timeout,
exit, exit,
replay
} }
//打点常量定义 //打点常量定义
@ -125,8 +124,6 @@ namespace Guru
public const string PropertyNoAds = "no_ads"; // 玩家是否去广告 public const string PropertyNoAds = "no_ads"; // 玩家是否去广告
public const string PropertyATTStatus = "att_status"; // ATT 状态 public const string PropertyATTStatus = "att_status"; // ATT 状态
public const string PropertyGDPR = "gdpr"; // GDPR状态 public const string PropertyGDPR = "gdpr"; // GDPR状态
public const string PropertySignUpMethod = "sign_up_method"; // 用户登录方式
// 经济相关 // 经济相关
public const string ParameterBalance = "balance"; // 用于余额 public const string ParameterBalance = "balance"; // 用于余额

View File

@ -1,6 +1,7 @@
namespace Guru namespace Guru
{ {
using System; using System;
using System.Collections.Generic;
using Firebase.Analytics; using Firebase.Analytics;
using Firebase.Crashlytics; using Firebase.Crashlytics;
using Firebase.Extensions; using Firebase.Extensions;
@ -19,49 +20,52 @@ namespace Guru
private static bool _hasGotUid; // 已取得UID private static bool _hasGotUid; // 已取得UID
private static DateTime _lastReportRateDate; //上次上报信息的日期 private static DateTime _lastReportRateDate; //上次上报信息的日期
private static double _reportSuccessInterval; // 上报频率 private static double _reportSuccessInterval; // 上报频率
#if UNITY_IOS
private const string VALUE_NOT_FOR_IOS = "not_support_for_ios";
#endif
// private const string VALUE_ONLY_FOR_IOS = "idfa_only_for_ios";
private const string VALUE_NOT_FOR_IOS = "not_support_for_ios";
private const string VALUE_ONLY_FOR_IOS = "idfa_only_for_ios";
public static bool IsDebug { get; set; } = false;
private static bool _isGuruAnalyticInitOnce = false; private static bool _isGuruAnalyticInitOnce = false;
public static void InitGuruAnalyticService(string firebaseId) /// <summary>
/// 初始化Guru自打点系统 (请优先于 Firebase 初始化调用)
/// </summary>
public static void InstallGuruAnalytics(bool isDebug = false, bool enableErrorLog = false)
{ {
if (_isGuruAnalyticInitOnce) return; if (_isGuruAnalyticInitOnce) return;
_isGuruAnalyticInitOnce = true;
try
{
#if UNITY_EDITOR
IsDebug = true;
#else
IsDebug = isDebug;
#endif
string appId = IPMConfig.IPM_X_APP_ID;
string deviceInfo = new DeviceInfoData().ToString();
GuruAnalytics.Init(appId, deviceInfo, OnGuruAnalyticsInitComplete, IsDebug, enableErrorLog); // 初始化(带Header)
_hasGotFirebaseId = false;
_hasGotAdId = false;
_hasGotAdjustId = false;
_hasGotDeviceId = false;
_hasGotUid = false;
_lastReportRateDate = DateTime.Now;
_reportSuccessInterval = 120; // 2分钟上报一次
UpdateAllValues();
try _isGuruAnalyticInitOnce = true;
{ }
string appId = IPMConfig.IPM_X_APP_ID; catch (Exception ex)
string deviceInfo = new DeviceInfoData().ToString(); {
LogCrashlytics(ex);
_hasGotFirebaseId = false; }
_hasGotAdId = false;
_hasGotAdjustId = false;
_hasGotDeviceId = false;
_hasGotUid = false;
_lastReportRateDate = DateTime.Now;
_reportSuccessInterval = 120; // 2分钟上报一次
if(!string.IsNullOrEmpty(firebaseId))
GuruAnalytics.Instance.SetFirebaseId(firebaseId); // 在自打点初始化之前, 需要调用一下设置 FirebaseId
GuruAnalytics.Init(appId, deviceInfo, () =>
{
SetUserProperty(GuruAnalyticsConfigManager.KEY_GURU_ANALYTICS_EXP, GuruAnalytics.Instance.ExperimentGroupId);
OnGuruAnalyticsInitComplete();
}, IsDebug); // Android 初始化
UpdateAllUserProperties();
}
catch (Exception ex)
{
LogCrashlytics(ex);
}
} }
#region 各ID上报信息
#region 各ID上报信息
/// <summary> /// <summary>
/// 设置用户ID /// 设置用户ID
@ -242,12 +246,10 @@ namespace Guru
#endif #endif
/// <summary> /// <summary>
/// 上报中台打点的用户属性 /// 获取全部ID
/// </summary> /// </summary>
private static void UpdateAllUserProperties() private static void UpdateAllValues()
{ {
Debug.Log($"{TAG} --- UpdateAllValues");
SetUid(); SetUid();
SetDeviceId(); SetDeviceId();
SetAdjustId(); SetAdjustId();
@ -293,21 +295,33 @@ namespace Guru
try try
{ {
GuruAnalytics.Instance.SetUserProperty(key, value); GuruAnalytics.Instance.SetUserProperty(key, value);
UpdateAllUserProperties(); // 同步所有的ID UpdateAllValues(); // 同步所有的ID
} }
catch (Exception ex) catch (Exception e)
{ {
if (IsFirebaseReady) Crashlytics.LogException(e);
{
Crashlytics.LogException(ex);
}
else
{
Debug.LogException(ex);
}
} }
} }
/// <summary>
/// 自定义事件打点
/// </summary>
/// <param name="key"></param>
/// <param name="data"></param>
private static void TrackEventGuru(string key, Dictionary<string, dynamic> data = null, int priority = -1)
{
try
{
if (data == null) data = new Dictionary<string, dynamic>();
GuruAnalytics.Instance.LogEvent(key, data, priority);
UpdateAllValues(); // 同步所有的ID
}
catch (Exception e)
{
Crashlytics.LogException(e);
}
}
/// <summary> /// <summary>
/// 设置太极02阀值 /// 设置太极02阀值
@ -329,14 +343,7 @@ namespace Guru
} }
catch (Exception e) catch (Exception e)
{ {
if (IsFirebaseReady) Crashlytics.LogException(e);
{
Crashlytics.LogException(e);
}
else
{
Debug.LogWarning(e);
}
} }
} }

View File

@ -6,6 +6,7 @@ namespace Guru
using System.Collections.Generic; using System.Collections.Generic;
using Facebook.Unity; using Facebook.Unity;
using UnityEngine; using UnityEngine;
using Guru.Ads;
//游戏通用模版打点定义 //游戏通用模版打点定义
public static partial class Analytics public static partial class Analytics
@ -411,20 +412,11 @@ namespace Guru
public static void FBPurchase(double revenue, string currency, string type, string platfrom) public static void FBPurchase(double revenue, string currency, string type, string platfrom)
=> FBPurchase((float)revenue, currency, type, platfrom); => FBPurchase((float)revenue, currency, type, platfrom);
/// <summary> /// <summary>
/// Google ARO买量点 /// Google ARO买量点
/// </summary> /// </summary>
/// <param name="impressionData">广告收入数据</param> /// <param name="impressionData">广告收入数据</param>
/// <param name="value"></param>
/// <param name="currency"></param>
/// <param name="adPlatform"></param>
/// <param name="adUnitId"></param>
/// <param name="adPlacement"></param>
/// <param name="adSource"></param>
/// <param name="adFormat"></param>
/// <param name="creativeId"></param>
/// <param name="reviewCreativeId"></param>
/// <a href="https://docs.google.com/spreadsheets/d/1lFWLeOGJgq34QDBTfl6OpNh7MoI37ehGrhdbxlOrJgs/edit#gid=983654222"></a> /// <a href="https://docs.google.com/spreadsheets/d/1lFWLeOGJgq34QDBTfl6OpNh7MoI37ehGrhdbxlOrJgs/edit#gid=983654222"></a>
/// <li> /// <li>
/// value double eg0.002 /// value double eg0.002
@ -435,28 +427,21 @@ namespace Guru
/// ad_unit_name string 广告位名称 /// ad_unit_name string 广告位名称
/// ad_creative_id string 广告素材id /// ad_creative_id string 广告素材id
/// </li> /// </li>
public static void ADImpression(double value, string currency = "USD", string adPlatform = "", public static void ADImpression(AdImpressionData impressionData)
string adSource = "", string adFormat = "", string adUnitId = "", string adPlacement= "",
string creativeId = "", string reviewCreativeId = "")
{ {
ADImpression(new Dictionary<string, dynamic>() TrackEvent(EventAdImpression, new Dictionary<string, dynamic>()
{ {
[ParameterValue] = value, [ParameterValue] = impressionData.value,
[ParameterCurrency] = currency, [ParameterCurrency] = impressionData.currency,
[ParameterAdPlatform] = adPlatform, [ParameterAdPlatform] = impressionData.ad_platform,
[ParameterAdSource] = adSource, [ParameterAdSource] = impressionData.ad_source,
[ParameterAdFormat] = adFormat, [ParameterAdFormat] = impressionData.ad_format,
[ParameterAdUnitName] = adUnitId, [ParameterAdUnitName] = impressionData.ad_unit_name,
[ParameterAdPlacement] = adPlacement, [ParameterAdPlacement] = impressionData.ad_placement,
[ParameterAdCreativeId] = creativeId, [ParameterAdCreativeId] = impressionData.ad_creative_id,
[ParameterReviewCreativeId] = reviewCreativeId, [ParameterReviewCreativeId] = impressionData.review_creative_id,
}); });
} }
public static void ADImpression(Dictionary<string, object> data)
{
TrackEvent(EventAdImpression, data);
}
public static void TchAdAbnormalEvent(double value) public static void TchAdAbnormalEvent(double value)
{ {

View File

@ -1,7 +1,6 @@
using System.Collections; using System.Threading;
using UnityEngine;
namespace Guru namespace Guru
{ {
@ -40,14 +39,35 @@ namespace Guru
}; };
} }
} }
private static EventSetting DefaultEventSetting => EventSetting.GetDefaultSetting(); private static EventSetting DefaultEventSetting => EventSetting.GetDefaultSetting();
private static bool _isInitOnce; //Analytics是否初始化完成 private static bool _isInitOnce; //Analytics是否初始化完成
public static bool EnableDebugAnalytics; //允许Debug包上报打点 public static bool EnableDebugAnalytics; //允许Debug包上报打点
private static bool IsDebug => PlatformUtil.IsDebug(); public static bool IsDebugMode => PlatformUtil.IsDebug();
private static bool IsFirebaseReady => FirebaseUtil.IsFirebaseInitialized; private static bool IsFirebaseReady => FirebaseUtil.IsFirebaseInitialized;
private static bool IsReady
{
get
{
//Analytics没有初始化不上报打点
if (!_isInitOnce) return false;
//Firebase服务没有初始化完成不上报打点
if (!IsFirebaseReady) return false;
#if !UNITY_EDITOR
//开发环境打点不上报
if (IsDebugMode && !EnableDebugAnalytics)
return false;
#endif
return true;
}
}
private static AdjustEventDriver _adjustEventDriver; private static AdjustEventDriver _adjustEventDriver;
private static FBEventDriver _fbEventDriver; private static FBEventDriver _fbEventDriver;
private static FirebaseEventDriver _firebaseEventDriver; private static FirebaseEventDriver _firebaseEventDriver;
@ -56,10 +76,7 @@ namespace Guru
#region 初始化 #region 初始化
/// <summary> public static void InitAnalytics()
/// 初始化打点模块
/// </summary>
public static void Init()
{ {
if (_isInitOnce) return; if (_isInitOnce) return;
_isInitOnce = true; _isInitOnce = true;
@ -68,21 +85,22 @@ namespace Guru
_fbEventDriver = new FBEventDriver(); _fbEventDriver = new FBEventDriver();
_firebaseEventDriver = new FirebaseEventDriver(); _firebaseEventDriver = new FirebaseEventDriver();
_guruEventDriver = new GuruEventDriver(); _guruEventDriver = new GuruEventDriver();
// ------- 初始化自打点 ----------
InstallGuruAnalytics(IsDebug);
} }
/// <summary>
/// 外部拉起 Firebase 初始化完成回调
/// </summary>
public static void OnFirebaseInitCompleted() public static void OnFirebaseInitCompleted()
{ {
Debug.Log($"[SDK] --- Analytics Init After FirebaseReady:{IsFirebaseReady}"); Debug.Log($"[SDK] --- Analytics Init After FirebaseReady:{IsFirebaseReady}");
// --- 初始化 Crashlytics --- // -------- 初始化 Crashlytics ----------
CrashlyticsAgent.Init(); CrashlyticsAgent.Init();
FirebaseAnalytics.SetAnalyticsCollectionEnabled(true); FirebaseAnalytics.SetAnalyticsCollectionEnabled(true);
FirebaseAnalytics.SetSessionTimeoutDuration(new TimeSpan(0, 30, 0)); FirebaseAnalytics.SetSessionTimeoutDuration(new TimeSpan(0, 30, 0));
SetUserProperty(FirebaseAnalytics.UserPropertySignUpMethod, "Google");
// --- 上报用户事件 ---
SetUserProperty(PropertyDeviceID, IPMConfig.IPM_DEVICE_ID); SetUserProperty(PropertyDeviceID, IPMConfig.IPM_DEVICE_ID);
// SetUserProperty(PropertyFirstOpenTime, FirstOpenTime); // SetUserProperty(PropertyFirstOpenTime, FirstOpenTime);
@ -100,7 +118,7 @@ namespace Guru
_adjustEventDriver.TriggerFlush(); _adjustEventDriver.TriggerFlush();
} }
public static void OnGuruAnalyticsInitComplete() private static void OnGuruAnalyticsInitComplete()
{ {
_guruEventDriver.TriggerFlush(); _guruEventDriver.TriggerFlush();
} }
@ -134,13 +152,13 @@ namespace Guru
/// <summary> /// <summary>
/// Firebase上报用户ID /// Firebase上报用户ID
/// </summary> /// </summary>
/// <param name="uid">通过Auth认证地用户ID</param> /// <param name="userID">通过Auth认证地用户ID</param>
public static void SetFirebaseUserId(string uid) public static void SetUserIDProperty(string userID)
{ {
if (!IsFirebaseReady) return; Log.I(TAG,$"SetUserIDProperty -> userID:{userID}");
Log.I(TAG,$"SetUserIDProperty -> userID:{uid}"); if (!IsReady) return;
FirebaseAnalytics.SetUserId(uid);
Crashlytics.SetUserId(uid); FirebaseAnalytics.SetUserId(userID);
} }
/// <summary> /// <summary>
@ -150,7 +168,7 @@ namespace Guru
{ {
if (!_isInitOnce) if (!_isInitOnce)
{ {
throw new Exception($"[{TAG}][SDK] Analytics did not initialized, Call <Analytics.{nameof(Init)}()> first!"); throw new Exception($"[{TAG}][SDK] Analytics did not initialized, Call <Analytics.{nameof(InitAnalytics)}()> first!");
} }
if (IsDebug && !EnableDebugAnalytics) if (IsDebug && !EnableDebugAnalytics)
@ -182,25 +200,6 @@ namespace Guru
} }
/// <summary>
/// Firebase 上报用户属性
/// </summary>
/// <param name="propertyName"></param>
/// <param name="propertyValue"></param>
private static void FirebaseSetUserProperty(string propertyName, string propertyValue)
{
if (IsFirebaseReady)
{
FirebaseAnalytics.SetUserProperty(propertyName, propertyValue);
}
else
{
Debug.Log($"{TAG} --- Firebase not ready, call Firebase Init first!");
}
}
#endregion #endregion
#region 打点上报 #region 打点上报
@ -218,7 +217,7 @@ namespace Guru
{ {
if (!_isInitOnce) if (!_isInitOnce)
{ {
throw new Exception($"[{TAG}][SDK] Analytics did not initialized, Call <Analytics.{nameof(Init)}()> first!"); throw new Exception($"[{TAG}][SDK] Analytics did not initialized, Call <Analytics.{nameof(InitAnalytics)}()> first!");
} }
if (IsDebug && !EnableDebugAnalytics) if (IsDebug && !EnableDebugAnalytics)
@ -353,5 +352,5 @@ namespace Guru
} }
} }

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 005347f128bb460f9312778f93e39a9b
timeCreated: 1679986193

View File

@ -0,0 +1,206 @@
namespace Guru
{
using System;
using System.Globalization;
using UnityEngine;
using System.IO;
/// <summary>
/// 应用的全局标准属性
/// </summary>
public static class StandardProperties
{
#region 初始化
/// <summary>
/// 全局属性初始化 需要在应用启动时调用一次
/// </summary>
public static void Init()
{
// 首次安装初始化各种参数
if (IsFirstInstall)
{
string key = "";
FirstInstallDate = DateTime.Now;
key = nameof(SoundEffectEnabled);
if (!HasKey(key)) SoundEffectEnabled = true;
key = nameof(VibrationEnabled);
if (!HasKey(key)) VibrationEnabled = true;
key = nameof(FirstInstallVersion);
if (!HasKey(key)) FirstInstallVersion = FullVersion;
Save();
}
}
private static bool IsFirstInstall => HasKey(nameof(FirstInstallDate));
private static bool HasKey(string key) => PlayerPrefs.HasKey(key);
private static void Save() => PlayerPrefs.Save();
#endregion
#region 标准属性值
/// <summary>
/// FirebaseId
/// </summary>
public static string FirebaseId
{
get => PlayerPrefs.GetString(nameof(FirebaseId), "");
set {
if (!string.IsNullOrEmpty(value))
{
PlayerPrefs.SetString(nameof(FirebaseId), value);
GuruAnalytics.Instance.SetFirebaseId(value);
}
}
}
/// <summary>
/// Adjust ADID
/// </summary>
public static string AdjustId
{
get => PlayerPrefs.GetString(nameof(AdjustId), "");
set {
if (!string.IsNullOrEmpty(value))
{
PlayerPrefs.SetString(nameof(AdjustId), value);
GuruAnalytics.Instance.SetAdjustId(value);
}
}
}
/// <summary>
/// Google ADID
/// </summary>
public static string GoogleAdId
{
get => PlayerPrefs.GetString(nameof(GoogleAdId), "");
set {
if (!string.IsNullOrEmpty(value))
{
PlayerPrefs.SetString(nameof(GoogleAdId), value);
GuruAnalytics.Instance.SetAdId(value);
}
}
}
/// <summary>
/// 免费金币资源
/// </summary>
public static int Coin
{
get => PlayerPrefs.GetInt(nameof(Coin), 0);
set => PlayerPrefs.SetInt(nameof(Coin), value);
}
/// <summary>
/// 付费金币资源
/// </summary>
public static int IAPCoin
{
get => PlayerPrefs.GetInt(nameof(IAPCoin), 0);
set => PlayerPrefs.SetInt(nameof(IAPCoin), value);
}
/// <summary>
/// 用户累计购买次数
/// </summary>
public static int PurchaseCount
{
get => PlayerPrefs.GetInt(nameof(PurchaseCount), 0);
set => PlayerPrefs.SetInt(nameof(PurchaseCount), value);
}
/// <summary>
/// 首次安装日期
/// </summary>
public static DateTime FirstInstallDate
{
get
{
var key = nameof(FirstInstallDate);
if (PlayerPrefs.HasKey(key))
{
var value = PlayerPrefs.GetString(key, "");
if (!string.IsNullOrEmpty(value)) return DateTime.Parse(value);
}
var now = DateTime.Now.ToUniversalTime();
FirstInstallDate = now;
return now;
}
set => PlayerPrefs.SetString(nameof(FirstInstallDate),
value.ToUniversalTime().ToString(CultureInfo.InvariantCulture));
}
/// <summary>
/// 首次安装版本号
/// </summary>
public static string FirstInstallVersion
{
get => PlayerPrefs.GetString(nameof(FirstInstallVersion), "");
set => PlayerPrefs.SetString(nameof(FirstInstallVersion), value);
}
/// <summary>
/// 当前应用的版本
/// </summary>
public static string AppVersion => Application.version;
/// <summary>
/// 当前应用的版本Code
/// </summary>
public static string VersionCode
{
get
{
var path = $"{Application.streamingAssetsPath}/{GuruCore.VERSION_CODE_FILE}";
if (File.Exists(path)) return File.ReadAllText(path);
return "";
}
}
/// <summary>
/// 完整版本号
/// </summary>
public static string FullVersion
{
get
{
var code = VersionCode;
if (string.IsNullOrEmpty(code)) code = "unknown";
return $"{AppVersion}-{code}";
}
}
/// <summary>
/// 音效开关
/// </summary>
public static bool SoundEffectEnabled
{
get => PlayerPrefs.GetInt(nameof(SoundEffectEnabled), 0) == 1;
set => PlayerPrefs.SetInt(nameof(SoundEffectEnabled), value? 1: 0);
}
/// <summary>
/// 震动开关
/// </summary>
public static bool VibrationEnabled
{
get => PlayerPrefs.GetInt(nameof(VibrationEnabled), 0) == 1;
set => PlayerPrefs.SetInt(nameof(VibrationEnabled), value? 1: 0);
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2b2215a925974a3593ff2c6db2cf02fc
timeCreated: 1679986312

View File

@ -27,7 +27,7 @@ namespace Guru
return ((MonoBehaviour)this).StartDelayed(delay, callback); return ((MonoBehaviour)this).StartDelayed(delay, callback);
} }
public Coroutine StartDelayedWithFrame(int framesOfDelay, Action callback) public Coroutine StartDelayed(int framesOfDelay, Action callback)
{ {
return ((MonoBehaviour)this).StartDelayed(framesOfDelay, callback); return ((MonoBehaviour)this).StartDelayed(framesOfDelay, callback);
} }

View File

@ -2,5 +2,5 @@ using UnityEngine;
public static class NetworkUtil public static class NetworkUtil
{ {
public static bool IsNetAvailable => Application.internetReachability != NetworkReachability.NotReachable; public static bool IsNetAvaliable => Application.internetReachability != NetworkReachability.NotReachable;
} }

View File

@ -1,99 +1,58 @@
using Firebase.Auth;
using Firebase.Extensions;
using UnityEngine;
namespace Guru namespace Guru
{ {
using System; using System;
using Firebase.Auth;
using Firebase.Extensions;
using UnityEngine;
public static partial class FirebaseUtil public static partial class FirebaseUtil
{ {
private static FirebaseUser FirebaseUser => FirebaseAuth.DefaultInstance.CurrentUser; public static FirebaseUser CurrentUser => FirebaseAuth.DefaultInstance.CurrentUser;
private const float LOGIN_RETRY_MAX_TIME = 60; // 最大请求间隔时间
private const float LOGIN_RETRY_INTERVAL = 10; // 最大请求间隔时间
private static float _retryDelayTime = 10; // 登录重试时间
/// <summary>
/// 登录 Firebase 用户
/// </summary>
/// <param name="authToken"></param>
/// <param name="onLoginResult"></param>
/// <param name="autoRetry"></param>
private static void LoginFirebaseWithToken(Action<bool> onLoginResult = null, string authToken = "", bool autoRetry = true)
{
// #1 Firebase 已获取用户
if (FirebaseUser != null)
{
Log.I(LOG_TAG, $"[Auth] user existsUserId:{FirebaseUser.UserId}");
onLoginResult?.Invoke(true);
return;
}
if (string.IsNullOrEmpty(authToken)) authToken = IPMConfig.IPM_FIREBASE_TOKEN;
Log.I(LOG_TAG, $"[Auth] Firebase Token:{authToken}");
if (!string.IsNullOrEmpty(authToken) && NetworkUtil.IsNetAvailable)
{
LoginFirebase(authToken, autoRetry, onLoginResult);
return;
}
// Token 为空 或 网络不可用
if (autoRetry)
{
// 继续重试
DelayCallFirebaseLogin(Mathf.Min(_retryDelayTime, LOGIN_RETRY_MAX_TIME), onLoginResult, authToken);
_retryDelayTime += LOGIN_RETRY_INTERVAL; // 最大重试间隔 60s
}
else
{
// 不再重试
onLoginResult?.Invoke(false);
}
}
private static void LoginFirebase(string authToken = "", bool autoRetry = true, Action<bool> onLoginResult = null)
{
FirebaseAuth.DefaultInstance.SignInWithCustomTokenAsync(authToken)
.ContinueWithOnMainThread(task =>
{
// ----- Task failed -----
if (task.IsCanceled || task.IsFaulted)
{
Log.E(LOG_TAG,"[Auth] SignInWithCustomTokenAsync encountered an error: " + task.Exception);
if (autoRetry)
{
DelayCallFirebaseLogin(_retryDelayTime, onLoginResult, authToken); // 自动重试
}
else
{
onLoginResult?.Invoke(false); // 不再重试
}
return;
}
// ----- Check Result -----
bool success = FirebaseUser != null;
onLoginResult?.Invoke(success);
_retryDelayTime = LOGIN_RETRY_INTERVAL;
Analytics.SetUserProperty(Analytics.PropertySignUpMethod, "google"); // 上报用户登录属性
});
}
private static readonly WaitForSeconds _wait = new WaitForSeconds(10);
// TODO:P0 协程不应该用在此场景
/// <summary> public static void AuthUser(Action onSuccessHandler = null)
/// 延迟调用 Firebase 登录请求
/// </summary>
/// <param name="delaySeconds"></param>
/// <param name="callback"></param>
/// <param name="token"></param>
private static void DelayCallFirebaseLogin(float delaySeconds, Action<bool> callback, string token = "")
{ {
var delay = new WaitForSeconds(delaySeconds); //FirebaseAuth获取用户验证并同步用户数据
CoroutineHelper.Instance.StartDelayed(delay, ()=> LoginFirebaseWithToken(callback, token)); if (CurrentUser != null)
{
Log.I(LOG_TAG, $"[Auth] user existsUserId:{CurrentUser.UserId}");
OnFirebaseAuthResult?.Invoke(true);
onSuccessHandler?.Invoke();
return;
}
string authToken = IPMConfig.IPM_FIREBASE_TOKEN;
Log.I(LOG_TAG, $"[Auth] Firebase Token:{authToken}");
if (string.IsNullOrEmpty(authToken) || !NetworkUtil.IsNetAvaliable)
{
CoroutineHelper.Instance.StartDelayed(_wait, ()=> AuthUser(onSuccessHandler));
OnFirebaseAuthResult?.Invoke(false);
return;
}
FirebaseAuth.DefaultInstance.SignInWithCustomTokenAsync(authToken)
.ContinueWithOnMainThread(task =>
{
// ----- Task failed -----
if (task.IsCanceled || task.IsFaulted)
{
Log.E(LOG_TAG,"[Auth] SignInWithCustomTokenAsync encountered an error: " + task.Exception);
OnFirebaseAuthResult?.Invoke(false);
CoroutineHelper.Instance.StartDelayed(_wait, () => AuthUser(onSuccessHandler));
return;
}
// ----- User is NULL -----
if (CurrentUser == null)
{
OnFirebaseAuthResult?.Invoke(false);
CoroutineHelper.Instance.StartDelayed(_wait, ()=> AuthUser(onSuccessHandler));
return;
}
// ----- Success -----
OnFirebaseAuthResult?.Invoke(true); // 最后判定是成功的
onSuccessHandler?.Invoke();
});
} }
} }
} }

View File

@ -5,8 +5,6 @@ namespace Guru
{ {
public static partial class FirebaseUtil public static partial class FirebaseUtil
{ {
public static void InitCrashlytics() public static void InitCrashlytics()
{ {
if(!string.IsNullOrEmpty(IPMConfig.IPM_UID)) if(!string.IsNullOrEmpty(IPMConfig.IPM_UID))
@ -19,7 +17,6 @@ namespace Guru
/// </summary> /// </summary>
public static void SetUserID(string userID) public static void SetUserID(string userID)
{ {
if (!IsFirebaseInitialized) return;
Crashlytics.SetUserId(userID); Crashlytics.SetUserId(userID);
} }
@ -29,7 +26,6 @@ namespace Guru
/// </summary> /// </summary>
public static void SetCustomData(string key, string value) public static void SetCustomData(string key, string value)
{ {
if (!IsFirebaseInitialized) return;
Crashlytics.SetCustomKey(key, value); Crashlytics.SetCustomKey(key, value);
} }
@ -38,7 +34,6 @@ namespace Guru
/// </summary> /// </summary>
public static void LogMessage(string message) public static void LogMessage(string message)
{ {
if (!IsFirebaseInitialized) return;
Crashlytics.Log(message); Crashlytics.Log(message);
} }
@ -47,7 +42,6 @@ namespace Guru
/// </summary> /// </summary>
public static void LogException(Exception exception) public static void LogException(Exception exception)
{ {
if (!IsFirebaseInitialized) return;
Crashlytics.LogException(exception); Crashlytics.LogException(exception);
} }
} }

View File

@ -1,12 +1,10 @@
using System;
using System.Collections;
using Firebase.Messaging;
using UnityEngine;
namespace Guru namespace Guru
{ {
using System;
using System.Collections;
using Firebase.Messaging;
using UnityEngine;
public static partial class FirebaseUtil public static partial class FirebaseUtil
{ {
private static int _messageRetry = 5; private static int _messageRetry = 5;
@ -19,7 +17,7 @@ namespace Guru
_isAutoFetchFcmToken = value; _isAutoFetchFcmToken = value;
} }
private static void InitializeMessage() public static void InitializeMessage()
{ {
if (_isAutoFetchFcmToken) if (_isAutoFetchFcmToken)
{ {
@ -50,7 +48,7 @@ namespace Guru
if (task.IsFaulted || task.IsCanceled) if (task.IsFaulted || task.IsCanceled)
{ {
CoroutineHelper.Instance.StartDelayed(10, GetFCMToken); CoroutineHelper.Instance.StartDelayed(_wait, GetFCMToken);
} }
else else
{ {
@ -65,7 +63,7 @@ namespace Guru
private static void UploadDeviceInfo() private static void UploadDeviceInfo()
{ {
if (!NetworkUtil.IsNetAvailable) if (!NetworkUtil.IsNetAvaliable)
{ {
double retryDelay = Math.Pow(2, _messageRetry); double retryDelay = Math.Pow(2, _messageRetry);
_messageRetry++; _messageRetry++;
@ -86,14 +84,14 @@ namespace Guru
} }
} }
private static void OnTokenReceived(object sender, TokenReceivedEventArgs token) private static void OnTokenReceived(object sender, Firebase.Messaging.TokenReceivedEventArgs token)
{ {
#if UNITY_IOS #if UNITY_IOS
DeviceUtil.SetiOSBadge(); DeviceUtil.SetiOSBadge();
#endif #endif
} }
public static void OnMessageReceived(object sender, MessageReceivedEventArgs e) public static void OnMessageReceived(object sender, Firebase.Messaging.MessageReceivedEventArgs e)
{ {
#if UNITY_IOS #if UNITY_IOS
DeviceUtil.SetiOSBadge(); DeviceUtil.SetiOSBadge();

View File

@ -96,7 +96,7 @@ namespace Guru
|| _remoteConfigInstance.Info.LastFetchStatus != LastFetchStatus.Success) || _remoteConfigInstance.Info.LastFetchStatus != LastFetchStatus.Success)
{ {
Log.E(LOG_TAG, "Firebase RemoteConfig Fetch Failure"); Log.E(LOG_TAG, "Firebase RemoteConfig Fetch Failure");
CoroutineHelper.Instance.StartDelayed(10, FetchRemoteValue); CoroutineHelper.Instance.StartDelayed(_wait, FetchRemoteValue);
return; return;
} }

View File

@ -1,214 +1,160 @@
using System;
using Firebase;
using Firebase.Analytics;
using Firebase.Extensions;
using UnityEngine;
namespace Guru namespace Guru
{ {
using System;
using Firebase;
using Firebase.Analytics;
using Firebase.Extensions;
using UnityEngine;
public static partial class FirebaseUtil public static partial class FirebaseUtil
{ {
private static readonly string LOG_TAG = "Firebase"; private static readonly string LOG_TAG = "Firebase";
private static bool _isReady = false; private static bool _isReady = false;
public static bool IsReady => _isReady && IsFirebaseInitialized; public static bool IsReady => _isReady;
private static DependencyStatus DependencyStatus = DependencyStatus.UnavailableOther; public static DependencyStatus DependencyStatus = DependencyStatus.UnavailableOther;
public static bool IsFirebaseInitialized => DependencyStatus == DependencyStatus.Available; public static bool IsFirebaseInitialized => DependencyStatus == DependencyStatus.Available;
public static Action<bool> onInitComplete;
private static Action<bool> _onCheckAndFixDepsHandler; public static Action<bool> OnFirebaseAuthResult;
private static Action<string> _onGetFirebaseIdHandler; public static Action<bool> OnUserAuthResult;
private static Action<bool> _onGetGuruUIDHandler; // TODO: 需要从 FirebaseUtils 内拿走
private static Action<bool> _onFirebaseLoginResult; public static Action<string> OnAdjustDeeplinkCallback = null;
/// <summary>
/// 初始化 Firebase public static void InitFirebase(Action callback)
/// </summary>
/// <param name="onDepsCheckResult">Firebase 自身解决依赖结果回调</param>
/// <param name="onGetFirebaseId">异步获取到 FirebaseId 回调</param>
/// <param name="onGetGuruUIDResult">Firebase 授权回调</param>
/// <param name="onFirebaseLoginResult"></param>
public static void Init(Action<bool> onDepsCheckResult, Action<string> onGetFirebaseId = null,
Action<bool> onGetGuruUIDResult = null, Action<bool> onFirebaseLoginResult = null)
{ {
_isReady = false; _isReady = false;
_onCheckAndFixDepsHandler = onDepsCheckResult; // Analytics.InitAnalytics(); // 打点提前初始化
_onGetFirebaseIdHandler = onGetFirebaseId;
_onGetGuruUIDHandler = onGetGuruUIDResult;
_onFirebaseLoginResult = onFirebaseLoginResult;
// 初始化 Firebase 依赖 // Loom.StartUp(); // 确保主线程开启
// 初始化 Fireabse 依赖
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => { FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
DependencyStatus = task.Result; DependencyStatus = task.Result;
if (DependencyStatus == DependencyStatus.Available) if (DependencyStatus == DependencyStatus.Available)
{ {
_isReady = true; _isReady = true;
OnFirebaseDepsCheckSuccess(); // Deps 处理通过 InitializeFirebaseComp();
callback?.Invoke();
} }
else else
{ {
Log.E(LOG_TAG, "Could not resolve all Firebase dependencies: " + DependencyStatus); Log.E(LOG_TAG, "Could not resolve all Firebase dependencies: " + DependencyStatus);
} }
_onCheckAndFixDepsHandler?.Invoke(_isReady); onInitComplete?.Invoke(_isReady);
}); });
} }
private static void InitializeFirebaseComp()
/// <summary>
/// Deps 处理通过
/// 初始化各模块
/// </summary>
private static void OnFirebaseDepsCheckSuccess()
{ {
InitCrashlytics(); // 老项目沿用此逻辑 InitCrashlytics(); // 老项目沿用此逻辑
InitRemoteConfig(); // 老项目沿用此逻辑 InitRemoteConfig(); // 老项目沿用此逻辑
InitializeMessage(); // 初始化 Messaging 服务 // TODO: 需要从 FirebaseUtils 内拿走
GetFirebaseIdAsync(); // 开始获取 FirebaseId InitAdjustService(); // 初始化 adjust 服务
StartVerifyTokenAndAuthAsync(); //开始验证授权和Token if (IPMConfig.IPM_UID.IsNullOrEmpty())
}
#region FirebaseId
private static void GetFirebaseIdAsync()
{
// 异步获取 FirebaseID
FirebaseAnalytics.GetAnalyticsInstanceIdAsync()
.ContinueWithOnMainThread(task =>
{
string fid = task.Result;
if (task.IsCompleted && !string.IsNullOrEmpty(fid))
{
// 保存本地ID备份
IPMConfig.FIREBASE_ID = fid; // 保存FirebaseID
Debug.Log($"[SDK] --- Get FirebaseID: {fid}");
}
else
{
Debug.LogError($"[SDK] --- Fetch FirebaseID failed on start!");
}
_onGetFirebaseIdHandler?.Invoke(fid);
});
}
#endregion
#region Token and Auth
/// <summary>
/// 异步验证所有 Token 有效期
/// </summary>
private static void StartVerifyTokenAndAuthAsync()
{
if (string.IsNullOrEmpty(IPMConfig.IPM_UID))
{ {
Log.I(LOG_TAG, "没有存储UID时从中台获取匿名认证授权"); Log.I(LOG_TAG, "没有存储UID时从中台获取匿名认证授权");
// 没有存储UID时从中台获取匿名认证授权 //没有存储UID时从中台获取匿名认证授权
StartGuruLoginWithDeviceId(success => new AuthUserRequest()
{ .SetRetryTimes(-1)
_onGetGuruUIDHandler?.Invoke(success); .SetSuccessCallBack(() =>
{
if (success) { OnUserAuthResult?.Invoke(true);
// 用户 UID 不为空 AuthUser(InitializeMessage);
StartLoginWithFirebase(); }).SetFailCallBack(() =>
} {
}); OnUserAuthResult?.Invoke(false);
})
.Send();
} }
else else
{ {
// 检查中台 Token 是否过期 InitializeMessage();
if (IsGuruTokenExpired()) int currentTimeStamp = TimeUtil.GetCurrentTimeStampSecond();
if(currentTimeStamp - IPMConfig.IPM_TOKEN_TIME >= IPMConfig.TOKEN_VALID_TIME)
{ {
RefreshGuruToken(); //中台Token失效从中台重新获取Token
new RefreshTokenRequest().SetRetryTimes(-1).Send();
} }
// 检查中台 Firebase Token 是否过期 if(currentTimeStamp - IPMConfig.IPM_FIREBASE_TOKEN_TIME >= IPMConfig.FIREBASE_TOKEN_VALID_TIME)
if (IsFirebaseTokenExpired())
{ {
RefreshFirebaseToken(StartLoginWithFirebase); // 重新获取 Firebase Token //中台firebaseToken失效从中台重新获取firebaseToken
new RefreshFirebaseTokenRequest()
.SetRetryTimes(-1)
.SetSuccessCallBack(()=> AuthUser(InitializeMessage))
.Send();
} }
else else
{ {
StartLoginWithFirebase(); AuthUser(InitializeMessage);
} }
} }
} }
#region 启动 Adjust 服务
// TODO: 需要从 FirebaseUtils 内拿走
/// <summary> /// <summary>
/// 使用设备 ID 进行中台匿名认证 /// 启动 Adjust 服务
/// </summary> /// </summary>
private static void StartGuruLoginWithDeviceId(Action<bool> onLoginResult = null) private static void InitAdjustService()
{ {
// 没有存储UID时从中台获取匿名认证授权 FirebaseAnalytics.GetAnalyticsInstanceIdAsync()
var request = new AuthUserRequest() .ContinueWithOnMainThread(task =>
.SetRetryTimes(-1) // 不成功的话会一直请求
.SetSuccessCallBack(() =>
{ {
onLoginResult?.Invoke(true);
}).SetFailCallBack(() => if (task.IsCompleted && !string.IsNullOrEmpty(task.Result))
{ {
onLoginResult?.Invoke(false); // 保存本地ID备份
string fid = task.Result;
IPMConfig.FIREBASE_ID = fid; // 保存FirebaseID
}
else
{
UnityEngine.Debug.LogError($"Fetch FirebaseID failed on start!");
}
// 启动 AdjustService
string appToken = GuruSettings.Instance.AdjustSetting?.GetAppToken() ?? "";
string fbAppId = GuruSettings.Instance.IPMSetting.FacebookAppId;
if (!string.IsNullOrEmpty(IPMConfig.ADJUST_ID))
ReportAdjustId(IPMConfig.ADJUST_ID); // 二次启动后,若有值则立即上报属性
AdjustService.StartService(appToken, fbAppId, OnGetAdjustId, OnAdjustDeeplinkCallback);
}); });
request.Send();
} }
/// <summary> private static void OnGetAdjustId(string adjustId)
/// Firebase Token 是否过期
/// </summary>
/// <returns></returns>
private static bool IsFirebaseTokenExpired()
{ {
int currentTimeStamp = TimeUtil.GetCurrentTimeStampSecond(); // 获取 ADID
return currentTimeStamp - IPMConfig.IPM_TOKEN_TIME >= IPMConfig.TOKEN_VALID_TIME; if (string.IsNullOrEmpty(adjustId))
} {
adjustId = "not_set";
private static void RefreshFirebaseToken(Action onFirebaseTokenRefreshed = null) }
{ else
//中台firebaseToken失效从中台重新获取firebaseToken {
var request = new RefreshFirebaseTokenRequest() IPMConfig.ADJUST_ID = adjustId;
.SetRetryTimes(-1) }
.SetSuccessCallBack(()=> onFirebaseTokenRefreshed?.Invoke()); ReportAdjustId(adjustId);
request.Send();
} Analytics.OnAdjustInitComplete();
/// <summary>
/// Guru Token 是否过期
/// </summary>
/// <returns></returns>
private static bool IsGuruTokenExpired()
{
int currentTimeStamp = TimeUtil.GetCurrentTimeStampSecond();
return currentTimeStamp - IPMConfig.IPM_FIREBASE_TOKEN_TIME >= IPMConfig.FIREBASE_TOKEN_VALID_TIME;
} }
private static void RefreshGuruToken()
private static void ReportAdjustId(string adjustId)
{ {
//中台Token失效从中台重新获取Token FirebaseAnalytics.SetUserProperty("adjust_id", adjustId); // 仅上报 Firebase 用户属性
var request = new RefreshTokenRequest() Debug.Log($"[SDK] --- Firebase + Adjust ID: {adjustId}");
.SetRetryWaitSeconds(10)
.SetRetryTimes(-1); // 不成功的话会一直请求
request.Send();
} }
#endregion #endregion
#region Firebase 用户登录
/// <summary>
/// 开始登录 Firebase
/// </summary>
private static void StartLoginWithFirebase()
{
LoginFirebaseWithToken(OnFirebaseLoginComplete, IPMConfig.IPM_FIREBASE_TOKEN); // 成功后进行 Firebase 认证
}
/// <summary>
/// Firebase 认证用户完成
/// </summary>
/// <param name="success"></param>
private static void OnFirebaseLoginComplete(bool success)
{
_onFirebaseLoginResult?.Invoke(success);
}
#endregion
} }
} }

View File

@ -44,7 +44,8 @@ namespace Guru
this.Log(response); this.Log(response);
this.Log(responseData.data.ToString()); this.Log(responseData.data.ToString());
Analytics.SetFirebaseUserId(responseData.data.uid); Analytics.SetUserIDProperty(responseData.data.uid);
Crashlytics.SetUserId(responseData.data.uid);
IPMConfig.IPM_UID = responseData.data.uid; IPMConfig.IPM_UID = responseData.data.uid;
IPMConfig.IPM_UID_INT = responseData.data.uidInt; IPMConfig.IPM_UID_INT = responseData.data.uidInt;

View File

@ -29,7 +29,8 @@ namespace Guru
this.Log(responseData.data.ToString()); this.Log(responseData.data.ToString());
IPMConfig.IPM_TOKEN = responseData.data.token; IPMConfig.IPM_TOKEN = responseData.data.token;
IPMConfig.IPM_TOKEN_TIME = TimeUtil.GetCurrentTimeStampSecond(); IPMConfig.IPM_TOKEN_TIME = TimeUtil.GetCurrentTimeStampSecond();
this.Log($"[SDK] --- RefreshTokenRequest Success: {responseData.data.token}"); PlayerPrefs.SetString("IPM_TOKEN", responseData.data.token);
this.Log("@@@ Send OK!");
} }
} }
} }

View File

@ -13,7 +13,7 @@ namespace Guru
protected abstract UnityWebRequest CreateRequest(); protected abstract UnityWebRequest CreateRequest();
protected abstract void RequestSuccessCallBack(string response); protected abstract void RequestSuccessCallBack(string response);
private WaitForSeconds _waitTime = new WaitForSeconds(5); private readonly WaitForSeconds _waitTime = new WaitForSeconds(5);
private int _retryTimes = 3; private int _retryTimes = 3;
private int _timeOut = 90; private int _timeOut = 90;
@ -28,12 +28,6 @@ namespace Guru
return this; return this;
} }
public RequestBase SetRetryWaitSeconds(int waitSeconds)
{
_waitTime = new WaitForSeconds(waitSeconds);;
return this;
}
public RequestBase SetTimeOut(int timeOut) public RequestBase SetTimeOut(int timeOut)
{ {
_timeOut = timeOut; _timeOut = timeOut;

View File

@ -2,10 +2,13 @@
namespace Guru namespace Guru
{ {
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using Const=AdStatusConsts; using Consts=AdStatusConsts;
public partial class AdStatusPresenter public partial class AdStatusPresenter
{ {
@ -101,10 +104,12 @@ namespace Guru
private StringBuilder _infoBuff; private StringBuilder _infoBuff;
private string CreateMonitorInfo() private string CreateMonitorInfo()
{ {
string msg; string msg = "";
bool loaded = false;
if (ADService.Instance == null || !ADService.Instance.IsInitialized) if (ADService.Instance == null || !ADService.Instance.IsInitialized)
{ {
msg = ColoredText("AdService not initialized...", Const.ColorRed); msg = ColoredText("AdService not initialized...", Consts.ColorRed);
return msg; return msg;
} }
@ -113,7 +118,7 @@ namespace Guru
if (_curBadsInfo == null) if (_curBadsInfo == null)
{ {
msg = $"BADS: {ColoredText("not ready", Const.ColorRed)}\n"; msg = $"BADS: {ColoredText("not ready", Consts.ColorRed)}\n";
} }
else else
{ {
@ -121,25 +126,25 @@ namespace Guru
switch (_curBadsInfo.status) switch (_curBadsInfo.status)
{ {
case AdStatusType.Loaded: case AdStatusType.Loaded:
msg = $"BADS: {ColoredText("loaded", Const.ColorGreen)}\n\tnetwork: {_curBadsInfo.network}\n\twaterfall: {_curBadsInfo.waterfall}\n"; msg = $"BADS: {ColoredText("loaded", Consts.ColorGreen)}\n\tnetwork: {_curBadsInfo.network}\n\twaterfall: {_curBadsInfo.waterfall}\n";
break; break;
case AdStatusType.LoadFailed: case AdStatusType.LoadFailed:
msg = $"BADS: {ColoredText("loading failed", Const.ColorRed)}\n\tmessage: {_curBadsInfo.info}\n"; msg = $"BADS: {ColoredText("loading failed", Consts.ColorRed)}\n\tmessage: {_curBadsInfo.info}\n";
break; break;
case AdStatusType.DisplayFailed: case AdStatusType.DisplayFailed:
msg = $"BADS: {ColoredText("display failed", Const.ColorRed)}\n\tmessage: {_curBadsInfo.info}\n"; msg = $"BADS: {ColoredText("display failed", Consts.ColorRed)}\n\tmessage: {_curBadsInfo.info}\n";
break; break;
case AdStatusType.Loading: case AdStatusType.Loading:
msg = $"BADS: {ColoredText("loading...", Const.ColorYellow)}\n\tformat: {_curBadsInfo.format}\n"; msg = $"BADS: {ColoredText("loading...", Consts.ColorYellow)}\n\tformat: {_curBadsInfo.format}\n";
break; break;
case AdStatusType.Paid: case AdStatusType.Paid:
msg = $"BADS: {ColoredText("display", Const.ColorGreen)}\n\tnetwork: {_curBadsInfo.network}\n\trevenue: {_curBadsInfo.revenue}\n"; msg = $"BADS: {ColoredText("display", Consts.ColorGreen)}\n\tnetwork: {_curBadsInfo.network}\n\trevenue: {_curBadsInfo.revenue}\n";
break; break;
case AdStatusType.NotReady: case AdStatusType.NotReady:
msg = $"BADS: {ColoredText("not ready", Const.ColorGray)}\n\t{ColoredText("---", Const.ColorGray)}\n"; msg = $"BADS: {ColoredText("not ready", Consts.ColorGray)}\n\t{ColoredText("---", Consts.ColorGray)}\n";
break; break;
default: default:
msg = $"BADS: {ColoredText("other", Const.ColorGray)}\n\tstatus: {ColoredText($"{_curBadsInfo.status}", Const.ColorYellow)}\n"; msg = $"BADS: {ColoredText("other", Consts.ColorGray)}\n\tstatus: {ColoredText($"{_curBadsInfo.status}", Consts.ColorYellow)}\n";
break; break;
} }
} }
@ -148,32 +153,32 @@ namespace Guru
if (_curIadsInfo == null) if (_curIadsInfo == null)
{ {
msg = $"IADS: {ColoredText("not ready", Const.ColorRed)}\n"; msg = $"IADS: {ColoredText("not ready", Consts.ColorRed)}\n";
} }
else else
{ {
switch (_curIadsInfo.status) switch (_curIadsInfo.status)
{ {
case AdStatusType.Loaded: case AdStatusType.Loaded:
msg = $"IADS: {ColoredText("loaded", Const.ColorGreen)}\n\tnetwork: {_curIadsInfo.network}\n\twaterfall: {_curIadsInfo.waterfall}\n"; msg = $"IADS: {ColoredText("loaded", Consts.ColorGreen)}\n\tnetwork: {_curIadsInfo.network}\n\twaterfall: {_curIadsInfo.waterfall}\n";
break; break;
case AdStatusType.LoadFailed: case AdStatusType.LoadFailed:
msg = $"IADS: {ColoredText("loading failed", Const.ColorRed)}\n\tmessage: {_curIadsInfo.info}\n"; msg = $"IADS: {ColoredText("loading failed", Consts.ColorRed)}\n\tmessage: {_curIadsInfo.info}\n";
break; break;
case AdStatusType.DisplayFailed: case AdStatusType.DisplayFailed:
msg = $"IADS: {ColoredText("display failed", Const.ColorRed)}\n\tmessage: {_curIadsInfo.info}\n"; msg = $"IADS: {ColoredText("display failed", Consts.ColorRed)}\n\tmessage: {_curIadsInfo.info}\n";
break; break;
case AdStatusType.Loading: case AdStatusType.Loading:
msg = $"IADS: {ColoredText("loading...", Const.ColorYellow)}\n\tformat: {_curIadsInfo.format}\n"; msg = $"IADS: {ColoredText("loading...", Consts.ColorYellow)}\n\tformat: {_curIadsInfo.format}\n";
break; break;
case AdStatusType.Paid: case AdStatusType.Paid:
msg = $"IADS: {ColoredText("get revenue", Const.ColorGreen)}\n\trevenue: {_curIadsInfo.revenue}\n"; msg = $"IADS: {ColoredText("get revenue", Consts.ColorGreen)}\n\trevenue: {_curIadsInfo.revenue}\n";
break; break;
case AdStatusType.NotReady: case AdStatusType.NotReady:
msg = $"IADS: {ColoredText("not ready", Const.ColorGray)}\n\t{ColoredText("---", Const.ColorGray)}\n"; msg = $"IADS: {ColoredText("not ready", Consts.ColorGray)}\n\t{ColoredText("---", Consts.ColorGray)}\n";
break; break;
default: default:
msg = $"IADS: {ColoredText("other", Const.ColorGray)}\n\tstatus: {ColoredText($"{_curIadsInfo.status}", Const.ColorYellow)}\n"; msg = $"IADS: {ColoredText("other", Consts.ColorGray)}\n\tstatus: {ColoredText($"{_curIadsInfo.status}", Consts.ColorYellow)}\n";
break; break;
} }
} }
@ -182,32 +187,32 @@ namespace Guru
if (_curRadsInfo == null) if (_curRadsInfo == null)
{ {
msg = $"RADS: {ColoredText("not ready", Const.ColorRed)}\n"; msg = $"RADS: {ColoredText("not ready", Consts.ColorRed)}\n";
} }
else else
{ {
switch (_curRadsInfo.status) switch (_curRadsInfo.status)
{ {
case AdStatusType.Loaded: case AdStatusType.Loaded:
msg = $"RADS: {ColoredText("loaded", Const.ColorGreen)}\n\tnetwork: {_curRadsInfo.network}\n\twaterfall: {_curRadsInfo.waterfall}\n"; msg = $"RADS: {ColoredText("loaded", Consts.ColorGreen)}\n\tnetwork: {_curRadsInfo.network}\n\twaterfall: {_curRadsInfo.waterfall}\n";
break; break;
case AdStatusType.LoadFailed: case AdStatusType.LoadFailed:
msg = $"RADS: {ColoredText("loading failed", Const.ColorRed)}\n\tmessage: {_curRadsInfo.info}\n"; msg = $"RADS: {ColoredText("loading failed", Consts.ColorRed)}\n\tmessage: {_curRadsInfo.info}\n";
break; break;
case AdStatusType.DisplayFailed: case AdStatusType.DisplayFailed:
msg = $"RADS: {ColoredText("display failed", Const.ColorRed)}\n\tmessage: {_curRadsInfo.info}\n"; msg = $"RADS: {ColoredText("display failed", Consts.ColorRed)}\n\tmessage: {_curRadsInfo.info}\n";
break; break;
case AdStatusType.Loading: case AdStatusType.Loading:
msg = $"RADS: {ColoredText("loading...", Const.ColorYellow)}\n\tformat: {_curRadsInfo.format}\n"; msg = $"RADS: {ColoredText("loading...", Consts.ColorYellow)}\n\tformat: {_curRadsInfo.format}\n";
break; break;
case AdStatusType.Paid: case AdStatusType.Paid:
msg = $"RADS: {ColoredText("get revenue", Const.ColorGreen)}\n\trevenue: {_curRadsInfo.revenue}\n"; msg = $"RADS: {ColoredText("get revenue", Consts.ColorGreen)}\n\trevenue: {_curRadsInfo.revenue}\n";
break; break;
case AdStatusType.NotReady: case AdStatusType.NotReady:
msg = $"RADS: {ColoredText("not ready", Const.ColorGray)}\n\t{ColoredText("---", Const.ColorGray)}\n"; msg = $"RADS: {ColoredText("not ready", Consts.ColorGray)}\n\t{ColoredText("---", Consts.ColorGray)}\n";
break; break;
default: default:
msg = $"RADS: {ColoredText("other", Const.ColorGray)}\n\tstatus: {ColoredText($"{_curRadsInfo.status}", Const.ColorYellow)}\n"; msg = $"RADS: {ColoredText("other", Consts.ColorGray)}\n\tstatus: {ColoredText($"{_curRadsInfo.status}", Consts.ColorYellow)}\n";
break; break;
} }
} }
@ -242,13 +247,13 @@ namespace Guru
private void AddCallbacks() private void AddCallbacks()
{ {
//----------------- Banner ----------------- //----------------- Banner -----------------
ADService.Instance.OnBannerStartLoad += OnBannerStartLoadEvent; ADService.OnBannerStartLoad += OnBannerStartLoadEvent;
MaxSdkCallbacks.Banner.OnAdLoadedEvent += OnBannerAdLoadedEvent; MaxSdkCallbacks.Banner.OnAdLoadedEvent += OnBannerAdLoadedEvent;
MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += OnBannerAdLoadFailEvent; MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += OnBannerAdLoadFailEvent;
MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent += OnBannerAdRevenuePaidEvent; MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent += OnBannerAdRevenuePaidEvent;
MaxSdkCallbacks.Banner.OnAdClickedEvent += OnBannerAdClickedEvent; MaxSdkCallbacks.Banner.OnAdClickedEvent += OnBannerAdClickedEvent;
//----------------- Interstitials ----------------- //----------------- Interstitials -----------------
ADService.Instance.OnInterstitialStartLoad += OnInterStartLoadEvent; ADService.OnInterstitialStartLoad += OnInterStartLoadEvent;
MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnInterAdLoadedEvent; MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnInterAdLoadedEvent;
MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnInterAdLoadFailEvent; MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnInterAdLoadFailEvent;
MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent += OnInterAdDisplayFailEvent; MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent += OnInterAdDisplayFailEvent;
@ -256,7 +261,7 @@ namespace Guru
MaxSdkCallbacks.Interstitial.OnAdClickedEvent += OnInterAdClickedEvent; MaxSdkCallbacks.Interstitial.OnAdClickedEvent += OnInterAdClickedEvent;
MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += OnInterAdHiddenEvent; MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += OnInterAdHiddenEvent;
//----------------- Reward ----------------- //----------------- Reward -----------------
ADService.Instance.OnRewardedStartLoad += OnRewardedStartLoad; ADService.OnRewardedStartLoad += OnRewardedStartLoad;
MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnRewardAdLoadedEvent; MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnRewardAdLoadedEvent;
MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnRewardAdLoadFailEvent; MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnRewardAdLoadFailEvent;
MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += OnRewardAdDisplayFailEvent; MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += OnRewardAdDisplayFailEvent;
@ -268,13 +273,13 @@ namespace Guru
private void RemoveCallbacks() private void RemoveCallbacks()
{ {
//----------------- Banner ----------------- //----------------- Banner -----------------
ADService.Instance.OnBannerStartLoad -= OnBannerStartLoadEvent; ADService.OnBannerStartLoad -= OnBannerStartLoadEvent;
MaxSdkCallbacks.Banner.OnAdLoadedEvent -= OnBannerAdLoadedEvent; MaxSdkCallbacks.Banner.OnAdLoadedEvent -= OnBannerAdLoadedEvent;
MaxSdkCallbacks.Banner.OnAdLoadFailedEvent -= OnBannerAdLoadFailEvent; MaxSdkCallbacks.Banner.OnAdLoadFailedEvent -= OnBannerAdLoadFailEvent;
MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent -= OnBannerAdRevenuePaidEvent; MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent -= OnBannerAdRevenuePaidEvent;
MaxSdkCallbacks.Banner.OnAdClickedEvent -= OnBannerAdClickedEvent; MaxSdkCallbacks.Banner.OnAdClickedEvent -= OnBannerAdClickedEvent;
//----------------- Interstitials ----------------- //----------------- Interstitials -----------------
ADService.Instance.OnInterstitialStartLoad -= OnInterStartLoadEvent; ADService.OnInterstitialStartLoad -= OnInterStartLoadEvent;
MaxSdkCallbacks.Interstitial.OnAdLoadedEvent -= OnInterAdLoadedEvent; MaxSdkCallbacks.Interstitial.OnAdLoadedEvent -= OnInterAdLoadedEvent;
MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent -= OnInterAdLoadFailEvent; MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent -= OnInterAdLoadFailEvent;
MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent -= OnInterAdDisplayFailEvent; MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent -= OnInterAdDisplayFailEvent;
@ -282,7 +287,7 @@ namespace Guru
MaxSdkCallbacks.Interstitial.OnAdClickedEvent -= OnInterAdClickedEvent; MaxSdkCallbacks.Interstitial.OnAdClickedEvent -= OnInterAdClickedEvent;
MaxSdkCallbacks.Interstitial.OnAdHiddenEvent -= OnInterAdHiddenEvent; MaxSdkCallbacks.Interstitial.OnAdHiddenEvent -= OnInterAdHiddenEvent;
//----------------- Reward ----------------- //----------------- Reward -----------------
ADService.Instance.OnRewardedStartLoad -= OnRewardedStartLoad; ADService.OnRewardedStartLoad -= OnRewardedStartLoad;
MaxSdkCallbacks.Rewarded.OnAdLoadedEvent -= OnRewardAdLoadedEvent; MaxSdkCallbacks.Rewarded.OnAdLoadedEvent -= OnRewardAdLoadedEvent;
MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent -= OnRewardAdLoadFailEvent; MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent -= OnRewardAdLoadFailEvent;
MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent -= OnRewardAdDisplayFailEvent; MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent -= OnRewardAdDisplayFailEvent;

View File

@ -1,16 +1,13 @@
namespace Guru.Network namespace Guru.Network
{ {
using Guru; using Guru;
using System; using System;
using UnityEngine; using UnityEngine;
using System.Collections;
public class NetworkStatusMonitor public class NetworkStatusMonitor
{ {
private const string Tag = "[NET]"; private const string Tag = "[NET]";
private const string NETWORK_STATUS_NONE = "none"; private const string NETWORK_STATUS_NONE = "none";
private const string NETWORK_STATUS_MOBILE = "mobile"; private const string NETWORK_STATUS_MOBILE = "mobile";
private const string NETWORK_STATUS_WIFI = "wifi"; private const string NETWORK_STATUS_WIFI = "wifi";
@ -19,40 +16,6 @@ namespace Guru.Network
private const string NETWORK_STATUS_TETHER = "tether"; private const string NETWORK_STATUS_TETHER = "tether";
private const string NETWORK_STATUS_BLUETOOTH = "bluetooth"; private const string NETWORK_STATUS_BLUETOOTH = "bluetooth";
private const string NETWORK_STATUS_OTHER = "other"; private const string NETWORK_STATUS_OTHER = "other";
private const int CHECK_STATUS_INTERVAL_SECOND = 10;
private DateTime LastTriggerDate
{
get => _saveData.LastReportDate;
set => _saveData.LastReportDate = value;
}
private string LastNetworkStatus
{
get => _saveData.LastReportStatus;
set => _saveData.LastReportStatus = value;
}
private readonly Action<string> _onNetworkStatusChanged;
private readonly Action<string> _onFirstOfflineToday;
private readonly NetworkMonitorData _saveData;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="onNetworkStatusChanged"></param>
/// <param name="onFirstOfflineToday"></param>
public NetworkStatusMonitor(Action<string> onNetworkStatusChanged, Action<string> onFirstOfflineToday)
{
_onNetworkStatusChanged = onNetworkStatusChanged;
_onFirstOfflineToday = onFirstOfflineToday;
_saveData = new NetworkMonitorData(); // 读取数据
//TODO: 此处不应使用协程实现, 协程只用在 UI 上。
CoroutineHelper.Instance.StartCoroutine(OnCheckingNetworkStatus(CHECK_STATUS_INTERVAL_SECOND));
}
/// <summary> /// <summary>
/// 获取网络状态 /// 获取网络状态
@ -60,136 +23,15 @@ namespace Guru.Network
/// <returns></returns> /// <returns></returns>
public string GetNetworkStatus() public string GetNetworkStatus()
{ {
var internetReachability = Application.internetReachability; switch (Application.internetReachability)
switch (internetReachability)
{ {
case NetworkReachability.ReachableViaCarrierDataNetwork:
return NETWORK_STATUS_MOBILE;
case NetworkReachability.ReachableViaLocalAreaNetwork: case NetworkReachability.ReachableViaLocalAreaNetwork:
return NETWORK_STATUS_WIFI; return NETWORK_STATUS_WIFI;
case NetworkReachability.ReachableViaCarrierDataNetwork:
return NETWORK_STATUS_MOBILE;
} }
return NETWORK_STATUS_NONE; return NETWORK_STATUS_NONE;
} }
/// <summary>
/// 用户是否已经失去了链接
/// </summary>
/// <returns></returns>
private bool IsUserOffline() => Application.internetReachability == NetworkReachability.NotReachable;
/// <summary>
/// 当前是可以打点上报
/// </summary>
/// <returns></returns>
private bool IsSameDay(DateTime date) => DateTime.UtcNow.DayOfYear != date.DayOfYear && DateTime.UtcNow.Year != date.Year;
/// <summary>
/// 设置打点时间
/// </summary>
private void SetTriggerFirstOfflineDate() => LastTriggerDate = DateTime.UtcNow;
private bool ShouldTriggerFirstOffline()
{
return IsUserOffline() && IsSameDay(LastTriggerDate);
}
private void UpdateNetworkStatus()
{
var status = GetNetworkStatus();
#if DEBUG
Debug.Log($"{Tag} --- Update network status:{status}");
#endif
if (status != LastNetworkStatus)
{
_onNetworkStatusChanged(status);
}
if (ShouldTriggerFirstOffline())
{
Debug.Log($"{Tag} --- Report Offline: {LastNetworkStatus}");
_onFirstOfflineToday?.Invoke(LastNetworkStatus);
SetTriggerFirstOfflineDate();
}
LastNetworkStatus = status;
}
/// <summary>
/// 每隔一段时间检测一次网络状态
/// </summary>
/// <param name="interval"></param>
/// <returns></returns>
private IEnumerator OnCheckingNetworkStatus(float interval)
{
while (true)
{
UpdateNetworkStatus();
yield return new WaitForSeconds(interval);
}
}
[Serializable]
class NetworkMonitorData
{
private const string K_NETWORK_MONITOR_DATA = "guru_network_monitor_data";
private const string NETWORK_STATUS_NOT_SET = "not_set";
private DateTime _lastReportDate;
public DateTime LastReportDate
{
get => _lastReportDate;
set
{
_lastReportDate = value;
Save(true);
}
}
private string _lastReportStatus;
public string LastReportStatus
{
get => _lastReportStatus;
set
{
_lastReportStatus = value;
Save();
}
}
public NetworkMonitorData()
{
Load(); // 立即加载数据
}
private void Load()
{
var raw = PlayerPrefs.GetString(K_NETWORK_MONITOR_DATA, "");
_lastReportStatus = NETWORK_STATUS_NOT_SET;
_lastReportDate = new DateTime(1970, 1, 1);
if (!string.IsNullOrEmpty(raw))
{
try
{
var arr = raw.Split("|");
if (arr.Length > 0) _lastReportStatus = arr[0];
if (arr.Length > 1) _lastReportDate = DateTime.Parse(arr[1]);
}
catch (Exception ex)
{
Debug.LogError(ex);
}
}
}
public void Save(bool force = false)
{
var buffer = $"{_lastReportStatus}|{_lastReportDate:g}";
PlayerPrefs.SetString(K_NETWORK_MONITOR_DATA, buffer);
if(force) PlayerPrefs.Save();
}
}
} }
} }