Compare commits
	
		
			14 Commits 
		
	
	
		
			cf832d88ad
			...
			c8b85b0f79
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | c8b85b0f79 | |
|  | 9cf0dd5801 | |
|  | 719be42b59 | |
|  | 95271aaa98 | |
|  | a768b1de90 | |
|  | 720b47dcbf | |
|  | b4fbb5d3da | |
|  | 3f0f13be6c | |
|  | 23c1b8f4fc | |
|  | 96750cc3d8 | |
|  | 548959dd34 | |
|  | 9de443249d | |
|  | 6dd2574763 | |
|  | bd39fb09f1 | 
|  | @ -53,9 +53,12 @@ namespace Guru | |||
| 		/// </summary> | ||||
| 		/// <param name="appToken"></param> | ||||
| 		/// <param name="fbAppId">MIR 追踪 AppID</param> | ||||
| 		/// <param name="onInitComplete"></param> | ||||
| 		/// <param name="deviceId"></param> | ||||
| 		/// <param name="onInitComplete">初始化完成的时候会返回 AdjustId </param> | ||||
| 		/// <param name="onDeeplinkCallback"></param> | ||||
| 		public static void StartService(string appToken, string fbAppId = "", Action<string> onInitComplete = null, Action<string> onDeeplinkCallback = null) | ||||
| 		/// <param name="firebaseId"></param> | ||||
| 		public static void StartService(string appToken, string fbAppId = "", string firebaseId = "", string deviceId = "",  | ||||
| 			Action<string> onInitComplete = null, Action<string> onDeeplinkCallback = null) | ||||
| 		{ | ||||
| 			if (string.IsNullOrEmpty(appToken)) | ||||
| 			{ | ||||
|  | @ -65,7 +68,7 @@ namespace Guru | |||
| 
 | ||||
| 			_onInitComplete = onInitComplete; | ||||
| 
 | ||||
| 			InstallEvent(IPMConfig.FIREBASE_ID, IPMConfig.IPM_DEVICE_ID); // 注入启动参数 | ||||
| 			InstallEvent(firebaseId, deviceId); // 注入启动参数 | ||||
| 
 | ||||
| 			AdjustEnvironment environment = GetAdjustEnvironment(); | ||||
| 			AdjustConfig config = new AdjustConfig(appToken, environment); | ||||
|  |  | |||
|  | @ -20,19 +20,19 @@ namespace Guru | |||
|                 bool res; | ||||
|                 from = $"{files}/{AndroidLib}.f"; | ||||
|                 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..."); | ||||
|                 from = $"{files}/{AndroidLib}.f.meta"; | ||||
|                 to = $"{Application.dataPath}/Plugins/Android/{AndroidLib}.meta"; | ||||
|                 CopyFile(from, to); | ||||
|                 CopyFile(from, to);  // 无需覆盖 | ||||
|                  | ||||
|                 from = $"{files}/{iOSLib}.f"; | ||||
|                 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..."); | ||||
|                 from = $"{files}/{iOSLib}.f.meta"; | ||||
|                 to = $"{Application.dataPath}/Plugins/iOS/{iOSLib}.meta"; | ||||
|                 CopyFile(from, to); | ||||
|                 CopyFile(from, to);  // 无需覆盖 | ||||
|                  | ||||
|                 AssetDatabase.Refresh(); | ||||
|             } | ||||
|  | @ -55,25 +55,25 @@ namespace Guru | |||
|             return Path.GetFullPath($"{Application.dataPath}/../Packages/com.guru.unity.sdk.core/Runtime/GuruAdjust/Editor/Signature"); | ||||
|         } | ||||
| 
 | ||||
