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

760 lines
24 KiB
C#

namespace Guru
{
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Debug = UnityEngine.Debug;
public partial class GuruSDK: MonoBehaviour
{
public const string Version = "1.0.12.3"; //SDK_VERSION
public const string Tag = "[Guru]";
public const string ServicesConfigKey = "guru_services";
private static GuruSDK _instance;
/// <summary>
/// 单利引用
/// </summary>
public static GuruSDK Instance
{
get
{
if(null == _instance)
{
_instance = CreateInstance();
}
return _instance;
}
}
private GuruSDKInitConfig _initConfig;
private Action<bool> _onCompleteCallback;
private static GuruSDKModel _model;
internal static GuruSDKInitConfig InitConfig => Instance._initConfig;
internal 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;
/// <summary>
/// Debug Mode
/// </summary>
public static bool IsDebugMode
{
get
{
#if UNITY_EDITOR || DEBUG
return true;
#endif
return _isDebugEnabled;
}
}
/// <summary>
/// 初始化成功标志位
/// </summary>
public static bool IsInitialSuccess { get; private set; } = false;
/// <summary>
/// Firebase 就绪标志位
/// </summary>
public static bool IsFirebaseReady { get; private set; } = false;
/// <summary>
/// 服务就绪标志位
/// </summary>
public static bool IsServiceReady { get; private set; } = false;
#region 初始化
private static GuruSDK CreateInstance()
{
var go = new GameObject(nameof(GuruSDK));
DontDestroyOnLoad(go);
_instance = go.AddComponent<GuruSDK>();
return _instance;
}
public static GuruSDKInitConfig BuildConfig(
bool useCustomConsent = false,
bool autoLoadAds = true,
bool iapEnabled = true,
bool autoRecordFinishedLevels = true,
bool debugMode = false,
bool isBuyNoAds = false,
Action<string> onDeeplinkCallback = null,
string bannerColor = "#00000000",
Dictionary<string, object> defaultRemoteData = null,
byte[] googleKeys = null,
byte[] appleRootCerts = null)
{
var config = GuruSDKInitConfig.Build(useCustomConsent, autoLoadAds, iapEnabled,
autoRecordFinishedLevels, isBuyNoAds, onDeeplinkCallback, bannerColor,
debugMode, defaultRemoteData, googleKeys, appleRootCerts);
return config;
}
public static void Init(Action<bool> onComplete)
{
Init(GuruSDKInitConfig.Build(), onComplete);
}
public static void Init(GuruSDKInitConfig config, Action<bool> onComplete)
{
_initTime = DateTime.Now.ToUniversalTime();
// ----- First Open Time -----
// SetFirstOpenTime(GetFirstOpenTime()); // FirstOpenTime
LogI($"#1 ---- Guru SDK [{Version}] ----\n{config.ToString()}");
Instance.StartWithConfig(config, onComplete);
}
private static string GetFirstOpenTime()
{
string firstOpenTime = IPMConfig.FirstOpenTime;
if (string.IsNullOrEmpty(firstOpenTime))
{
firstOpenTime = TimeUtil.GetCurrentTimeStamp().ToString();
IPMConfig.FirstOpenTime = firstOpenTime;
}
return firstOpenTime;
}
/// <summary>
/// 启动SDK
/// </summary>
/// <param name="config"></param>
/// <param name="onComplete"></param>
private void StartWithConfig(GuruSDKInitConfig config, Action<bool> onComplete)
{
Model.PropBLevel.OnValueChanged += OnBLevelChanged;
Model.PropBPlay.OnValueChanged += OnBPlayChanged;
IsInitialSuccess = false;
_initConfig = config;
_onCompleteCallback = onComplete;
_isDebugEnabled = config.DebugMode;
InitAssets();
}
private void InitAssets()
{
InitUpdaters(); // Updaters
InitThreadHandler(); // 初始化线程处理器
}
void Start()
{
//---- Start All tools ----
LogI($"#2 --- InitFirebase ---");
//---------- Start Firebase ------------
FirebaseUtil.onInitComplete += OnFirebaseReady;
FirebaseUtil.OnUserAuthResult += OnUserAuthResult;
FirebaseUtil.OnFirebaseAuthResult += OnFirebaseAuthResult;
FirebaseUtil.InitFirebase(null, InitConfig.OnDeeplinkCallback); // 确保所有的逻辑提前被调用到
LogI($"#2.1 --- InitFacebook ---");
//---------- Start Facebook ------------
FBService.Instance.StartService();
LogI($"#2.2 --- Call SDK init complete -> callback: { (_onCompleteCallback == null ? "Null" : _onCompleteCallback.ToString()) } ---");
IsInitialSuccess = true;
_onCompleteCallback?.Invoke(true);
}
private void OnUserAuthResult(bool success)
{
if (success && string.IsNullOrEmpty(IPMConfig.IPM_UID))
{
success = false;
}
Callbacks.SDK._onUserAuthResult?.Invoke(success);
if (success)
{
if (GuruIAP.Instance != null)
{
GuruIAP.Instance.SetUID(UID);
GuruIAP.Instance.SetUUID(UUID);
}
UpdateAllUserProperties(); // 同步所有用户属性打点
}
}
private void OnFirebaseAuthResult(bool success)
{
Callbacks.SDK._onFirebaseAuthResult?.Invoke(success);
}
/// <summary>
/// 开始各种组件初始化
/// </summary>
private void OnFirebaseReady(bool success)
{
FirebaseUtil.onInitComplete -= OnFirebaseReady;
LogI($"#3 --- On FirebaseDeps:{success} ---");
IsFirebaseReady = success;
Callbacks.SDK._onFirebaseReady?.Invoke(success);
// LogFirebaseDeps(success);
LogI($"#3.5 --- Call InitRemoteConfig ---");
// 开始Remote Manager初始化
RemoteConfigManager.Init(BuildDefaultRemoteData(_initConfig.DefaultRemoteData));
RemoteConfigManager.OnFetchCompleted += OnFetchRemoteCallback;
LogI($"#4 --- Apply remote services config ---");
// 根据缓存的云控配置来初始化参数
InitAllServices();
LogI($"#5 --- sync sdk time ---");
var sp = DateTime.Now.ToUniversalTime() - _initTime;
LogSDKInitTime(sp.TotalSeconds);
// 上报所有初始化用户属性
UpdateAllUserProperties();
}
/// <summary>
/// 注入云控参数基础数据
/// </summary>
/// <param name="dict"></param>
/// <returns></returns>
private Dictionary<string, object> BuildDefaultRemoteData(Dictionary<string, object> dict)
{
if (dict == null) dict = new Dictionary<string, object>(3);
// 注入默认的 Services 配置值
string json = Model.LoadDefaltServicesConfigJson();
if (!string.IsNullOrEmpty(json)) dict[ServicesConfigKey] = json;
return dict;
}
/// <summary>
/// 拉取云控参数完成
/// </summary>
/// <param name="success"></param>
private void OnFetchRemoteCallback(bool success)
{
LogI($"#6 --- Remote fetch complete: {success} ---");
ABTestManager.Init(); // 启动AB测试解析器
Callbacks.Remote._onRemoteFetchComplete?.Invoke(success);
}
private void Update()
{
UpdateAllUpdates(); // 驱动所有的更新器
}
#endregion
#region App Remote Update
/// <summary>
/// Apply Cloud guru-service configs for sdk assets
/// </summary>
private void InitAllServices()
{
//-------- SetUserProperties ---------
InitUserProperties();
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 GuruSttings ---");
// 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 =>
{
UnityEngine.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)
{
LogException("[IAP] GoogleKeys is null when using IAPService! Integration failed. App will Exit");
}
InitIAP(UID, _initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
}, ex =>
{
UnityEngine.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.SuccessLevelId); // 启动Keyword管理器
}, ex =>
{
UnityEngine.Debug.LogError($"--- ERROR on Keywords: {ex.Message}");
});
}
#if UNITY_IOS
if (appleReview)
{
// StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程
Try(() =>
{
LogI($"#4.5.0 --- StartAppleReviewFlow ---");
StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程
}, ex =>
{
Debug.LogError($"--- ERROR on StartAppleReviewFlow: {ex.Message}");
});
return;
}
#endif
//----------- Set Consent ----------------
if (!InitConfig.UseCustomConsent && !appleReview)
{
// LogI($"--- #3 Start Consent Flow ---");
// StartConsentFlow();
Try(() =>
{
LogI($"#4.5 --- StartConsentFlow ---");
StartConsentFlow();
}, ex =>
{
UnityEngine.Debug.LogError($"--- ERROR on StartConsentFlow: {ex.Message}");
});
}
IsServiceReady = true;
// 中台服务初始化结束
Callbacks.SDK._onGuruServiceReady?.Invoke();
#if UNITY_ANDROID
// Android 命令行调试
StartAndroidDebugCmds();
#endif
}
/// <summary>
/// Get the guru-service cloud config value;
/// User can fetch the cloud guru-service config by using Custom Service Key
/// </summary>
/// <returns></returns>
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;
UnityEngine.Debug.Log($"{Tag} --- No remote data found with: {key} -> Using default key {ServicesConfigKey} and local data!!!");
}
if (!string.IsNullOrEmpty(json))
{
return JsonParser.ToObject<GuruServicesConfig>(json);
}
return null;
}
private void Try(Action method, Action<Exception> onException = null, Action onFinal = null)
{
if (method == null) return;
try
{
method.Invoke();
}
catch (Exception ex)
{
LogException(ex);
// ignored
onException?.Invoke(ex);
}
finally
{
// Finally
onFinal?.Invoke();
}
}
#endregion
#region Apple 审核流程逻辑
#if UNITY_IOS
private void StartAppleReviewFlow()
{
CheckAttStatus();
}
#endif
#endregion
#region 数据
private void OnBLevelChanged(int blevel)
{
SetUserBLevel(blevel);
}
private void OnBPlayChanged(int bplay)
{
SetUserBPlay(bplay);
}
#endregion
#region Logging
internal static void LogI(object message)
{
UnityEngine.Debug.Log($"{Tag} {message}");
}
internal static void LogW(object message)
{
UnityEngine.Debug.LogWarning($"{Tag} {message}");
}
internal static void LogE(object message)
{
UnityEngine.Debug.LogError($"{Tag} {message}");
}
internal static void LogException(string message)
{
LogException( new Exception($"{Tag} {message}"));
}
internal static void LogException(Exception e)
{
UnityEngine.Debug.LogException(e);
}
/// <summary>
/// 上报崩溃信息
/// </summary>
/// <param name="message"></param>
public static void Report(string message)
{
Analytics.LogCrashlytics(message, false);
}
/// <summary>
/// 上报异常
/// </summary>
/// <param name="message"></param>
public static void ReportException(string message)
{
Analytics.LogCrashlytics(message);
}
/// <summary>
/// 上报异常 Exception
/// </summary>
/// <param name="ex"></param>
public static void ReportException(Exception ex)
{
Analytics.LogCrashlytics(ex);
}
#endregion
#region 生命周期
/// <summary>
/// 暂停时处理
/// </summary>
/// <param name="paused"></param>
private void OnAppPauseHandler(bool paused)
{
if(paused) Model.Save(true); // 强制保存数据
Callbacks.App._onAppPaused?.Invoke(paused);
}
private void OnApplicationPause(bool paused)
{
OnAppPauseHandler(paused);
}
private void OnApplicationFocus(bool hasFocus)
{
OnAppPauseHandler(!hasFocus);
}
private void OnApplicationQuit()
{
Model.Save(true);
Callbacks.App._onAppQuit?.Invoke();
}
#endregion
#region 延迟处理
/// <summary>
/// 启动协程
/// </summary>
/// <param name="enumerator"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 延时执行
/// </summary>
/// <param name="seconds"></param>
/// <param name="callback"></param>
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<IUpdater> _updaterRunningList;
private List<IUpdater> _updaterRemoveList;
private void InitUpdaters()
{
_updaterRunningList = new List<IUpdater>(20);
_updaterRemoveList = new List<IUpdater>(20);
}
private void UpdateAllUpdates()
{
int i = 0;
IUpdater updater;
// ---- Updater Trigger ----
if (_updaterRunningList.Count > 0)
{
i = 0;
while (i < _updaterRunningList.Count)
{
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();
}
}
/// <summary>
/// 注册帧更新器
/// </summary>
/// <param name="updater"></param>
public static void RegisterUpdater(IUpdater updater)
{
Instance.AddUpdater(updater);
updater.Start();
}
private void AddUpdater(IUpdater updater)
{
if (_updaterRunningList == null) _updaterRunningList = new List<IUpdater>(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 = 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));
}) as DeviceInfoUploadRequest;
if (request != null)
{
request.SetPushEnabled(enabled);
request.Send();
}
}
#endregion
}
}