558 lines
17 KiB
C#
558 lines
17 KiB
C#
namespace Guru
|
|
{
|
|
using UnityEngine;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
|
|
public partial class GuruSDK: MonoBehaviour
|
|
{
|
|
public const string Version = "1.0.7";
|
|
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;
|
|
|
|
#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,
|
|
Dictionary<string, object> defaultRemoteData = null,
|
|
byte[] googleKeys = null,
|
|
byte[] appleRootCerts = null)
|
|
{
|
|
var config = GuruSDKInitConfig.Build(useCustomConsent, autoLoadAds, iapEnabled,
|
|
autoRecordFinishedLevels, 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();
|
|
LogI($"---- Guru SDK init ----\n{config.ToString()}");
|
|
Instance.StartWithConfig(config, onComplete);
|
|
}
|
|
|
|
|
|
/// <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;
|
|
|
|
GuruRepoter.Install(); // Install Crashlytics Logger
|
|
//--- 之后的逻辑放在 Start 方法内 ---
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
//---- Init All tools ----
|
|
LogI($"--- InitFirebase ---");
|
|
//---------- Start Firebase ------------
|
|
FirebaseUtil.InitFirebase(OnFirebaseReady);
|
|
// FirebaseUtil.OnFetchRemoteSuccess+= OnFetchRemoteCallback;
|
|
//---------- Start Facebook ------------
|
|
FBService.Instance.StartService();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 开始各种组件初始化
|
|
/// </summary>
|
|
private void OnFirebaseReady()
|
|
{
|
|
LogI($"--- OnFirebaseReady ---");
|
|
IsInitialSuccess = true;
|
|
|
|
if(!string.IsNullOrEmpty(IPMConfig.IPM_UID)) SetUID(IPMConfig.IPM_UID);
|
|
LogI($"--- UID:{IPMConfig.IPM_UID} ---");
|
|
|
|
LogI($"--- Init RemoteConfig ---");
|
|
// 开始Remote Manager初始化
|
|
RemoteConfigManager.Init(BuildDefaultRemoteData(_initConfig.DefaultRemoteData));
|
|
RemoteConfigManager.OnFetchCompleted += OnFetchRemoteCallback;
|
|
|
|
// 延迟执行 SDK 回调
|
|
Delay(0, () =>
|
|
{
|
|
LogI($"--- SDK Init Complete -> Callback: { (_onCompleteCallback == null ? "Null" : "NotNull") } ---");
|
|
_onCompleteCallback?.Invoke(true);
|
|
});
|
|
|
|
LogI($"--- Apply online services config ---");
|
|
// 根据上次的云控配置来初始化参数
|
|
SetupServicesConfig();
|
|
|
|
var sp = DateTime.Now.ToUniversalTime() - _initTime;
|
|
LogSDKInitTime(sp.TotalSeconds);
|
|
}
|
|
|
|
/// <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($"--- Remote fetch complete: {success} ---");
|
|
ABTestManager.Init(); // 启动AB测试解析器
|
|
Callbacks.Remote._onRemoteFetchComplete?.Invoke(success);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region App Remote Update
|
|
|
|
private void SetupServicesConfig()
|
|
{
|
|
bool useKeywords = false;
|
|
bool useIAP = true;
|
|
bool appleReview = false;
|
|
|
|
var services = GetRemoteServicesConfig();
|
|
if (services != null)
|
|
{
|
|
_appServicesConfig = services;
|
|
useKeywords = _appServicesConfig.KeywordsEnabled();
|
|
useIAP = _appServicesConfig.IsIAPEnabled();
|
|
|
|
|
|
Try(() =>
|
|
{
|
|
LogI($"--- Init apply services ---");
|
|
//--------------------------------
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
//---------------------------------
|
|
}, ex =>
|
|
{
|
|
Debug.LogError($"--- ERROR on apply services: {ex.Message}");
|
|
});
|
|
|
|
|
|
}
|
|
|
|
if (useIAP)
|
|
{
|
|
// InitIAP(_initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
|
|
Try(() =>
|
|
{
|
|
LogI($"--- Init IAP ---");
|
|
InitIAP(_initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
|
|
}, ex =>
|
|
{
|
|
Debug.LogError($"--- ERROR on useIAP: {ex.Message}");
|
|
});
|
|
|
|
}
|
|
|
|
if (useKeywords)
|
|
{
|
|
// KeywordsManager.Install(Model.IsIAPUser, Model.SuccessLevelCount); // 启动Keyword管理器
|
|
Try(() =>
|
|
{
|
|
LogI($"--- Init Keywords ---");
|
|
KeywordsManager.Install(Model.IsIAPUser, Model.SuccessLevelCount); // 启动Keyword管理器
|
|
}, ex =>
|
|
{
|
|
Debug.LogError($"--- ERROR on Keywords: {ex.Message}");
|
|
});
|
|
}
|
|
|
|
#if UNITY_IOS
|
|
if (appleReview)
|
|
{
|
|
// StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程
|
|
Try(() =>
|
|
{
|
|
LogI($"--- StartAppleReviewFlow ---");
|
|
StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程
|
|
}, ex =>
|
|
{
|
|
Debug.LogError($"--- ERROR on StartAppleReviewFlow: {ex.Message}");
|
|
});
|
|
return;
|
|
}
|
|
#endif
|
|
if (!InitConfig.UseCustomConsent && !appleReview)
|
|
{
|
|
// LogI($"--- #3 Start Consent Flow ---");
|
|
// StartConsentFlow();
|
|
Try(() =>
|
|
{
|
|
LogI($"--- #1. StartConsentFlow ---");
|
|
StartConsentFlow();
|
|
}, ex =>
|
|
{
|
|
Debug.LogError($"--- ERROR on StartConsentFlow: {ex.Message}");
|
|
});
|
|
}
|
|
}
|
|
|
|
private GuruServicesConfig GetRemoteServicesConfig()
|
|
{
|
|
var json = GetRemoteString(ServicesConfigKey);
|
|
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);
|
|
}
|
|
|
|
public static string UID => _model?.UserId ?? IPMConfig.IPM_UID;
|
|
|
|
public static string DeviceId => IPMConfig.IPM_DEVICE_ID ?? ""; // TODO: change it to _model member later.
|
|
|
|
public static string PushToken => IPMConfig.IPM_PUSH_TOKEN ?? ""; // TODO: change it to _model member later.
|
|
|
|
public static string SupportEmail => GuruSettings.SupportEmail ?? "";
|
|
|
|
public static string StoreUrl
|
|
{
|
|
get
|
|
{
|
|
string url = "";
|
|
#if UNITY_EDITOR
|
|
url = "https://test@com.guru.ai";
|
|
#elif UNITY_ANDROID
|
|
url = GuruSettings?.AndroidStoreUrl ?? "";
|
|
#elif UNITY_IOS
|
|
url = GuruSettings?.IOSStoreUrl ?? "";
|
|
#endif
|
|
return url;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Misc
|
|
|
|
/// <summary>
|
|
/// 打开页面
|
|
/// </summary>
|
|
/// <param name="url"></param>
|
|
public static void OpenURL(string url)
|
|
{
|
|
GuruWebview.OpenPage(url);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Logging
|
|
|
|
internal static void LogI(object message)
|
|
{
|
|
Debug.Log($"{Tag} {message}");
|
|
}
|
|
|
|
internal static void LogW(object message)
|
|
{
|
|
Debug.LogWarning($"{Tag} {message}");
|
|
}
|
|
|
|
internal static void LogE(object message)
|
|
{
|
|
Debug.LogError($"{Tag} {message}");
|
|
}
|
|
|
|
|
|
internal static void LogException(string message)
|
|
{
|
|
LogException( new Exception($"{Tag} {message}"));
|
|
}
|
|
|
|
internal static void LogException(Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 上报崩溃信息
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
public static void Report(string message)
|
|
{
|
|
GuruRepoter.Log(message);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 上报异常
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
public static void ReportException(string message)
|
|
{
|
|
GuruRepoter.LogException(message);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 上报异常 Exception
|
|
/// </summary>
|
|
/// <param name="ex"></param>
|
|
public static void ReportException(Exception ex)
|
|
{
|
|
GuruRepoter.LogException(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
|
|
|
|
}
|
|
} |