|         private static bool CopyFile(string source, string dest) | ||||
|         private static bool CopyFile(string from, string to, bool overwrite = false) | ||||
|         { | ||||
|             if (File.Exists(source)) | ||||
|             if (File.Exists(to) && !overwrite) | ||||
|             { | ||||
|                 if (!File.Exists(dest)) | ||||
|                 { | ||||
|                     File.Delete(dest); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var destDir = Directory.GetParent(dest); | ||||
|                     if(!destDir.Exists) destDir.Create(); | ||||
|                 } | ||||
|                 // 如果目标文件存在, 且不允许覆写, 则不进行拷贝 | ||||
|                 return false; | ||||
|             } | ||||
|              | ||||
|             if (File.Exists(from)) | ||||
|             { | ||||
|                 // 确保拷贝目录存在 | ||||
|                 var destDir = Directory.GetParent(to); | ||||
|                 if(destDir != null && !destDir.Exists) destDir.Create(); | ||||
|                  | ||||
|                 File.Copy(source, dest, true); | ||||
|                 File.Copy(from, to, overwrite); | ||||
|                 return true; | ||||
|             } | ||||
|             | ||||
|             Debug.Log($"<colo=red>File not found: {source}...</color>"); | ||||
|             Debug.Log($"<colo=red>File not found: {from}...</color>"); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,18 +8,20 @@ Sample Dependencies.xml: | |||
|         <androidPackage spec="androidx.core:core:1.7.0" /> | ||||
| <!--        <androidPackage spec="androidx.appcompat:appcompat:1.5.1" />--> | ||||
|         <androidPackage spec="androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" /> | ||||
|         <androidPackage spec="androidx.work:work-runtime:2.7.1" /> | ||||
|         <androidPackage spec="androidx.work:work-runtime-ktx:2.7.1" /> | ||||
|         <androidPackage spec="androidx.work:work-rxjava2:2.7.1" /> | ||||
|         <androidPackage spec="androidx.work:work-runtime:2.8.1" /> | ||||
|         <androidPackage spec="androidx.work:work-runtime-ktx:2.8.1" /> | ||||
|         <androidPackage spec="androidx.work:work-rxjava2:2.8.1" /> | ||||
|         <androidPackage spec="androidx.lifecycle:lifecycle-process:2.4.0" /> | ||||
|         <androidPackage spec="com.jakewharton.timber:timber:4.7.1" /> | ||||
|         <androidPackage spec="com.google.code.gson:gson:2.8.5" /> | ||||
|         <androidPackage spec="androidx.room:room-runtime:2.4.3" /> | ||||
|         <androidPackage spec="androidx.room:room-rxjava2:2.4.3" /> | ||||
|         <androidPackage spec="androidx.room:room-runtime:2.6.1" /> | ||||
|         <androidPackage spec="androidx.room:room-rxjava2:2.6.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:adapter-rxjava2:2.7.1" /> | ||||
|         <androidPackage spec="com.squareup.okhttp3:okhttp:4.9.3" /> | ||||
|         <androidPackage spec="com.squareup.okhttp3:okhttp:4.12.0" /> | ||||
|         <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.squareup.retrofit2:retrofit:2.7.1" />--> | ||||
|     </androidPackages> | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -1,5 +1,5 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 32eda01e213614348899eefe856392d3 | ||||
| guid: 8009d3bf70bb4438599a32f6ea601f9d | ||||
| PluginImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
										
											Binary file not shown.
										
									
								
							|  | @ -1,6 +1,3 @@ | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
|     using System; | ||||
|  | @ -14,7 +11,7 @@ namespace Guru | |||
|     public class GuruAnalytics | ||||
|     { | ||||
|         // Plugin Version | ||||
|         private const string Version = "1.11.1"; | ||||
|         private const string Version = "1.12.0"; | ||||
|          | ||||
|         public static readonly string Tag = "[ANU]"; | ||||
|         private static readonly string ActionName = "logger_error"; | ||||
|  | @ -85,6 +82,9 @@ namespace Guru | |||
|         /// </summary> | ||||
|         private readonly List<int> _errorCodeList = new List<int>(); | ||||
|         private bool _enableErrorLog; | ||||
| 
 | ||||
|         private string _experimentGroupId; | ||||
|         public string ExperimentGroupId => _experimentGroupId; | ||||
|          | ||||
|         /// <summary> | ||||
|         /// 启动日志错误上报 | ||||
|  | @ -105,20 +105,46 @@ namespace Guru | |||
|         /// <summary> | ||||
|         /// 初始化接口 | ||||
|         /// </summary> | ||||
|         public static void Init(string appId, string deviceInfo, Action onInitComplete, bool isDebug = false,  | ||||
|             bool enableErrorLog = false) | ||||
|         public static void Init(string appId, string deviceInfo, Action onInitComplete, bool isDebug = false) | ||||
|         { | ||||
|             Debug.Log($"{Tag} --- Guru Analytics [{Version}] initialing..."); | ||||
| 
 | ||||
|             if (_instance == null) | ||||
|             { | ||||
|                 _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); | ||||
| #endif            | ||||
|                  | ||||
|                 _instance.EnableErrorLog = enableErrorLog; | ||||
|                 _instance._isReady = true; | ||||
|                 _instance._experimentGroupId = groupId; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|          | ||||
|         /// <summary> | ||||
|         /// 设置视图名称 | ||||
|         /// </summary> | ||||
|  | @ -450,92 +476,6 @@ 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) | ||||
|         { | ||||
|             var ac = (AnalyticsCode)code; | ||||
|  | @ -558,11 +498,13 @@ namespace Guru | |||
|                 case AnalyticsCode.ERROR_ZIP: | ||||
|                 case AnalyticsCode.ERROR_DNS_CACHE: | ||||
|                 case AnalyticsCode.CRONET_INTERCEPTOR: | ||||
|                 case AnalyticsCode.EVENT_LOOKUP: | ||||
|                 case AnalyticsCode.EVENT_SESSION_ACTIVE:     | ||||
|                     canCatch = true; | ||||
|                     break; | ||||
|             } | ||||
| 
 | ||||
|             if (code > 100 && code <= 200) | ||||
|             if (!canCatch && code is > 100 and <= 200) | ||||
|             { | ||||
|                 // 100 < code <= 200 | ||||
|                 canCatch = true; | ||||
|  | @ -601,6 +543,8 @@ namespace Guru | |||
|          | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|      | ||||
|     /// <summary> | ||||
|     /// 网络状态枚举 | ||||
|     /// </summary> | ||||
|  | @ -608,21 +552,24 @@ namespace Guru | |||
|     { | ||||
|         Unknown = -1, | ||||
|          | ||||
|         DELETE_EXPIRED = 12, | ||||
|         UPLOAD_FAIL = 14, | ||||
|         NETWORK_LOST = 22, | ||||
|         CRONET_INIT_FAIL = 26, | ||||
|         CRONET_INIT_EXCEPTION = 27, | ||||
|         DELETE_EXPIRED = 12,            // 删除过期事件 | ||||
|         UPLOAD_FAIL = 14,               // 上报事件失败 | ||||
|         NETWORK_LOST = 22,              // 网络状态不可用 | ||||
|         CRONET_INIT_FAIL = 26,          // 开启Cronet失败 | ||||
|         CRONET_INIT_EXCEPTION = 27,     // 开启Cronet报错 | ||||
|          | ||||
|         ERROR_API = 101, | ||||
|         ERROR_RESPONSE = 102, | ||||
|         ERROR_CACHE_CONTROL = 103, | ||||
|         ERROR_DELETE_EXPIRED = 104, | ||||
|         ERROR_LOAD_MARK = 105, | ||||
|         ERROR_DNS = 106, | ||||
|         ERROR_ZIP = 107, | ||||
|         ERROR_DNS_CACHE = 108, | ||||
|         CRONET_INTERCEPTOR = 109, | ||||
|         ERROR_API = 101,                // 调用api出错 | ||||
|         ERROR_RESPONSE = 102,           // api返回结果错误 | ||||
|         ERROR_CACHE_CONTROL = 103,      // 设置cacheControl出错 | ||||
|         ERROR_DELETE_EXPIRED = 104,     // 删除过期事件出错 | ||||
|         ERROR_LOAD_MARK = 105,          // 从数据库取事件以及更改事件状态为正在上报出错 | ||||
|         ERROR_DNS = 106,                // dns 错误 | ||||
|         ERROR_ZIP = 107,                // zip 错误 | ||||
|         ERROR_DNS_CACHE = 108,          // zip 错误 | ||||
|         CRONET_INTERCEPTOR = 109,       // cronet拦截器 | ||||
|          | ||||
|         EVENT_LOOKUP = 1003, | ||||
|         EVENT_SESSION_ACTIVE = 1004, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,274 @@ | |||
| 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; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: a840d9218d324971b528c639ddfea270 | ||||
| timeCreated: 1721722549 | ||||
|  | @ -17,7 +17,7 @@ namespace Guru | |||
|         void SetUid(string uid); | ||||
|         bool IsDebug { get; } | ||||
|         bool EnableErrorLog { get; set; } | ||||
|         void LogEvent(string eventName, string parameters, int priority = -1); | ||||
|         void LogEvent(string eventName, string parameters, int priority = 0); | ||||
|         void ReportEventSuccessRate(); // 上报任务成功率 | ||||
|         void SetTch02Value(double value); // 设置太极02数值 | ||||
|         void InitCallback(string objName, string method); // 设置回调对象参数 | ||||
|  |  | |||
|  | @ -1,7 +1,3 @@ | |||
| 
 | ||||
| 
 | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
|     using System; | ||||
|  | @ -12,20 +8,16 @@ namespace Guru | |||
|          | ||||
| #if UNITY_ANDROID | ||||
|          | ||||
|         public static readonly string AnalyticsClassName = "com.guru.unity.analytics.Analytics"; | ||||
|         private static readonly string AnalyticsClassName = "com.guru.unity.analytics.Analytics"; | ||||
|         private static AndroidJavaClass _classAnalytics; | ||||
|         private static AndroidJavaClass ClassAnalytics => _classAnalytics ??= new AndroidJavaClass(AnalyticsClassName); | ||||
| 
 | ||||
| #endif | ||||
|         private static bool _isDebug = false; | ||||
|         public static bool UseWorker = true; | ||||
|         public static bool UseCronet = false; | ||||
|         public static string BaseUrl = ""; | ||||
|              | ||||
|         #region 工具方法 | ||||
| 
 | ||||
|          | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 调用静态方法 | ||||
|         /// </summary> | ||||
|  | @ -64,7 +56,7 @@ namespace Guru | |||
|             { | ||||
|                 if (ClassAnalytics != null) | ||||
|                 { | ||||
|                     if(_isDebug) Debug.Log($"{GuruAnalytics.Tag} Android call static <{typeof(T).ToString()}> :: {methodName}"); | ||||
|                     if(_isDebug) Debug.Log($"{GuruAnalytics.Tag} Android call static <{typeof(T)}> :: {methodName}"); | ||||
|                     return ClassAnalytics.CallStatic<T>(methodName, args); | ||||
|                 } | ||||
|             } | ||||
|  | @ -80,17 +72,60 @@ namespace Guru | |||
| 
 | ||||
|         #region 接口实现 | ||||
| 
 | ||||
|         public async void Init(string appId, string deviceInfo, Action onInitComplete, bool isDebug = false) | ||||
|         /// <summary> | ||||
|         /// 默认的启动参数 | ||||
|         /// </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; | ||||
|             string bundleId = Application.identifier; | ||||
|             // 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); // 调用接口 | ||||
|             CallSDKInit(appId, deviceInfo, bundleId, baseUrl, uploadIpAddress , true, false, _isDebug); // 调用接口    | ||||
|              | ||||
|             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) | ||||
|         { | ||||
|  | @ -134,10 +169,7 @@ namespace Guru | |||
|         } | ||||
| 
 | ||||
|         public bool IsDebug => CallStatic<bool>("isDebug"); | ||||
|         public void LogEvent(string eventName, string parameters, int priority = -1) | ||||
|         {  | ||||
|             CallStatic("logEvent", eventName, parameters, priority); | ||||
|         } | ||||
|         public void LogEvent(string eventName, string parameters, int priority = 0) => CallStatic("logEvent", eventName, parameters, priority); | ||||
|         public void ReportEventSuccessRate() => CallStatic("reportEventRate"); | ||||
|         public void SetTch02Value(double value) => CallStatic("setTch02Value", value); | ||||
|         public void InitCallback(string objName, string method) => CallStatic("initCallback", objName, method); | ||||
|  |  | |||
|  | @ -125,7 +125,7 @@ namespace Guru | |||
| 
 | ||||
|         public bool IsDebug => _isDebug; | ||||
| 
 | ||||
|         public void LogEvent(string eventName, string data, int priority = -1) | ||||
|         public void LogEvent(string eventName, string data, int priority = 0) | ||||
|         { | ||||
| #if UNITY_IOS | ||||
|             unityLogEvent(eventName, data); | ||||
|  |  | |||
|  | @ -92,7 +92,7 @@ namespace Guru | |||
|         public bool IsDebug => _isDebug; | ||||
|          | ||||
| 
 | ||||
|         public void LogEvent(string eventName, string parameters, int priority = -1) | ||||
|         public void LogEvent(string eventName, string parameters, int priority = 0) | ||||
|         { | ||||
|             if (_isShowLog) | ||||
|             { | ||||
|  |  | |||
|  | @ -1,8 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 82693d012b64748c8ac997389b0426a7 | ||||
| folderAsset: yes | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -1,143 +0,0 @@ | |||
| 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 | ||||
|          | ||||
|          | ||||
|          | ||||
|          | ||||
|          | ||||
|          | ||||
|          | ||||
|          | ||||
|     } | ||||
| } | ||||
|  | @ -1,3 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 8fde2a18f7d347408a6b869ee03e7de9 | ||||
| timeCreated: 1672712830 | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -1,7 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: ea095a004daab4fc096aa297c21fca99 | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -116,7 +116,7 @@ namespace Guru | |||
|          | ||||
|         private void OnLoadMaxBanner() | ||||
|         { | ||||
| 	        _badsloadStartTime = DateTime.UtcNow; | ||||
| 	        _badsLoadStartTime = DateTime.UtcNow; | ||||
| 	        _chanelMax.LoadBannerAD(); | ||||
| 	        OnBannerStartLoad?.Invoke(_chanelMax.MaxBADSSlotID); | ||||
|         } | ||||
|  |  | |||
|  | @ -1,12 +1,11 @@ | |||
| using System.Linq; | ||||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
|     using System; | ||||
|     using UnityEngine; | ||||
|     using System.Collections.Generic; | ||||
|     using Guru.Ads; | ||||
|      | ||||
|     //TODO: 将 BADS,IADS,RADS 分别放到不同的类中,方便维护 | ||||
|     public abstract class ADServiceBase<T> : IADService where T : new() | ||||
|     { | ||||
|         // 单利定义 | ||||
|  | @ -21,9 +20,9 @@ namespace Guru | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         protected static readonly string Tag = "[SDK][ADS]"; | ||||
|         private const string Tag = "[SDK][ADS]"; | ||||
|         public bool IsInitialized => MaxSdk.IsInitialized() || _isServiceStarted; | ||||
|         protected bool IsNetworkEnabled => Application.internetReachability != NetworkReachability.NotReachable; | ||||
|         private static bool IsNetworkEnabled => Application.internetReachability != NetworkReachability.NotReachable; | ||||
| 
 | ||||
|         private const int MAX_ADS_RELOAD_INTERVAL = 6; // 广告加载最高时间为 2 的 6 次方 = 64秒 | ||||
| 
 | ||||
|  | @ -31,28 +30,23 @@ namespace Guru | |||
| 
 | ||||
|         protected Action _onSdkInitReady; | ||||
|          | ||||
|         public static Action<string> OnBannerStartLoad; | ||||
|         public static Action OnBannerLoaded; | ||||
|         public Action<string> OnBannerStartLoad; | ||||
|         public Action OnBannerLoaded; | ||||
| 
 | ||||
|         public static Action<string> OnInterstitialStartLoad; | ||||
|         public static Action OnInterstitialLoaded; | ||||
|         public static Action OnInterstitialFailed; | ||||
|         public static Action OnInterstitialClosed; | ||||
|         public Action<string> OnInterstitialStartLoad; | ||||
|         public Action OnInterstitialLoaded; | ||||
|         public Action OnInterstitialFailed; | ||||
|         public Action OnInterstitialClosed; | ||||
| 
 | ||||
|         public static Action<string> OnRewardedStartLoad; | ||||
|         public static Action OnRewardLoaded; | ||||
|         public static Action OnRewardFailed; | ||||
|         public static Action OnRewardClosed; | ||||
|         public Action<string> OnRewardedStartLoad; | ||||
|         public Action OnRewardLoaded; | ||||
|         public Action OnRewardFailed; | ||||
|         public Action OnRewardClosed; | ||||
|          | ||||
|         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; | ||||
|         private AdsModel _model; | ||||
|         protected AdsInitSpec _initSpec = null; | ||||
| 
 | ||||
|         private AdImpressionDriver _impressionDriver; | ||||
| 
 | ||||
|         public AdsModel Model | ||||
|         private AdsModel Model | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|  | @ -67,8 +61,7 @@ namespace Guru | |||
|         /// 启动广告服务 | ||||
|         /// </summary> | ||||
|         /// <param name="callback">广告初始化回调</param> | ||||
|         /// <param name="autoLoadAds">自动启动广告加载</param> | ||||
|         /// <param name="isDebugMode">debug模式</param> | ||||
|         /// <param name="initSpec">初始化配置参数</param> | ||||
|         public virtual void StartService(Action callback = null, AdsInitSpec initSpec = null) | ||||
|         { | ||||
|             if (IsInitialized) return; // 已经初始化后, 无需再次初始化 | ||||
|  | @ -82,9 +75,6 @@ namespace Guru | |||
|             if(_model == null) _model = AdsModel.Create(); | ||||
|             this.Log("AD SDK Start Init"); | ||||
| 
 | ||||
|             _impressionDriver = new AdImpressionDriver(); | ||||
|             _impressionDriver.Init(ReportAdsRevenue); // 初始化 Impression 驱动器 | ||||
|              | ||||
|             InitMaxCallbacks(); // 初始化 MAX 广告 | ||||
|             InitService(); // 内部继承接口 | ||||
|         } | ||||
|  | @ -99,13 +89,14 @@ namespace Guru | |||
|              | ||||
|             //--------------- MRec 回调 ----------------- | ||||
|             // MaxSdkCallbacks.MRec.OnAdRevenuePaidEvent += OnAdRevenuePaidEvent; | ||||
| 
 | ||||
|              | ||||
|             //--------------- Banner 回调 ----------------- | ||||
|             MaxSdkCallbacks.Banner.OnAdLoadedEvent += OnBannerLoadedEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += OnBannerFailedEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdClickedEvent += OnBannerClickedEvent; | ||||
|             // MaxSdkCallbacks.Banner.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent += OnBannerRevenuePaidEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent; | ||||
| 
 | ||||
|             //--------------- IV 回调 ----------------- | ||||
|             MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnInterstitialLoadedEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnInterstitialFailedEvent; | ||||
|  | @ -114,7 +105,7 @@ namespace Guru | |||
|             MaxSdkCallbacks.Interstitial.OnAdDisplayedEvent += OnInterstitialDisplayEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += OnInterstitialDismissedEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent += OnInterstitialPaidEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent; | ||||
|             // MaxSdkCallbacks.Interstitial.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent; | ||||
|             //--------------- RV 回调 ----------------- | ||||
|             MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnRewardedAdLoadedEvent; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnRewardedAdFailedEvent; | ||||
|  | @ -122,20 +113,15 @@ namespace Guru | |||
|             MaxSdkCallbacks.Rewarded.OnAdDisplayedEvent += OnRewardedAdDisplayedEvent; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdClickedEvent += OnRewardedAdClickedEvent; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdHiddenEvent += OnRewardedAdDismissedEvent; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnRewardedAdReceivedRewardEvent; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent += OnRewardedAdPaidEvent; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent; | ||||
|              | ||||
|             //--------------- Creative 回调 ----------------- | ||||
|              | ||||
|             MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnRewardedAdReceivedRewardEvent; | ||||
|             // MaxSdkCallbacks.Rewarded.OnAdReviewCreativeIdGeneratedEvent += OnAdReviewCreativeIdGeneratedEvent; | ||||
|              | ||||
|              | ||||
|             //-------------- SDK 初始化 ------------------- | ||||
|             MaxSdk.SetExtraParameter("enable_black_screen_fixes", "true"); // 修复黑屏 | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         protected virtual void InitService() | ||||
|         { | ||||
|         } | ||||
|  | @ -163,7 +149,7 @@ namespace Guru | |||
|         /// 可加载广告 | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         public virtual bool CanLoadAds() | ||||
|         private bool CanLoadAds() | ||||
|         { | ||||
|             return IsInitialized && IsNetworkEnabled; | ||||
|         } | ||||
|  | @ -214,26 +200,21 @@ namespace Guru | |||
|         /// <summary> | ||||
|         /// 上报广告收益 | ||||
|         /// </summary> | ||||
|         /// <param name="adInfo"></param> | ||||
|         /// <param name="reviewedCreativeId"></param> | ||||
|         private void ReportAdsRevenue(AdImpressionData data) | ||||
|         /// <param name="data"></param> | ||||
|         private void ReportAdsRevenue(AdParams data) | ||||
|         { | ||||
|             // #1 ad_impression | ||||
|             Analytics.ADImpression(data); | ||||
|             Analytics.ADImpression(data.ToAdImpressionData()); | ||||
|              | ||||
|             // #2 tch_001 和 tch_02 | ||||
|             // #2 tch_001 | ||||
|             double revenue = data.value; | ||||
|             CalcTch001Value(revenue); | ||||
|             CalcTch02Value(revenue); | ||||
|                  | ||||
|             // #3 adjust_ad_revenue | ||||
|             AdjustService.TrackADRevenue(data.value, data.currency, data.ad_source, data.ad_unit_name, data.ad_placement); | ||||
|             AdjustService.TrackADRevenue(data.value, data.currency, data.adSource, data.adUnitId, data.networkPlacement); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|          | ||||
| 
 | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 计算太极001收益 | ||||
|         /// </summary> | ||||
|  | @ -242,10 +223,10 @@ namespace Guru | |||
|         { | ||||
|             TchAD001RevValue += revenue; | ||||
|             double revenueValue = TchAD001RevValue; | ||||
|             Debug.Log($"[TaichConfig] get <TchAD001RevValue> totally: {revenueValue}"); | ||||
|             // Debug.Log($"{Tag} --- [Tch] get <TchAD001RevValue> totally: {revenueValue}"); | ||||
|             if (revenueValue >= Analytics.Tch001TargetValue) | ||||
|             { | ||||
|                 Debug.Log($"[TaichConfig] call <tch_ad_rev_roas_001> with value: {revenueValue}"); | ||||
|                 Debug.Log($"{Tag} --- [Tch] call <tch_ad_rev_roas_001> with value: {revenueValue}"); | ||||
|                 Analytics.Tch001ADRev(revenueValue); | ||||
|                 TchAD001RevValue = 0.0; | ||||
|             } | ||||
|  | @ -274,15 +255,15 @@ namespace Guru | |||
| 
 | ||||
|         #region Banner Ads | ||||
| 
 | ||||
|         private string _backColorStr = "#50A436"; | ||||
|         private Color _backColor = new Color(0, 0, 0, 0); | ||||
|         private string _badsCategory; | ||||
|         protected DateTime _badsloadStartTime; | ||||
|         protected DateTime _badsLoadStartTime; | ||||
|         private bool _bannerVisible = false; | ||||
|         public bool IsBannerVisible => _bannerVisible; | ||||
|         private int _badsloadedNum = 0; | ||||
|         private int _badsLoadedNum = 0; | ||||
|         private int _badsLoadFailNum = 0; | ||||
|          | ||||
|          | ||||
|         /// <summary> | ||||
|         /// 获取动作间隔之间的毫秒数 | ||||
|         /// </summary> | ||||
|  | @ -309,32 +290,32 @@ namespace Guru | |||
|         /// <summary> | ||||
|         /// Banner MAX 加载方式 | ||||
|         /// </summary> | ||||
|         protected void LoadMaxBannerAd() | ||||
|         private void LoadMaxBannerAd() | ||||
|         { | ||||
|             OnLoadBads(); | ||||
|             // 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 | ||||
|             var id = GetBannerID(); | ||||
|             MaxSdk.CreateBanner(id, MaxSdkBase.BannerPosition.BottomCenter); | ||||
|             MaxSdk.SetBannerExtraParameter(id, "adaptive_banner", "false"); | ||||
|             var adUnitId = GetBannerID(); | ||||
|             MaxSdk.CreateBanner(adUnitId, MaxSdkBase.BannerPosition.BottomCenter); | ||||
|             MaxSdk.SetBannerExtraParameter(adUnitId, "adaptive_banner", "false"); | ||||
|             // Set background or background color for banners to be fully functional | ||||
|             MaxSdk.SetBannerBackgroundColor(id, _backColor); | ||||
|             MaxSdk.SetBannerBackgroundColor(adUnitId, _backColor); | ||||
|             // Analytics.ADBadsLoad(GetBannerID()); | ||||
|             Analytics.ADBadsLoad(AdParams.Build(id)); | ||||
|             Analytics.TrackAdBadsLoad(AdParams.Build(adUnitId:adUnitId).ToAdLoadedData()); | ||||
|         } | ||||
| 
 | ||||
|         public void OnLoadBads() | ||||
|         private void OnLoadBads() | ||||
|         { | ||||
|             _badsloadStartTime = DateTime.UtcNow; | ||||
|             _badsLoadStartTime = DateTime.UtcNow; | ||||
|         } | ||||
| 
 | ||||
|         protected virtual void OnBadsLoaded() | ||||
|         private void OnBadsLoaded() | ||||
|         { | ||||
|             _badsloadStartTime = DateTime.UtcNow; | ||||
|             _badsLoadStartTime = DateTime.UtcNow; | ||||
|             OnBannerLoaded?.Invoke(); | ||||
|         } | ||||
|          | ||||
|         public virtual void SetBannerAutoRefresh(bool value = true, string adUnitId = "") | ||||
|         public void SetBannerAutoRefresh(bool value = true, string adUnitId = "") | ||||
|         { | ||||
|             if(string.IsNullOrEmpty(adUnitId)) adUnitId = GetBannerID(); | ||||
|             if (value) | ||||
|  | @ -360,9 +341,10 @@ namespace Guru | |||
|             SetBannerAutoRefresh(true, adUnitId); | ||||
|             if (!_bannerVisible) | ||||
|             { | ||||
|                 // 从隐藏状态到显示,累计的加载次数和失败次数清零 | ||||
|                 _bannerVisible = true; | ||||
|                 OnBannerImpEvent(adUnitId); | ||||
|                 _badsloadedNum = 0; | ||||
|                 _badsLoadedNum = 0; | ||||
|                 _badsLoadFailNum = 0; | ||||
|             } | ||||
|         } | ||||
|  | @ -370,7 +352,7 @@ namespace Guru | |||
|         /// <summary> | ||||
|         /// 隐藏 Banner | ||||
|         /// </summary> | ||||
|         public virtual void HideBanner() | ||||
|         public void HideBanner() | ||||
|         { | ||||
|             string adUnitId = GetBannerID(); | ||||
|             MaxSdk.HideBanner(adUnitId); | ||||
|  | @ -386,47 +368,44 @@ namespace Guru | |||
|         /// 设置 Banner 背景颜色 | ||||
|         /// </summary> | ||||
|         /// <param name="color"></param> | ||||
|         public virtual void SetBannerBackgroundColor(Color color) | ||||
|         public void SetBannerBackgroundColor(Color color) | ||||
|         { | ||||
|             _backColor = color; | ||||
|         } | ||||
| 
 | ||||
|         private void OnBannerLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||
|         { | ||||
|             _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)); | ||||
|             _badsLoadedNum++; // 记录加载次数 | ||||
|             Debug.Log( $"[SDK][Ads][Loaded] --- adUnitId:{adUnitId}    Revenue:{adInfo.Revenue}    Type:{adInfo.AdFormat}    CreativeId:{adInfo.CreativeIdentifier}"); | ||||
|             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) | ||||
|         { | ||||
|             _badsLoadFailNum ++; | ||||
|             // 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 ?? "")); | ||||
|             _badsLoadFailNum++; // 记录加载失败次数  | ||||
|             Analytics.TrackAdBadsFailed(BuildBadsFailedData(adUnitId, errorInfo)); | ||||
|         } | ||||
| 
 | ||||
|         private void OnBannerClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||
|         { | ||||
|             // Analytics.ADBadsClick(adUnitId, _badsCategory); | ||||
|             Analytics.ADBadsClick(AdParams.Build(adUnitId, adInfo, _badsCategory)); | ||||
|             Analytics.TrackAdBadsClick(BuildBadsClickData(adInfo, adUnitId)); | ||||
|         } | ||||
| 
 | ||||
|         private void OnBannerImpEvent(string adUnitId) | ||||
|         { | ||||
|             // Analytics.ADBadsClick(adUnitId, _badsCategory); | ||||
|             Analytics.ADBadsImp(AdParams.Build(adUnitId, category: _badsCategory)); | ||||
|             Analytics.TrackAdBadsImp(BuildBadsImpData(adUnitId)); | ||||
|         } | ||||
|          | ||||
|          | ||||
|         private void OnBannerHideEvent() | ||||
|         { | ||||
|             Analytics.ADBadsHide(_badsloadedNum, _badsLoadFailNum); | ||||
|             Analytics.TrackAdBadsHide(_badsLoadedNum, _badsLoadFailNum); | ||||
|         } | ||||
|          | ||||
|         /// <summary> | ||||
|  | @ -438,8 +417,7 @@ namespace Guru | |||
|         { | ||||
|             if (_bannerVisible) | ||||
|             { | ||||
|                 // Banner 只有显示时才上报收益值 | ||||
|                 AppendImpressionData(adInfo, eventName:Analytics.EventBadsPaid, itemCategory:_badsCategory); | ||||
|                 ReportAdsRevenue(AdParams.Build(adInfo, adUnitId)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -450,7 +428,7 @@ namespace Guru | |||
|         private string _iadsCategory = "main"; | ||||
|         private int _interstitialRetryAttempt; | ||||
|         private Action _interCloseAction; | ||||
|         protected bool _isIadsLoading = false; | ||||
|         private bool _isIadsLoading = false; | ||||
|         protected DateTime _iadsLoadStartTime; | ||||
|         private DateTime _iadsDisplayStartTime; | ||||
|          | ||||
|  | @ -468,15 +446,15 @@ namespace Guru | |||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         protected void LoadMaxInterstitial() | ||||
|         private void LoadMaxInterstitial() | ||||
|         { | ||||
|             OnLoadIads(); | ||||
|             var id = GetInterstitialID(); | ||||
|             Analytics.ADIadsLoad(AdParams.Build(id)); | ||||
|             MaxSdk.LoadInterstitial(id); | ||||
|             var adUnitId = GetInterstitialID(); | ||||
|             Analytics.TrackAdIadsLoad(AdParams.Build(adUnitId:adUnitId).ToAdLoadedData()); | ||||
|             MaxSdk.LoadInterstitial(adUnitId); | ||||
|         } | ||||
| 
 | ||||
|         public void OnLoadIads() | ||||
|         private void OnLoadIads() | ||||
|         { | ||||
|             _iadsLoadStartTime = DateTime.UtcNow; | ||||
|         } | ||||
|  | @ -491,8 +469,7 @@ namespace Guru | |||
|         /// <summary> | ||||
|         /// 显示插屏广告 | ||||
|         /// </summary> | ||||
|         /// <param name="rewardAction">广告奖励回调</param> | ||||
|         /// <param name="failAction">广告失败回调</param> | ||||
|         /// <param name="category">广告奖励回调</param> | ||||
|         /// <param name="dismissAction">广告界面关闭回调</param> | ||||
|         public virtual void ShowInterstitialAD(string category, Action dismissAction = null) | ||||
|         { | ||||
|  | @ -513,7 +490,6 @@ namespace Guru | |||
|             MaxSdk.ShowInterstitial(GetInterstitialID()); | ||||
| 
 | ||||
|             _iadsDisplayStartTime = DateTime.UtcNow; | ||||
|             // RequestInterstitialAD(); // 直接加载下一个广告 | ||||
|         } | ||||
| 
 | ||||
|         protected virtual void OnInterstitialLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||
|  | @ -522,12 +498,9 @@ namespace Guru | |||
|             // Interstitial ad is ready to be shown. MaxSdk.IsInterstitialReady(interstitialAdUnitId) will now return 'true' | ||||
|             // Reset retry attempt | ||||
|             // Analytics.ADIadsLoaded(adUnitId, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory); | ||||
|             Analytics.ADIadsLoaded(AdParams.Build(adUnitId, | ||||
|                 duration: GetActionDuration(_iadsLoadStartTime), category: _iadsCategory)); | ||||
|             Analytics.TrackAdIadsLoaded(BuildIadsLoadedData(adInfo, adUnitId)); | ||||
|             _interstitialRetryAttempt = 0; | ||||
| 
 | ||||
|             Debug.Log( $"[SDK][Ads][Loaded] --- adUnitId:{adUnitId}    Revenue:{adInfo.Revenue}    Type:{adInfo.AdFormat}    CreativeId:{adInfo.CreativeIdentifier}"); | ||||
|              | ||||
|             OnInterstitialLoaded?.Invoke(); | ||||
|         } | ||||
| 
 | ||||
|  | @ -542,11 +515,7 @@ namespace Guru | |||
|             float retryDelay = GetRetryDelaySeconds(_interstitialRetryAttempt); | ||||
|             DelayCall(retryDelay, RequestInterstitialAD); | ||||
|             // Analytics.ADIadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory); | ||||
|             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 ?? "")); | ||||
|             Analytics.TrackAdIadsFailed(BuildIadsFailedData(adUnitId, errorInfo)); | ||||
| 
 | ||||
|             OnInterstitialFailed?.Invoke(); | ||||
|         } | ||||
|  | @ -558,10 +527,7 @@ namespace Guru | |||
|             this.LogError( | ||||
|                 $"InterstitialFailedToDisplayEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}"); | ||||
|             // Analytics.ADIadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory); | ||||
|             Analytics.ADIadsFailed(AdParams.Build(adUnitId, | ||||
|                 duration: GetActionDuration(_iadsDisplayStartTime), category: _iadsCategory, | ||||
|                 errorCode: (int)errorInfo.Code, | ||||
|                 waterfallName: errorInfo.WaterfallInfo?.Name ?? "")); | ||||
|             Analytics.TrackAdIadsFailed(BuildIadsFailedData(adUnitId, errorInfo)); | ||||
|             DelayCall(2.0f, RequestInterstitialAD); | ||||
|         } | ||||
|          | ||||
|  | @ -569,16 +535,13 @@ namespace Guru | |||
|         protected virtual void OnInterstitialDisplayEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||
|         { | ||||
|             // Analytics.ADIadsImp(adUnitId, _iadsCategory); | ||||
|             AppendImpressionData(adInfo, eventName:Analytics.EventIadsImp, itemCategory:_iadsCategory); | ||||
|             Analytics.TrackAdIadsImp(BuildIadsImpData(adInfo, adUnitId)); | ||||
|         } | ||||
| 
 | ||||
|         protected virtual void OnInterstitialClickEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||
|         { | ||||
|             // Analytics.ADIadsClick(adUnitId, _iadsCategory); | ||||
|             if(string.IsNullOrEmpty(_iadsCategory)) _iadsCategory = "not_set"; | ||||
|             AppendImpressionData(adInfo,  | ||||
|                 eventName:Analytics.EventIadsClick, | ||||
|                 itemCategory:_iadsCategory); | ||||
|             Analytics.TrackAdIadsClick(BuildIadsClickData(adInfo, adUnitId)); | ||||
|         } | ||||
|          | ||||
|         // Close  | ||||
|  | @ -588,13 +551,7 @@ namespace Guru | |||
|             _interCloseAction?.Invoke(); | ||||
|             OnInterstitialClosed?.Invoke(); | ||||
|             // Analytics.ADIadsClose(adUnitId, _iadsCategory); | ||||
|             Analytics.ADIadsClose(new Dictionary<string, object>() | ||||
|             { | ||||
|                 [Analytics.ParameterItemCategory] = _iadsCategory, | ||||
|                 [Analytics.ParameterDuration] = GetActionDuration(_iadsDisplayStartTime), | ||||
|             }); | ||||
|              | ||||
|             _impressionDriver.CleanCreativeId(adInfo.CreativeIdentifier); | ||||
|             Analytics.TrackAdIadsClose(BuildIadsCloseData(adInfo, adUnitId)); | ||||
|              | ||||
|             //延时加载下一个广告 | ||||
|             DelayCall(2.0f, RequestInterstitialAD); | ||||
|  | @ -602,7 +559,9 @@ namespace Guru | |||
| 
 | ||||
|         private void OnInterstitialPaidEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||
|         { | ||||
|             AppendImpressionData(adInfo, eventName:Analytics.EventIadsPaid, itemCategory: _iadsCategory); | ||||
|             var p = AdParams.Build(adInfo, adUnitId, category:_iadsCategory); | ||||
|             Analytics.TrackAdIadsPaid(p.ToAdPaidData()); | ||||
|             ReportAdsRevenue(p); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -610,14 +569,14 @@ namespace Guru | |||
| 
 | ||||
|         #region Rewarded Ads | ||||
| 
 | ||||
|         private string _rewardCategory = "main"; | ||||
|         private string _radsCategory = "main"; | ||||
|         private int _rewardRetryAttempt; | ||||
|         protected DateTime _radsLoadStartTime; | ||||
|         protected DateTime _radsShowStartTime; | ||||
|         private DateTime _radsDisplayStartTime; | ||||
|         private Action _rvRewardAction; | ||||
|         private Action<string> _rvFailAction; | ||||
|         private Action _rvDismissAction; | ||||
|         protected bool _isRadsLoading = false; | ||||
|         private bool _isRadsLoading = false; | ||||
|         public bool IsRadsLoading => _isRadsLoading; | ||||
| 
 | ||||
|         public virtual void RequestRewardedAD() | ||||
|  | @ -634,17 +593,17 @@ namespace Guru | |||
|         /// <summary> | ||||
|         /// 默认加载 MAX 广告逻辑 | ||||
|         /// </summary> | ||||
|         protected void LoadMaxRewardAd(string unitId = "") | ||||
|         private void LoadMaxRewardAd() | ||||
|         { | ||||
|             if (IsRadsLoading) return; | ||||
|              | ||||
|             OnLoadRads(); | ||||
|             var id = GetRewardedID(); | ||||
|             Analytics.ADRadsLoad(AdParams.Build(id)); // 上报打点 | ||||
|             MaxSdk.LoadRewardedAd(id); | ||||
|             var adUnitId = GetRewardedID(); | ||||
|             Analytics.TrackAdRadsLoad(AdParams.Build(adUnitId:adUnitId).ToAdLoadData()); // 上报打点 | ||||
|             MaxSdk.LoadRewardedAd(adUnitId); | ||||
|         } | ||||
| 
 | ||||
|         public void OnLoadRads() | ||||
|         private void OnLoadRads() | ||||
|         { | ||||
|             _radsLoadStartTime = DateTime.UtcNow; | ||||
|         } | ||||
|  | @ -661,7 +620,8 @@ namespace Guru | |||
|         /// <summary> | ||||
|         /// 显示激励视频广告 | ||||
|         /// </summary> | ||||
|         /// <param name="rewardAction">广告奖励回调</param> | ||||
|         /// <param name="category">广告奖励回调</param> | ||||
|         /// <param name="rewardAction">广告失败回调</param> | ||||
|         /// <param name="failAction">广告失败回调</param> | ||||
|         /// <param name="dismissAction">广告界面关闭回调</param> | ||||
|         public virtual void ShowRewardedAD(string category, Action rewardAction = null, | ||||
|  | @ -679,13 +639,13 @@ namespace Guru | |||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             _rewardCategory = category; | ||||
|             _radsCategory = category; | ||||
|             _rvRewardAction = rewardAction; | ||||
|             _rvFailAction = failAction; | ||||
|             _rvDismissAction = dismissAction; | ||||
|             MaxSdk.ShowRewardedAd(GetRewardedID()); | ||||
| 
 | ||||
|             _radsShowStartTime = DateTime.UtcNow; | ||||
|             _radsDisplayStartTime = DateTime.UtcNow; | ||||
|              | ||||
|             // RequestRewardedAD(); | ||||
|         } | ||||
|  | @ -698,8 +658,7 @@ namespace Guru | |||
|             // Reset retry attempt | ||||
|             // this.Log("OnRewardedAdLoadedEvent"); | ||||
|             // Analytics.ADRadsLoaded(adUnitId, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory); | ||||
|             Analytics.ADRadsLoaded(AdParams.Build(adUnitId, | ||||
|                 duration: GetActionDuration(_radsLoadStartTime), category: _iadsCategory)); | ||||
|             Analytics.TrackAdRadsLoaded(BuildRadsLoadedData(adInfo, adUnitId)); | ||||
|             _rewardRetryAttempt = 0; | ||||
|              | ||||
|             Debug.Log( $"[SDK][Ads][Loaded] --- adUnitId:{adUnitId}    Revenue:{adInfo.Revenue}    Type:{adInfo.AdFormat}    CreativeId:{adInfo.CreativeIdentifier}"); | ||||
|  | @ -716,10 +675,7 @@ namespace Guru | |||
|             this.LogError( | ||||
|                 $"OnRewardedAdFailedEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}"); | ||||
|             // Analytics.ADRadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory); | ||||
|             Analytics.ADRadsFailed(AdParams.Build(adUnitId, | ||||
|                 duration: GetActionDuration(_radsLoadStartTime), category: _rewardCategory, | ||||
|                 errorCode: (int)errorInfo.Code, | ||||
|                 waterfallName: errorInfo?.WaterfallInfo?.Name ?? "")); | ||||
|             Analytics.TrackAdRadsFailed(BuildRadsFailedData(adUnitId, errorInfo)); | ||||
|             _rewardRetryAttempt++; | ||||
|             float retryDelay = GetRetryDelaySeconds(_rewardRetryAttempt); | ||||
|             DelayCall(retryDelay, RequestRewardedAD); | ||||
|  | @ -734,10 +690,7 @@ namespace Guru | |||
|             this.LogError( | ||||
|                 $"OnRewardedAdFailedToDisplayEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}"); | ||||
|             // Analytics.ADRadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _radsLoadStartTime), _rewardCategory); | ||||
|             Analytics.ADRadsFailed(AdParams.Build(adUnitId, | ||||
|                 duration: GetActionDuration(_radsShowStartTime), category: _rewardCategory, | ||||
|                 errorCode: (int)errorInfo.Code, | ||||
|                 waterfallName: errorInfo?.WaterfallInfo?.Name ?? "")); | ||||
|             Analytics.TrackAdRadsFailed(BuildRadsFailedData(adUnitId, errorInfo)); | ||||
|             _rvFailAction?.Invoke("OnRewardedAdFailedToDisplayEvent"); | ||||
|             DelayCall(2.0f, RequestRewardedAD); | ||||
| 
 | ||||
|  | @ -748,16 +701,17 @@ namespace Guru | |||
|         { | ||||
|             this.Log("OnRewardedAdDisplayedEvent"); | ||||
|             // Analytics.ADRadsImp(adUnitId, _rewardCategory); | ||||
|             AppendImpressionData(adInfo, eventName:Analytics.EventRadsImp, itemCategory: _rewardCategory); | ||||
|             Analytics.TrackAdRadsImp(BuildRadsImpData(adInfo, adUnitId)); | ||||
|         } | ||||
| 
 | ||||
|         protected virtual void OnRewardedAdClickedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||
|         { | ||||
|             this.Log("OnRewardedAdClickedEvent"); | ||||
|             // Analytics.ADRadsClick(adUnitId, _rewardCategory); | ||||
|             AppendImpressionData(adInfo, eventName:Analytics.EventRadsClick, itemCategory: _rewardCategory); | ||||
|             Analytics.TrackAdRadsClick(BuildRadsClickData(adInfo, adUnitId)); | ||||
|         } | ||||
|          | ||||
|          | ||||
|         // rads_close | ||||
|         protected virtual void OnRewardedAdDismissedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||
|         { | ||||
|  | @ -767,13 +721,7 @@ namespace Guru | |||
|             OnRewardClosed?.Invoke(); | ||||
|              | ||||
|             // Analytics.ADRadsClose(adUnitId, _rewardCategory); | ||||
|             Analytics.ADRadsClose(new Dictionary<string, object>() | ||||
|             { | ||||
|                 [Analytics.ParameterItemCategory] = _rewardCategory, | ||||
|                 [Analytics.ParameterDuration] = GetActionDuration(_radsShowStartTime), | ||||
|             }); | ||||
| 
 | ||||
|             _impressionDriver.CleanCreativeId(adInfo.CreativeIdentifier); | ||||
|             Analytics.TrackAdRadsClose(BuildRadsCloseData(adInfo, adUnitId)); | ||||
|              | ||||
|             //延时加载下一个广告 | ||||
|             DelayCall(2.0f, RequestRewardedAD); | ||||
|  | @ -784,19 +732,18 @@ namespace Guru | |||
|             MaxSdkBase.AdInfo adInfo) | ||||
|         { | ||||
|             this.Log("OnRewardedAdReceivedRewardEvent"); | ||||
|             // Analytics.ADRadsRewarded(adUnitId, _rewardCategory); | ||||
|             AppendImpressionData(adInfo, eventName:Analytics.EventRadsRewarded, itemCategory: _rewardCategory); | ||||
|             Analytics.TrackAdRadsRewarded(BuildRadsRewardedData(adInfo, adUnitId)); | ||||
|             // Rewarded ad was displayed and user should receive the reward | ||||
|             _rvRewardAction?.Invoke(); | ||||
|         } | ||||
| 
 | ||||
|         // rads_paid | ||||
|         private void OnRewardedAdPaidEvent(string adUnitId, MaxSdkBase.AdInfo adInfo) | ||||
|         { | ||||
|             this.Log("OnRewardedAdReceivedRewardEvent"); | ||||
|             AppendImpressionData(adInfo, eventName:Analytics.EventRadsPaid, itemCategory: _rewardCategory); | ||||
|             var p = AdParams.Build(adInfo, adUnitId, category:_iadsCategory); | ||||
|             Analytics.TrackAdRadsPaid(p.ToAdPaidData()); | ||||
|             ReportAdsRevenue(p); | ||||
|         } | ||||
|          | ||||
|          | ||||
|         #endregion | ||||
| 
 | ||||
|         #region Ad Settings | ||||
|  | @ -822,8 +769,8 @@ namespace Guru | |||
| 
 | ||||
|         public void ShowMaxDebugPanel() | ||||
|         { | ||||
|             if (!IsInitialized) return; | ||||
| #if !UNITY_EDITOR | ||||
|             if (!IsInitialized) return; | ||||
| 	        MaxSdk.ShowMediationDebugger(); | ||||
| #endif | ||||
|         } | ||||
|  | @ -839,86 +786,107 @@ namespace Guru | |||
| 
 | ||||
|         #endregion | ||||
| 
 | ||||
|         #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; | ||||
|             } | ||||
|         #region Build AdParams | ||||
| 
 | ||||
|             _impressionDriver.SetReviewCreativeId(adInfo.CreativeIdentifier, reviewCreativeId); // 缓存 ReviewCreateId | ||||
| 
 | ||||
|             if (adUnitId == GetBannerID() || adInfo.AdFormat.ToUpper().Contains("BANNER")) | ||||
|             { | ||||
|                 // 清理 BADS CID 缓存 | ||||
|                 _badsCreativeIds.Add(adInfo.CreativeIdentifier); | ||||
|                 if (_badsCreativeIds.Count > MAX_BADS_CID_NUMBER) | ||||
|                 { | ||||
|                     var cid = _badsCreativeIds[0]; | ||||
|                     _badsCreativeIds.Remove(cid); | ||||
|                     _impressionDriver.CleanCreativeId(cid); | ||||
|                 } | ||||
|             } | ||||
|         //---------- BADS Params -------------- | ||||
|         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(); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         //---------- IADS Params -------------- | ||||
|         private Dictionary<string, object> BuildIadsLoadedData(MaxSdk.AdInfo adInfo, string adUnitId) | ||||
|         { | ||||
|             int duration = GetActionDuration(_iadsLoadStartTime); | ||||
|             var p = AdParams.Build(adInfo, adUnitId, duration:duration); | ||||
|             return p.ToAdLoadedData(); | ||||
|         } | ||||
|          | ||||
|         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 | ||||
|     } | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
|      | ||||
| } | ||||
|  | @ -15,7 +15,7 @@ namespace Guru | |||
| 
 | ||||
| 
 | ||||
|         public static AdsInitSpec Build(bool loadBanner = true, bool loadInterstitial = true, bool loadReward = true, | ||||
|             bool autoLoad = true, bool isDebug = false, string bannerColorHex = "#00000000") | ||||
|             bool autoLoad = true, bool isDebug = false, string bannerColorHex = "#00000040") | ||||
|         { | ||||
|             return new AdsInitSpec | ||||
|             { | ||||
|  |  | |||
|  | @ -53,7 +53,7 @@ namespace Guru | |||
|             MaxSdk.SetBannerExtraParameter(MaxBADSSlotID, "adaptive_banner", "false"); | ||||
|             // Set background or background color for banners to be fully functional | ||||
|             MaxSdk.SetBannerBackgroundColor(MaxBADSSlotID, _backColor); | ||||
|             Analytics.ADBadsLoad(AdParams.Build(MaxBADSSlotID)); | ||||
|             Analytics.TrackAdBadsLoad(AdParams.Build(adUnitId:MaxBADSSlotID).ToAdLoadData()); | ||||
|         } | ||||
|          | ||||
|         public void SetBannerBackColor(string hex) | ||||
|  | @ -69,7 +69,7 @@ namespace Guru | |||
|         public bool IsInterstitialRequestOver => true; | ||||
|         public void LoadInterstitialAD() | ||||
|         { | ||||
|             Analytics.ADIadsLoad(AdParams.Build(MaxIADSSlotID)); // 上报打点 | ||||
|             Analytics.TrackAdIadsLoad(AdParams.Build(adUnitId:MaxIADSSlotID).ToAdLoadData()); // 上报打点 | ||||
|             MaxSdk.LoadInterstitial(MaxIADSSlotID); | ||||
|         } | ||||
| 
 | ||||
|  | @ -80,7 +80,7 @@ namespace Guru | |||
|         public bool IsRewardRequestOver => true; | ||||
|         public void LoadRewardAD() | ||||
|         { | ||||
|             Analytics.ADRadsLoad(AdParams.Build(MaxRADSSlotID)); // 上报打点 | ||||
|             Analytics.TrackAdRadsLoad(AdParams.Build(adUnitId:MaxRADSSlotID).ToAdLoadData()); // 上报打点 | ||||
|             MaxSdk.LoadRewardedAd(MaxRADSSlotID); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,185 +0,0 @@ | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 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; | ||||
|         } | ||||
|          | ||||
|          | ||||
|          | ||||
|     } | ||||
| } | ||||
|  | @ -1,3 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 1aca2a4507b94dfe9f88040125708edb | ||||
| timeCreated: 1721883318 | ||||
|  | @ -1,251 +0,0 @@ | |||
| 
 | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
