update: 优化 GuruSDK 初始化配置。 添加 Builder 模式
parent
42c7302ea3
commit
48c5235335
|
|
@ -7,7 +7,7 @@ namespace Guru
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 启动参数配置
|
/// 启动参数配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class GuruSDKInitConfig
|
public class GuruSDKInitConfig
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 使用自定义的ConsentFlow启动流程
|
/// 使用自定义的ConsentFlow启动流程
|
||||||
|
|
@ -58,6 +58,10 @@ namespace Guru
|
||||||
/// 启用 AdjustDeeplink
|
/// 启用 AdjustDeeplink
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<string> OnAdjustDeeplinkCallback = null;
|
public Action<string> OnAdjustDeeplinkCallback = null;
|
||||||
|
/// <summary>
|
||||||
|
/// 自打点启动参数
|
||||||
|
/// </summary>
|
||||||
|
public GuruAnalyticsInitConfig CustomAnalyticsInitConfig = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 支付初始化Keys
|
/// 支付初始化Keys
|
||||||
|
|
@ -65,51 +69,6 @@ namespace Guru
|
||||||
public byte[] GoogleKeys; // 数据取自 GooglePlayTangle.Data();
|
public byte[] GoogleKeys; // 数据取自 GooglePlayTangle.Data();
|
||||||
public byte[] AppleRootCerts; // 数据取自 AppleTangle.Data();
|
public byte[] AppleRootCerts; // 数据取自 AppleTangle.Data();
|
||||||
|
|
||||||
#region Initialization
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构建启动配置
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static GuruSDKInitConfig Build(
|
|
||||||
bool useCustomConsent = false,
|
|
||||||
bool autoLoadAds = true,
|
|
||||||
bool iapEnabled = true,
|
|
||||||
bool autoRecordFinishedLevels = true,
|
|
||||||
bool isBuyNoAds = false,
|
|
||||||
string bannerBackgroundColor = "#00000000",
|
|
||||||
bool debugMode = false,
|
|
||||||
Action<string> onAdjustDeeplinkCallback = null,
|
|
||||||
Dictionary<string, object> defaultRemoteData = null,
|
|
||||||
byte[] googleKeys = null,
|
|
||||||
byte[] appleRootCerts = null,
|
|
||||||
bool debugEnableEventLog = false)
|
|
||||||
{
|
|
||||||
// 创建启动用参数
|
|
||||||
GuruSDKInitConfig config = new GuruSDKInitConfig()
|
|
||||||
{
|
|
||||||
UseCustomConsent = useCustomConsent,
|
|
||||||
AutoLoadWhenAdsReady = autoLoadAds,
|
|
||||||
IAPEnabled = iapEnabled,
|
|
||||||
AutoRecordFinishedLevels = autoRecordFinishedLevels,
|
|
||||||
IsBuyNoAds = isBuyNoAds,
|
|
||||||
BannerBackgroundColor = bannerBackgroundColor,
|
|
||||||
DebugMode = debugMode,
|
|
||||||
OnAdjustDeeplinkCallback = onAdjustDeeplinkCallback,
|
|
||||||
GoogleKeys = googleKeys,
|
|
||||||
AppleRootCerts = appleRootCerts,
|
|
||||||
DefaultRemoteData = defaultRemoteData ?? new Dictionary<string, object>(),
|
|
||||||
EnableDebugLogEvent = debugEnableEventLog,
|
|
||||||
};
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
config.DebugMode = true;
|
|
||||||
#endif
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Print
|
#region Print
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
|
@ -134,4 +93,100 @@ namespace Guru
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构建器
|
||||||
|
/// </summary>
|
||||||
|
public class GuruSDKInitConfigBuilder
|
||||||
|
{
|
||||||
|
|
||||||
|
private GuruSDKInitConfig _config = new GuruSDKInitConfig();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构建配置
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public GuruSDKInitConfig Build()
|
||||||
|
{
|
||||||
|
return _config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GuruSDKInitConfigBuilder SetUseCustomConsent(bool value)
|
||||||
|
{
|
||||||
|
_config.UseCustomConsent = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetAutoLoadWhenAdsReady(bool value)
|
||||||
|
{
|
||||||
|
_config.AutoLoadWhenAdsReady = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetIAPEnabled(bool value)
|
||||||
|
{
|
||||||
|
_config.IAPEnabled = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetAutoRecordFinishedLevels(bool value)
|
||||||
|
{
|
||||||
|
_config.AutoRecordFinishedLevels = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetIsBuyNoAds(bool value)
|
||||||
|
{
|
||||||
|
_config.IsBuyNoAds = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetBannerBackgroundColor(string value)
|
||||||
|
{
|
||||||
|
_config.BannerBackgroundColor = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetDebugMode(bool value)
|
||||||
|
{
|
||||||
|
_config.DebugMode = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetOnAdjustDeeplinkCallback(Action<string> callback)
|
||||||
|
{
|
||||||
|
_config.OnAdjustDeeplinkCallback = callback;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetGoogleKeys(byte[] value)
|
||||||
|
{
|
||||||
|
_config.GoogleKeys = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetAppleRootCerts(byte[] value)
|
||||||
|
{
|
||||||
|
_config.AppleRootCerts = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetDefaultRemoteData(Dictionary<string, object> value)
|
||||||
|
{
|
||||||
|
_config.DefaultRemoteData = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetEnableDebugLogEvent(bool value)
|
||||||
|
{
|
||||||
|
_config.EnableDebugLogEvent = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetCustomAnalyticsInitConfig(GuruAnalyticsInitConfig value)
|
||||||
|
{
|
||||||
|
_config.CustomAnalyticsInitConfig = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetCustomServiceKey(string value)
|
||||||
|
{
|
||||||
|
_config.CustomServiceKey = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GuruSDKInitConfigBuilder SetAutoNotificationPermission(bool value)
|
||||||
|
{
|
||||||
|
_config.AutoNotificationPermission = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -334,25 +334,25 @@ namespace Guru
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Action<bool> _onUserAuthResult;
|
private static Action<bool> _onUserAuthResult;
|
||||||
public static event Action<bool> OnUserAuthResult
|
public static event Action<bool> OnGuruUserAuthResult
|
||||||
{
|
{
|
||||||
add => _onUserAuthResult += value;
|
add => _onUserAuthResult += value;
|
||||||
remove => _onUserAuthResult -= value;
|
remove => _onUserAuthResult -= value;
|
||||||
}
|
}
|
||||||
internal static void InvokeOnUserAuthResult(bool success)
|
internal static void InvokeOnGuruUserAuthResult(bool success)
|
||||||
{
|
{
|
||||||
_onUserAuthResult?.Invoke(success);
|
_onUserAuthResult?.Invoke(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Action<bool> _onFirebaseAuthResult;
|
private static Action<bool> _onFirebaseUserAuthResult;
|
||||||
public static event Action<bool> OnFirebaseAuthResult
|
public static event Action<bool> OnFirebaseUserAuthResult
|
||||||
{
|
{
|
||||||
add => _onFirebaseAuthResult += value;
|
add => _onFirebaseUserAuthResult += value;
|
||||||
remove => _onFirebaseAuthResult -= value;
|
remove => _onFirebaseUserAuthResult -= value;
|
||||||
}
|
}
|
||||||
internal static void InvokeOnFirebaseAuthResult(bool success)
|
internal static void InvokeOnFirebaseAuthResult(bool success)
|
||||||
{
|
{
|
||||||
_onFirebaseAuthResult?.Invoke(success);
|
_onFirebaseUserAuthResult?.Invoke(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepLink 回调
|
// DeepLink 回调
|
||||||
|
|
|
||||||
|
|
@ -89,33 +89,10 @@ namespace Guru
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO : 下个版本需要将 整个 GuruSDK 做功能性的拆分
|
// TODO : 下个版本需要将 整个 GuruSDK 做功能性的拆分
|
||||||
// GuruSDk.Callbacks -> GuruSDkCallbacks 所有的内置回调改为成员变量,
|
|
||||||
// 去掉所有的内部类, 去掉所有的 Static
|
|
||||||
// Static 只用于常量
|
|
||||||
// TODO: 下一个版本改为标准的 Builder 模式
|
|
||||||
public static GuruSDKInitConfig BuildConfig(
|
|
||||||
bool useCustomConsent = false,
|
|
||||||
bool autoLoadAds = true,
|
|
||||||
bool iapEnabled = true,
|
|
||||||
bool autoRecordFinishedLevels = true,
|
|
||||||
bool debugMode = false,
|
|
||||||
bool isBuyNoAds = false,
|
|
||||||
Action<string> onAdjustDeeplinkCallback = null,
|
|
||||||
string bannerColor = "#00000000",
|
|
||||||
Dictionary<string, object> defaultRemoteData = null,
|
|
||||||
byte[] googleKeys = null,
|
|
||||||
byte[] appleRootCerts = null,
|
|
||||||
bool debugEnableEventLog = false)
|
|
||||||
{
|
|
||||||
var config = GuruSDKInitConfig.Build(useCustomConsent, autoLoadAds, iapEnabled,
|
|
||||||
autoRecordFinishedLevels, isBuyNoAds, bannerColor,
|
|
||||||
debugMode, onAdjustDeeplinkCallback, defaultRemoteData, googleKeys, appleRootCerts, debugEnableEventLog);
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Init(Action<bool> onComplete)
|
public static void Init(Action<bool> onComplete)
|
||||||
{
|
{
|
||||||
Init(GuruSDKInitConfig.Build(), onComplete);
|
Init(new GuruSDKInitConfigBuilder().Build(), onComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Init(GuruSDKInitConfig config, Action<bool> onComplete)
|
public static void Init(GuruSDKInitConfig config, Action<bool> onComplete)
|
||||||
|
|
@ -155,12 +132,11 @@ namespace Guru
|
||||||
|
|
||||||
private void InitServices()
|
private void InitServices()
|
||||||
{
|
{
|
||||||
Analytics.InitAnalytics(); // 打点提前初始化
|
Analytics.Init(); // 打点提前初始化
|
||||||
//---- Start All tools ----
|
//---- Start All tools ----
|
||||||
LogI($"#2 --- InitFirebase ---");
|
LogI($"#2 --- InitFirebase ---");
|
||||||
//---------- Start Firebase ------------
|
//---------- Start Firebase ------------
|
||||||
StartFirebaseService();
|
StartFirebaseService();
|
||||||
|
|
||||||
LogI($"#2.1 --- InitFacebook ---");
|
LogI($"#2.1 --- InitFacebook ---");
|
||||||
//---------- Start Facebook ------------
|
//---------- Start Facebook ------------
|
||||||
FBService.Instance.StartService(Analytics.OnFBInitComplete);
|
FBService.Instance.StartService(Analytics.OnFBInitComplete);
|
||||||
|
|
@ -168,75 +144,6 @@ namespace Guru
|
||||||
IsInitialSuccess = true;
|
IsInitialSuccess = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 启动 Firebase 服务
|
|
||||||
/// </summary>
|
|
||||||
private void StartFirebaseService()
|
|
||||||
{
|
|
||||||
FirebaseUtil.onInitComplete += OnFirebaseReady;
|
|
||||||
FirebaseUtil.OnUserAuthResult += OnUserAuthResult;
|
|
||||||
FirebaseUtil.OnFirebaseAuthResult += OnFirebaseAuthResult;
|
|
||||||
|
|
||||||
if (InitConfig.OnAdjustDeeplinkCallback != null)
|
|
||||||
{
|
|
||||||
//TODO: 下个版本 AdjustService 和 Firebase 解耦
|
|
||||||
FirebaseUtil.OnAdjustDeeplinkCallback = InitConfig.OnAdjustDeeplinkCallback; // 挂载 Deeplink 的回调
|
|
||||||
}
|
|
||||||
|
|
||||||
FirebaseUtil.InitFirebase(Analytics.OnFirebaseInitCompleted); // 确保所有的逻辑提前被调用到
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUserAuthResult(bool success)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (success && string.IsNullOrEmpty(IPMConfig.IPM_UID))
|
|
||||||
{
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
Callbacks.SDK.InvokeOnUserAuthResult(success);
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
Model.UserId = IPMConfig.IPM_UID;
|
|
||||||
if (GuruIAP.Instance != null)
|
|
||||||
{
|
|
||||||
GuruIAP.Instance.SetUID(UID);
|
|
||||||
GuruIAP.Instance.SetUUID(UUID);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetUID(UID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnFirebaseAuthResult(bool success)
|
|
||||||
{
|
|
||||||
Callbacks.SDK.InvokeOnFirebaseAuthResult(success);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 开始各种组件初始化
|
|
||||||
/// </summary>
|
|
||||||
private void OnFirebaseReady(bool success)
|
|
||||||
{
|
|
||||||
FirebaseUtil.onInitComplete -= OnFirebaseReady;
|
|
||||||
LogI($"#3 --- On FirebaseDeps: {success} ---");
|
|
||||||
IsFirebaseReady = success;
|
|
||||||
Callbacks.SDK.InvokeOnFirebaseReady(success);
|
|
||||||
// LogFirebaseDeps(success);
|
|
||||||
|
|
||||||
LogI($"#3.5 --- Call InitRemoteConfig ---");
|
|
||||||
// 开始Remote Manager初始化
|
|
||||||
RemoteConfigManager.Init(BuildDefaultRemoteData(_initConfig.DefaultRemoteData));
|
|
||||||
RemoteConfigManager.OnFetchCompleted += OnFetchRemoteCallback;
|
|
||||||
|
|
||||||
LogI($"#4 --- Apply remote services config ---");
|
|
||||||
// 根据缓存的云控配置来初始化参数
|
|
||||||
InitAllGuruServices();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 注入云控参数基础数据
|
/// 注入云控参数基础数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -264,8 +171,6 @@ namespace Guru
|
||||||
Callbacks.Remote.InvokeOnRemoteFetchComplete(success);
|
Callbacks.Remote.InvokeOnRemoteFetchComplete(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
UpdateAllUpdates(); // 驱动所有的更新器
|
UpdateAllUpdates(); // 驱动所有的更新器
|
||||||
|
|
@ -274,6 +179,8 @@ namespace Guru
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region App Remote Update
|
#region App Remote Update
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -798,6 +705,132 @@ namespace Guru
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Firebase 服务
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动 Firebase 服务
|
||||||
|
/// </summary>
|
||||||
|
private void StartFirebaseService()
|
||||||
|
{
|
||||||
|
FirebaseUtil.Init(OnFirebaseDepsCheckResult,
|
||||||
|
OnGetFirebaseId,
|
||||||
|
OnGetGuruUID,
|
||||||
|
OnFirebaseLoginResult); // 确保所有的逻辑提前被调用到
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetGuruUID(bool success)
|
||||||
|
{
|
||||||
|
Callbacks.SDK.InvokeOnGuruUserAuthResult(success);
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
Model.UserId = IPMConfig.IPM_UID;
|
||||||
|
if (GuruIAP.Instance != null)
|
||||||
|
{
|
||||||
|
GuruIAP.Instance.SetUID(UID);
|
||||||
|
GuruIAP.Instance.SetUUID(UUID);
|
||||||
|
}
|
||||||
|
SetUID(UID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void OnGetFirebaseId(string fid)
|
||||||
|
{
|
||||||
|
// 初始化 Adjust 服务
|
||||||
|
InitAdjustService(fid, InitConfig.OnAdjustDeeplinkCallback);
|
||||||
|
|
||||||
|
// 初始化自打点
|
||||||
|
var config = InitConfig.CustomAnalyticsInitConfig; // 获取外置的启动配置
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
// 创建默认的配置
|
||||||
|
config = new GuruAnalyticsInitConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
Analytics.InitGuruAnalyticService(config, fid);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFirebaseLoginResult(bool success)
|
||||||
|
{
|
||||||
|
Callbacks.SDK.InvokeOnFirebaseAuthResult(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始各种组件初始化
|
||||||
|
/// </summary>
|
||||||
|
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初始化
|
||||||
|
RemoteConfigManager.Init(BuildDefaultRemoteData(_initConfig.DefaultRemoteData));
|
||||||
|
RemoteConfigManager.OnFetchCompleted += OnFetchRemoteCallback;
|
||||||
|
|
||||||
|
LogI($"#4 --- Apply remote services config ---");
|
||||||
|
// 根据缓存的云控配置来初始化参数
|
||||||
|
InitAllGuruServices();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Adjust服务
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动 Adjust 服务
|
||||||
|
/// </summary>
|
||||||
|
private static void InitAdjustService(string firebaseId, Action<string> onDeeplinkCallback = null)
|
||||||
|
{
|
||||||
|
// 启动 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, firebaseId, DeviceId,
|
||||||
|
OnGetAdjustId, onDeeplinkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnGetAdjustId(string adjustId)
|
||||||
|
{
|
||||||
|
// 获取 ADID
|
||||||
|
if (string.IsNullOrEmpty(adjustId))
|
||||||
|
{
|
||||||
|
adjustId = "not_set";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IPMConfig.ADJUST_ID = adjustId;
|
||||||
|
}
|
||||||
|
ReportAdjustId(adjustId);
|
||||||
|
|
||||||
|
Analytics.OnAdjustInitComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void ReportAdjustId(string adjustId)
|
||||||
|
{
|
||||||
|
SetUserProperty("adjust_id", adjustId);
|
||||||
|
Debug.Log($"[SDK] --- Firebase + Adjust ID: {adjustId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue