update: 更新接口, 删除包引用

feature/Inventory
胡宇飞 2023-12-27 20:25:57 +08:00
parent a0532a5ced
commit 29f0d32c36
15 changed files with 659 additions and 160 deletions

View File

@ -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

View File

@ -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;

3
Runtime/GuruRating.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 53abb1f4e73b4b44907c0a3c0a406079
timeCreated: 1687327402

View File

@ -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!");
```

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6fd1be4cf92d4854b9fad97b1bd11b7b
timeCreated: 1687327432

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d608c1853fbb4c4e9538d1af2ee2c65e
timeCreated: 1687327422

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b3f63a18067c45da82d3521e18127a88
timeCreated: 1687328000

View File

@ -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
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8d36e44d41f0423f8f9f6c23600ec512
timeCreated: 1687328021

View File

@ -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);
} }
} }

View File

@ -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();
}
} }
} }

View File

@ -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

View File

@ -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); // 直接保存
} }

View File

@ -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); //直接加载页面
}
} }
} }
} }

View File

@ -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"
} }
} }