|          | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -1,3 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 7194cdd8ed1447508011264371e2c23d | ||||
| timeCreated: 1722313472 | ||||
|  | @ -0,0 +1,254 @@ | |||
| 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; | ||||
| 	    } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 0341a8c153f04535a4193ce32086315a | ||||
| timeCreated: 1722396384 | ||||
|  | @ -1,17 +1,13 @@ | |||
| using System.Collections.Generic; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
| 	using System.Collections.Generic; | ||||
| 	using UnityEngine; | ||||
| 	 | ||||
|     public static partial class Analytics | ||||
|     { | ||||
| 	    #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 DefaultWaterfall = "unknown"; | ||||
|  | @ -21,89 +17,32 @@ namespace Guru | |||
| 	     | ||||
| 	    #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 ------------------------- | ||||
| 	    public static void ADBadsLoad(AdParams adParams) | ||||
| 	    public static void TrackAdBadsLoad(Dictionary<string, object> data) | ||||
| 	    { | ||||
| 		    TrackEvent(EventBadsLoad, BuildAdEventData(adParams)); | ||||
| 		    TrackEvent(EventBadsLoad, data); | ||||
| 	    } | ||||
| 	    public static void ADBadsLoaded(AdParams adParams) | ||||
| 	    public static void TrackAdBadsLoaded(Dictionary<string, object> data) | ||||
| 	    { | ||||
| 		    TrackEvent(EventBadsLoaded, BuildAdEventData(adParams)); | ||||
| 		    TrackEvent(EventBadsLoaded, data); | ||||
| 	    } | ||||
| 	    public static void ADBadsFailed(AdParams adParams) | ||||
| 	    public static void TrackAdBadsFailed(Dictionary<string, object> data) | ||||
| 	    { | ||||
| 		    TrackEvent(EventBadsFailed, BuildAdEventData(adParams)); | ||||
| 		    TrackEvent(EventBadsFailed, data); | ||||
| 	    } | ||||
| 	    /// <summary> | ||||
| 	    /// 广告点击 | ||||
| 	    /// </summary> | ||||
| 		public static void ADBadsClick(AdParams adParams) | ||||
| 		public static void TrackAdBadsClick(Dictionary<string, object> data) | ||||
| 	    { | ||||
| 		    TrackEvent(EventBadsClick, BuildAdEventData(adParams)); | ||||
| 		    TrackEvent(EventBadsClick, data); | ||||
| 	    } | ||||
| 	    public static void ADBadsImp(AdParams adParams) | ||||
| 	    public static void TrackAdBadsImp(Dictionary<string, object> data) | ||||
| 	    { | ||||
| 		    TrackEvent(EventBadsImp, BuildAdEventData(adParams)); | ||||
| 		    TrackEvent(EventBadsImp, data); | ||||
| 	    } | ||||
| 	     | ||||
| 	    public static void ADBadsHide( int loadedTimes, int failTimes) | ||||
| 	    public static void TrackAdBadsHide( int loadedTimes, int failTimes) | ||||
| 	    { | ||||
| 		    var dict = new Dictionary<string, object>() | ||||
| 		    { | ||||
|  | @ -116,36 +55,31 @@ namespace Guru | |||
| 		/// <summary> | ||||
| 		/// 广告拉取 | ||||
| 		/// </summary> | ||||
| 		public static void ADIadsLoad(AdParams adParams) | ||||
| 		public static void TrackAdIadsLoad(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventIadsLoad, BuildAdEventData(adParams)); | ||||
| 			TrackEvent(EventIadsLoad, data); | ||||
| 		} | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// 广告拉取成功 | ||||
| 		/// </summary> | ||||
| 		public static void ADIadsLoaded(AdParams adParams) | ||||
| 		public static void TrackAdIadsLoaded(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventIadsLoaded, BuildAdEventData(adParams)); | ||||
| 			TrackEvent(EventIadsLoaded, data); | ||||
| 		} | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// 广告拉取失败 | ||||
| 		/// </summary> | ||||
| 		public static void ADIadsFailed(AdParams adParams) | ||||
| 		public static void TrackAdIadsFailed(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventIadsFailed, BuildAdEventData(adParams)); | ||||
| 			TrackEvent(EventIadsFailed, data); | ||||
| 		} | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// 广告展示 | ||||
| 		/// </summary> | ||||
| 		public static void ADIadsImp(AdParams adParams) | ||||
| 		{ | ||||
| 			ADIadsImp(BuildAdEventData(adParams)); | ||||
| 		} | ||||
| 		 | ||||
| 		public static void ADIadsImp(Dictionary<string, dynamic> data) | ||||
| 		public static void TrackAdIadsImp(Dictionary<string, dynamic> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventIadsImp, data); | ||||
| 		} | ||||
|  | @ -153,12 +87,7 @@ namespace Guru | |||
| 		/// <summary> | ||||
| 		/// 广告点击 | ||||
| 		/// </summary> | ||||
| 		public static void ADIadsClick(AdParams adParams) | ||||
| 		{ | ||||
| 			ADIadsClick(BuildAdEventData(adParams)); | ||||
| 		} | ||||
| 		 | ||||
| 		public static void ADIadsClick(Dictionary<string, object> data) | ||||
| 		public static void TrackAdIadsClick(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventIadsClick, data); | ||||
| 		} | ||||
|  | @ -166,12 +95,7 @@ namespace Guru | |||
| 		/// <summary> | ||||
| 		/// 用户关闭广告 | ||||
| 		/// </summary> | ||||
| 		public static void ADIadsClose(AdParams adParams) | ||||
| 		{ | ||||
| 			ADIadsClose(BuildAdEventData(adParams)); | ||||
| 		} | ||||
| 		 | ||||
| 		public static void ADIadsClose(Dictionary<string, object> data) | ||||
| 		public static void TrackAdIadsClose(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventIadsClose, data); | ||||
| 		} | ||||
|  | @ -180,7 +104,7 @@ namespace Guru | |||
| 		/// 插屏广告收到奖励 | ||||
| 		/// </summary> | ||||
| 		/// <param name="data"></param> | ||||
| 		public static void ADIadsPaid(Dictionary<string, object> data) | ||||
| 		public static void TrackAdIadsPaid(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventIadsPaid, data); | ||||
| 		} | ||||
|  | @ -189,33 +113,28 @@ namespace Guru | |||
| 		/// <summary> | ||||
| 		/// 广告开始加载 | ||||
| 		/// </summary> | ||||
| 		public static void ADRadsLoad(AdParams adParams) | ||||
| 		public static void TrackAdRadsLoad(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventRadsLoad, BuildAdEventData(adParams)); | ||||
| 			TrackEvent(EventRadsLoad, data); | ||||
| 		} | ||||
| 		/// <summary> | ||||
| 		/// 广告拉取成功 | ||||
| 		/// </summary> | ||||
| 		public static void ADRadsLoaded(AdParams adParams) | ||||
| 		public static void TrackAdRadsLoaded(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventRadsLoaded, BuildAdEventData(adParams)); | ||||
| 			TrackEvent(EventRadsLoaded, data); | ||||
| 		} | ||||
| 		/// <summary> | ||||
| 		/// 广告拉取失败 | ||||
| 		/// </summary> | ||||
| 		public static void ADRadsFailed(AdParams adParams) | ||||
| 		public static void TrackAdRadsFailed(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventRadsFailed, BuildAdEventData(adParams)); | ||||
| 			TrackEvent(EventRadsFailed, data); | ||||
| 		} | ||||
| 		/// <summary> | ||||
| 		/// 广告展示 | ||||
| 		/// </summary> | ||||
| 		public static void ADRadsImp(AdParams adParams) | ||||
| 		{ | ||||
| 			ADRadsImp(BuildAdEventData(adParams)); | ||||
| 		} | ||||
| 		 | ||||
| 		public static void ADRadsImp(Dictionary<string, dynamic> data) | ||||
| 		public static void TrackAdRadsImp(Dictionary<string, dynamic> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventRadsImp, data); | ||||
| 		} | ||||
|  | @ -225,19 +144,14 @@ namespace Guru | |||
| 		/// <summary> | ||||
| 		/// 广告完成观看发奖励 | ||||
| 		/// </summary> | ||||
| 		public static void ADRadsRewarded(AdParams adParams) | ||||
| 		{ | ||||
| 			ADRadsRewarded(BuildAdEventData(adParams)); | ||||
| 		} | ||||
| 		 | ||||
| 		public static void ADRadsRewarded(Dictionary<string, object> data) | ||||
| 		public static void TrackAdRadsRewarded(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventRadsRewarded, data); | ||||
| 			 | ||||
| 			if (RadsRewardCount == 0) | ||||
| 			{ | ||||
| 				RadsRewardCount = 1; | ||||
| 				ADRadsFirstRewarded(data); | ||||
| 				TrackAdRadsFirstRewarded(data); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
|  | @ -245,12 +159,11 @@ namespace Guru | |||
| 		/// 插屏广告收到奖励 | ||||
| 		/// </summary> | ||||
| 		/// <param name="data"></param> | ||||
| 		public static void ADRadsPaid(Dictionary<string, object> data) | ||||
| 		public static void TrackAdRadsPaid(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventRadsPaid, data); | ||||
| 		} | ||||
| 		 | ||||
| 
 | ||||
