namespace Guru { using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using Debug = UnityEngine.Debug; using Guru.Network; using System.Linq; public partial class GuruSDK: MonoBehaviour { // SDK_VERSION public const string Version = "1.1.0"; // Const private const string Tag = "[Guru]"; public const string ServicesConfigKey = "guru_services"; private static GuruSDK _instance; /// /// 单利引用 /// public static GuruSDK Instance { get { if(null == _instance) { _instance = CreateInstance(); } return _instance; } } private GuruSDKInitConfig _initConfig; private static GuruSDKInitConfig InitConfig => Instance._initConfig; private static GuruSDKModel Model => GuruSDKModel.Instance; private static GuruServicesConfig _appServicesConfig; private static GuruSettings _guruSettings; private static GuruSettings GuruSettings { get { if (_guruSettings == null) _guruSettings = GuruSettings.Instance; return _guruSettings; } } private static DateTime _initTime; private static bool _isDebugEnabled = false; /// /// Debug Mode /// public static bool IsDebugMode { get { #if UNITY_EDITOR || DEBUG return true; #endif return _isDebugEnabled; } } /// /// 初始化成功标志位 /// public static bool IsInitialSuccess { get; private set; } = false; /// /// Firebase 就绪标志位 /// public static bool IsFirebaseReady { get; private set; } = false; /// /// 服务就绪标志位 /// public static bool IsServiceReady { get; private set; } = false; private Firebase.Auth.FirebaseUser _firebaseUser; [Obsolete("获取 FirebaseUser 的属性接口即将废弃,请改用 来异步获取该属性")] public static Firebase.Auth.FirebaseUser FirebaseUser => Instance?._firebaseUser ?? null; #region 初始化 private static GuruSDK CreateInstance() { var go = new GameObject(nameof(GuruSDK)); DontDestroyOnLoad(go); _instance = go.AddComponent(); return _instance; } // TODO : 下个版本需要将 整个 GuruSDK 做功能性的拆分 public static void Init(Action onComplete) { Init(GuruSDKInitConfig.Builder().Build(), onComplete); } public static void Init(GuruSDKInitConfig config, Action onComplete) { _initTime = DateTime.UtcNow; // ----- First Open Time ----- // SetFirstOpenTime(GetFirstOpenTime()); // FirstOpenTime LogI($"#1 ---- Guru SDK [{Version}] ----\n{config}"); Instance.StartWithConfig(config, onComplete); } /// /// 启动SDK /// /// /// private void StartWithConfig(GuruSDKInitConfig config, Action onComplete) { IsInitialSuccess = false; _initConfig = config; _isDebugEnabled = config.DebugMode; if (config.EnableDebugLogEvent) Analytics.EnableDebugAnalytics = true; // 允许 Debug 模式下打点 if (!config.AutoNotificationPermission) FirebaseUtil.SetAutoFetchFcmToken(false); // 不允许自动启动获取 FCM Token InitUpdaters(); // Updaters InitThreadHandler(); // 初始化线程处理器 InitServices(); // 初始化所有的服务 InitNetworkMonitor(); // 网络状态 onComplete?.Invoke(true); } private void InitServices() { //---------- Start Analytics ------------ LogI($"#1.1 ---- Init Analytics ----"); // 初始化打点类 Analytics.Init(); // 从 Model 中注入打点属性初始值 Analytics.SetFirstOpenTime(IPMConfig.FIRST_OPEN_TIME); Analytics.SetIsIapUser(Model.IsIapUser); // Analytics.SetBLevel(Model.BLevel); // Analytics.SetBPlay(Model.BPlay); //---- Start All tools ---- LogI($"#2 --- InitFirebase ---"); //---------- Start Firebase ------------ StartFirebaseService(); LogI($"#2.1 --- InitFacebook ---"); //---------- Start Facebook ------------ FBService.Instance.StartService(Analytics.OnFBInitComplete); IsInitialSuccess = true; } /// /// 注入云控参数基础数据 /// /// private string LoadDefaultGuruServiceJson() { // 加载本地 Services 配置值 var txtAsset = Resources.Load(ServicesConfigKey); if (txtAsset != null) { return txtAsset.text; } return ""; } /// /// 拉取云控参数完成 /// /// private void OnFetchRemoteCallback(bool success) { LogI($"#6 --- Remote fetch complete: {success} ---"); ABTestManager.Init(); // 启动AB测试解析器 Callbacks.Remote.InvokeOnRemoteFetchComplete(success); } private void Update() { UpdateAllUpdates(); // 驱动所有的更新器 } #endregion #region App Remote Update /// /// Apply Cloud guru-service configs for sdk assets /// private void InitAllGuruServices() { // -------- Init Analytics --------- SetSDKEventPriority(); // -------- Init Notification ----------- InitNotiPermission(); bool useKeywords = false; bool useIAP = _initConfig.IAPEnabled; bool appleReview = false; // bool enableAnaErrorLog = false; //----------- Set GuruServices ---------------- var services = GetRemoteServicesConfig(); if (services != null) { _appServicesConfig = services; useKeywords = _appServicesConfig.KeywordsEnabled(); // 使用初始化配置中的 IAPEnable来联合限定, 如果本地写死关闭则不走云控开启 useIAP = _initConfig.IAPEnabled && _appServicesConfig.IsIAPEnabled(); // enableAnaErrorLog = _appServicesConfig.EnableAnaErrorLog(); Try(() => { LogI($"#4.1 --- Start apply services ---"); //---------------------------------------------------------------- // 自打点设置错误上报 // if(enableAnaErrorLog) GuruAnalytics.EnableErrorLog = true; // adjust 事件设置 if (null != _appServicesConfig.adjust_settings && null != GuruSettings) { // 更新 Adjust Tokens GuruSettings.UpdateAdjustTokens( _appServicesConfig.adjust_settings.AndroidToken(), _appServicesConfig.adjust_settings.iOSToken()); // 更新 Adjust Events GuruSettings.UpdateAdjustEvents(_appServicesConfig.adjust_settings.events); } LogI($"#4.2 --- Start GuruSettings ---"); // GuruSettings 设置 if (null != _appServicesConfig.app_settings) { if (_appServicesConfig.Tch02Value() > 0) { Analytics.EnableTch02Event = true; Analytics.SetTch02TargetValue(_appServicesConfig.Tch02Value()); } // 设置获取设备 UUID 的方法 if (_appServicesConfig.UseUUID()) { IPMConfig.UsingUUID = true; // 开始使用 UUID 作为 DeviceID 标识 } #if UNITY_IOS // 苹果审核标志位 appleReview = _appServicesConfig.IsAppReview(); #endif if (null != GuruSettings) { // 更新和升级 GuruSettings 对应的值 GuruSettings.UpdateAppSettings( _appServicesConfig.app_settings.bundle_id, _appServicesConfig.fb_settings?.fb_app_id ?? "", _appServicesConfig.app_settings.support_email, _appServicesConfig.app_settings.privacy_url, _appServicesConfig.app_settings.terms_url, _appServicesConfig.app_settings.android_store, _appServicesConfig.app_settings.ios_store, _appServicesConfig.parameters?.using_uuid ?? false, _appServicesConfig.parameters?.cdn_host ?? ""); _appBundleId = _appServicesConfig.app_settings.bundle_id; // 配置预设的 BundleId } } //--------------------------------- }, ex => { Debug.LogError($"--- ERROR on apply services: {ex.Message}"); }); } //----------- Set IAP ---------------- if (useIAP) { // InitIAP(_initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP Try(() => { LogI($"#4.3 --- Start IAP ---"); if (_initConfig.GoogleKeys == null || _initConfig.AppleRootCerts == null) { LogEx("[IAP] GoogleKeys is null when using IAPService! Integration failed. App will Exit"); } InitIAP(UID, _initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP }, ex => { Debug.LogError($"--- ERROR on useIAP: {ex.Message}"); }); } //----------- Set Keywords ---------------- if (useKeywords) { // KeywordsManager.Install(Model.IsIAPUser, Model.SuccessLevelId); // 启动Keyword管理器 Try(() => { LogI($"#4.4 --- Start Keywords ---"); KeywordsManager.Install(Model.IsIapUser, Model.BLevel); // 启动Keyword管理器 }, ex => { Debug.LogError($"--- ERROR on Keywords: {ex.Message}"); }); } #if UNITY_IOS if (appleReview) { // StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程 Try(() => { LogI($"#4.5 --- StartAppleReviewFlow ---"); StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程 }, ex => { Debug.LogError($"--- ERROR on StartAppleReviewFlow: {ex.Message}"); }); return; } #endif //----------- Set Consent ---------------- if (!InitConfig.UseCustomConsent && !appleReview) { LogI($"#4.6 --- Start Consent Flow ---"); Try(StartConsentFlow, ex => { Debug.LogError($"--- ERROR on StartConsentFlow: {ex.Message}"); }); } #if UNITY_ANDROID LogI($"#5.1 --- Android StartAndroidDebug Cmd lines---"); // Android 命令行调试 StartAndroidDebugCmds(); #endif IsServiceReady = true; // 中台服务初始化结束 Callbacks.SDK.InvokeOnGuruServiceReady(); } /// /// Get the guru-service cloud config value; /// User can fetch the cloud guru-service config by using Custom Service Key /// /// private GuruServicesConfig GetRemoteServicesConfig() { string defaultJson = GetRemoteString(ServicesConfigKey); bool useCustomKey = false; string key = ServicesConfigKey; if (!string.IsNullOrEmpty(_initConfig.CustomServiceKey)) { key = _initConfig.CustomServiceKey; useCustomKey = true; } var json = GetRemoteString(key); // Cloud cached data if (string.IsNullOrEmpty(json) && useCustomKey && !string.IsNullOrEmpty(defaultJson)) { // No remote data fetched from cloud, should use default values json = defaultJson; Debug.Log($"{Tag} --- No remote data found with: {key} -> Using default key {ServicesConfigKey} and local data!!!"); } if (!string.IsNullOrEmpty(json)) { return JsonParser.ToObject(json); } return null; } private void Try(Action method, Action onException = null, Action onFinal = null) { if (method == null) return; try { method.Invoke(); } catch (Exception ex) { LogEx(ex); // ignored onException?.Invoke(ex); } finally { // Finally onFinal?.Invoke(); } } #endregion #region Apple 审核流程逻辑 #if UNITY_IOS private void StartAppleReviewFlow() { CheckAttStatus(); } #endif #endregion #region Logging private static void LogI(object message) { Debug.Log($"{Tag} {message}"); } private static void LogW(object message) { Debug.LogWarning($"{Tag} {message}"); } private static void LogE(object message) { Debug.LogError($"{Tag} {message}"); } private static void LogEx(string message) { LogEx( new Exception($"{Tag} {message}")); } private static void LogEx(Exception e) { Debug.LogException(e); } /// /// 上报崩溃信息 /// /// public static void Report(string message) { Analytics.LogCrashlytics(message, false); } /// /// 上报异常 /// /// public static void ReportException(string message) { Analytics.LogCrashlytics(message); } /// /// 上报异常 Exception /// /// public static void ReportException(Exception ex) { Analytics.LogCrashlytics(ex); } #endregion #region 生命周期 /// /// 暂停时处理 /// /// private void OnAppPauseHandler(bool paused) { if(paused) Model.Save(true); // 强制保存数据 Callbacks.App.InvokeOnAppPaused(paused); } private void OnApplicationPause(bool paused) { OnAppPauseHandler(paused); } private void OnApplicationFocus(bool hasFocus) { OnAppPauseHandler(!hasFocus); } private void OnApplicationQuit() { Model.Save(true); Callbacks.App.InvokeOnAppQuit(); } #endregion #region 延迟处理 /// /// 启动协程 /// /// /// public static Coroutine DoCoroutine(IEnumerator enumerator) { return Instance != null ? Instance.StartCoroutine(enumerator) : null; } public static void KillCoroutine(Coroutine coroutine) { if(coroutine != null) Instance.StopCoroutine(coroutine); } /// /// 延时执行 /// /// /// public static Coroutine Delay(float seconds, Action callback) { return DoCoroutine(Instance.OnDelayCall(seconds, callback)); } private IEnumerator OnDelayCall(float delay, Action callback) { if (delay > 0) { yield return new WaitForSeconds(delay); } else { yield return null; } callback?.Invoke(); } #endregion #region 帧更新 Updater private List _updaterRunningList; private List _updaterRemoveList; private void InitUpdaters() { _updaterRunningList = new List(20); _updaterRemoveList = new List(20); } private void UpdateAllUpdates() { int i = 0; // ---- Updater Trigger ---- if (_updaterRunningList.Count > 0) { i = 0; while (i < _updaterRunningList.Count) { var updater = _updaterRunningList[i]; if (updater != null) { if (updater.State == UpdaterState.Running) { updater.OnUpdate(); } else if(updater.State == UpdaterState.Kill) { _updaterRemoveList.Add(updater); } } else { _updaterRunningList.RemoveAt(i); i--; } i++; } } if (_updaterRemoveList.Count > 0) { i = 0; while (i < _updaterRemoveList.Count) { RemoveUpdater(_updaterRemoveList[i]); i++; } _updaterRemoveList.Clear(); } } /// /// 注册帧更新器 /// /// public static void RegisterUpdater(IUpdater updater) { Instance.AddUpdater(updater); updater.Start(); } private void AddUpdater(IUpdater updater) { if (_updaterRunningList == null) _updaterRunningList = new List(20); _updaterRunningList.Add(updater); } private void RemoveUpdater(IUpdater updater) { if (_updaterRunningList != null && updater != null) { _updaterRunningList.Remove(updater); } } #endregion #region 中台推送管理 private static int _messageRetry = 0; public static void SetPushNotificationEnabled(bool enabled) { DeviceInfoUploadRequest request = (DeviceInfoUploadRequest) new DeviceInfoUploadRequest() .SetRetryTimes(1) .SetSuccessCallBack(() => { _messageRetry = 0; Debug.Log($"[SDK] --- Set Push Enabled: {enabled} success"); }) .SetFailCallBack(() => { double retryDelay = Math.Pow(2, _messageRetry); _messageRetry++; CoroutineHelper.Instance.StartDelayed((float)retryDelay, ()=> SetPushNotificationEnabled(enabled)); }); if (request == null) return; request.SetPushEnabled(enabled); request.Send(); } #endregion #region Deeplink /// /// 添加回调链接 /// /// private void OnDeeplinkCallback(string deeplink) { Callbacks.SDK.InvokeDeeplinkCallback(deeplink); // 尝试调用回调 } #endregion #region 网络状态上报 private NetworkStatusMonitor _networkStatusMonitor; private string _lastNetworkStatus; private void InitNetworkMonitor() { _networkStatusMonitor = new NetworkStatusMonitor(Analytics.SetNetworkStatus, lastStatus => { LogEvent("guru_offline", new Dictionary() { ["from"] = lastStatus }); }); } /// /// 获取当前的网络状态 /// /// private string GetNetworkStatus() => _networkStatusMonitor.GetNetworkStatus(); #endregion #region Firebase 服务 /// /// 启动 Firebase 服务 /// private void StartFirebaseService() { FirebaseUtil.Init(OnFirebaseDepsCheckResult, OnGetFirebaseId, OnGetGuruUID, OnFirebaseLoginResult); // 确保所有的逻辑提前被调用到 } private void OnGetGuruUID(bool success) { if (success) { Model.UserId = IPMConfig.IPM_UID; if (GuruIAP.Instance != null) { GuruIAP.Instance.SetUID(UID); GuruIAP.Instance.SetUUID(UUID); } // 自打点设置用户 ID Analytics.SetUid(UID); // Crashlytics 设置 uid CrashlyticsAgent.SetUserId(UID); // 上报所有的事件 Analytics.ShouldFlushGuruEvents(); } Callbacks.SDK.InvokeOnGuruUserAuthResult(success); } private void OnGetFirebaseId(string fid) { // 初始化 Adjust 服务 InitAdjustService(fid, InitConfig.OnAdjustDeeplinkCallback); // 初始化自打点 Analytics.InitGuruAnalyticService(fid); //---------- Event SDK Info ------------ LogI($"#6.0 --- SDK is ready, report Info ---"); LogSDKInfo((DateTime.UtcNow - _initTime).TotalSeconds); } // TODO: 需要之后用宏隔离应用和实现 // Auth 登录认证 private void OnFirebaseLoginResult(bool success, Firebase.Auth.FirebaseUser firebaseUser) { _firebaseUser = firebaseUser; Callbacks.SDK.InvokeOnFirebaseAuthResult(success, firebaseUser); } /// /// 开始各种组件初始化 /// private void OnFirebaseDepsCheckResult(bool success) { LogI($"#3 --- On FirebaseDeps: {success} ---"); IsFirebaseReady = success; Callbacks.SDK.InvokeOnFirebaseReady(success); Analytics.OnFirebaseInitCompleted(); LogI($"#3.5 --- Call InitRemoteConfig ---"); // 开始Remote Manager初始化 var defaultGuruServiceJson = LoadDefaultGuruServiceJson(); var dict = _initConfig.DefaultRemoteData.ToDictionary( entry => entry.Key, entry => entry.Value); if (!string.IsNullOrEmpty(defaultGuruServiceJson)) { dict[ServicesConfigKey] = defaultGuruServiceJson; } RemoteConfigManager.Init(dict); RemoteConfigManager.OnFetchCompleted += OnFetchRemoteCallback; LogI($"#4 --- Apply remote services config ---"); // 根据缓存的云控配置来初始化参数 InitAllGuruServices(); } #endregion #region Adjust服务 /// /// 启动 Adjust 服务 /// private static void InitAdjustService(string firebaseId, Action onDeeplinkCallback = null) { // 启动 AdjustService string appToken = GuruSettings.Instance.AdjustSetting?.GetAppToken() ?? ""; string fbAppId = GuruSettings.Instance.IPMSetting.FacebookAppId; // if (!string.IsNullOrEmpty(IPMConfig.ADJUST_ID)) // Analytics.SetAdjustId(IPMConfig.ADJUST_ID); // 二次启动后,若有值则立即上报属性 AdjustService.Instance.Start(appToken, fbAppId, firebaseId, DeviceId, OnAdjustInitComplete, onDeeplinkCallback ,OnGetGoogleAdId ); } /// /// Adjust 初始化结束 /// /// /// /// private static void OnAdjustInitComplete(string adjustId, string idfv, string idfa) { Debug.Log($"{Tag} --- OnAdjustInitComplete: adjustId:{adjustId} idfv:{idfv} idfa:{idfa}"); // 获取 ADID if (string.IsNullOrEmpty(adjustId)) adjustId = "not_set"; if (string.IsNullOrEmpty(idfv)) idfv = "not_set"; if (string.IsNullOrEmpty(idfa)) idfa = "not_set"; IPMConfig.ADJUST_ID = adjustId; IPMConfig.ADJUST_IDFV = idfv; IPMConfig.ADJUST_IDFA = idfa; Analytics.SetAdjustId(adjustId); Analytics.SetIDFV(idfv); Analytics.SetIDFA(idfa); Analytics.OnAdjustInitComplete(); } private static void OnGetGoogleAdId(string googleAdId) { Debug.Log($"{Tag} --- OnGetGoogleAdId: {googleAdId}"); // IPMConfig.ADJUST_GOOGLE_ADID = googleAdId; Analytics.SetGoogleAdId(googleAdId); } #endregion } }