diff --git a/Runtime/GuruCore/Runtime/Analytics/Analytics.cs b/Runtime/GuruCore/Runtime/Analytics/Analytics.cs index 3654715..68e83c2 100644 --- a/Runtime/GuruCore/Runtime/Analytics/Analytics.cs +++ b/Runtime/GuruCore/Runtime/Analytics/Analytics.cs @@ -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; @@ -142,16 +142,16 @@ namespace Guru { FirebaseAnalytics.LogEvent(eventName); } + + if (eventSetting.EnableAdjustAnalytics) + { + Adjust.trackEvent(CreateAdjustEvent(eventName)); + } if (eventSetting.EnableFacebookAnalytics) { FB.LogAppEvent(eventName); } - - if (eventSetting.EnableAdjustAnalytics) - { - Adjust.trackEvent(CreateAdjustEvent(eventName)); - } } /// @@ -191,11 +191,6 @@ namespace Guru } Dictionary dict = new Dictionary(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 diff --git a/Runtime/GuruCore/Runtime/Settings/GuruSettings.cs b/Runtime/GuruCore/Runtime/Settings/GuruSettings.cs index 41cd4b0..b7f1425 100644 --- a/Runtime/GuruCore/Runtime/Settings/GuruSettings.cs +++ b/Runtime/GuruCore/Runtime/Settings/GuruSettings.cs @@ -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"); } + /// + /// 运行时更新Adjust 的 AppToken + /// + /// + /// + public void UpdateAdjustTokens(string androidToken, string iosToken) + { + if(!string.IsNullOrEmpty(androidToken)) + AdjustSetting.androidAppToken = androidToken; + if(!string.IsNullOrEmpty(iosToken)) + AdjustSetting.iOSAppToken = iosToken; + } + + /// + /// 运行时更新 Adjust 的事件列表 + /// + public void UpdateAdjustEvents(IList events) + { + if (events != null && events.Count > 0) + { + List evtList = new List(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 adjustEventList; + [SerializeField] internal List 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; diff --git a/Runtime/GuruRating.meta b/Runtime/GuruRating.meta new file mode 100644 index 0000000..3c1c4c7 --- /dev/null +++ b/Runtime/GuruRating.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 53abb1f4e73b4b44907c0a3c0a406079 +timeCreated: 1687327402 \ No newline at end of file diff --git a/Runtime/GuruRating/README.md b/Runtime/GuruRating/README.md new file mode 100644 index 0000000..84845a4 --- /dev/null +++ b/Runtime/GuruRating/README.md @@ -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!"); +``` + diff --git a/Runtime/GuruRating/README.md.meta b/Runtime/GuruRating/README.md.meta new file mode 100644 index 0000000..1533867 --- /dev/null +++ b/Runtime/GuruRating/README.md.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6fd1be4cf92d4854b9fad97b1bd11b7b +timeCreated: 1687327432 \ No newline at end of file diff --git a/Runtime/GuruRating/Runtime.meta b/Runtime/GuruRating/Runtime.meta new file mode 100644 index 0000000..8d80042 --- /dev/null +++ b/Runtime/GuruRating/Runtime.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d608c1853fbb4c4e9538d1af2ee2c65e +timeCreated: 1687327422 \ No newline at end of file diff --git a/Runtime/GuruRating/Runtime/Code.meta b/Runtime/GuruRating/Runtime/Code.meta new file mode 100644 index 0000000..84d8fc7 --- /dev/null +++ b/Runtime/GuruRating/Runtime/Code.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b3f63a18067c45da82d3521e18127a88 +timeCreated: 1687328000 \ No newline at end of file diff --git a/Runtime/GuruRating/Runtime/Code/GuruRating.cs b/Runtime/GuruRating/Runtime/Code/GuruRating.cs new file mode 100644 index 0000000..8c20898 --- /dev/null +++ b/Runtime/GuruRating/Runtime/Code/GuruRating.cs @@ -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; + + /// + /// 评价管理器 + /// + public class GuruRating: Singleton + { + + public static bool ShowLog { get; set; } = false; + + #region 初始化 + + protected override void Init() + { + base.Init(); + } + + #endregion + + #region 评价显示 + +#if UNITY_ANDROID + + private bool _isOnRating = false; + /// + /// 显示Google的评价 + /// + private void ShowGoogleRating() + { + if (_isOnRating) return; + _isOnRating = true; + OnGoogleRating(); + } + + /// + /// 启动Google评价流程 + /// + 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 + /// + /// 显示苹果的Rating + /// + private void ShowAppleRating() => Device.RequestStoreReview(); // 显示苹果的评价面板 +#endif + + #endregion + + #region 发送邮件 + + /// + /// 设置邮件显示 + /// + /// + /// + 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); + } + + /// + /// 获取默认的邮件标题 + /// + /// + private string GetDefaultMailTitle() + { + return $"Some suggestions for improving {GuruSettings.Instance.ProductName}"; + } + + /// + /// 获取默认的邮件内容模版 + /// + 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 工具接口 + + /// + /// 转换为URL编码 + /// + /// + /// + public static string EscapeUrl(string str) + { + return UnityWebRequest.EscapeURL(str).Replace("+", "%20"); + } + + + + #endregion + + #region 公开接口 + + /// + /// 显示评价面板 + /// + 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 + } + + + /// + /// 设置邮件反馈 + /// + /// + /// + public static void SetEmailFeedback(string email, string subject = "", string body = "") + { + Instance.SendEmail(email, subject, body); + } + + #endregion + + } +} \ No newline at end of file diff --git a/Runtime/GuruRating/Runtime/Code/GuruRating.cs.meta b/Runtime/GuruRating/Runtime/Code/GuruRating.cs.meta new file mode 100644 index 0000000..2543cd0 --- /dev/null +++ b/Runtime/GuruRating/Runtime/Code/GuruRating.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8d36e44d41f0423f8f9f6c23600ec512 +timeCreated: 1687328021 \ No newline at end of file diff --git a/Runtime/GuruRemote/Runtime/IRemoteConfig.cs b/Runtime/GuruRemote/Runtime/IRemoteConfig.cs index a20bf3d..cc4d018 100644 --- a/Runtime/GuruRemote/Runtime/IRemoteConfig.cs +++ b/Runtime/GuruRemote/Runtime/IRemoteConfig.cs @@ -1,5 +1,3 @@ -using Google; - namespace Guru { using System; @@ -7,21 +5,10 @@ namespace Guru /// /// 运控配置接口类 /// - public interface IRemoteConfig + public interface IRemoteConfig where T : IRemoteConfig { bool enable { get; set; } - string key { get; } - string value { get; } - - Action OnValueChanged { get; set; } - - void Init(); + Action OnValueChanged { get; set; } string ToJson(); - string GetDefaultValue(); - string GetRemoteValue(); - - void GetRemoteJsonAsync(Action onValueLoaded); - - } } \ No newline at end of file diff --git a/Runtime/GuruRemote/Runtime/RemoteConfigBase.cs b/Runtime/GuruRemote/Runtime/RemoteConfigBase.cs index 023f3e3..7059290 100644 --- a/Runtime/GuruRemote/Runtime/RemoteConfigBase.cs +++ b/Runtime/GuruRemote/Runtime/RemoteConfigBase.cs @@ -1,45 +1,20 @@ -using System; - namespace Guru { - public abstract class RemoteConfigBase: IRemoteConfig + using System; + + public abstract class RemoteConfigBase: IRemoteConfig where T : IRemoteConfig { - /// /// 配置是否可用 /// public bool enable { get; set; } = true; - - public virtual string key { get; } = "remote-config-base"; - public virtual string value { get; } = "{ \"enable\": true }"; - public Action OnValueChanged { get; set; } - - - public void Init() - { - - } - + public Action OnValueChanged { get; set; } /// /// 转为Json /// /// - public virtual string ToJson() - => JsonParser.ToJson(this); - - public string GetDefaultValue() - { - throw new NotImplementedException(); - } - - public string GetRemoteValue() - { - throw new NotImplementedException(); - } - - public void GetRemoteJsonAsync(Action onValueLoaded) - { - throw new NotImplementedException(); - } + public virtual string ToJson() => JsonParser.ToJson(this); + + } } \ No newline at end of file diff --git a/Runtime/GuruRemote/Runtime/RemoteConfigManager.cs b/Runtime/GuruRemote/Runtime/RemoteConfigManager.cs index ed6aae4..1b26dbc 100644 --- a/Runtime/GuruRemote/Runtime/RemoteConfigManager.cs +++ b/Runtime/GuruRemote/Runtime/RemoteConfigManager.cs @@ -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 /// 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 - { - get - { - if(_model == null) _model = RemoteConfigModel.LoadOrCreate(); - return _model; - } - } - private Dictionary _defaults; + internal RemoteConfigModel Model => _model ??= RemoteConfigModel.LoadOrCreate(); + + private static Dictionary _defaultValues; public static event Action OnFetchCompleted; + + private Dictionary> _changeEvents; + + private Dictionary _staticValues; - public static void Init(Dictionary defaults, double updateHours = DefaultUpdateHours, bool isDebug = false) + public static void Init(Dictionary 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 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 dict) + { + if (!_initOnce) return; + Instance.AppendDefaultValues(dict); } #region 初始化 - private void InitAssets(Dictionary defaults, double updateHours = DefaultUpdateHours, bool isDebug = false) + private void InitAssets(Dictionary defaults = null, double updateHours = DefaultUpdateHours, bool isDebug = false) { - _defaults = defaults; - _updateHours = updateHours; + _fetchIntervalHours = updateHours; _isDebug = isDebug; _firebaseRemote = FirebaseRemoteConfig.DefaultInstance; if (_firebaseRemote == null) @@ -65,37 +69,46 @@ namespace Guru LogE("Can't find FirebaseRemoteConfig.DefaultInstance, init failed."); 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>(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 defaults) { - var span = _isDebug? TimeSpan.Zero : TimeSpan.FromHours(_updateHours); + if (defaults != null) + { + if(_defaultValues == null) _defaultValues = new Dictionary(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,6 +119,9 @@ namespace Guru LogE($" --- FetchAllConfigs fails: {res}"); success = false; } + + if (success) OnFetchDataCompleted(); + OnFetchCompleted?.Invoke(success); }); } @@ -113,6 +129,59 @@ namespace Guru #endregion + #region Model + + /// + /// 判断是否为 Config 参数 + /// + /// + /// + 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; + } + + /// + /// 拉取成功 + /// + private void OnFetchDataCompleted() + { + var values = _firebaseRemote.AllValues; + var updates = new Dictionary(values.Count); + var configs = new Dictionary(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 数据接口 public string GetStringValue(string key, string defaultValue = "") @@ -185,7 +254,7 @@ namespace Guru { try { - return _firebaseRemote.GetValue(key).BooleanValue; + return _firebaseRemote?.GetValue(key).BooleanValue ?? defaultValue; } catch (Exception e) { @@ -195,9 +264,35 @@ namespace Guru return defaultValue; } + /// + /// 获取全部值 + /// + /// + public static Dictionary GetAllValues() + { + try + { + return (Dictionary)(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 云控值获取 public static string GetString(string key, string defaultValue = "") @@ -232,14 +327,132 @@ namespace Guru #endregion - #region 云控配置获取 - + /// + /// 注册云控配置对象 + /// + /// + /// + public static void RegisterConfig(string key, string defaultJson) + { + Instance.Model.SetDefaultConfig(key, defaultJson); // 配置默认值 + } + + + /// + /// 获取云控配置 + /// + /// + /// + /// + public static T GetConfig(string key) where T : IRemoteConfig + { + var config = Instance.Model.Get(key); + return config; + } #endregion + + #region 监听云控值变化 + + /// + /// 注册值变化事件 + /// + /// + /// + public static void RegisterOnValueChanged(string key, Action onValueChanged) + { + Instance.AddOnValueChangeListener(key, onValueChanged); + } + /// + /// 取消注册值变化事件 + /// + /// + /// + public static void UnRegisterOnValueChanged(string key, Action onValueChanged) + { + Instance.RemoveOnValueChangeListener(key, onValueChanged); + } + + + private void AddOnValueChangeListener(string key, Action onValueChanged) + { + if (_changeEvents == null) _changeEvents = new Dictionary>(30); + + if (HasOnValueChangeListener(key)) + { + _changeEvents[key] += onValueChanged; + } + else + { + _changeEvents[key] = onValueChanged; + } + } + + private void RemoveOnValueChangeListener(string key, Action 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 updates) + { + Dictionary changes = new Dictionary(updates.Count); + + if (_staticValues == null) _staticValues = new Dictionary(); + + 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 diff --git a/Runtime/GuruRemote/Runtime/RemoteConfigModel.cs b/Runtime/GuruRemote/Runtime/RemoteConfigModel.cs index abee9d4..19d8ad9 100644 --- a/Runtime/GuruRemote/Runtime/RemoteConfigModel.cs +++ b/Runtime/GuruRemote/Runtime/RemoteConfigModel.cs @@ -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 Data; + public Dictionary configs; public long last_modified = 0; private float _lastSavedTime = 0; @@ -37,7 +38,7 @@ namespace Guru /// /// 默认赋值数据 /// - private Dictionary _defaultData; + private Dictionary _defConfigs; /// /// 加载数据 @@ -70,8 +71,8 @@ namespace Guru /// public RemoteConfigModel() { - _defaultData = new Dictionary(20); - Data = new Dictionary(20); + _defConfigs = new Dictionary(20); + configs = new Dictionary(20); } /// @@ -79,7 +80,7 @@ namespace Guru /// /// /// - public bool HasKey(string key) => Data.ContainsKey(key); + public bool HasKey(string key) => configs.ContainsKey(key); /// /// 保存数据 @@ -100,13 +101,12 @@ namespace Guru /// /// /// - 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 /// /// /// - public void SetDefault(string key, T config) where T : IRemoteConfig + public void SetDefaultConfig(string key, T config) where T : IRemoteConfig { var json = config.ToJson(); - SetDefault(key, json); + SetDefaultConfig(key, json); } /// @@ -127,15 +127,59 @@ namespace Guru /// /// /// - public T Get(string key) where T : IRemoteConfig + public T Get(string key) where T : IRemoteConfig { - if(HasKey(key)) return JsonParser.ToObject(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(json); + } + + Log.E(RemoteConfigManager.Tag, $" --- Remote Key {key} has never been registered."); return default(T); } - - public void Set(string key, T config) where T : IRemoteConfig + + + /// + /// 设置对象值 + /// + /// + /// + /// + internal void SetConfigValue(string key, string value) { - + configs[key] = value; + Save(); + } + + /// + /// 更新所有的配置 + /// + /// + public void UpdateConfigs(Dictionary 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); // 直接保存 } diff --git a/Runtime/GuruWebview/Runtime/GuruWebview.cs b/Runtime/GuruWebview/Runtime/GuruWebview.cs index 4c512a9..e245c6e 100644 --- a/Runtime/GuruWebview/Runtime/GuruWebview.cs +++ b/Runtime/GuruWebview/Runtime/GuruWebview.cs @@ -1,17 +1,17 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - namespace Guru { + using System.Collections; + using System.Collections.Generic; + using UnityEngine; /// /// Guru 内置浏览器 /// 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() { var go = new GameObject("guru_webview"); @@ -23,10 +23,14 @@ namespace Guru } /// - /// 打开链接 + /// 打开页面 /// - /// - public static void OpenPage(string url, bool showToolbar = true) + /// 页面链接 + /// 显示工具条 + /// 等待加载完成后再显示页面 + /// 淡入显示效果 + 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); //直接加载页面 + } } } -} - +} \ No newline at end of file diff --git a/package.json b/package.json index 4a437d4..716e80d 100644 --- a/package.json +++ b/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" } } \ No newline at end of file