| 		private static int RadsRewardCount | ||||
| 		{ | ||||
| 			get => PlayerPrefs.GetInt(nameof(RadsRewardCount), 0); | ||||
|  | @ -260,7 +173,7 @@ namespace Guru | |||
| 		/// <summary> | ||||
| 		/// 用户首次完成观看 | ||||
| 		/// </summary> | ||||
| 		public static void ADRadsFirstRewarded(Dictionary<string, object> data) | ||||
| 		private static void TrackAdRadsFirstRewarded(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventFirstRadsRewarded, data); | ||||
| 		} | ||||
|  | @ -268,12 +181,7 @@ namespace Guru | |||
| 		/// <summary> | ||||
| 		/// 广告点击 | ||||
| 		/// </summary> | ||||
| 		public static void ADRadsClick(AdParams adParams) | ||||
| 		{ | ||||
| 			ADRadsClick(BuildAdEventData(adParams)); | ||||
| 		} | ||||
| 		 | ||||
| 		public static void ADRadsClick(Dictionary<string, object> data) | ||||
| 		public static void TrackAdRadsClick(Dictionary<string, object> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventRadsClick, data); | ||||
| 		} | ||||
|  | @ -281,12 +189,7 @@ namespace Guru | |||
| 		/// <summary> | ||||
| 		/// 用户关闭广告 | ||||
| 		/// </summary> | ||||
| 		public static void ADRadsClose(AdParams adParams) | ||||
| 		{ | ||||
| 			ADRadsClose( BuildAdEventData(adParams)); | ||||
| 		} | ||||
| 		 | ||||
| 		public static void ADRadsClose(Dictionary<string, dynamic> data) | ||||
| 		public static void TrackAdRadsClose(Dictionary<string, dynamic> data) | ||||
| 		{ | ||||
| 			TrackEvent(EventRadsClose, data); | ||||
| 		} | ||||
|  | @ -329,78 +232,6 @@ namespace Guru | |||
| 	    #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; | ||||
| 	    } | ||||
|     } | ||||
|      | ||||
|      | ||||
| } | ||||
|  | @ -6,6 +6,7 @@ namespace Guru | |||
| 		fail, | ||||
| 		timeout, | ||||
| 		exit, | ||||
| 		replay | ||||
| 	} | ||||
| 	 | ||||
| 	//打点常量定义 | ||||
|  | @ -124,6 +125,8 @@ namespace Guru | |||
| 		public const string PropertyNoAds = "no_ads"; // 玩家是否去广告 | ||||
| 		public const string PropertyATTStatus = "att_status";  // ATT 状态 | ||||
| 		public const string PropertyGDPR = "gdpr"; // GDPR状态 | ||||
| 		public const string PropertySignUpMethod = "sign_up_method"; // 用户登录方式 | ||||
| 
 | ||||
