429 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			429 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Globalization;
 | 
						|
using Firebase.Crashlytics;
 | 
						|
using UnityEngine;
 | 
						|
 | 
						|
 | 
						|
namespace Guru
 | 
						|
{
 | 
						|
    
 | 
						|
    
 | 
						|
    /// <summary>
 | 
						|
    /// MAX KeyWord管理器
 | 
						|
    /// </summary>
 | 
						|
    public class KeywordsManager
 | 
						|
    {
 | 
						|
        public const string Version = "0.4.0"; 
 | 
						|
        
 | 
						|
        public static readonly string Tag = "[KWM]";
 | 
						|
 | 
						|
        public static readonly DateTime BaseDate = new DateTime(1970, 1, 1).ToUniversalTime();
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// 日期转时间戳 (Ticks)
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="date"></param>
 | 
						|
        /// <returns></returns>
 | 
						|
        public static long DateToTicks(DateTime date) => (date.ToUniversalTime() - BaseDate).Ticks;
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// 时间戳 (Ticks) 转日期
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="stamp"></param>
 | 
						|
        /// <returns></returns>
 | 
						|
        public static DateTime TicksToDate(long stamp) => BaseDate.AddTicks(stamp);
 | 
						|
        
 | 
						|
        /// <summary>
 | 
						|
        /// 首次安装时间
 | 
						|
        /// </summary>
 | 
						|
        public static DateTime FirstInstallDate
 | 
						|
        {
 | 
						|
            get
 | 
						|
            {
 | 
						|
                var key = nameof(FirstInstallDate);
 | 
						|
                if (PlayerPrefs.HasKey(key))
 | 
						|
                {
 | 
						|
                    var value = PlayerPrefs.GetString(key, "");
 | 
						|
                    if (!string.IsNullOrEmpty(value) && long.TryParse(value, out var ticks))
 | 
						|
                    {
 | 
						|
                        return TicksToDate(ticks);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                
 | 
						|
                Debug.Log($"{Tag} Can't find first install date, using NOW!!");
 | 
						|
                FirstInstallDate = UTCNow;
 | 
						|
                return UTCNow;
 | 
						|
            }
 | 
						|
 | 
						|
            set => PlayerPrefs.SetString(nameof(FirstInstallDate), DateToTicks(value).ToString(new CultureInfo("en")));
 | 
						|
        }
 | 
						|
        public static DateTime UTCNow => DateTime.Now.ToUniversalTime();
 | 
						|
        
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// 当前的LT
 | 
						|
        /// </summary>
 | 
						|
        public static int CurrentLT => Instance?.GetLTValue() ?? -1;
 | 
						|
        
 | 
						|
        private static KeywordsManager _instance;
 | 
						|
        public static KeywordsManager Instance {
 | 
						|
            get {
 | 
						|
                if(_instance == null) _instance = new KeywordsManager();
 | 
						|
                return _instance;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        public static readonly List<int> ItDays = new List<int>()
 | 
						|
        {
 | 
						|
            0, 1, 2, 3, 4, 5, 6, 14, 30, 60, 90, 120, 180
 | 
						|
        };
 | 
						|
        
 | 
						|
        private static string _badsWaterfallName = "null";
 | 
						|
        private static string _iadsWaterfallName = "null";
 | 
						|
        private static string _radsWaterfallName = "null";
 | 
						|
 | 
						|
        // Banner 瀑布流名称
 | 
						|
        public static string BadsWaterfallName => _badsWaterfallName;
 | 
						|
        // Interstital 瀑布流名称
 | 
						|
        public static string IadsWaterfallName => _iadsWaterfallName;
 | 
						|
        // Reward 瀑布流名称
 | 
						|
        public static string RadsWaterfallName => _radsWaterfallName;
 | 
						|
        /// <summary>
 | 
						|
        /// 是否具有用户标签
 | 
						|
        /// </summary>
 | 
						|
        public static bool IsUserMarked
 | 
						|
        {
 | 
						|
            get
 | 
						|
            {
 | 
						|
                if(!PlayerPrefs.HasKey(nameof(IsUserMarked))) return false;
 | 
						|
                return !string.IsNullOrEmpty(PlayerPrefs.GetString(nameof(IsUserMarked), ""));
 | 
						|
            }
 | 
						|
            set => PlayerPrefs.SetString(nameof(IsUserMarked), value? "marked": "");
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// 是否是付费用户
 | 
						|
        /// 在用户执行任何一次购买的时候需要更新该接口
 | 
						|
        /// </summary>
 | 
						|
        public static bool IsIapUser
 | 
						|
        {
 | 
						|
            get => PlayerPrefs.GetInt(nameof(IsIapUser), 0) == 1;
 | 
						|
            set => PlayerPrefs.SetInt(nameof(IsIapUser), value ? 1 : 0);
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        #region 初始化
 | 
						|
        
 | 
						|
        /// <summary>
 | 
						|
        /// 安装服务
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="isIAPUser">标记用户是否为付费用户</param>
 | 
						|
        /// <param name="finishedLevels">当前完成关卡数. 不做关卡过滤时. 默认赋值0即可</param>
 | 
						|
        public static void Install(bool isIAPUser = false, int finishedLevels = 0)
 | 
						|
        {
 | 
						|
            try
 | 
						|
            {
 | 
						|
                // 首先检查用户属性
 | 
						|
                if (finishedLevels > 0 && !CheckTargetUser(finishedLevels))
 | 
						|
                {
 | 
						|
                    Debug.Log($"{Tag} User is not new installing. Skip user....");
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
                
 | 
						|
                if (KeywordsConfig.Enabled()) // 云控开关, 如果不需要云控, 可注释此行
 | 
						|
                {
 | 
						|
                    Instance.Init();
 | 
						|
                    if (!IsIapUser && isIAPUser) IsIapUser = true; // 判断未赋值时, 更新此变量
 | 
						|
                }
 | 
						|
            }
 | 
						|
            catch (Exception e)
 | 
						|
            {
 | 
						|
                Crashlytics.LogException(e);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// 初始化
 | 
						|
        /// </summary>
 | 
						|
        private void Init()
 | 
						|
        {
 | 
						|
            var lt = GetLTValue();  // 初始化立即更新LT
 | 
						|
            Debug.Log($"{Tag} is inited. LT:{lt}"); // 显示启动日志
 | 
						|
            SetupMaxCallbacks();
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        private void SetupMaxCallbacks()
 | 
						|
        {
 | 
						|
            MaxSdkCallbacks.OnSdkInitializedEvent += OnMaxSdkInitializedCallBack;
 | 
						|
            
 | 
						|
            MaxSdkCallbacks.Banner.OnAdLoadedEvent += OnBannerLoaded;
 | 
						|
            MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += OnBannerLoadFailed;
 | 
						|
            MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnInterstitialLoaded;
 | 
						|
            MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnInterstitialLoadFailed;
 | 
						|
            MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnRewardedLoaded;
 | 
						|
            MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnRewardedLoadedFailed;
 | 
						|
        }
 | 
						|
        
 | 
						|
        /// <summary>
 | 
						|
        /// 检查是否是目标用户
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="finishedLevels"></param>
 | 
						|
        /// <returns></returns>
 | 
						|
        public static bool CheckTargetUser(int finishedLevels)
 | 
						|
        {
 | 
						|
            // 如果用户已经被标记, 则继续上报
 | 
						|
            if (IsUserMarked) return true;
 | 
						|
 | 
						|
            // 用户未标记, 则根据完成的关卡判断用户是否是新用户
 | 
						|
            // 新用户则给标记
 | 
						|
            if (finishedLevels <= 2)
 | 
						|
            {
 | 
						|
                IsUserMarked = true;
 | 
						|
                Debug.Log($"{Tag} User is new installing. Mark user as target....");
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
            
 | 
						|
            // 用户未标记且已经开始过游戏了, 则视为老用户
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        
 | 
						|
        #endregion
 | 
						|
        
 | 
						|
        #region Keywords
 | 
						|
        
 | 
						|
        /// <summary>
 | 
						|
        /// 上报关键字
 | 
						|
        /// </summary>
 | 
						|
        private void ReportKeyword()
 | 
						|
        {
 | 
						|
            var lt = GetLTValue();
 | 
						|
            bool paid = IsIapUser;
 | 
						|
            string version = Application.version;
 | 
						|
            var keywords = new [] { $"lt:{lt}", $"paid:{paid.ToString().ToLower()}", $"app_version:{version}" };
 | 
						|
            SetMaxKeywords(keywords);
 | 
						|
        }
 | 
						|
        
 | 
						|
        /// <summary>
 | 
						|
        /// 获取LT值
 | 
						|
        /// </summary>
 | 
						|
        /// <returns></returns>
 | 
						|
        private int GetLTValue(int days = 0)
 | 
						|
        {
 | 
						|
            // Debug.Log($"{Tag} Get first install date: {FirstInstallDate}");
 | 
						|
            if(0 == days) days = (int)(UTCNow - FirstInstallDate).TotalDays;
 | 
						|
            if(days > ItDays[ItDays.Count-1]) return ItDays[ItDays.Count-1];
 | 
						|
            
 | 
						|
            for (int i = 0; i < ItDays.Count; i++)
 | 
						|
            {
 | 
						|
                if (days < ItDays[i])
 | 
						|
                {
 | 
						|
                    if (i > 0)
 | 
						|
                    {
 | 
						|
                        return ItDays[i - 1];
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        return ItDays[0];
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return ItDays[0];
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// 设置Keywords
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="keywords"></param>
 | 
						|
        private void SetMaxKeywords(string[] keywords)
 | 
						|
        {
 | 
						|
            if ( null == keywords ) return;
 | 
						|
            Debug.Log($"{Tag} set keywords: [{string.Join(",", keywords)}]");
 | 
						|
            MaxSdk.TargetingData.ClearAll();
 | 
						|
            MaxSdk.TargetingData.Keywords = keywords;
 | 
						|
        }
 | 
						|
        
 | 
						|
 | 
						|
        #endregion
 | 
						|
        
 | 
						|
        #region WaterfallName
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Banner WaterfallInfo
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="waterfallInfo"></param>
 | 
						|
        private void OnGetBannerWaterInfo(MaxSdkBase.WaterfallInfo waterfallInfo)
 | 
						|
        {
 | 
						|
            _badsWaterfallName = waterfallInfo.Name;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// IV WaterfallInfo
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="waterfallInfo"></param>
 | 
						|
        private void OnGetIVWaterInfo(MaxSdkBase.WaterfallInfo waterfallInfo)
 | 
						|
        {
 | 
						|
            _iadsWaterfallName = waterfallInfo.Name;
 | 
						|
        }
 | 
						|
        
 | 
						|
        /// <summary>
 | 
						|
        /// RV WaterfallInfo
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="waterfallInfo"></param>
 | 
						|
        private void OnGetRVWaterInfo(MaxSdkBase.WaterfallInfo waterfallInfo)
 | 
						|
        {
 | 
						|
            _radsWaterfallName = waterfallInfo.Name;
 | 
						|
        }
 | 
						|
        
 | 
						|
        #endregion
 | 
						|
        
 | 
						|
        #region Max 生命周期监控
 | 
						|
 | 
						|
        private void OnMaxSdkInitializedCallBack(MaxSdkBase.SdkConfiguration obj)
 | 
						|
        {
 | 
						|
            ReportKeyword();
 | 
						|
        }
 | 
						|
        
 | 
						|
        
 | 
						|
        private void OnBannerLoaded(string adUnitId, MaxSdkBase.AdInfo adInfo)
 | 
						|
        {
 | 
						|
            OnGetBannerWaterInfo(adInfo.WaterfallInfo);
 | 
						|
        }
 | 
						|
        
 | 
						|
        private void OnBannerLoadFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
 | 
						|
        {
 | 
						|
            OnGetBannerWaterInfo(errorInfo.WaterfallInfo);
 | 
						|
        }
 | 
						|
        
 | 
						|
        private void OnRewardedLoadedFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
 | 
						|
        {
 | 
						|
            OnGetRVWaterInfo(errorInfo.WaterfallInfo);
 | 
						|
        }
 | 
						|
 | 
						|
        private void OnRewardedLoaded(string adUnitId, MaxSdkBase.AdInfo adInfo)
 | 
						|
        {
 | 
						|
            OnGetRVWaterInfo(adInfo.WaterfallInfo);
 | 
						|
        }
 | 
						|
 | 
						|
        private void OnInterstitialLoadFailed(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
 | 
						|
        {
 | 
						|
            OnGetIVWaterInfo(errorInfo.WaterfallInfo);
 | 
						|
        }
 | 
						|
 | 
						|
        private void OnInterstitialLoaded(string adUnitId, MaxSdkBase.AdInfo adInfo)
 | 
						|
        {
 | 
						|
            OnGetIVWaterInfo(adInfo.WaterfallInfo);
 | 
						|
        }
 | 
						|
 | 
						|
        #endregion
 | 
						|
        
 | 
						|
        #region Unit Test
 | 
						|
 | 
						|
#if UNITY_EDITOR
 | 
						|
        
 | 
						|
        public static void DebugKeywords(string[] kw) => Instance?.SetMaxKeywords(kw);
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// LT取值测试
 | 
						|
        /// </summary>
 | 
						|
        [UnityEditor.MenuItem("Test/Test LT")]
 | 
						|
        public static void TestFetchLT()
 | 
						|
        {
 | 
						|
            var days = 0;
 | 
						|
            days = 0;
 | 
						|
            Debug.Log($"Days: {days} => TL: {Instance.GetLTValue(days)}");
 | 
						|
            days = 1;
 | 
						|
            Debug.Log($"Days: {days} => TL: {Instance.GetLTValue(days)}");
 | 
						|
            days = 2;
 | 
						|
            Debug.Log($"Days: {days} => TL: {Instance.GetLTValue(days)}");
 | 
						|
            days = 5;
 | 
						|
            Debug.Log($"Days: {days} => TL: {Instance.GetLTValue(days)}");
 | 
						|
            days = 12;
 | 
						|
            Debug.Log($"Days: {days} => TL: {Instance.GetLTValue(days)}");
 | 
						|
            days = 15;
 | 
						|
            Debug.Log($"Days: {days} => TL: {Instance.GetLTValue(days)}");
 | 
						|
            days = 55;
 | 
						|
            Debug.Log($"Days: {days} => TL: {Instance.GetLTValue(days)}");
 | 
						|
            days = 80;
 | 
						|
            Debug.Log($"Days: {days} => TL: {Instance.GetLTValue(days)}");
 | 
						|
            days = 179;
 | 
						|
            Debug.Log($"Days: {days} => TL: {Instance.GetLTValue(days)}");
 | 
						|
            days = 365;
 | 
						|
            Debug.Log($"Days: {days} => TL: {Instance.GetLTValue(days)}");
 | 
						|
        }
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
        #endregion
 | 
						|
 | 
						|
    }
 | 
						|
    
 | 
						|
    #region 云控参数
 | 
						|
    
 | 
						|
    [Serializable]
 | 
						|
    internal class KeywordsConfig
 | 
						|
    {
 | 
						|
        public const string KEY = "keywords_config";
 | 
						|
        public bool enable = false;
 | 
						|
        
 | 
						|
        /// <summary>
 | 
						|
        /// 获取功能是否开启
 | 
						|
        /// </summary>
 | 
						|
        /// <returns></returns>
 | 
						|
        public static bool Enabled()
 | 
						|
        {
 | 
						|
            var cfg = Get();
 | 
						|
            return cfg?.enable ?? false;
 | 
						|
        }
 | 
						|
 | 
						|
        public static KeywordsConfig Get()
 | 
						|
        {
 | 
						|
            try
 | 
						|
            {
 | 
						|
                string raw = GetRemoteValue(KEY);
 | 
						|
                if (!string.IsNullOrEmpty(raw))
 | 
						|
                {
 | 
						|
                    return JsonUtility.FromJson<KeywordsConfig>(raw);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            catch (Exception e)
 | 
						|
            {
 | 
						|
                Debug.LogError(e);
 | 
						|
                Crashlytics.LogException(e);
 | 
						|
            }
 | 
						|
            
 | 
						|
            return new KeywordsConfig();  // 返回默认值
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// 获取Online值
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="key"></param>
 | 
						|
        /// <returns></returns>
 | 
						|
        private static string GetRemoteValue(string key)
 | 
						|
        {
 | 
						|
            var remote = Firebase.RemoteConfig.FirebaseRemoteConfig.DefaultInstance;
 | 
						|
            if (remote != null)
 | 
						|
            {
 | 
						|
                try
 | 
						|
                {
 | 
						|
                    return remote.GetValue(key).StringValue;
 | 
						|
                }
 | 
						|
                catch (Exception e)
 | 
						|
                {
 | 
						|
                    Crashlytics.LogException(e);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return "";
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
    }
 | 
						|
    
 | 
						|
    #endregion
 | 
						|
    
 | 
						|
} |