update: 更新接口, 删除包引用
							parent
							
								
									a0532a5ced
								
							
						
					
					
						commit
						29f0d32c36
					
				|  | @ -12,8 +12,8 @@ namespace Guru | |||
| 		public class EventSetting | ||||
| 		{ | ||||
| 			public bool EnableFirebaseAnalytics; | ||||
| 			public bool EnableFacebookAnalytics; | ||||
| 			public bool EnableAdjustAnalytics; | ||||
| 			public bool EnableFacebookAnalytics; | ||||
| 		} | ||||
| 
 | ||||
| 		private static EventSetting _defaultEventSetting; | ||||
|  | @ -143,15 +143,15 @@ namespace Guru | |||
| 				FirebaseAnalytics.LogEvent(eventName); | ||||
| 			} | ||||
| 			 | ||||
| 			if (eventSetting.EnableFacebookAnalytics) | ||||
| 			{ | ||||
| 				FB.LogAppEvent(eventName); | ||||
| 			} | ||||
| 
 | ||||
| 			if (eventSetting.EnableAdjustAnalytics) | ||||
| 			{ | ||||
| 				Adjust.trackEvent(CreateAdjustEvent(eventName)); | ||||
| 			} | ||||
| 
 | ||||
| 			if (eventSetting.EnableFacebookAnalytics) | ||||
| 			{ | ||||
| 				FB.LogAppEvent(eventName); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/// <summary> | ||||
|  | @ -191,11 +191,6 @@ namespace Guru | |||
| 			} | ||||
| 
 | ||||
| 			Dictionary<string, object> dict = new Dictionary<string, object>(extras); | ||||
| 			if (eventSetting.EnableFacebookAnalytics) | ||||
| 			{ | ||||
| 				FB.LogAppEvent(eventName, null, dict); | ||||
| 			} | ||||
| 
 | ||||
| 			if (eventSetting.EnableAdjustAnalytics) | ||||
| 			{ | ||||
| 				AdjustEvent adjustEvent = Analytics.CreateAdjustEvent(eventName); | ||||
|  | @ -208,6 +203,11 @@ namespace Guru | |||
| 					Adjust.trackEvent(adjustEvent); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			if (eventSetting.EnableFacebookAnalytics) | ||||
| 			{ | ||||
| 				FB.LogAppEvent(eventName, null, dict); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		#endregion | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ namespace Guru | |||
| 		[Header("产品包名")] | ||||
| 		public string GameIdentifier = "com.guru.default.product"; | ||||
| 		[Header("产品反馈邮箱(评分反馈邮箱)")] | ||||
| 		public string SupportEmail =  "xxxx@fungame.studio"; | ||||
| 		public string SupportEmail =  "test@fungame.studio"; | ||||
| 		[Header("隐私协议URL")] | ||||
| 		public string PriacyUrl = ""; | ||||
| 		[Header("服务条款URL")] | ||||
|  | @ -52,6 +52,45 @@ namespace Guru | |||
| 		{ | ||||
| 			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] | ||||
|  | @ -73,7 +112,7 @@ namespace Guru | |||
| 		[SerializeField] private bool enalbeFirebaseAnalytics = true; | ||||
| 		[SerializeField] private bool enalbeFacebookAnalytics = true; | ||||
| 		[SerializeField] private bool enalbeAdjustAnalytics = true; | ||||
| 		[SerializeField] private List<AdjustEvent> adjustEventList; | ||||
| 		[SerializeField] internal List<AdjustEvent> adjustEventList; | ||||
| 
 | ||||
| 		public int LevelEndSuccessNum => levelEndSuccessNum; | ||||
| 		public bool EnalbeFirebaseAnalytics => enalbeFirebaseAnalytics; | ||||
|  | @ -132,8 +171,8 @@ namespace Guru | |||
| 	[Serializable] | ||||
| 	public class AdjustSetting | ||||
| 	{ | ||||
| 		[SerializeField] private string androidAppToken; | ||||
| 		[SerializeField] private string iOSAppToken; | ||||
| 		[SerializeField] internal string androidAppToken; | ||||
| 		[SerializeField] internal string iOSAppToken; | ||||
| 
 | ||||
| 		public string AndroidAppToken => androidAppToken; | ||||
| 		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 | ||||
| { | ||||
|     using System; | ||||
|  | @ -7,21 +5,10 @@ namespace Guru | |||
|     /// <summary> | ||||
|     /// 运控配置接口类 | ||||
|     /// </summary> | ||||
|     public interface IRemoteConfig | ||||
|     public interface IRemoteConfig<T> where T : IRemoteConfig<T> | ||||
|     { | ||||
|         bool enable { get; set; } | ||||
|         string key { get; } | ||||
|         string value { get; } | ||||
| 
 | ||||
|         Action<IRemoteConfig> OnValueChanged { get; set; } | ||||
| 
 | ||||
|         void Init(); | ||||
|         Action<T> OnValueChanged { get; set; }  | ||||
|         string ToJson(); | ||||
|         string GetDefaultValue(); | ||||
|         string GetRemoteValue(); | ||||
| 
 | ||||
|         void GetRemoteJsonAsync(Action<string> onValueLoaded); | ||||
|          | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -1,45 +1,20 @@ | |||
| using System; | ||||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
|     public abstract class RemoteConfigBase: IRemoteConfig | ||||
|     { | ||||
|     using System; | ||||
|      | ||||
|     public abstract class RemoteConfigBase<T>: IRemoteConfig<T> where T : IRemoteConfig<T> | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 配置是否可用 | ||||
|         /// </summary> | ||||
|         public bool enable { get; set; } = true; | ||||
| 
 | ||||
|         public virtual string key { get; } = "remote-config-base"; | ||||
|         public virtual string value { get; } = "{ \"enable\": true }"; | ||||
|         public Action<IRemoteConfig> OnValueChanged { get; set; } | ||||
| 
 | ||||
| 
 | ||||
|         public void Init() | ||||
|         { | ||||
|              | ||||
|         } | ||||
| 
 | ||||
|         public Action<T> OnValueChanged { get; set; } | ||||
|         /// <summary> | ||||
|         /// 转为Json | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         public virtual string ToJson() | ||||
|             => JsonParser.ToJson(this); | ||||
|         public virtual string ToJson() => 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 | ||||
| { | ||||
|     using System; | ||||
|     using System.Collections.Generic; | ||||
|     using System.Linq; | ||||
|     using JetBrains.Annotations; | ||||
|     using UnityEngine; | ||||
|     using Firebase.RemoteConfig; | ||||
|     using Firebase.Extensions; | ||||
|  | @ -13,31 +15,29 @@ namespace Guru | |||
|     /// </summary> | ||||
|     public class RemoteConfigManager | ||||
|     { | ||||
|         public const double DefaultUpdateHours = 12; | ||||
|         private const string Tag = "[Remote]"; | ||||
|         public const double DefaultUpdateHours = 2; | ||||
|         public const double DefaultFetchTimeout = 15; | ||||
|         internal const string Tag = "[Remote]"; | ||||
|         private static bool _initOnce = false; | ||||
|         private static RemoteConfigManager _instance; | ||||
|         public static RemoteConfigManager Instance => _instance ??= new RemoteConfigManager(); | ||||
|          | ||||
|         private FirebaseRemoteConfig _firebaseRemote; | ||||
|         private static bool _isDebug = false; | ||||
|         private static double _updateHours = DefaultUpdateHours; | ||||
|         private static double _fetchIntervalHours = DefaultUpdateHours; | ||||
| 
 | ||||
|         private RemoteConfigModel _model; | ||||
|         internal RemoteConfigModel Model => _model ??= RemoteConfigModel.LoadOrCreate(); | ||||
|          | ||||
|         internal RemoteConfigModel Model | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 if(_model == null) _model = RemoteConfigModel.LoadOrCreate(); | ||||
|                 return _model; | ||||
|             } | ||||
|         } | ||||
|         private Dictionary<string, object> _defaults; | ||||
|         private static Dictionary<string, object> _defaultValues; | ||||
| 
 | ||||
|         public static event Action<bool> OnFetchCompleted; | ||||
| 
 | ||||
|         public static void Init(Dictionary<string, object> defaults, double updateHours = DefaultUpdateHours, bool isDebug = false) | ||||
|         private Dictionary<string, Action<string,string>> _changeEvents; | ||||
| 
 | ||||
|         private Dictionary<string, string> _staticValues; | ||||
|          | ||||
|         public static void Init(Dictionary<string, object> defaults = null, double updateHours = DefaultUpdateHours, bool isDebug = false) | ||||
|         { | ||||
|             if (_initOnce) return; | ||||
|             Instance.InitAssets(defaults, updateHours, isDebug); | ||||
|  | @ -45,19 +45,23 @@ namespace Guru | |||
|          | ||||
|         // 拉取所有的线上配置数据 | ||||
|         // onFetchComplete 传参 true: 拉取成功   false: 拉取失败 | ||||
|         public static void FetchAll(Action<bool> onFetchComplete = null) | ||||
|         public static void FetchAll(bool immediately = false) | ||||
|         { | ||||
|             OnFetchCompleted += onFetchComplete; | ||||
|             if (!_initOnce) Init(null, _updateHours, _isDebug); | ||||
|             Instance.FetchAllConfigs(); | ||||
|             if (!_initOnce) Init(null, _fetchIntervalHours, _isDebug); | ||||
|             Instance.FetchAllConfigs(immediately); | ||||
|         } | ||||
| 
 | ||||
|         public static void AddDefaultValues(Dictionary<string, object> dict) | ||||
|         { | ||||
|             if (!_initOnce) return; | ||||
|             Instance.AppendDefaultValues(dict); | ||||
|         } | ||||
| 
 | ||||
|         #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; | ||||
|             _updateHours = updateHours; | ||||
|             _fetchIntervalHours = updateHours; | ||||
|             _isDebug = isDebug; | ||||
|             _firebaseRemote = FirebaseRemoteConfig.DefaultInstance; | ||||
|             if (_firebaseRemote == null) | ||||
|  | @ -66,36 +70,45 @@ namespace Guru | |||
|                 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; | ||||
|              | ||||
|             // 监听事件合集 | ||||
|             _changeEvents = new Dictionary<string, Action<string,string>>(30); | ||||
|              | ||||
|             FetchAllConfigs(); | ||||
|         } | ||||
| 
 | ||||
|          | ||||
|          | ||||
| 
 | ||||
|         // private void OnConfigUpdateListener() | ||||
|         // { | ||||
|         //     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() | ||||
|         private void AppendDefaultValues(Dictionary<string, object> defaults) | ||||
|         { | ||||
|             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) | ||||
|                 .ContinueWithOnMainThread(task => | ||||
|                 { | ||||
|  | @ -106,11 +119,67 @@ namespace Guru | |||
|                         LogE($" --- FetchAllConfigs fails: {res}"); | ||||
|                         success = false; | ||||
|                     } | ||||
| 
 | ||||
|                     if (success) OnFetchDataCompleted(); | ||||
|                      | ||||
|                     OnFetchCompleted?.Invoke(success); | ||||
|                 }); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         #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 数据接口 | ||||
|  | @ -185,7 +254,7 @@ namespace Guru | |||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     return _firebaseRemote.GetValue(key).BooleanValue; | ||||
|                     return _firebaseRemote?.GetValue(key).BooleanValue ?? defaultValue; | ||||
|                 } | ||||
|                 catch (Exception e) | ||||
|                 { | ||||
|  | @ -195,8 +264,34 @@ namespace Guru | |||
|             return defaultValue; | ||||
|         } | ||||
| 
 | ||||
|         #endregion | ||||
|         /// <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 | ||||
|          | ||||
|         #region 云控值获取 | ||||
| 
 | ||||
|  | @ -232,15 +327,133 @@ namespace Guru | |||
| 
 | ||||
|         #endregion | ||||
|          | ||||
|          | ||||
|         #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 | ||||
| 
 | ||||
|         #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 | ||||
| 
 | ||||
|         private static void LogI(string msg, params object[] args) | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace Guru | ||||
|  | @ -12,7 +13,7 @@ namespace Guru | |||
|     { | ||||
|         private static float SaveInterval = 2f; | ||||
|         private const string SaveKey = "comr.guru.remote.model.save"; | ||||
|         public Dictionary<string, string> Data; | ||||
|         public Dictionary<string, string> configs; | ||||
|         public long last_modified = 0; | ||||
| 
 | ||||
|         private float _lastSavedTime = 0; | ||||
|  | @ -37,7 +38,7 @@ namespace Guru | |||
|         /// <summary> | ||||
|         /// 默认赋值数据 | ||||
|         /// </summary> | ||||
|         private Dictionary<string, string> _defaultData; | ||||
|         private Dictionary<string, string> _defConfigs; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 加载数据 | ||||
|  | @ -70,8 +71,8 @@ namespace Guru | |||
|         /// </summary> | ||||
|         public RemoteConfigModel() | ||||
|         { | ||||
|             _defaultData = new Dictionary<string, string>(20); | ||||
|             Data = new Dictionary<string, string>(20); | ||||
|             _defConfigs = new Dictionary<string, string>(20); | ||||
|             configs = new Dictionary<string, string>(20); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|  | @ -79,7 +80,7 @@ namespace Guru | |||
|         /// </summary> | ||||
|         /// <param name="key"></param> | ||||
|         /// <returns></returns> | ||||
|         public bool HasKey(string key) => Data.ContainsKey(key); | ||||
|         public bool HasKey(string key) => configs.ContainsKey(key); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 保存数据 | ||||
|  | @ -100,13 +101,12 @@ namespace Guru | |||
|         /// </summary> | ||||
|         /// <param name="key"></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)) | ||||
|             { | ||||
|                 Data[key] = value; | ||||
|                 Save(); | ||||
|                 SetConfigValue(key, value); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|  | @ -115,10 +115,10 @@ namespace Guru | |||
|         /// </summary> | ||||
|         /// <param name="key"></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(); | ||||
|             SetDefault(key, json); | ||||
|             SetDefaultConfig(key, json); | ||||
|         } | ||||
|          | ||||
|         /// <summary> | ||||
|  | @ -127,15 +127,59 @@ namespace Guru | |||
|         /// <param name="key"></param> | ||||
|         /// <typeparam name="T"></typeparam> | ||||
|         /// <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); | ||||
|         } | ||||
|          | ||||
|         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,16 +1,16 @@ | |||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
|     using System.Collections; | ||||
|     using System.Collections.Generic; | ||||
|     using UnityEngine; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Guru 内置浏览器 | ||||
|     /// </summary> | ||||
|     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() | ||||
|         { | ||||
|  | @ -23,10 +23,14 @@ namespace Guru | |||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 打开链接 | ||||
|         /// 打开页面 | ||||
|         /// </summary> | ||||
|         /// <param name="url"></param> | ||||
|         public static void OpenPage(string url, bool showToolbar = true) | ||||
|         /// <param name="url">页面链接</param> | ||||
|         /// <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}"); | ||||
|             var view = CreateWebView(); | ||||
|  | @ -40,14 +44,19 @@ namespace Guru | |||
|             } | ||||
| 
 | ||||
|             view.Load(url); | ||||
|             view.OnPageFinished += (v, code, msg) => | ||||
|             if (waitForReady) | ||||
|             { | ||||
|                 // 加载完成后展示页面 | ||||
|                 view.Show(true); | ||||
|             }; | ||||
| 
 | ||||
|                 view.OnPageFinished += (v, code, msg) => | ||||
|                 { | ||||
|                     // 加载完成后展示页面 | ||||
|                     view.Show(fadeIn, duration:WindowFadeDuration); | ||||
|                 }; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 view.Show(fadeIn, duration:WindowFadeDuration);   //直接加载页面 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										16
									
								
								package.json
								
								
								
								
							
							
						
						
									
										16
									
								
								package.json
								
								
								
								
							|  | @ -12,21 +12,5 @@ | |||
|   "relatedPackages": { | ||||
|   }, | ||||
|   "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