| 		 | ||||
| 		// 经济相关 | ||||
| 		public const string ParameterBalance = "balance"; // 用于余额 | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| namespace Guru | ||||
| { | ||||
| 	using System; | ||||
| 	using System.Collections.Generic; | ||||
| 	using Firebase.Analytics; | ||||
| 	using Firebase.Crashlytics; | ||||
| 	using Firebase.Extensions; | ||||
|  | @ -20,52 +19,49 @@ namespace Guru | |||
| 	    private static bool _hasGotUid; // 已取得UID | ||||
| 	    private static DateTime _lastReportRateDate; //上次上报信息的日期 | ||||
| 	    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; | ||||
| 
 | ||||
| 	    /// <summary> | ||||
| 	    /// 初始化Guru自打点系统 (请优先于 Firebase 初始化调用) | ||||
| 	    /// </summary> | ||||
| 	    public static void InstallGuruAnalytics(bool isDebug = false, bool enableErrorLog = false) | ||||
| 	    public static void InitGuruAnalyticService(string firebaseId) | ||||
| 	    { | ||||
| 
 | ||||
| 		    if (_isGuruAnalyticInitOnce) return; | ||||
| 		     | ||||
| 	        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(); | ||||
| 		    _isGuruAnalyticInitOnce = true; | ||||
| 
 | ||||
| 		        _isGuruAnalyticInitOnce = true; | ||||
| 	        } | ||||
| 	        catch (Exception ex) | ||||
| 	        { | ||||
| 		        LogCrashlytics(ex); | ||||
| 	        } | ||||
| 		    try | ||||
| 		    { | ||||
| 			    string appId = IPMConfig.IPM_X_APP_ID; | ||||
| 			    string deviceInfo = new DeviceInfoData().ToString(); | ||||
| 
 | ||||
| 			    _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> | ||||
| 		/// 设置用户ID | ||||
|  | @ -246,10 +242,12 @@ namespace Guru | |||
| #endif	     | ||||
| 	     | ||||
| 		/// <summary> | ||||
| 		/// 获取全部ID | ||||
| 		/// 上报中台打点的用户属性 | ||||
| 		/// </summary> | ||||
| 		private static void UpdateAllValues() | ||||
| 		private static void UpdateAllUserProperties() | ||||
| 		{ | ||||
| 			 | ||||
| 			Debug.Log($"{TAG} --- UpdateAllValues"); | ||||
| 			SetUid(); | ||||
| 			SetDeviceId(); | ||||
| 			SetAdjustId(); | ||||
|  | @ -295,33 +293,21 @@ namespace Guru | |||
| 			try | ||||
| 			{ | ||||
| 				GuruAnalytics.Instance.SetUserProperty(key, value); | ||||
| 				UpdateAllValues(); // 同步所有的ID | ||||
| 				UpdateAllUserProperties(); // 同步所有的ID | ||||
| 			} | ||||
| 			catch (Exception e) | ||||
| 			catch (Exception ex) | ||||
| 			{ | ||||
| 				Crashlytics.LogException(e); | ||||
| 				if (IsFirebaseReady) | ||||
| 				{ | ||||
| 					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> | ||||
| 	    /// 设置太极02阀值 | ||||
|  | @ -343,7 +329,14 @@ namespace Guru | |||
| 		    } | ||||
| 		    catch (Exception e) | ||||
| 		    { | ||||
| 			    Crashlytics.LogException(e); | ||||
| 			    if (IsFirebaseReady) | ||||
| 			    { | ||||
| 				    Crashlytics.LogException(e); | ||||
| 			    } | ||||
| 			    else | ||||
| 			    { | ||||
| 				    Debug.LogWarning(e); | ||||
| 			    } | ||||
| 		    } | ||||
| 	    } | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,7 +6,6 @@ namespace Guru | |||
| 	using System.Collections.Generic; | ||||
| 	using Facebook.Unity; | ||||
| 	using UnityEngine; | ||||
| 	using Guru.Ads; | ||||
| 	 | ||||
| 	//游戏通用模版打点定义 | ||||
|     public static partial class Analytics | ||||
|  | @ -412,11 +411,20 @@ namespace Guru | |||
| 
 | ||||
| 	    public static void FBPurchase(double revenue, string currency, string type, string platfrom) | ||||
| 		    => FBPurchase((float)revenue, currency, type, platfrom); | ||||
| 	     | ||||
| 
 | ||||
| 	    /// <summary> | ||||
| 	    /// Google ARO买量点 | ||||
| 	    /// </summary> | ||||
| 	    /// <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> | ||||
| 	    /// <li> | ||||
| 	    /// value			double	eg:0.002 | ||||
|  | @ -427,21 +435,28 @@ namespace Guru | |||
| 	    /// ad_unit_name		string	广告位名称 | ||||
| 	    /// ad_creative_id		string	广告素材id | ||||
| 	    /// </li> | ||||
| 	    public static void ADImpression(AdImpressionData impressionData) | ||||
| 	    public static void ADImpression(double value, string currency = "USD", string adPlatform = "", | ||||
| 		    string adSource = "", string adFormat = "", string adUnitId = "", string adPlacement= "", | ||||
| 		    string creativeId = "", string reviewCreativeId = "") | ||||
| 	    { | ||||
| 		    TrackEvent(EventAdImpression, new Dictionary<string, dynamic>() | ||||
| 		    ADImpression(new Dictionary<string, dynamic>() | ||||
| 		    { | ||||
| 			    [ParameterValue] = impressionData.value, | ||||
| 			    [ParameterCurrency] = impressionData.currency, | ||||
| 			    [ParameterAdPlatform] = impressionData.ad_platform, | ||||
| 			    [ParameterAdSource] = impressionData.ad_source, | ||||
| 			    [ParameterAdFormat] = impressionData.ad_format, | ||||
| 			    [ParameterAdUnitName] = impressionData.ad_unit_name, | ||||
| 			    [ParameterAdPlacement] = impressionData.ad_placement, | ||||
| 			    [ParameterAdCreativeId] = impressionData.ad_creative_id, | ||||
| 			    [ParameterReviewCreativeId] = impressionData.review_creative_id, | ||||
| 			    [ParameterValue] = value, | ||||
| 			    [ParameterCurrency] = currency, | ||||
| 			    [ParameterAdPlatform] = adPlatform, | ||||
| 			    [ParameterAdSource] = adSource, | ||||
| 			    [ParameterAdFormat] = adFormat, | ||||
| 			    [ParameterAdUnitName] = adUnitId, | ||||
| 			    [ParameterAdPlacement] = adPlacement, | ||||
| 			    [ParameterAdCreativeId] = creativeId, | ||||
| 			    [ParameterReviewCreativeId] = reviewCreativeId, | ||||
|  		    }); | ||||
| 	    } | ||||
| 	     | ||||
| 	    public static void ADImpression(Dictionary<string, object> data) | ||||
| 	    { | ||||
| 		    TrackEvent(EventAdImpression, data); | ||||
| 	    } | ||||
| 
 | ||||
| 	    public static void TchAdAbnormalEvent(double value) | ||||
| 	    { | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| 
 | ||||
| 
 | ||||
| using System.Threading; | ||||
| using System.Collections; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
|  | @ -39,35 +40,14 @@ namespace Guru | |||
| 				}; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private static EventSetting DefaultEventSetting => EventSetting.GetDefaultSetting(); | ||||
| 
 | ||||
| 		private static bool _isInitOnce;				//Analytics是否初始化完成 | ||||
| 		public static bool EnableDebugAnalytics;	//允许Debug包上报打点 | ||||
| 
 | ||||
| 		public static bool IsDebugMode => PlatformUtil.IsDebug(); | ||||
| 		private static bool IsDebug => PlatformUtil.IsDebug(); | ||||
| 		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 FBEventDriver _fbEventDriver; | ||||
| 		private static FirebaseEventDriver _firebaseEventDriver; | ||||
|  | @ -76,7 +56,10 @@ namespace Guru | |||
| 		 | ||||
| 		#region 初始化 | ||||
| 
 | ||||
| 		public static void InitAnalytics() | ||||
| 		/// <summary> | ||||
| 		/// 初始化打点模块 | ||||
| 		/// </summary> | ||||
| 		public static void Init() | ||||
| 		{ | ||||
| 			if (_isInitOnce) return; | ||||
| 			_isInitOnce = true; | ||||
|  | @ -85,22 +68,21 @@ namespace Guru | |||
| 			_fbEventDriver = new FBEventDriver(); | ||||
| 			_firebaseEventDriver = new FirebaseEventDriver(); | ||||
| 			_guruEventDriver = new GuruEventDriver(); | ||||
| 			 | ||||
| 			// ------- 初始化自打点 ---------- | ||||
| 			InstallGuruAnalytics(IsDebug); | ||||
| 		} | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 		 | ||||
| 		/// <summary> | ||||
| 		/// 外部拉起 Firebase 初始化完成回调 | ||||
| 		/// </summary> | ||||
| 		public static void OnFirebaseInitCompleted() | ||||
| 		{ | ||||
| 			Debug.Log($"[SDK] --- Analytics Init After FirebaseReady:{IsFirebaseReady}"); | ||||
| 			 | ||||
| 			// -------- 初始化 Crashlytics ---------- | ||||
| 			// --- 初始化 Crashlytics --- | ||||
| 			CrashlyticsAgent.Init(); | ||||
| 			FirebaseAnalytics.SetAnalyticsCollectionEnabled(true); | ||||
| 			FirebaseAnalytics.SetSessionTimeoutDuration(new TimeSpan(0, 30, 0)); | ||||
| 			SetUserProperty(FirebaseAnalytics.UserPropertySignUpMethod, "Google"); | ||||
| 			 | ||||
| 			// --- 上报用户事件 --- | ||||
| 			SetUserProperty(PropertyDeviceID, IPMConfig.IPM_DEVICE_ID); | ||||
| 			// SetUserProperty(PropertyFirstOpenTime, FirstOpenTime); | ||||
| 			 | ||||
|  | @ -118,7 +100,7 @@ namespace Guru | |||
| 			_adjustEventDriver.TriggerFlush(); | ||||
| 		} | ||||
| 		 | ||||
| 		private static void OnGuruAnalyticsInitComplete() | ||||
| 		public static void OnGuruAnalyticsInitComplete() | ||||
| 		{ | ||||
|             _guruEventDriver.TriggerFlush(); | ||||
| 		} | ||||
|  | @ -152,13 +134,13 @@ namespace Guru | |||
| 		/// <summary> | ||||
| 		/// Firebase上报用户ID | ||||
| 		/// </summary> | ||||
| 		/// <param name="userID">通过Auth认证地用户ID</param> | ||||
| 		public static void SetUserIDProperty(string userID) | ||||
| 		/// <param name="uid">通过Auth认证地用户ID</param> | ||||
| 		public static void SetFirebaseUserId(string uid) | ||||
| 		{ | ||||
| 			Log.I(TAG,$"SetUserIDProperty -> userID:{userID}"); | ||||
| 			if (!IsReady) return; | ||||
| 			 | ||||
| 			FirebaseAnalytics.SetUserId(userID); | ||||
| 			if (!IsFirebaseReady) return; | ||||
| 			Log.I(TAG,$"SetUserIDProperty -> userID:{uid}"); | ||||
| 			FirebaseAnalytics.SetUserId(uid);			 | ||||
| 			Crashlytics.SetUserId(uid); | ||||
| 		} | ||||
| 
 | ||||
| 		/// <summary> | ||||
|  | @ -168,7 +150,7 @@ namespace Guru | |||
| 		{ | ||||
| 			if (!_isInitOnce) | ||||
| 			{ | ||||
| 				throw new Exception($"[{TAG}][SDK] Analytics did not initialized, Call <Analytics.{nameof(InitAnalytics)}()> first!"); | ||||
| 				throw new Exception($"[{TAG}][SDK] Analytics did not initialized, Call <Analytics.{nameof(Init)}()> first!"); | ||||
| 			} | ||||
| 			 | ||||
| 			if (IsDebug && !EnableDebugAnalytics) | ||||
|  | @ -200,6 +182,25 @@ 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 | ||||
| 
 | ||||
| 		#region 打点上报 | ||||
|  | @ -217,7 +218,7 @@ namespace Guru | |||
| 		{ | ||||
| 			if (!_isInitOnce) | ||||
| 			{ | ||||
| 				throw new Exception($"[{TAG}][SDK] Analytics did not initialized, Call <Analytics.{nameof(InitAnalytics)}()> first!"); | ||||
| 				throw new Exception($"[{TAG}][SDK] Analytics did not initialized, Call <Analytics.{nameof(Init)}()> first!"); | ||||
| 			} | ||||
| 			 | ||||
| 			if (IsDebug && !EnableDebugAnalytics) | ||||
|  | @ -352,5 +353,5 @@ namespace Guru | |||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	 | ||||
| 
 | ||||
| } | ||||
|  | @ -1,3 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 005347f128bb460f9312778f93e39a9b | ||||
| timeCreated: 1679986193 | ||||
|  | @ -1,206 +0,0 @@ | |||
| 
 | ||||
| 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 | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -1,3 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 2b2215a925974a3593ff2c6db2cf02fc | ||||
| timeCreated: 1679986312 | ||||
|  | @ -27,7 +27,7 @@ namespace Guru | |||
| 			return ((MonoBehaviour)this).StartDelayed(delay, callback); | ||||
| 		} | ||||
| 
 | ||||
| 		public Coroutine StartDelayed(int framesOfDelay, Action callback) | ||||
| 		public Coroutine StartDelayedWithFrame(int framesOfDelay, Action callback) | ||||
| 		{ | ||||
| 			return ((MonoBehaviour)this).StartDelayed(framesOfDelay, callback); | ||||
| 		} | ||||
|  |  | |||
|  | @ -2,5 +2,5 @@ using UnityEngine; | |||
| 
 | ||||
| public static class NetworkUtil | ||||
| { | ||||
|     public static bool IsNetAvaliable => Application.internetReachability != NetworkReachability.NotReachable; | ||||
|     public static bool IsNetAvailable => Application.internetReachability != NetworkReachability.NotReachable; | ||||
| } | ||||
|  | @ -1,58 +1,99 @@ | |||
| using Firebase.Auth; | ||||
| using Firebase.Extensions; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
| 	using System; | ||||
| 	using Firebase.Auth; | ||||
| 	using Firebase.Extensions; | ||||
| 	using UnityEngine; | ||||
| 	 | ||||
| 	public static partial class FirebaseUtil | ||||
| 	{ | ||||
| 		public static FirebaseUser CurrentUser => FirebaseAuth.DefaultInstance.CurrentUser; | ||||
| 		 | ||||
| 		private static readonly WaitForSeconds _wait = new WaitForSeconds(10); | ||||
| 		 | ||||
| 		public static void AuthUser(Action onSuccessHandler = null) | ||||
| 		private static FirebaseUser FirebaseUser => 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) | ||||
| 		{ | ||||
| 			//FirebaseAuth获取用户验证并同步用户数据 | ||||
| 			if (CurrentUser != null) | ||||
| 			// #1 Firebase 已获取用户 | ||||
| 			if (FirebaseUser != null) | ||||
| 			{ | ||||
| 				Log.I(LOG_TAG, $"[Auth] user exists,UserId:{CurrentUser.UserId}"); | ||||
| 				OnFirebaseAuthResult?.Invoke(true); | ||||
| 				onSuccessHandler?.Invoke(); | ||||
| 				Log.I(LOG_TAG, $"[Auth] user exists,UserId:{FirebaseUser.UserId}"); | ||||
| 				onLoginResult?.Invoke(true); | ||||
| 				return; | ||||
| 			} | ||||
| 			 | ||||
| 			string authToken = IPMConfig.IPM_FIREBASE_TOKEN; | ||||
| 			if (string.IsNullOrEmpty(authToken)) authToken = IPMConfig.IPM_FIREBASE_TOKEN; | ||||
| 			Log.I(LOG_TAG, $"[Auth] Firebase Token:{authToken}"); | ||||
| 			if (string.IsNullOrEmpty(authToken) || !NetworkUtil.IsNetAvaliable) | ||||
| 
 | ||||
| 			if (!string.IsNullOrEmpty(authToken) && NetworkUtil.IsNetAvailable) | ||||
| 			{ | ||||
| 				CoroutineHelper.Instance.StartDelayed(_wait, ()=> AuthUser(onSuccessHandler)); | ||||
| 				OnFirebaseAuthResult?.Invoke(false); | ||||
| 
 | ||||
| 				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 => | ||||
| 			.ContinueWithOnMainThread(task => | ||||
| 			{ | ||||
| 				// ----- Task failed -----  | ||||
| 				if (task.IsCanceled || task.IsFaulted) | ||||
| 				{ | ||||
| 					// ----- Task failed -----  | ||||
| 					if (task.IsCanceled || task.IsFaulted) | ||||
| 					Log.E(LOG_TAG,"[Auth] SignInWithCustomTokenAsync encountered an error: " + task.Exception); | ||||
| 					if (autoRetry) | ||||
| 					{ | ||||
| 						Log.E(LOG_TAG,"[Auth] SignInWithCustomTokenAsync encountered an error: " + task.Exception); | ||||
| 						OnFirebaseAuthResult?.Invoke(false); | ||||
| 						CoroutineHelper.Instance.StartDelayed(_wait, () => AuthUser(onSuccessHandler)); | ||||
| 						return; | ||||
| 						DelayCallFirebaseLogin(_retryDelayTime, onLoginResult, authToken); // 自动重试 | ||||
| 					} | ||||
| 					// ----- User is NULL -----  | ||||
| 					if (CurrentUser == null) | ||||
| 					else | ||||
| 					{ | ||||
| 						OnFirebaseAuthResult?.Invoke(false); | ||||
| 						CoroutineHelper.Instance.StartDelayed(_wait, ()=> AuthUser(onSuccessHandler)); | ||||
| 						return; | ||||
| 						onLoginResult?.Invoke(false); // 不再重试 | ||||
| 					} | ||||
| 					// ----- Success -----  | ||||
| 					OnFirebaseAuthResult?.Invoke(true); // 最后判定是成功的 | ||||
| 					onSuccessHandler?.Invoke(); | ||||
| 				}); | ||||
| 					return; | ||||
| 				} | ||||
| 				// ----- Check Result -----  | ||||
| 				bool success = FirebaseUser != null; | ||||
| 				onLoginResult?.Invoke(success); | ||||
| 				_retryDelayTime = LOGIN_RETRY_INTERVAL; | ||||
| 				Analytics.SetUserProperty(Analytics.PropertySignUpMethod, "google"); // 上报用户登录属性 | ||||
| 			}); | ||||
| 		} | ||||
| 		 | ||||
| 
 | ||||
| 		// TODO:P0 协程不应该用在此场景  | ||||
| 		/// <summary> | ||||
| 		/// 延迟调用 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); | ||||
| 			CoroutineHelper.Instance.StartDelayed(delay, ()=> LoginFirebaseWithToken(callback, token)); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -5,6 +5,8 @@ namespace Guru | |||
| { | ||||
| 	public static partial class FirebaseUtil | ||||
| 	{ | ||||
| 		 | ||||
| 		 | ||||
| 		public static void InitCrashlytics() | ||||
| 		{ | ||||
| 			if(!string.IsNullOrEmpty(IPMConfig.IPM_UID)) | ||||
|  | @ -17,6 +19,7 @@ namespace Guru | |||
| 		/// </summary> | ||||
| 		public static void SetUserID(string userID) | ||||
| 		{ | ||||
| 			if (!IsFirebaseInitialized) return; | ||||
| 			Crashlytics.SetUserId(userID); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -26,6 +29,7 @@ namespace Guru | |||
| 		/// </summary> | ||||
| 		public static void SetCustomData(string key, string value) | ||||
| 		{ | ||||
| 			if (!IsFirebaseInitialized) return; | ||||
| 			Crashlytics.SetCustomKey(key, value); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -34,6 +38,7 @@ namespace Guru | |||
| 		/// </summary> | ||||
| 		public static void LogMessage(string message) | ||||
| 		{ | ||||
| 			if (!IsFirebaseInitialized) return; | ||||
| 			Crashlytics.Log(message); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -42,6 +47,7 @@ namespace Guru | |||
| 		/// </summary> | ||||
| 		public static void LogException(Exception exception) | ||||
| 		{ | ||||
| 			if (!IsFirebaseInitialized) return; | ||||
| 			Crashlytics.LogException(exception); | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -1,10 +1,12 @@ | |||
| using System; | ||||
| using System.Collections; | ||||
| using Firebase.Messaging; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
| 	using System; | ||||
| 	using System.Collections; | ||||
| 	using Firebase.Messaging; | ||||
| 	using UnityEngine; | ||||
| 	 | ||||
| 	public static partial class FirebaseUtil | ||||
| 	{ | ||||
| 		private static int _messageRetry = 5; | ||||
|  | @ -17,7 +19,7 @@ namespace Guru | |||
| 			_isAutoFetchFcmToken = value; | ||||
| 		} | ||||
| 
 | ||||
| 		public static void InitializeMessage() | ||||
| 		private static void InitializeMessage() | ||||
| 		{ | ||||
| 			if (_isAutoFetchFcmToken) | ||||
| 			{ | ||||
|  | @ -48,7 +50,7 @@ namespace Guru | |||
| 
 | ||||
| 			if (task.IsFaulted || task.IsCanceled) | ||||
| 			{ | ||||
| 				CoroutineHelper.Instance.StartDelayed(_wait, GetFCMToken); | ||||
| 				CoroutineHelper.Instance.StartDelayed(10, GetFCMToken); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
|  | @ -63,7 +65,7 @@ namespace Guru | |||
| 
 | ||||
| 		private static void UploadDeviceInfo() | ||||
| 		{ | ||||
| 			if (!NetworkUtil.IsNetAvaliable) | ||||
| 			if (!NetworkUtil.IsNetAvailable) | ||||
| 			{ | ||||
| 				double retryDelay = Math.Pow(2, _messageRetry); | ||||
| 				_messageRetry++; | ||||
|  | @ -84,14 +86,14 @@ namespace Guru | |||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		private static void OnTokenReceived(object sender, Firebase.Messaging.TokenReceivedEventArgs token) | ||||
| 		private static void OnTokenReceived(object sender, TokenReceivedEventArgs token) | ||||
|         { | ||||
| #if UNITY_IOS | ||||
| 	        DeviceUtil.SetiOSBadge(); | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
| 		public static void OnMessageReceived(object sender, Firebase.Messaging.MessageReceivedEventArgs e) | ||||
| 		public static void OnMessageReceived(object sender, MessageReceivedEventArgs e) | ||||
|         { | ||||
| #if UNITY_IOS | ||||
| 	        DeviceUtil.SetiOSBadge(); | ||||
|  |  | |||
|  | @ -96,7 +96,7 @@ namespace Guru | |||
| 				    || _remoteConfigInstance.Info.LastFetchStatus != LastFetchStatus.Success) | ||||
| 				{ | ||||
| 					Log.E(LOG_TAG, "Firebase RemoteConfig Fetch Failure"); | ||||
| 					CoroutineHelper.Instance.StartDelayed(_wait, FetchRemoteValue); | ||||
| 					CoroutineHelper.Instance.StartDelayed(10, FetchRemoteValue); | ||||
| 					return; | ||||
| 				} | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,160 +1,214 @@ | |||
| using System; | ||||
| using Firebase; | ||||
| using Firebase.Analytics; | ||||
| using Firebase.Extensions; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
| 	using System; | ||||
| 	using Firebase; | ||||
| 	using Firebase.Analytics; | ||||
| 	using Firebase.Extensions; | ||||
| 	using UnityEngine; | ||||
| 	 | ||||
| 	public static partial class FirebaseUtil | ||||
| 	{ | ||||
| 		private static readonly string LOG_TAG = "Firebase"; | ||||
| 		private static bool _isReady = false; | ||||
| 		public static bool IsReady => _isReady; | ||||
| 		 | ||||
| 		public static DependencyStatus DependencyStatus = DependencyStatus.UnavailableOther; | ||||
| 		public static bool IsFirebaseInitialized => DependencyStatus == DependencyStatus.Available; | ||||
| 		public static Action<bool> onInitComplete; | ||||
| 		public static bool IsReady => _isReady && IsFirebaseInitialized; | ||||
| 
 | ||||
| 		public static Action<bool> OnFirebaseAuthResult; | ||||
| 		public static Action<bool> OnUserAuthResult; | ||||
| 		// TODO: 需要从 FirebaseUtils 内拿走 | ||||
| 		public static Action<string> OnAdjustDeeplinkCallback = null;  | ||||
| 		 | ||||
| 		 | ||||
| 		public static void InitFirebase(Action callback) | ||||
| 		private static DependencyStatus DependencyStatus = DependencyStatus.UnavailableOther; | ||||
| 		public static bool IsFirebaseInitialized => DependencyStatus == DependencyStatus.Available; | ||||
| 
 | ||||
| 		private static Action<bool> _onCheckAndFixDepsHandler; | ||||
| 		private static Action<string> _onGetFirebaseIdHandler; | ||||
| 		private static Action<bool> _onGetGuruUIDHandler; | ||||
| 		private static Action<bool> _onFirebaseLoginResult; | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// 初始化 Firebase | ||||
| 		/// </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; | ||||
| 			// Analytics.InitAnalytics(); // 打点提前初始化 | ||||
| 			_onCheckAndFixDepsHandler = onDepsCheckResult; | ||||
| 			_onGetFirebaseIdHandler = onGetFirebaseId; | ||||
| 			_onGetGuruUIDHandler = onGetGuruUIDResult; | ||||
| 			_onFirebaseLoginResult = onFirebaseLoginResult; | ||||
| 			 | ||||
| 			// Loom.StartUp(); // 确保主线程开启 | ||||
| 			 | ||||
| 			// 初始化 Fireabse 依赖 | ||||
| 			// 初始化 Firebase 依赖 | ||||
| 			FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => { | ||||
| 				DependencyStatus = task.Result; | ||||
| 				if (DependencyStatus == DependencyStatus.Available) | ||||
| 				{ | ||||
| 					_isReady = true; | ||||
| 					InitializeFirebaseComp(); | ||||
| 					callback?.Invoke(); | ||||
| 					OnFirebaseDepsCheckSuccess(); // Deps 处理通过 | ||||
| 				}  | ||||
| 				else  | ||||
| 				{ | ||||
| 					Log.E(LOG_TAG, "Could not resolve all Firebase dependencies: " + DependencyStatus); | ||||
| 				} | ||||
| 				onInitComplete?.Invoke(_isReady); | ||||
| 				_onCheckAndFixDepsHandler?.Invoke(_isReady); | ||||
| 			}); | ||||
| 		} | ||||
| 		private static void InitializeFirebaseComp() | ||||
| 		 | ||||
| 		/// <summary> | ||||
| 		/// Deps 处理通过 | ||||
| 		/// 初始化各模块 | ||||
| 		/// </summary> | ||||
| 		private static void OnFirebaseDepsCheckSuccess() | ||||
| 		{ | ||||
| 			InitCrashlytics();  // 老项目沿用此逻辑 | ||||
| 			InitRemoteConfig();	// 老项目沿用此逻辑 | ||||
| 			// TODO: 需要从 FirebaseUtils 内拿走 | ||||
| 			InitAdjustService();  // 初始化 adjust 服务 | ||||
| 			InitializeMessage(); // 初始化 Messaging 服务 | ||||
| 			GetFirebaseIdAsync();  // 开始获取 FirebaseId | ||||
| 			 | ||||
| 			if (IPMConfig.IPM_UID.IsNullOrEmpty()) | ||||
| 			{ | ||||
| 				Log.I(LOG_TAG, "没有存储UID时,从中台获取匿名认证授权"); | ||||
| 				//没有存储UID时,从中台获取匿名认证授权 | ||||
| 				new AuthUserRequest() | ||||
| 					.SetRetryTimes(-1) | ||||
| 					.SetSuccessCallBack(() => | ||||
| 					{ | ||||
| 						OnUserAuthResult?.Invoke(true); | ||||
| 						AuthUser(InitializeMessage); | ||||
| 					}).SetFailCallBack(() => | ||||
| 					{ | ||||
| 						OnUserAuthResult?.Invoke(false); | ||||
| 					}) | ||||
| 					.Send(); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				InitializeMessage(); | ||||
| 				int currentTimeStamp = TimeUtil.GetCurrentTimeStampSecond(); | ||||
| 				if(currentTimeStamp - IPMConfig.IPM_TOKEN_TIME >= IPMConfig.TOKEN_VALID_TIME) | ||||
| 				{ | ||||
| 					//中台Token失效,从中台重新获取Token | ||||
| 					new RefreshTokenRequest().SetRetryTimes(-1).Send(); | ||||
| 				} | ||||
| 				 | ||||
| 				if(currentTimeStamp - IPMConfig.IPM_FIREBASE_TOKEN_TIME >= IPMConfig.FIREBASE_TOKEN_VALID_TIME) | ||||
| 				{ | ||||
| 					//中台firebaseToken失效,从中台重新获取firebaseToken | ||||
| 					new RefreshFirebaseTokenRequest() | ||||
| 						.SetRetryTimes(-1) | ||||
| 						.SetSuccessCallBack(()=> AuthUser(InitializeMessage)) | ||||
| 						.Send(); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					AuthUser(InitializeMessage); | ||||
| 				} | ||||
| 			} | ||||
| 			StartVerifyTokenAndAuthAsync(); //开始验证授权和Token | ||||
| 		} | ||||
| 
 | ||||
| 		#region 启动 Adjust 服务 | ||||
| 		 | ||||
| 		// TODO: 需要从 FirebaseUtils 内拿走 | ||||
| 		/// <summary> | ||||
| 		/// 启动 Adjust 服务 | ||||
| 		/// </summary> | ||||
| 		private static void InitAdjustService() | ||||
| 		#region FirebaseId | ||||
| 
 | ||||
| 		private static void GetFirebaseIdAsync() | ||||
| 		{ | ||||
| 			// 异步获取 FirebaseID | ||||
| 			FirebaseAnalytics.GetAnalyticsInstanceIdAsync() | ||||
| 				.ContinueWithOnMainThread(task => | ||||
| 				{ | ||||
| 					 | ||||
| 					if (task.IsCompleted && !string.IsNullOrEmpty(task.Result))  | ||||
| 					string fid = task.Result; | ||||
| 					if (task.IsCompleted && !string.IsNullOrEmpty(fid)) | ||||
| 					{ | ||||
| 						// 保存本地ID备份 | ||||
| 						string fid = task.Result; | ||||
| 						IPMConfig.FIREBASE_ID = fid; // 保存FirebaseID | ||||
| 						Debug.Log($"[SDK] --- Get FirebaseID: {fid}"); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						UnityEngine.Debug.LogError($"Fetch FirebaseID failed on start!"); | ||||
| 						Debug.LogError($"[SDK] --- 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); | ||||
| 
 | ||||
| 					_onGetFirebaseIdHandler?.Invoke(fid); | ||||
| 				}); | ||||
| 		} | ||||
| 
 | ||||
| 		private static void OnGetAdjustId(string adjustId) | ||||
| 		#endregion | ||||
| 
 | ||||
| 		#region Token and Auth | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// 异步验证所有 Token 有效期 | ||||
| 		/// </summary> | ||||
| 		private static void StartVerifyTokenAndAuthAsync() | ||||
| 		{ | ||||
| 			// 获取 ADID  | ||||
| 			if (string.IsNullOrEmpty(adjustId)) | ||||
| 			if (string.IsNullOrEmpty(IPMConfig.IPM_UID)) | ||||
| 			{ | ||||
| 				adjustId = "not_set"; | ||||
| 				Log.I(LOG_TAG, "没有存储UID时,从中台获取匿名认证授权"); | ||||
| 				// 没有存储UID时,从中台获取匿名认证授权 | ||||
| 				StartGuruLoginWithDeviceId(success => | ||||
| 				{ | ||||
| 					_onGetGuruUIDHandler?.Invoke(success); | ||||
| 				 | ||||
| 					if (success) { | ||||
| 						// 用户 UID 不为空 | ||||
| 						StartLoginWithFirebase(); | ||||
| 					} | ||||
| 				}); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				IPMConfig.ADJUST_ID = adjustId; | ||||
| 				// 检查中台 Token 是否过期 | ||||
| 				if (IsGuruTokenExpired()) | ||||
| 				{ | ||||
| 					RefreshGuruToken(); | ||||
| 				} | ||||
| 
 | ||||
| 				// 检查中台 Firebase Token 是否过期 | ||||
| 				if (IsFirebaseTokenExpired()) | ||||
| 				{ | ||||
| 					RefreshFirebaseToken(StartLoginWithFirebase); // 重新获取 Firebase Token | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					StartLoginWithFirebase(); | ||||
| 				} | ||||
| 			} | ||||
| 			ReportAdjustId(adjustId); | ||||
| 			 | ||||
| 			Analytics.OnAdjustInitComplete(); | ||||
| 		} | ||||
| 		 | ||||
| 
 | ||||
| 
 | ||||
| 		private static void ReportAdjustId(string adjustId) | ||||
| 		/// <summary> | ||||
| 		/// 使用设备 ID 进行中台匿名认证 | ||||
| 		/// </summary> | ||||
| 		private static void StartGuruLoginWithDeviceId(Action<bool> onLoginResult = null) | ||||
| 		{ | ||||
| 			FirebaseAnalytics.SetUserProperty("adjust_id", adjustId); // 仅上报 Firebase 用户属性 | ||||
| 			Debug.Log($"[SDK] --- Firebase + Adjust ID: {adjustId}"); | ||||
| 			// 没有存储UID时,从中台获取匿名认证授权 | ||||
| 			var request = new AuthUserRequest() | ||||
| 				.SetRetryTimes(-1) // 不成功的话会一直请求 | ||||
| 				.SetSuccessCallBack(() => | ||||
| 				{ | ||||
| 					onLoginResult?.Invoke(true); | ||||
| 				}).SetFailCallBack(() => | ||||
| 				{ | ||||
| 					onLoginResult?.Invoke(false); | ||||
| 				}); | ||||
| 			request.Send(); | ||||
| 		} | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Firebase Token 是否过期 | ||||
| 		/// </summary> | ||||
| 		/// <returns></returns> | ||||
| 		private static bool IsFirebaseTokenExpired() | ||||
| 		{ | ||||
| 			int currentTimeStamp = TimeUtil.GetCurrentTimeStampSecond(); | ||||
| 			return currentTimeStamp - IPMConfig.IPM_TOKEN_TIME >= IPMConfig.TOKEN_VALID_TIME; | ||||
| 		} | ||||
| 		 | ||||
| 		private static void RefreshFirebaseToken(Action onFirebaseTokenRefreshed = null) | ||||
| 		{ | ||||
| 			//中台firebaseToken失效,从中台重新获取firebaseToken | ||||
| 			var request = new RefreshFirebaseTokenRequest() | ||||
| 				.SetRetryTimes(-1) | ||||
| 				.SetSuccessCallBack(()=> onFirebaseTokenRefreshed?.Invoke()); | ||||
| 			request.Send(); | ||||
| 		} | ||||
| 		 | ||||
| 		/// <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() | ||||
| 		{ | ||||
| 			//中台Token失效,从中台重新获取Token | ||||
| 			var request = new RefreshTokenRequest() | ||||
| 				.SetRetryWaitSeconds(10) | ||||
| 				.SetRetryTimes(-1);  // 不成功的话会一直请求 | ||||
| 			request.Send(); | ||||
| 		} | ||||
| 		#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 | ||||
| 	} | ||||
| } | ||||
|  | @ -44,8 +44,7 @@ namespace Guru | |||
| 			this.Log(response); | ||||
| 			this.Log(responseData.data.ToString()); | ||||
| 			 | ||||
| 			Analytics.SetUserIDProperty(responseData.data.uid); | ||||
| 			Crashlytics.SetUserId(responseData.data.uid); | ||||
| 			Analytics.SetFirebaseUserId(responseData.data.uid); | ||||
| 			 | ||||
| 			IPMConfig.IPM_UID = responseData.data.uid; | ||||
| 			IPMConfig.IPM_UID_INT = responseData.data.uidInt; | ||||
|  |  | |||
|  | @ -29,8 +29,7 @@ namespace Guru | |||
| 			this.Log(responseData.data.ToString()); | ||||
| 			IPMConfig.IPM_TOKEN = responseData.data.token; | ||||
| 			IPMConfig.IPM_TOKEN_TIME = TimeUtil.GetCurrentTimeStampSecond(); | ||||
| 			PlayerPrefs.SetString("IPM_TOKEN", responseData.data.token); | ||||
| 			this.Log("@@@ Send OK!"); | ||||
| 			this.Log($"[SDK] --- RefreshTokenRequest Success: {responseData.data.token}"); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -13,7 +13,7 @@ namespace Guru | |||
| 		protected abstract UnityWebRequest CreateRequest(); | ||||
| 		protected abstract void RequestSuccessCallBack(string response); | ||||
| 		 | ||||
| 		private readonly WaitForSeconds _waitTime = new WaitForSeconds(5); | ||||
| 		private WaitForSeconds _waitTime = new WaitForSeconds(5); | ||||
| 
 | ||||
| 		private int _retryTimes = 3; | ||||
| 		private int _timeOut = 90; | ||||
|  | @ -28,6 +28,12 @@ namespace Guru | |||
| 			return this; | ||||
| 		} | ||||
| 		 | ||||
| 		public RequestBase SetRetryWaitSeconds(int waitSeconds) | ||||
| 		{ | ||||
| 			_waitTime = new WaitForSeconds(waitSeconds);; | ||||
| 			return this; | ||||
| 		} | ||||
| 		 | ||||
| 		public RequestBase SetTimeOut(int timeOut) | ||||
| 		{ | ||||
| 			_timeOut = timeOut; | ||||
|  |  | |||
|  | @ -2,13 +2,10 @@ | |||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
|     using System; | ||||
|     using System.Collections.Generic; | ||||
|     using System.Text; | ||||
|     using UnityEngine; | ||||
|     using Consts=AdStatusConsts; | ||||
|      | ||||
|      | ||||
|     using Const=AdStatusConsts; | ||||
|      | ||||
|     public partial class AdStatusPresenter | ||||
|     { | ||||
|  | @ -104,12 +101,10 @@ namespace Guru | |||
|         private StringBuilder _infoBuff; | ||||
|         private string CreateMonitorInfo() | ||||
|         { | ||||
|             string msg = ""; | ||||
|             bool loaded = false; | ||||
|              | ||||
|             string msg; | ||||
|             if (ADService.Instance == null || !ADService.Instance.IsInitialized) | ||||
|             { | ||||
|                 msg = ColoredText("AdService not initialized...", Consts.ColorRed); | ||||
|                 msg = ColoredText("AdService not initialized...", Const.ColorRed); | ||||
|                 return msg; | ||||
|             } | ||||
|              | ||||
|  | @ -118,7 +113,7 @@ namespace Guru | |||
|              | ||||
|             if (_curBadsInfo == null) | ||||
|             { | ||||
|                 msg = $"BADS: {ColoredText("not ready", Consts.ColorRed)}\n"; | ||||
|                 msg = $"BADS: {ColoredText("not ready", Const.ColorRed)}\n"; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|  | @ -126,25 +121,25 @@ namespace Guru | |||
|                 switch (_curBadsInfo.status) | ||||
|                 { | ||||
|                     case AdStatusType.Loaded: | ||||
|                         msg = $"BADS: {ColoredText("loaded", Consts.ColorGreen)}\n\tnetwork: {_curBadsInfo.network}\n\twaterfall: {_curBadsInfo.waterfall}\n"; | ||||
|                         msg = $"BADS: {ColoredText("loaded", Const.ColorGreen)}\n\tnetwork: {_curBadsInfo.network}\n\twaterfall: {_curBadsInfo.waterfall}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.LoadFailed: | ||||
|                         msg = $"BADS: {ColoredText("loading failed", Consts.ColorRed)}\n\tmessage: {_curBadsInfo.info}\n"; | ||||
|                         msg = $"BADS: {ColoredText("loading failed", Const.ColorRed)}\n\tmessage: {_curBadsInfo.info}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.DisplayFailed: | ||||
|                         msg = $"BADS: {ColoredText("display failed", Consts.ColorRed)}\n\tmessage: {_curBadsInfo.info}\n"; | ||||
|                         msg = $"BADS: {ColoredText("display failed", Const.ColorRed)}\n\tmessage: {_curBadsInfo.info}\n"; | ||||
|                         break;   | ||||
|                     case AdStatusType.Loading: | ||||
|                         msg = $"BADS: {ColoredText("loading...", Consts.ColorYellow)}\n\tformat: {_curBadsInfo.format}\n"; | ||||
|                         msg = $"BADS: {ColoredText("loading...", Const.ColorYellow)}\n\tformat: {_curBadsInfo.format}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.Paid: | ||||
|                         msg = $"BADS: {ColoredText("display", Consts.ColorGreen)}\n\tnetwork: {_curBadsInfo.network}\n\trevenue: {_curBadsInfo.revenue}\n"; | ||||
|                         msg = $"BADS: {ColoredText("display", Const.ColorGreen)}\n\tnetwork: {_curBadsInfo.network}\n\trevenue: {_curBadsInfo.revenue}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.NotReady: | ||||
|                         msg = $"BADS: {ColoredText("not ready", Consts.ColorGray)}\n\t{ColoredText("---", Consts.ColorGray)}\n"; | ||||
|                         msg = $"BADS: {ColoredText("not ready", Const.ColorGray)}\n\t{ColoredText("---", Const.ColorGray)}\n"; | ||||
|                         break; | ||||
|                     default: | ||||
|                         msg = $"BADS: {ColoredText("other", Consts.ColorGray)}\n\tstatus: {ColoredText($"{_curBadsInfo.status}", Consts.ColorYellow)}\n"; | ||||
|                         msg = $"BADS: {ColoredText("other", Const.ColorGray)}\n\tstatus: {ColoredText($"{_curBadsInfo.status}", Const.ColorYellow)}\n"; | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|  | @ -153,32 +148,32 @@ namespace Guru | |||
| 
 | ||||
|             if (_curIadsInfo == null) | ||||
|             { | ||||
|                 msg = $"IADS: {ColoredText("not ready", Consts.ColorRed)}\n"; | ||||
|                 msg = $"IADS: {ColoredText("not ready", Const.ColorRed)}\n"; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 switch (_curIadsInfo.status) | ||||
|                 { | ||||
|                     case AdStatusType.Loaded: | ||||
|                         msg = $"IADS: {ColoredText("loaded", Consts.ColorGreen)}\n\tnetwork: {_curIadsInfo.network}\n\twaterfall: {_curIadsInfo.waterfall}\n"; | ||||
|                         msg = $"IADS: {ColoredText("loaded", Const.ColorGreen)}\n\tnetwork: {_curIadsInfo.network}\n\twaterfall: {_curIadsInfo.waterfall}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.LoadFailed: | ||||
|                         msg = $"IADS: {ColoredText("loading failed", Consts.ColorRed)}\n\tmessage: {_curIadsInfo.info}\n"; | ||||
|                         msg = $"IADS: {ColoredText("loading failed", Const.ColorRed)}\n\tmessage: {_curIadsInfo.info}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.DisplayFailed: | ||||
|                         msg = $"IADS: {ColoredText("display failed", Consts.ColorRed)}\n\tmessage: {_curIadsInfo.info}\n"; | ||||
|                         msg = $"IADS: {ColoredText("display failed", Const.ColorRed)}\n\tmessage: {_curIadsInfo.info}\n"; | ||||
|                         break;   | ||||
|                     case AdStatusType.Loading: | ||||
|                         msg = $"IADS: {ColoredText("loading...", Consts.ColorYellow)}\n\tformat: {_curIadsInfo.format}\n"; | ||||
|                         msg = $"IADS: {ColoredText("loading...", Const.ColorYellow)}\n\tformat: {_curIadsInfo.format}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.Paid: | ||||
|                         msg = $"IADS: {ColoredText("get revenue", Consts.ColorGreen)}\n\trevenue: {_curIadsInfo.revenue}\n"; | ||||
|                         msg = $"IADS: {ColoredText("get revenue", Const.ColorGreen)}\n\trevenue: {_curIadsInfo.revenue}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.NotReady: | ||||
|                         msg = $"IADS: {ColoredText("not ready", Consts.ColorGray)}\n\t{ColoredText("---", Consts.ColorGray)}\n"; | ||||
|                         msg = $"IADS: {ColoredText("not ready", Const.ColorGray)}\n\t{ColoredText("---", Const.ColorGray)}\n"; | ||||
|                         break; | ||||
|                     default: | ||||
|                         msg = $"IADS: {ColoredText("other", Consts.ColorGray)}\n\tstatus: {ColoredText($"{_curIadsInfo.status}", Consts.ColorYellow)}\n"; | ||||
|                         msg = $"IADS: {ColoredText("other", Const.ColorGray)}\n\tstatus: {ColoredText($"{_curIadsInfo.status}", Const.ColorYellow)}\n"; | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|  | @ -187,32 +182,32 @@ namespace Guru | |||
| 
 | ||||
|             if (_curRadsInfo == null) | ||||
|             { | ||||
|                 msg = $"RADS: {ColoredText("not ready", Consts.ColorRed)}\n"; | ||||
|                 msg = $"RADS: {ColoredText("not ready", Const.ColorRed)}\n"; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 switch (_curRadsInfo.status) | ||||
|                 { | ||||
|                     case AdStatusType.Loaded: | ||||
|                         msg = $"RADS: {ColoredText("loaded", Consts.ColorGreen)}\n\tnetwork: {_curRadsInfo.network}\n\twaterfall: {_curRadsInfo.waterfall}\n"; | ||||
|                         msg = $"RADS: {ColoredText("loaded", Const.ColorGreen)}\n\tnetwork: {_curRadsInfo.network}\n\twaterfall: {_curRadsInfo.waterfall}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.LoadFailed: | ||||
|                         msg = $"RADS: {ColoredText("loading failed", Consts.ColorRed)}\n\tmessage: {_curRadsInfo.info}\n"; | ||||
|                         msg = $"RADS: {ColoredText("loading failed", Const.ColorRed)}\n\tmessage: {_curRadsInfo.info}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.DisplayFailed: | ||||
|                         msg = $"RADS: {ColoredText("display failed", Consts.ColorRed)}\n\tmessage: {_curRadsInfo.info}\n"; | ||||
|                         msg = $"RADS: {ColoredText("display failed", Const.ColorRed)}\n\tmessage: {_curRadsInfo.info}\n"; | ||||
|                         break;   | ||||
|                     case AdStatusType.Loading: | ||||
|                         msg = $"RADS: {ColoredText("loading...", Consts.ColorYellow)}\n\tformat: {_curRadsInfo.format}\n"; | ||||
|                         msg = $"RADS: {ColoredText("loading...", Const.ColorYellow)}\n\tformat: {_curRadsInfo.format}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.Paid: | ||||
|                         msg = $"RADS: {ColoredText("get revenue", Consts.ColorGreen)}\n\trevenue: {_curRadsInfo.revenue}\n"; | ||||
|                         msg = $"RADS: {ColoredText("get revenue", Const.ColorGreen)}\n\trevenue: {_curRadsInfo.revenue}\n"; | ||||
|                         break; | ||||
|                     case AdStatusType.NotReady: | ||||
|                         msg = $"RADS: {ColoredText("not ready", Consts.ColorGray)}\n\t{ColoredText("---", Consts.ColorGray)}\n"; | ||||
|                         msg = $"RADS: {ColoredText("not ready", Const.ColorGray)}\n\t{ColoredText("---", Const.ColorGray)}\n"; | ||||
|                         break; | ||||
|                     default: | ||||
|                         msg = $"RADS: {ColoredText("other", Consts.ColorGray)}\n\tstatus: {ColoredText($"{_curRadsInfo.status}", Consts.ColorYellow)}\n"; | ||||
|                         msg = $"RADS: {ColoredText("other", Const.ColorGray)}\n\tstatus: {ColoredText($"{_curRadsInfo.status}", Const.ColorYellow)}\n"; | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|  | @ -247,13 +242,13 @@ namespace Guru | |||
|         private void AddCallbacks() | ||||
|         { | ||||
|             //----------------- Banner ----------------- | ||||
|             ADService.OnBannerStartLoad += OnBannerStartLoadEvent; | ||||
|             ADService.Instance.OnBannerStartLoad += OnBannerStartLoadEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdLoadedEvent += OnBannerAdLoadedEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += OnBannerAdLoadFailEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent += OnBannerAdRevenuePaidEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdClickedEvent += OnBannerAdClickedEvent; | ||||
|             //----------------- Interstitials ----------------- | ||||
|             ADService.OnInterstitialStartLoad += OnInterStartLoadEvent; | ||||
|             ADService.Instance.OnInterstitialStartLoad += OnInterStartLoadEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnInterAdLoadedEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnInterAdLoadFailEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent += OnInterAdDisplayFailEvent; | ||||
|  | @ -261,7 +256,7 @@ namespace Guru | |||
|             MaxSdkCallbacks.Interstitial.OnAdClickedEvent += OnInterAdClickedEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += OnInterAdHiddenEvent; | ||||
|             //----------------- Reward ----------------- | ||||
|             ADService.OnRewardedStartLoad += OnRewardedStartLoad; | ||||
|             ADService.Instance.OnRewardedStartLoad += OnRewardedStartLoad; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnRewardAdLoadedEvent; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnRewardAdLoadFailEvent; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += OnRewardAdDisplayFailEvent; | ||||
|  | @ -273,13 +268,13 @@ namespace Guru | |||
|         private void RemoveCallbacks() | ||||
|         { | ||||
|             //----------------- Banner ----------------- | ||||
|             ADService.OnBannerStartLoad -= OnBannerStartLoadEvent; | ||||
|             ADService.Instance.OnBannerStartLoad -= OnBannerStartLoadEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdLoadedEvent -= OnBannerAdLoadedEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdLoadFailedEvent -= OnBannerAdLoadFailEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent -= OnBannerAdRevenuePaidEvent; | ||||
|             MaxSdkCallbacks.Banner.OnAdClickedEvent -= OnBannerAdClickedEvent; | ||||
|             //----------------- Interstitials ----------------- | ||||
|             ADService.OnInterstitialStartLoad -= OnInterStartLoadEvent; | ||||
|             ADService.Instance.OnInterstitialStartLoad -= OnInterStartLoadEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdLoadedEvent -= OnInterAdLoadedEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent -= OnInterAdLoadFailEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent -= OnInterAdDisplayFailEvent; | ||||
|  | @ -287,7 +282,7 @@ namespace Guru | |||
|             MaxSdkCallbacks.Interstitial.OnAdClickedEvent -= OnInterAdClickedEvent; | ||||
|             MaxSdkCallbacks.Interstitial.OnAdHiddenEvent -= OnInterAdHiddenEvent; | ||||
|             //----------------- Reward ----------------- | ||||
|             ADService.OnRewardedStartLoad -= OnRewardedStartLoad; | ||||
|             ADService.Instance.OnRewardedStartLoad -= OnRewardedStartLoad; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdLoadedEvent -= OnRewardAdLoadedEvent; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent -= OnRewardAdLoadFailEvent; | ||||
|             MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent -= OnRewardAdDisplayFailEvent; | ||||
|  |  | |||
|  | @ -1,13 +1,16 @@ | |||
| 
 | ||||
| 
 | ||||
| namespace Guru.Network | ||||
| { | ||||
|     using Guru; | ||||
|     using System; | ||||
|     using UnityEngine; | ||||
| 
 | ||||
|     using System.Collections; | ||||
|      | ||||
|     public class NetworkStatusMonitor | ||||
|     { | ||||
|         private const string Tag = "[NET]"; | ||||
|          | ||||
|         private const string NETWORK_STATUS_NONE = "none"; | ||||
|         private const string NETWORK_STATUS_MOBILE = "mobile"; | ||||
|         private const string NETWORK_STATUS_WIFI = "wifi"; | ||||
|  | @ -16,6 +19,40 @@ namespace Guru.Network | |||
|         private const string NETWORK_STATUS_TETHER = "tether"; | ||||
|         private const string NETWORK_STATUS_BLUETOOTH = "bluetooth"; | ||||
|         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> | ||||
|         /// 获取网络状态 | ||||
|  | @ -23,15 +60,136 @@ namespace Guru.Network | |||
|         /// <returns></returns> | ||||
|         public string GetNetworkStatus() | ||||
|         { | ||||
|             switch (Application.internetReachability) | ||||
|             var internetReachability = Application.internetReachability; | ||||
|             switch (internetReachability) | ||||
|             { | ||||
|                 case NetworkReachability.ReachableViaLocalAreaNetwork: | ||||
|                     return NETWORK_STATUS_WIFI; | ||||
|                 case NetworkReachability.ReachableViaCarrierDataNetwork: | ||||
|                     return NETWORK_STATUS_MOBILE; | ||||
|                 case NetworkReachability.ReachableViaLocalAreaNetwork: | ||||
|                     return NETWORK_STATUS_WIFI; | ||||
|             } | ||||
|             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(); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue