update: 更新接口, 删除包引用
							parent
							
								
									a0532a5ced
								
							
						
					
					
						commit
						29f0d32c36
					
				|  | @ -12,8 +12,8 @@ namespace Guru | ||||||
| 		public class EventSetting | 		public class EventSetting | ||||||
| 		{ | 		{ | ||||||
| 			public bool EnableFirebaseAnalytics; | 			public bool EnableFirebaseAnalytics; | ||||||
| 			public bool EnableFacebookAnalytics; |  | ||||||
| 			public bool EnableAdjustAnalytics; | 			public bool EnableAdjustAnalytics; | ||||||
|  | 			public bool EnableFacebookAnalytics; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private static EventSetting _defaultEventSetting; | 		private static EventSetting _defaultEventSetting; | ||||||
|  | @ -142,16 +142,16 @@ namespace Guru | ||||||
| 			{ | 			{ | ||||||
| 				FirebaseAnalytics.LogEvent(eventName); | 				FirebaseAnalytics.LogEvent(eventName); | ||||||
| 			} | 			} | ||||||
|  | 			 | ||||||
|  | 			if (eventSetting.EnableAdjustAnalytics) | ||||||
|  | 			{ | ||||||
|  | 				Adjust.trackEvent(CreateAdjustEvent(eventName)); | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			if (eventSetting.EnableFacebookAnalytics) | 			if (eventSetting.EnableFacebookAnalytics) | ||||||
| 			{ | 			{ | ||||||
| 				FB.LogAppEvent(eventName); | 				FB.LogAppEvent(eventName); | ||||||
| 			} | 			} | ||||||
| 
 |  | ||||||
| 			if (eventSetting.EnableAdjustAnalytics) |  | ||||||
| 			{ |  | ||||||
| 				Adjust.trackEvent(CreateAdjustEvent(eventName)); |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
|  | @ -191,11 +191,6 @@ namespace Guru | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			Dictionary<string, object> dict = new Dictionary<string, object>(extras); | 			Dictionary<string, object> dict = new Dictionary<string, object>(extras); | ||||||
| 			if (eventSetting.EnableFacebookAnalytics) |  | ||||||
| 			{ |  | ||||||
| 				FB.LogAppEvent(eventName, null, dict); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (eventSetting.EnableAdjustAnalytics) | 			if (eventSetting.EnableAdjustAnalytics) | ||||||
| 			{ | 			{ | ||||||
| 				AdjustEvent adjustEvent = Analytics.CreateAdjustEvent(eventName); | 				AdjustEvent adjustEvent = Analytics.CreateAdjustEvent(eventName); | ||||||
|  | @ -208,6 +203,11 @@ namespace Guru | ||||||
| 					Adjust.trackEvent(adjustEvent); | 					Adjust.trackEvent(adjustEvent); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 			 | ||||||
|  | 			if (eventSetting.EnableFacebookAnalytics) | ||||||
|  | 			{ | ||||||
|  | 				FB.LogAppEvent(eventName, null, dict); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		#endregion | 		#endregion | ||||||
|  |  | ||||||
|  | @ -28,7 +28,7 @@ namespace Guru | ||||||
| 		[Header("产品包名")] | 		[Header("产品包名")] | ||||||
| 		public string GameIdentifier = "com.guru.default.product"; | 		public string GameIdentifier = "com.guru.default.product"; | ||||||
| 		[Header("产品反馈邮箱(评分反馈邮箱)")] | 		[Header("产品反馈邮箱(评分反馈邮箱)")] | ||||||
| 		public string SupportEmail =  "xxxx@fungame.studio"; | 		public string SupportEmail =  "test@fungame.studio"; | ||||||
| 		[Header("隐私协议URL")] | 		[Header("隐私协议URL")] | ||||||
| 		public string PriacyUrl = ""; | 		public string PriacyUrl = ""; | ||||||
| 		[Header("服务条款URL")] | 		[Header("服务条款URL")] | ||||||
|  | @ -52,6 +52,45 @@ namespace Guru | ||||||
| 		{ | 		{ | ||||||
| 			return Resources.Load<GuruSettings>("GuruSettings"); | 			return Resources.Load<GuruSettings>("GuruSettings"); | ||||||
| 		} | 		} | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// 运行时更新Adjust 的 AppToken | ||||||
|  | 		/// </summary> | ||||||
|  | 		/// <param name="androidToken"></param> | ||||||
|  | 		/// <param name="iosToken"></param> | ||||||
|  | 		public void UpdateAdjustTokens(string androidToken, string iosToken) | ||||||
|  | 		{ | ||||||
|  | 			if(!string.IsNullOrEmpty(androidToken)) | ||||||
|  | 				AdjustSetting.androidAppToken = androidToken; | ||||||
|  | 			if(!string.IsNullOrEmpty(iosToken)) | ||||||
|  | 				AdjustSetting.iOSAppToken = iosToken; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// 运行时更新 Adjust 的事件列表 | ||||||
|  | 		/// </summary> | ||||||
|  | 		public void UpdateAdjustEvents(IList<string> events) | ||||||
|  | 		{ | ||||||
|  | 			if (events != null && events.Count > 0) | ||||||
|  | 			{ | ||||||
|  | 				List<AnalyticsSetting.AdjustEvent> evtList = new List<AnalyticsSetting.AdjustEvent>(events.Count); | ||||||
|  | 				string key, atk, itk; | ||||||
|  | 				string[] tmp; | ||||||
|  | 				for (int i = 0; i < events.Count; i++) | ||||||
|  | 				{ | ||||||
|  | 					tmp = events[i].Split(','); | ||||||
|  | 					if (tmp != null && tmp.Length > 2) | ||||||
|  | 					{ | ||||||
|  | 						evtList.Add(new AnalyticsSetting.AdjustEvent() | ||||||
|  | 						{ | ||||||
|  | 							EventName = tmp[0], | ||||||
|  | 							AndroidToken = tmp[1], | ||||||
|  | 							IOSToken = tmp[2], | ||||||
|  | 						}); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				AnalyticsSetting.adjustEventList = evtList; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	[Serializable] | 	[Serializable] | ||||||
|  | @ -73,7 +112,7 @@ namespace Guru | ||||||
| 		[SerializeField] private bool enalbeFirebaseAnalytics = true; | 		[SerializeField] private bool enalbeFirebaseAnalytics = true; | ||||||
| 		[SerializeField] private bool enalbeFacebookAnalytics = true; | 		[SerializeField] private bool enalbeFacebookAnalytics = true; | ||||||
| 		[SerializeField] private bool enalbeAdjustAnalytics = true; | 		[SerializeField] private bool enalbeAdjustAnalytics = true; | ||||||
| 		[SerializeField] private List<AdjustEvent> adjustEventList; | 		[SerializeField] internal List<AdjustEvent> adjustEventList; | ||||||
| 
 | 
 | ||||||
| 		public int LevelEndSuccessNum => levelEndSuccessNum; | 		public int LevelEndSuccessNum => levelEndSuccessNum; | ||||||
| 		public bool EnalbeFirebaseAnalytics => enalbeFirebaseAnalytics; | 		public bool EnalbeFirebaseAnalytics => enalbeFirebaseAnalytics; | ||||||
|  | @ -132,8 +171,8 @@ namespace Guru | ||||||
| 	[Serializable] | 	[Serializable] | ||||||
| 	public class AdjustSetting | 	public class AdjustSetting | ||||||
| 	{ | 	{ | ||||||
| 		[SerializeField] private string androidAppToken; | 		[SerializeField] internal string androidAppToken; | ||||||
| 		[SerializeField] private string iOSAppToken; | 		[SerializeField] internal string iOSAppToken; | ||||||
| 
 | 
 | ||||||
| 		public string AndroidAppToken => androidAppToken; | 		public string AndroidAppToken => androidAppToken; | ||||||
| 		public string IOSAppToken => iOSAppToken; | 		public string IOSAppToken => iOSAppToken; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 53abb1f4e73b4b44907c0a3c0a406079 | ||||||
|  | timeCreated: 1687327402 | ||||||
|  | @ -0,0 +1,45 @@ | ||||||
|  | # Guru 评价管理器 | ||||||
|  | 
 | ||||||
|  | Version 0.1.0 | ||||||
|  | 
 | ||||||
|  | ## 简介 | ||||||
|  | 
 | ||||||
|  | Guru 评价管理器内置了多平台评价管理实现, 包括 | ||||||
|  | - GooglePlay 平台的 InAppReview 接口  | ||||||
|  | - AppStore 平台的评价接口 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## 库依赖 | ||||||
|  | 
 | ||||||
|  | ### **Android** | ||||||
|  | 
 | ||||||
|  | - 依赖于 `com.google.play.review` 的插件, 可在插件的 Package 文件夹内直接安装 | ||||||
|  | - 注意: 由于模板已集成 ExternalDependencyManager (`1.2.174`), 因此无需导入包内的EDM插件. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### **iOS** | ||||||
|  | 
 | ||||||
|  | - 直接使用 Unity 内部的 `iOS` 的 Review 接口 | ||||||
|  | 
 | ||||||
|  | ## 实现方式 | ||||||
|  | 
 | ||||||
|  | ### 显示评价 | ||||||
|  | 
 | ||||||
|  | 只需调用以下代码即可: | ||||||
|  | 
 | ||||||
|  | ```C# | ||||||
|  | // 直接显示包内的评价界面 | ||||||
|  | GuruRating.ShowRating(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### 提交反馈 | ||||||
|  |      | ||||||
|  | - 发送反馈时, 实际上是调用了系统的邮件App, 需要提供收件人的邮箱地址 | ||||||
|  | - 邮件的标题主体可以自由定制, 传入参数即可 | ||||||
|  | 
 | ||||||
|  | ```C# | ||||||
|  | // 向 support@fungame.com 的邮箱发送反馈信息. 邮件的标题和信息可以定制 | ||||||
|  | GuruRating.SetEmailFeedback("support@fungame.com", "Thank you for the feedback!"); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 6fd1be4cf92d4854b9fad97b1bd11b7b | ||||||
|  | timeCreated: 1687327432 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: d608c1853fbb4c4e9538d1af2ee2c65e | ||||||
|  | timeCreated: 1687327422 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: b3f63a18067c45da82d3521e18127a88 | ||||||
|  | timeCreated: 1687328000 | ||||||
|  | @ -0,0 +1,188 @@ | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using System; | ||||||
|  | #if UNITY_ANDROID | ||||||
|  |     using Google.Play.Review; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if UNITY_IOS | ||||||
|  |     using UnityEngine.iOS; | ||||||
|  | #endif | ||||||
|  |     using UnityEngine; | ||||||
|  |     using UnityEngine.Networking; | ||||||
|  |      | ||||||
|  |     /// <summary> | ||||||
|  |     /// 评价管理器 | ||||||
|  |     /// </summary> | ||||||
|  |     public class GuruRating: Singleton<GuruRating> | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         public static bool ShowLog { get; set; } = false; | ||||||
|  | 
 | ||||||
|  |         #region 初始化 | ||||||
|  | 
 | ||||||
|  |         protected override void Init() | ||||||
|  |         { | ||||||
|  |             base.Init(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region 评价显示 | ||||||
|  |          | ||||||
|  | #if UNITY_ANDROID | ||||||
|  | 
 | ||||||
|  |         private bool _isOnRating = false; | ||||||
|  |         /// <summary> | ||||||
|  |         /// 显示Google的评价 | ||||||
|  |         /// </summary> | ||||||
|  |         private void ShowGoogleRating() | ||||||
|  |         { | ||||||
|  |             if (_isOnRating) return; | ||||||
|  |             _isOnRating = true; | ||||||
|  |             OnGoogleRating(); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 启动Google评价流程 | ||||||
|  |         /// </summary> | ||||||
|  |         private void OnGoogleRating() | ||||||
|  |         { | ||||||
|  |             ReviewManager rm = new ReviewManager(); | ||||||
|  |             rm.RequestReviewFlow().Completed += pao => | ||||||
|  |             { | ||||||
|  |                 // 请求 PlayReviewInfo 对象 | ||||||
|  |                 if (pao.Error != ReviewErrorCode.NoError) | ||||||
|  |                 { | ||||||
|  |                     if(ShowLog) Debug.Log($"ReviewInfo Error: {pao.Error.ToString()}"); | ||||||
|  |                     OnGoogleRatingResult(false); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     // 启动应用内评价流程, 调用后可以不用关心后继的结果. 游戏继续流程即可 | ||||||
|  |                     rm.LaunchReviewFlow(pao.GetResult()).Completed += lao => | ||||||
|  |                     { | ||||||
|  |                         bool result = true; | ||||||
|  |                         if (lao.Error != ReviewErrorCode.NoError) | ||||||
|  |                         { | ||||||
|  |                             if(ShowLog) Debug.Log($"LaunchReview Error: {lao.Error.ToString()}"); | ||||||
|  |                             result = false; | ||||||
|  |                         } | ||||||
|  |                         OnGoogleRatingResult(result); | ||||||
|  |                         rm = null; | ||||||
|  |                     }; | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void OnGoogleRatingResult(bool success) | ||||||
|  |         { | ||||||
|  |             // On getting review result | ||||||
|  |             if(ShowLog) Debug.Log($"Google Review flow ends, result: {success}"); | ||||||
|  |             _isOnRating = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  |          | ||||||
|  | #if UNITY_IOS | ||||||
|  |         /// <summary> | ||||||
|  |         /// 显示苹果的Rating | ||||||
|  |         /// </summary> | ||||||
|  |         private void ShowAppleRating() => Device.RequestStoreReview();  // 显示苹果的评价面板 | ||||||
|  | #endif | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region 发送邮件 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 设置邮件显示 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="email"></param> | ||||||
|  |         /// <param name="body"></param> | ||||||
|  |         private void SendEmail(string email, string subject = "", string body = null) | ||||||
|  |         { | ||||||
|  |             if (string.IsNullOrEmpty(subject)) subject = GetDefaultMailTitle(); | ||||||
|  |             if (string.IsNullOrEmpty(body)) body = GetDefaultMailBody(); | ||||||
|  |              | ||||||
|  |             string url = $"mailto:{email}&subject={EscapeUrl(subject)}&body={EscapeUrl(body)}"; | ||||||
|  |             Application.OpenURL(url); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取默认的邮件标题 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         private string GetDefaultMailTitle() | ||||||
|  |         { | ||||||
|  |             return $"Some suggestions for improving {GuruSettings.Instance.ProductName}"; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取默认的邮件内容模版 | ||||||
|  |         /// </summary> | ||||||
|  |         private string GetDefaultMailBody() | ||||||
|  |         { | ||||||
|  |             string body = "Please Enter your message here\n\n\n\n" + | ||||||
|  |                           "________" + | ||||||
|  |                           "\n\nPlease Do Not Modify This\n\n" + | ||||||
|  |                           "Game: " + GuruSettings.Instance.ProductName + "\n\n" + | ||||||
|  |                           "Model: " + SystemInfo.deviceModel + "\n\n" + | ||||||
|  |                           "OS: " + SystemInfo.operatingSystem + "\n\n" + | ||||||
|  |                           "UserId: " + IPMConfig.IPM_UID + "\n\n" + | ||||||
|  |                           "Version: " + Application.version + "\n\n" + | ||||||
|  |                           "________"; | ||||||
|  |             return body; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |         #region 工具接口 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 转换为URL编码 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="str"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public static string EscapeUrl(string str) | ||||||
|  |         { | ||||||
|  |             return UnityWebRequest.EscapeURL(str).Replace("+", "%20"); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region 公开接口 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 显示评价面板 | ||||||
|  |         /// </summary> | ||||||
|  |         public static void ShowRating() | ||||||
|  |         { | ||||||
|  | #if UNITY_EDITOR | ||||||
|  |             Console.WriteLine($"Editor is Calling ShowRating api..."); | ||||||
|  | #elif UNITY_ANDROID | ||||||
|  |             Instance.ShowGoogleRating(); | ||||||
|  | #elif UNITY_IOS | ||||||
|  |             Instance.ShowAppleRating(); | ||||||
|  | #endif | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 设置邮件反馈 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="email"></param> | ||||||
|  |         /// <param name="body"></param> | ||||||
|  |         public static void SetEmailFeedback(string email, string subject = "", string body = "") | ||||||
|  |         { | ||||||
|  |             Instance.SendEmail(email, subject, body); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 8d36e44d41f0423f8f9f6c23600ec512 | ||||||
|  | timeCreated: 1687328021 | ||||||
|  | @ -1,5 +1,3 @@ | ||||||
| using Google; |  | ||||||
| 
 |  | ||||||
| namespace Guru | namespace Guru | ||||||
| { | { | ||||||
|     using System; |     using System; | ||||||
|  | @ -7,21 +5,10 @@ namespace Guru | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// 运控配置接口类 |     /// 运控配置接口类 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public interface IRemoteConfig |     public interface IRemoteConfig<T> where T : IRemoteConfig<T> | ||||||
|     { |     { | ||||||
|         bool enable { get; set; } |         bool enable { get; set; } | ||||||
|         string key { get; } |         Action<T> OnValueChanged { get; set; }  | ||||||
|         string value { get; } |  | ||||||
| 
 |  | ||||||
|         Action<IRemoteConfig> OnValueChanged { get; set; } |  | ||||||
| 
 |  | ||||||
|         void Init(); |  | ||||||
|         string ToJson(); |         string ToJson(); | ||||||
|         string GetDefaultValue(); |  | ||||||
|         string GetRemoteValue(); |  | ||||||
| 
 |  | ||||||
|         void GetRemoteJsonAsync(Action<string> onValueLoaded); |  | ||||||
|          |  | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1,45 +1,20 @@ | ||||||
| using System; |  | ||||||
| 
 |  | ||||||
| namespace Guru | namespace Guru | ||||||
| { | { | ||||||
|     public abstract class RemoteConfigBase: IRemoteConfig |     using System; | ||||||
|  |      | ||||||
|  |     public abstract class RemoteConfigBase<T>: IRemoteConfig<T> where T : IRemoteConfig<T> | ||||||
|     { |     { | ||||||
|          |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// 配置是否可用 |         /// 配置是否可用 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public bool enable { get; set; } = true; |         public bool enable { get; set; } = true; | ||||||
| 
 |         public Action<T> OnValueChanged { get; set; } | ||||||
|         public virtual string key { get; } = "remote-config-base"; |  | ||||||
|         public virtual string value { get; } = "{ \"enable\": true }"; |  | ||||||
|         public Action<IRemoteConfig> OnValueChanged { get; set; } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         public void Init() |  | ||||||
|         { |  | ||||||
|              |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// 转为Json |         /// 转为Json | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         public virtual string ToJson() |         public virtual string ToJson() => JsonParser.ToJson(this); | ||||||
|             => JsonParser.ToJson(this); |          | ||||||
| 
 |          | ||||||
|         public string GetDefaultValue() |  | ||||||
|         { |  | ||||||
|             throw new NotImplementedException(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public string GetRemoteValue() |  | ||||||
|         { |  | ||||||
|             throw new NotImplementedException(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public void GetRemoteJsonAsync(Action<string> onValueLoaded) |  | ||||||
|         { |  | ||||||
|             throw new NotImplementedException(); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1,9 +1,11 @@ | ||||||
| using System.Collections.Generic; | 
 | ||||||
| using System.Linq; |  | ||||||
| 
 | 
 | ||||||
| namespace Guru | namespace Guru | ||||||
| { | { | ||||||
|     using System; |     using System; | ||||||
|  |     using System.Collections.Generic; | ||||||
|  |     using System.Linq; | ||||||
|  |     using JetBrains.Annotations; | ||||||
|     using UnityEngine; |     using UnityEngine; | ||||||
|     using Firebase.RemoteConfig; |     using Firebase.RemoteConfig; | ||||||
|     using Firebase.Extensions; |     using Firebase.Extensions; | ||||||
|  | @ -13,31 +15,29 @@ namespace Guru | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public class RemoteConfigManager |     public class RemoteConfigManager | ||||||
|     { |     { | ||||||
|         public const double DefaultUpdateHours = 12; |         public const double DefaultUpdateHours = 2; | ||||||
|         private const string Tag = "[Remote]"; |         public const double DefaultFetchTimeout = 15; | ||||||
|  |         internal const string Tag = "[Remote]"; | ||||||
|         private static bool _initOnce = false; |         private static bool _initOnce = false; | ||||||
|         private static RemoteConfigManager _instance; |         private static RemoteConfigManager _instance; | ||||||
|         public static RemoteConfigManager Instance => _instance ??= new RemoteConfigManager(); |         public static RemoteConfigManager Instance => _instance ??= new RemoteConfigManager(); | ||||||
|          |          | ||||||
|         private FirebaseRemoteConfig _firebaseRemote; |         private FirebaseRemoteConfig _firebaseRemote; | ||||||
|         private static bool _isDebug = false; |         private static bool _isDebug = false; | ||||||
|         private static double _updateHours = DefaultUpdateHours; |         private static double _fetchIntervalHours = DefaultUpdateHours; | ||||||
| 
 | 
 | ||||||
|         private RemoteConfigModel _model; |         private RemoteConfigModel _model; | ||||||
| 
 |         internal RemoteConfigModel Model => _model ??= RemoteConfigModel.LoadOrCreate(); | ||||||
|         internal RemoteConfigModel Model |          | ||||||
|         { |         private static Dictionary<string, object> _defaultValues; | ||||||
|             get |  | ||||||
|             { |  | ||||||
|                 if(_model == null) _model = RemoteConfigModel.LoadOrCreate(); |  | ||||||
|                 return _model; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         private Dictionary<string, object> _defaults; |  | ||||||
| 
 | 
 | ||||||
|         public static event Action<bool> OnFetchCompleted; |         public static event Action<bool> OnFetchCompleted; | ||||||
|  | 
 | ||||||
|  |         private Dictionary<string, Action<string,string>> _changeEvents; | ||||||
|  | 
 | ||||||
|  |         private Dictionary<string, string> _staticValues; | ||||||
|          |          | ||||||
|         public static void Init(Dictionary<string, object> defaults, double updateHours = DefaultUpdateHours, bool isDebug = false) |         public static void Init(Dictionary<string, object> defaults = null, double updateHours = DefaultUpdateHours, bool isDebug = false) | ||||||
|         { |         { | ||||||
|             if (_initOnce) return; |             if (_initOnce) return; | ||||||
|             Instance.InitAssets(defaults, updateHours, isDebug); |             Instance.InitAssets(defaults, updateHours, isDebug); | ||||||
|  | @ -45,19 +45,23 @@ namespace Guru | ||||||
|          |          | ||||||
|         // 拉取所有的线上配置数据 |         // 拉取所有的线上配置数据 | ||||||
|         // onFetchComplete 传参 true: 拉取成功   false: 拉取失败 |         // onFetchComplete 传参 true: 拉取成功   false: 拉取失败 | ||||||
|         public static void FetchAll(Action<bool> onFetchComplete = null) |         public static void FetchAll(bool immediately = false) | ||||||
|         { |         { | ||||||
|             OnFetchCompleted += onFetchComplete; |             if (!_initOnce) Init(null, _fetchIntervalHours, _isDebug); | ||||||
|             if (!_initOnce) Init(null, _updateHours, _isDebug); |             Instance.FetchAllConfigs(immediately); | ||||||
|             Instance.FetchAllConfigs(); |         } | ||||||
|  | 
 | ||||||
|  |         public static void AddDefaultValues(Dictionary<string, object> dict) | ||||||
|  |         { | ||||||
|  |             if (!_initOnce) return; | ||||||
|  |             Instance.AppendDefaultValues(dict); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         #region 初始化 |         #region 初始化 | ||||||
| 
 | 
 | ||||||
|         private void InitAssets(Dictionary<string, object> defaults, double updateHours = DefaultUpdateHours, bool isDebug = false) |         private void InitAssets(Dictionary<string, object> defaults = null, double updateHours = DefaultUpdateHours, bool isDebug = false) | ||||||
|         { |         { | ||||||
|             _defaults = defaults; |             _fetchIntervalHours = updateHours; | ||||||
|             _updateHours = updateHours; |  | ||||||
|             _isDebug = isDebug; |             _isDebug = isDebug; | ||||||
|             _firebaseRemote = FirebaseRemoteConfig.DefaultInstance; |             _firebaseRemote = FirebaseRemoteConfig.DefaultInstance; | ||||||
|             if (_firebaseRemote == null) |             if (_firebaseRemote == null) | ||||||
|  | @ -65,37 +69,46 @@ namespace Guru | ||||||
|                 LogE("Can't find FirebaseRemoteConfig.DefaultInstance, init failed."); |                 LogE("Can't find FirebaseRemoteConfig.DefaultInstance, init failed."); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|        |  | ||||||
|              |              | ||||||
|             if (_defaults != null) |             // 设置默认配置 | ||||||
|  |             _firebaseRemote.SetConfigSettingsAsync(new ConfigSettings() | ||||||
|             { |             { | ||||||
|                 _firebaseRemote.SetDefaultsAsync(_defaults); |                 FetchTimeoutInMilliseconds = (ulong)(DefaultFetchTimeout * 1000), | ||||||
|             } |                 MinimumFetchInternalInMilliseconds = (ulong)(_fetchIntervalHours * 60 * 60 * 1000) | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             // 设置默认值 | ||||||
|  |             AppendDefaultValues(defaults); | ||||||
|             _initOnce = true; |             _initOnce = true; | ||||||
|              |              | ||||||
|  |             // 监听事件合集 | ||||||
|  |             _changeEvents = new Dictionary<string, Action<string,string>>(30); | ||||||
|  |              | ||||||
|             FetchAllConfigs(); |             FetchAllConfigs(); | ||||||
|         } |         } | ||||||
|          |  | ||||||
|          |  | ||||||
|          |  | ||||||
| 
 | 
 | ||||||
|         // private void OnConfigUpdateListener() |         private void AppendDefaultValues(Dictionary<string, object> defaults) | ||||||
|         // { |  | ||||||
|         //     if (evt.Error == RemoteConfigError.None) |  | ||||||
|         //     { |  | ||||||
|         //         LogI($"------- Config Update -------"); |  | ||||||
|         //         var list = evt.UpdatedKeys.ToArray(); |  | ||||||
|         //         for (int i = 0; i < list.Length; i++) |  | ||||||
|         //         { |  | ||||||
|         //             LogI($"[{i}] : {list[i]}"); |  | ||||||
|         //         } |  | ||||||
|         //     } |  | ||||||
|         // } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         private void FetchAllConfigs() |  | ||||||
|         { |         { | ||||||
|             var span = _isDebug? TimeSpan.Zero : TimeSpan.FromHours(_updateHours); |             if (defaults != null) | ||||||
|  |             { | ||||||
|  |                 if(_defaultValues == null) _defaultValues = new Dictionary<string, object>(20); | ||||||
|  |                 string key; | ||||||
|  |                 object value; | ||||||
|  |                 for(int i = 0; i < defaults.Keys.Count; i++) | ||||||
|  |                 { | ||||||
|  |                     key = defaults.Keys.ElementAt(i); | ||||||
|  |                     value = defaults.Values.ElementAt(i); | ||||||
|  |                     _defaultValues[key] = value; | ||||||
|  |                 } | ||||||
|  |                 _firebaseRemote?.SetDefaultsAsync(_defaultValues); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         private void FetchAllConfigs(bool immediately = false) | ||||||
|  |         { | ||||||
|  |             var span = TimeSpan.FromHours(_fetchIntervalHours); | ||||||
|  |             if(_isDebug || immediately) span = TimeSpan.Zero; | ||||||
|             _firebaseRemote.FetchAsync(span) |             _firebaseRemote.FetchAsync(span) | ||||||
|                 .ContinueWithOnMainThread(task => |                 .ContinueWithOnMainThread(task => | ||||||
|                 { |                 { | ||||||
|  | @ -106,6 +119,9 @@ namespace Guru | ||||||
|                         LogE($" --- FetchAllConfigs fails: {res}"); |                         LogE($" --- FetchAllConfigs fails: {res}"); | ||||||
|                         success = false; |                         success = false; | ||||||
|                     } |                     } | ||||||
|  | 
 | ||||||
|  |                     if (success) OnFetchDataCompleted(); | ||||||
|  |                      | ||||||
|                     OnFetchCompleted?.Invoke(success); |                     OnFetchCompleted?.Invoke(success); | ||||||
|                 }); |                 }); | ||||||
|         } |         } | ||||||
|  | @ -113,6 +129,59 @@ namespace Guru | ||||||
| 
 | 
 | ||||||
|         #endregion |         #endregion | ||||||
| 
 | 
 | ||||||
|  |         #region Model | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 判断是否为 Config 参数 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="rawStr"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         private bool IsRemoteConfigStr(string rawStr) | ||||||
|  |         { | ||||||
|  |             if (string.IsNullOrEmpty(rawStr)) return false; | ||||||
|  |              | ||||||
|  |             if (rawStr.TrimStart().StartsWith("{")  | ||||||
|  |                 && rawStr.TrimEnd().EndsWith("}")  | ||||||
|  |                 && rawStr.Contains("\"enable\":")) | ||||||
|  |             { | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 拉取成功 | ||||||
|  |         /// </summary> | ||||||
|  |         private void OnFetchDataCompleted() | ||||||
|  |         { | ||||||
|  |             var values = _firebaseRemote.AllValues; | ||||||
|  |             var updates = new Dictionary<string, string>(values.Count); | ||||||
|  |             var configs = new Dictionary<string, string>(values.Count); | ||||||
|  |             ConfigValue value; | ||||||
|  |             string key, str; | ||||||
|  |             for (int i = 0; i < values.Keys.Count; i++) | ||||||
|  |             { | ||||||
|  |                 key = values.Keys.ElementAt(i); | ||||||
|  |                 value = values.Values.ElementAt(i); | ||||||
|  |                 str = value.StringValue; | ||||||
|  | 
 | ||||||
|  |                 updates[key] = str; | ||||||
|  |                 if (IsRemoteConfigStr(str)) | ||||||
|  |                 { | ||||||
|  |                     configs[key] = str; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (configs.Count > 0) | ||||||
|  |             { | ||||||
|  |                 Model.UpdateConfigs(configs); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             DispatchUpdateValues(updates); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|         #region 数据接口 |         #region 数据接口 | ||||||
| 
 | 
 | ||||||
|         public string GetStringValue(string key, string defaultValue = "") |         public string GetStringValue(string key, string defaultValue = "") | ||||||
|  | @ -185,7 +254,7 @@ namespace Guru | ||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     return _firebaseRemote.GetValue(key).BooleanValue; |                     return _firebaseRemote?.GetValue(key).BooleanValue ?? defaultValue; | ||||||
|                 } |                 } | ||||||
|                 catch (Exception e) |                 catch (Exception e) | ||||||
|                 { |                 { | ||||||
|  | @ -195,9 +264,35 @@ namespace Guru | ||||||
|             return defaultValue; |             return defaultValue; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取全部值 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public static Dictionary<string, ConfigValue> GetAllValues() | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 return (Dictionary<string, ConfigValue>)(Instance._firebaseRemote?.AllValues ?? null); | ||||||
|  |             } | ||||||
|  |             catch (Exception e) | ||||||
|  |             { | ||||||
|  |                 LogException(e); | ||||||
|  |             } | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         [CanBeNull] | ||||||
|  |         public static string GetStaticValue(string key) | ||||||
|  |         { | ||||||
|  |             if (Instance._staticValues != null && Instance._staticValues.TryGetValue(key, out var value)) | ||||||
|  |             { | ||||||
|  |                 return value; | ||||||
|  |             } | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         #endregion |         #endregion | ||||||
|          |          | ||||||
|          |  | ||||||
|         #region 云控值获取 |         #region 云控值获取 | ||||||
| 
 | 
 | ||||||
|         public static string GetString(string key, string defaultValue = "") |         public static string GetString(string key, string defaultValue = "") | ||||||
|  | @ -232,14 +327,132 @@ namespace Guru | ||||||
| 
 | 
 | ||||||
|         #endregion |         #endregion | ||||||
|          |          | ||||||
|          |  | ||||||
|         #region 云控配置获取 |         #region 云控配置获取 | ||||||
| 
 | 
 | ||||||
|          |         /// <summary> | ||||||
|  |         /// 注册云控配置对象 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="key"></param> | ||||||
|  |         /// <param name="defaultJson"></param> | ||||||
|  |         public static void RegisterConfig(string key, string defaultJson) | ||||||
|  |         { | ||||||
|  |             Instance.Model.SetDefaultConfig(key, defaultJson); // 配置默认值 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取云控配置 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="key"></param> | ||||||
|  |         /// <typeparam name="T"></typeparam> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public static T GetConfig<T>(string key) where T : IRemoteConfig<T> | ||||||
|  |         { | ||||||
|  |             var config = Instance.Model.Get<T>(key); | ||||||
|  |             return config; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         #endregion |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region 监听云控值变化 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 注册值变化事件 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="key"></param> | ||||||
|  |         /// <param name="onValueChanged"></param> | ||||||
|  |         public static void RegisterOnValueChanged(string key, Action<string,string> onValueChanged) | ||||||
|  |         { | ||||||
|  |             Instance.AddOnValueChangeListener(key, onValueChanged); | ||||||
|  |         } | ||||||
|  |         /// <summary> | ||||||
|  |         /// 取消注册值变化事件 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="key"></param> | ||||||
|  |         /// <param name="onValueChanged"></param> | ||||||
|  |         public static void UnRegisterOnValueChanged(string key, Action<string,string> onValueChanged) | ||||||
|  |         { | ||||||
|  |             Instance.RemoveOnValueChangeListener(key, onValueChanged); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         private void AddOnValueChangeListener(string key, Action<string,string> onValueChanged) | ||||||
|  |         { | ||||||
|  |             if (_changeEvents == null) _changeEvents = new Dictionary<string, Action<string,string>>(30); | ||||||
|  | 
 | ||||||
|  |             if (HasOnValueChangeListener(key)) | ||||||
|  |             { | ||||||
|  |                 _changeEvents[key] += onValueChanged; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 _changeEvents[key] = onValueChanged; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         private void RemoveOnValueChangeListener(string key, Action<string,string> onValueChanged) | ||||||
|  |         { | ||||||
|  |             if (_changeEvents != null && HasOnValueChangeListener(key)) | ||||||
|  |             { | ||||||
|  |                 _changeEvents[key] -= onValueChanged; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private bool HasOnValueChangeListener(string key) | ||||||
|  |         { | ||||||
|  |             if (_changeEvents != null) | ||||||
|  |             { | ||||||
|  |                 return _changeEvents.ContainsKey(key); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         private void DispatchUpdateValues(Dictionary<string, string> updates) | ||||||
|  |         { | ||||||
|  |             Dictionary<string, string> changes = new Dictionary<string, string>(updates.Count); | ||||||
|  | 
 | ||||||
|  |             if (_staticValues == null) _staticValues = new Dictionary<string, string>(); | ||||||
|  | 
 | ||||||
|  |             string key, value; | ||||||
|  |             for (int i = 0; i < updates.Keys.Count; i++) | ||||||
|  |             { | ||||||
|  |                 key = updates.Keys.ElementAt(i); | ||||||
|  |                 value = updates.Values.ElementAt(i); | ||||||
|  | 
 | ||||||
|  |                 if (_staticValues.TryGetValue(key, out var oldValue)) | ||||||
|  |                 { | ||||||
|  |                     if (oldValue != updates[key] && _changeEvents.ContainsKey(key)) | ||||||
|  |                     { | ||||||
|  |                         changes[key] = value; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     changes[key] = value; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             // --------- 发送值变化事件 ------------ | ||||||
|  |             for (int i = 0; i < changes.Keys.Count; i++) | ||||||
|  |             { | ||||||
|  |                 key = updates.Keys.ElementAt(i); | ||||||
|  |                 value = updates.Values.ElementAt(i); | ||||||
|  | 
 | ||||||
|  |                 if (_changeEvents.TryGetValue(key, out var callback)) | ||||||
|  |                 { | ||||||
|  |                     callback?.Invoke(key, value); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             _staticValues = updates; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  |          | ||||||
|          |          | ||||||
|         #region Log |         #region Log | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
| using UnityEngine; | using UnityEngine; | ||||||
| 
 | 
 | ||||||
| namespace Guru | namespace Guru | ||||||
|  | @ -12,7 +13,7 @@ namespace Guru | ||||||
|     { |     { | ||||||
|         private static float SaveInterval = 2f; |         private static float SaveInterval = 2f; | ||||||
|         private const string SaveKey = "comr.guru.remote.model.save"; |         private const string SaveKey = "comr.guru.remote.model.save"; | ||||||
|         public Dictionary<string, string> Data; |         public Dictionary<string, string> configs; | ||||||
|         public long last_modified = 0; |         public long last_modified = 0; | ||||||
| 
 | 
 | ||||||
|         private float _lastSavedTime = 0; |         private float _lastSavedTime = 0; | ||||||
|  | @ -37,7 +38,7 @@ namespace Guru | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// 默认赋值数据 |         /// 默认赋值数据 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         private Dictionary<string, string> _defaultData; |         private Dictionary<string, string> _defConfigs; | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// 加载数据 |         /// 加载数据 | ||||||
|  | @ -70,8 +71,8 @@ namespace Guru | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public RemoteConfigModel() |         public RemoteConfigModel() | ||||||
|         { |         { | ||||||
|             _defaultData = new Dictionary<string, string>(20); |             _defConfigs = new Dictionary<string, string>(20); | ||||||
|             Data = new Dictionary<string, string>(20); |             configs = new Dictionary<string, string>(20); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | @ -79,7 +80,7 @@ namespace Guru | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="key"></param> |         /// <param name="key"></param> | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         public bool HasKey(string key) => Data.ContainsKey(key); |         public bool HasKey(string key) => configs.ContainsKey(key); | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// 保存数据 |         /// 保存数据 | ||||||
|  | @ -100,13 +101,12 @@ namespace Guru | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="key"></param> |         /// <param name="key"></param> | ||||||
|         /// <param name="value"></param> |         /// <param name="value"></param> | ||||||
|         public void SetDefault(string key, string value) |         public void SetDefaultConfig(string key, string value) | ||||||
|         { |         { | ||||||
|             _defaultData[key] = value; |             _defConfigs[key] = value; | ||||||
|             if (!HasKey(key)) |             if (!HasKey(key)) | ||||||
|             { |             { | ||||||
|                 Data[key] = value; |                 SetConfigValue(key, value); | ||||||
|                 Save(); |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  | @ -115,10 +115,10 @@ namespace Guru | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="key"></param> |         /// <param name="key"></param> | ||||||
|         /// <param name="config"></param> |         /// <param name="config"></param> | ||||||
|         public void SetDefault<T>(string key, T config) where T : IRemoteConfig |         public void SetDefaultConfig<T>(string key, T config) where T : IRemoteConfig<T> | ||||||
|         { |         { | ||||||
|             var json = config.ToJson(); |             var json = config.ToJson(); | ||||||
|             SetDefault(key, json); |             SetDefaultConfig(key, json); | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | @ -127,15 +127,59 @@ namespace Guru | ||||||
|         /// <param name="key"></param> |         /// <param name="key"></param> | ||||||
|         /// <typeparam name="T"></typeparam> |         /// <typeparam name="T"></typeparam> | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         public T Get<T>(string key) where T : IRemoteConfig |         public T Get<T>(string key) where T : IRemoteConfig<T> | ||||||
|         { |         { | ||||||
|             if(HasKey(key)) return JsonParser.ToObject<T>(Data[key]); |             string json = ""; | ||||||
|  |             if (HasKey(key)) | ||||||
|  |             { | ||||||
|  |                 json = configs[json]; | ||||||
|  |             } | ||||||
|  |             else if (_defConfigs.TryGetValue(key, out var defValue)) | ||||||
|  |             { | ||||||
|  |                 json = defValue;    | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (!string.IsNullOrEmpty(json)) | ||||||
|  |             { | ||||||
|  |                 return JsonParser.ToObject<T>(json); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             Log.E(RemoteConfigManager.Tag, $" --- Remote Key {key} has never been registered."); | ||||||
|             return default(T); |             return default(T); | ||||||
|         } |         } | ||||||
| 
 |          | ||||||
|         public void Set<T>(string key, T config) where T : IRemoteConfig |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 设置对象值 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="key"></param> | ||||||
|  |         /// <param name="value"></param> | ||||||
|  |         /// <typeparam name="T"></typeparam> | ||||||
|  |         internal void SetConfigValue(string key, string value) | ||||||
|         { |         { | ||||||
|              |             configs[key] = value; | ||||||
|  |             Save(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 更新所有的配置 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="updates"></param> | ||||||
|  |         public void UpdateConfigs(Dictionary<string, string> updates) | ||||||
|  |         { | ||||||
|  |             string key, value; | ||||||
|  |             for (int i = 0; i < updates.Keys.Count; i++) | ||||||
|  |             { | ||||||
|  |                 key = updates.Keys.ElementAt(i); | ||||||
|  |                 value = updates.Values.ElementAt(i); | ||||||
|  | 
 | ||||||
|  |                 if (!HasKey(key) || configs[key] != value) | ||||||
|  |                 { | ||||||
|  |                   // New Key or Value Changed | ||||||
|  |                   configs[key] = value; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Save(true); // 直接保存 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,17 +1,17 @@ | ||||||
| using System.Collections; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using UnityEngine; |  | ||||||
| 
 |  | ||||||
| namespace Guru | namespace Guru | ||||||
| { | { | ||||||
|  |     using System.Collections; | ||||||
|  |     using System.Collections.Generic; | ||||||
|  |     using UnityEngine; | ||||||
|      |      | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Guru 内置浏览器 |     /// Guru 内置浏览器 | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     public class GuruWebview |     public class GuruWebview | ||||||
|     { |     { | ||||||
|         public const string Version = "0.0.1"; |         public const string Version = "0.0.2"; | ||||||
| 
 |         public static float WindowFadeDuration = 0.35f; | ||||||
|  |          | ||||||
|         private static UniWebView CreateWebView() |         private static UniWebView CreateWebView() | ||||||
|         { |         { | ||||||
|             var go = new GameObject("guru_webview"); |             var go = new GameObject("guru_webview"); | ||||||
|  | @ -23,10 +23,14 @@ namespace Guru | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// 打开链接 |         /// 打开页面 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="url"></param> |         /// <param name="url">页面链接</param> | ||||||
|         public static void OpenPage(string url, bool showToolbar = true) |         /// <param name="showToolbar">显示工具条</param> | ||||||
|  |         /// <param name="waitForReady">等待加载完成后再显示页面</param> | ||||||
|  |         /// <param name="fadeIn">淡入显示效果</param> | ||||||
|  |         public static void OpenPage(string url, bool showToolbar = true,  | ||||||
|  |             bool waitForReady = true, bool fadeIn = true) | ||||||
|         { |         { | ||||||
|             Debug.Log($"---- Guru Open Url: {url}"); |             Debug.Log($"---- Guru Open Url: {url}"); | ||||||
|             var view = CreateWebView(); |             var view = CreateWebView(); | ||||||
|  | @ -40,14 +44,19 @@ namespace Guru | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             view.Load(url); |             view.Load(url); | ||||||
|             view.OnPageFinished += (v, code, msg) => |             if (waitForReady) | ||||||
|             { |             { | ||||||
|                 // 加载完成后展示页面 |                 view.OnPageFinished += (v, code, msg) => | ||||||
|                 view.Show(true); |                 { | ||||||
|             }; |                     // 加载完成后展示页面 | ||||||
| 
 |                     view.Show(fadeIn, duration:WindowFadeDuration); | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 view.Show(fadeIn, duration:WindowFadeDuration);   //直接加载页面 | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
							
								
								
									
										16
									
								
								package.json
								
								
								
								
							
							
						
						
									
										16
									
								
								package.json
								
								
								
								
							|  | @ -12,21 +12,5 @@ | ||||||
|   "relatedPackages": { |   "relatedPackages": { | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "com.unity.purchasing": "4.10.0", |  | ||||||
|     "com.unity.mobile.android-logcat": "1.3.2", |  | ||||||
|     "com.unity.editorcoroutines": "1.0.0", |  | ||||||
|     "com.coffee.upm-git-extension": "https://github.com/mob-sakai/UpmGitExtension.git", |  | ||||||
|     "com.google.external-dependency-manager": "https://github.com/GameWorkstore/com.google.external-dependency-manager.git", |  | ||||||
|     "com.google.firebase.crashlytics": "https://github.com/GameWorkstore/com.google.firebase.crashlytics.git#10.1.1", |  | ||||||
|     "com.google.firebase.analytics": "https://github.com/GameWorkstore/com.google.firebase.analytics.git#10.1.1", |  | ||||||
|     "com.google.firebase.app": "https://github.com/GameWorkstore/com.google.firebase.app.git#10.1.1", |  | ||||||
|     "com.google.firebase.auth": "https://github.com/GameWorkstore/com.google.firebase.auth.git#10.1.1", |  | ||||||
|     "com.google.firebase.firestore": "https://github.com/GameWorkstore/com.google.firebase.firestore.git#10.1.1", |  | ||||||
|     "com.google.firebase.messaging": "https://github.com/GameWorkstore/com.google.firebase.messaging.git#10.1.1", |  | ||||||
|     "com.google.firebase.remote-config": "https://github.com/GameWorkstore/com.google.firebase.remote-config.git#10.1.1", |  | ||||||
|     "com.google.firebase.dynamic-links": "https://github.com/GameWorkstore/com.google.firebase.dynamic-links.git#10.1.1", |  | ||||||
| 
 |  | ||||||
|     "com.guru.unity.adjust": "git@git.chengdu.pundit.company:castbox/com.guru.unity.adjust.git", |  | ||||||
|     "com.guru.unity.max": "git@git.chengdu.pundit.company:castbox/com.guru.unity.max.git" |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
		Loading…
	
		Reference in New Issue