Compare commits
	
		
			No commits in common. "main" and "1.0.0" have entirely different histories. 
		
	
	
		| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: f9c48f29655a4b8bb90520de5a7c2fa4
 | 
					 | 
				
			||||||
timeCreated: 1719552474
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,863 +0,0 @@
 | 
				
			||||||
#if GURU_SDK_DEV
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru.Editor
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using System.IO;
 | 
					 | 
				
			||||||
    using UnityEditor;
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    using UnityEngine.Networking;
 | 
					 | 
				
			||||||
    using System.Collections.Generic;
 | 
					 | 
				
			||||||
    using System.Linq;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public class GuruServiceJsonBuilder: EditorWindow
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        const string GuruProjectSettingsName = "guru-project-settings.cfg";
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Tool Version
 | 
					 | 
				
			||||||
        public const string Version = "0.2.0";
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private const string K_APP_SETTINGS = "app_settings";
 | 
					 | 
				
			||||||
        private const string K_ADJUST_SETTINGS = "adjust_settings";
 | 
					 | 
				
			||||||
        private const string K_FB_SETTINGS = "fb_settings";
 | 
					 | 
				
			||||||
        private const string K_AD_SETTINGS = "ad_settings";
 | 
					 | 
				
			||||||
        private const string K_IAP_SETTINGS = "iap_settings";
 | 
					 | 
				
			||||||
        private const char K_SPLITTER_TAB = '\t';
 | 
					 | 
				
			||||||
        private const char K_SPLITTER_COMMA = ',';
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private const string NoSelectionName = "------";
 | 
					 | 
				
			||||||
        private const string STATE_IDLE = "s_idle";
 | 
					 | 
				
			||||||
        private const string STATE_LOADING = "s_loading";
 | 
					 | 
				
			||||||
        private const string STATE_SUCCESS = "s_success";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static GuruServiceJsonBuilder _instance;
 | 
					 | 
				
			||||||
        private List<string> _projectNames;
 | 
					 | 
				
			||||||
        private int _selectedProjectIndex = 0;
 | 
					 | 
				
			||||||
        private static Dictionary<string, string> _publishLinks;
 | 
					 | 
				
			||||||
        public static Dictionary<string, string> PublishLinks
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (_publishLinks == null) _publishLinks = LoadProjectSettingsCfg();
 | 
					 | 
				
			||||||
                return _publishLinks;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Link & Keys
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private const string TSVLink = "https://docs.google.com/spreadsheets/d/e/{0}/pub?gid=0&single=true&output=tsv";
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Export JSON
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 从 TSV 文件进行转化
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="tsv"></param>
 | 
					 | 
				
			||||||
        /// <param name="savePath"></param>
 | 
					 | 
				
			||||||
        public static void ConvertFromTSV(string tsv, string savePath = "")
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(tsv))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                EditorUtility.DisplayDialog("空文件!", $"文件格式错误!\n{tsv}", "OK");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            var guru_service = EditorGuruServiceIO.CreateEmpty();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var lines = tsv.Split('\n');
 | 
					 | 
				
			||||||
            string line = "";
 | 
					 | 
				
			||||||
            for (int index = 0; index < lines.Length; index++)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                line = lines[index];
 | 
					 | 
				
			||||||
                if (!IsInvalidLine(line))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    //---------------- app_settings ----------------
 | 
					 | 
				
			||||||
                    if (line.StartsWith(K_APP_SETTINGS))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        index++;
 | 
					 | 
				
			||||||
                        while (!line.StartsWith(K_ADJUST_SETTINGS))
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            line = lines[index];
 | 
					 | 
				
			||||||
                            FillAppSettings(guru_service, line);
 | 
					 | 
				
			||||||
                            index++;
 | 
					 | 
				
			||||||
                            line = lines[index];
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    //---------------- adjust_settings ----------------
 | 
					 | 
				
			||||||
                    if (line.StartsWith(K_ADJUST_SETTINGS))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        index++;
 | 
					 | 
				
			||||||
                        FillAdjustSettings(guru_service, lines, ref index);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    //---------------- fb_settings ----------------
 | 
					 | 
				
			||||||
                    if (line.StartsWith(K_FB_SETTINGS))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        index++;
 | 
					 | 
				
			||||||
                        FillFacebookSettings(guru_service, lines, ref index);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    //---------------- ad_settings ----------------
 | 
					 | 
				
			||||||
                    if (line.StartsWith(K_AD_SETTINGS))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        index++;
 | 
					 | 
				
			||||||
                        FillAdSettings(guru_service, lines, ref index);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    //---------------- iap_settings ----------------
 | 
					 | 
				
			||||||
                    if (line.StartsWith(K_IAP_SETTINGS))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        index++;
 | 
					 | 
				
			||||||
                        FillProducts(guru_service, lines, ref index);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            guru_service.version = GetFileVersionByDate();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(savePath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var dir = Path.GetFullPath($"{Application.dataPath}/../output");
 | 
					 | 
				
			||||||
                if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
 | 
					 | 
				
			||||||
                savePath = $"{dir}/guru-services-{guru_service.app_settings.app_id.ToLower()}.json";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var arr = savePath.Split('/');
 | 
					 | 
				
			||||||
            var fileName = arr[arr.Length - 1];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            EditorGuruServiceIO.SourceServiceFilePath = savePath;
 | 
					 | 
				
			||||||
            EditorGuruServiceIO.SaveConfig(guru_service, savePath);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (EditorUtility.DisplayDialog("CONVERT SUCCESS!", $"Export Json File\n{fileName}\nto:\n{savePath}", "OK"))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                GuruEditorHelper.OpenPath(Directory.GetParent(savePath)?.FullName ?? Application.dataPath);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static void ConvertFromTsvFile(string tsvPath, string savePath = "")
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!File.Exists(tsvPath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                EditorUtility.DisplayDialog("FILE NOT FOUND!", $"File not exist:\n{tsvPath}", "OK");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            var tsvString = File.ReadAllText(tsvPath);
 | 
					 | 
				
			||||||
            if (!string.IsNullOrEmpty(tsvString))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ConvertFromTSV(tsvString, savePath);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// AppSettings 填充
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="settings"></param>
 | 
					 | 
				
			||||||
        /// <param name="line"></param>
 | 
					 | 
				
			||||||
        private static void FillAppSettings(GuruServicesConfig settings, string line)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // 对于空行和空值直接跳过
 | 
					 | 
				
			||||||
            if (IsInvalidLine(line)) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            string value = "";
 | 
					 | 
				
			||||||
            if (settings.app_settings == null) settings.app_settings = new GuruAppSettings();
 | 
					 | 
				
			||||||
            if (settings.parameters == null) settings.parameters = new GuruParameters();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //------------------- AppSettings -------------------------------
 | 
					 | 
				
			||||||
            // 拾取值和注入
 | 
					 | 
				
			||||||
            if (GetValue(line, "app_id", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.app_id = value;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "product_name", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.product_name = value;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "bundle_id", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.bundle_id = value;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "support_email", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.support_email = value;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "privacy_url", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.privacy_url = value;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "terms_url", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.terms_url = value;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "android_store", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.android_store = value;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "ios_store", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.ios_store = value;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "enable_firebase", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.enable_firebase = GetBool(value);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "enable_facebook", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.enable_facebook = GetBool(value);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "enable_adjust", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.enable_adjust = GetBool(value);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "enable_iap", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.enable_iap = GetBool(value);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "custom_keystore", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.app_settings.custom_keystore = GetBool(value);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            //------------------- Parameters -------------------------------
 | 
					 | 
				
			||||||
            else if (GetValue(line, "tch_020", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.parameters.tch_020 = GetDouble(value);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "using_uuid", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.parameters.using_uuid = GetBool(value);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "cdn_host", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.parameters.cdn_host = value;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "enable_errorlog", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.parameters.enable_errorlog = GetBool(value);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (GetValue(line, "level_end_success_num", out value))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                settings.parameters.level_end_success_num = GetInt(value);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// AdjustSettings 填充
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="settings"></param>
 | 
					 | 
				
			||||||
        /// <param name="line"></param>
 | 
					 | 
				
			||||||
        private static void FillAdjustSettings(GuruServicesConfig settings, string[] lines, ref int index)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if(settings.adjust_settings == null) settings.adjust_settings = new GuruAdjustSettings();
 | 
					 | 
				
			||||||
            string[] list = null;
 | 
					 | 
				
			||||||
            string line = lines[index];
 | 
					 | 
				
			||||||
            bool pass = false;
 | 
					 | 
				
			||||||
            List<string> events = new List<string>(40);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            while (!lines[index].StartsWith(K_FB_SETTINGS))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                line = lines[index];
 | 
					 | 
				
			||||||
                if (!IsInvalidLine(line))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (line.StartsWith("app_token"))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        list = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                        settings.adjust_settings.app_token = list;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        list = GetStringArray(line, 0, 3);
 | 
					 | 
				
			||||||
                        pass = list != null && !string.IsNullOrEmpty(list[0]) 
 | 
					 | 
				
			||||||
                                            && (!string.IsNullOrEmpty(list[1]) || !string.IsNullOrEmpty(list[2]));
 | 
					 | 
				
			||||||
                        if( pass) events.Add(string.Join(",", list));
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                index++;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            settings.adjust_settings.events = events.ToArray();
 | 
					 | 
				
			||||||
            index--;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static long GetFileVersionByDate()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var startDt = new DateTime(1970,1,1,0,0,0);
 | 
					 | 
				
			||||||
            return (long) (DateTime.UtcNow.Ticks - startDt.Ticks) / 10000;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void FillFacebookSettings(GuruServicesConfig settings, string[] lines, ref int index)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            string value = "";
 | 
					 | 
				
			||||||
            if(settings.fb_settings == null) settings.fb_settings = new GuruFbSettings();
 | 
					 | 
				
			||||||
            var line = "";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            while (!lines[index].StartsWith(K_AD_SETTINGS))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                line = lines[index];
 | 
					 | 
				
			||||||
                if (!IsInvalidLine(line))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    // 拾取值和注入
 | 
					 | 
				
			||||||
                    if (GetValue(line, "fb_app_id", out value))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        settings.fb_settings.fb_app_id = value;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else if (GetValue(line, "fb_client_token", out value))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        settings.fb_settings.fb_client_token = value;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                index++;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            index--;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static void FillAdSettings(GuruServicesConfig settings, string[] lines, ref int index)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            string value = "";
 | 
					 | 
				
			||||||
            if(settings.ad_settings == null) settings.ad_settings = new GuruAdSettings();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            var line = lines[index];
 | 
					 | 
				
			||||||
            // SDK Key
 | 
					 | 
				
			||||||
           
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            string[] max_ids_android = new string[3];
 | 
					 | 
				
			||||||
            string[] max_ids_ios = new string[3];
 | 
					 | 
				
			||||||
            string[] amazon_ids_android = new string[4];
 | 
					 | 
				
			||||||
            string[] amazon_ids_ios = new string[4];
 | 
					 | 
				
			||||||
            string[] pubmatic_ids_android = new string[3];
 | 
					 | 
				
			||||||
            string[] pubmatic_ids_ios = new string[3];
 | 
					 | 
				
			||||||
            string[] moloco_ids_android = new string[3];
 | 
					 | 
				
			||||||
            string[] moloco_ids_ios = new string[3];
 | 
					 | 
				
			||||||
            string[] tradplus_ids_android = new string[3];
 | 
					 | 
				
			||||||
            string[] tradplus_ids_ios = new string[3];
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            //------- 开始记录广告配置;
 | 
					 | 
				
			||||||
            string[] arr;
 | 
					 | 
				
			||||||
            while (!lines[index].StartsWith(K_IAP_SETTINGS))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                line = lines[index];
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                if (GetValue(line, "sdk_key", out value))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    settings.ad_settings.sdk_key = value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("admob_app_id"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    settings.ad_settings.admob_app_id = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("max_bads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    max_ids_android[0] = arr[0];
 | 
					 | 
				
			||||||
                    max_ids_ios[0] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("max_iads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    max_ids_android[1] = arr[0];
 | 
					 | 
				
			||||||
                    max_ids_ios[1] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("max_rads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    max_ids_android[2] = arr[0];
 | 
					 | 
				
			||||||
                    max_ids_ios[2] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("amazon_app_id"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    amazon_ids_android[0] = arr[0];
 | 
					 | 
				
			||||||
                    amazon_ids_ios[0] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("amazon_bads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    amazon_ids_android[1] = arr[0];
 | 
					 | 
				
			||||||
                    amazon_ids_ios[1] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("amazon_iads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    amazon_ids_android[2] = arr[0];
 | 
					 | 
				
			||||||
                    amazon_ids_ios[2] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("amazon_rads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    amazon_ids_android[3] = arr[0];
 | 
					 | 
				
			||||||
                    amazon_ids_ios[3] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("pubmatic_bads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    pubmatic_ids_android[0] = arr[0];
 | 
					 | 
				
			||||||
                    pubmatic_ids_ios[0] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("pubmatic_iads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    pubmatic_ids_android[1] = arr[0];
 | 
					 | 
				
			||||||
                    pubmatic_ids_ios[1] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("pubmatic_rads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    pubmatic_ids_android[2] = arr[0];
 | 
					 | 
				
			||||||
                    pubmatic_ids_ios[2] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("moloco_bads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    moloco_ids_android[0] = arr[0];
 | 
					 | 
				
			||||||
                    moloco_ids_ios[0] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("moloco_iads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    moloco_ids_android[1] = arr[0];
 | 
					 | 
				
			||||||
                    moloco_ids_ios[1] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("moloco_rads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    moloco_ids_android[2] = arr[0];
 | 
					 | 
				
			||||||
                    moloco_ids_ios[2] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("tradplus_bads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    tradplus_ids_android[0] = arr[0];
 | 
					 | 
				
			||||||
                    tradplus_ids_ios[0] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("tradplus_iads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    tradplus_ids_android[1] = arr[0];
 | 
					 | 
				
			||||||
                    tradplus_ids_ios[1] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (line.StartsWith("tradplus_rads"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    arr = GetStringArray(line, 1, 2);
 | 
					 | 
				
			||||||
                    tradplus_ids_android[2] = arr[0];
 | 
					 | 
				
			||||||
                    tradplus_ids_ios[2] = arr[1];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                index++;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            //-------- Fill all data -----------
 | 
					 | 
				
			||||||
            settings.ad_settings.max_ids_android = max_ids_android;
 | 
					 | 
				
			||||||
            settings.ad_settings.max_ids_ios = max_ids_ios;
 | 
					 | 
				
			||||||
            settings.ad_settings.amazon_ids_android = amazon_ids_android;
 | 
					 | 
				
			||||||
            settings.ad_settings.amazon_ids_ios = amazon_ids_ios;
 | 
					 | 
				
			||||||
            settings.ad_settings.pubmatic_ids_android = pubmatic_ids_android;
 | 
					 | 
				
			||||||
            settings.ad_settings.pubmatic_ids_ios = pubmatic_ids_ios;
 | 
					 | 
				
			||||||
            settings.ad_settings.moloco_ids_android = moloco_ids_android;
 | 
					 | 
				
			||||||
            settings.ad_settings.moloco_ids_ios = moloco_ids_ios;
 | 
					 | 
				
			||||||
            settings.ad_settings.tradplus_ids_android = tradplus_ids_android;
 | 
					 | 
				
			||||||
            settings.ad_settings.tradplus_ids_ios = tradplus_ids_ios;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            index--;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static void FillProducts(GuruServicesConfig settings, string[] lines, ref int index)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            string line = "";
 | 
					 | 
				
			||||||
            List<string> iaps = new List<string>(30);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            string[] arr;
 | 
					 | 
				
			||||||
            while (index < lines.Length)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                line = lines[index];
 | 
					 | 
				
			||||||
                if(IsInvalidLine(line)) continue;
 | 
					 | 
				
			||||||
                arr = GetStringArray(line, 0, 7);
 | 
					 | 
				
			||||||
                if(string.IsNullOrEmpty(arr[5])) arr[5] = "Store";
 | 
					 | 
				
			||||||
                if(string.IsNullOrEmpty(arr[6])) arr[6] = "0";
 | 
					 | 
				
			||||||
                iaps.Add(string.Join(",", arr).Replace("\r", ""));
 | 
					 | 
				
			||||||
                index++;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            settings.products = iaps.ToArray();
 | 
					 | 
				
			||||||
            index--;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Utils
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static bool GetBool(string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return value.ToLower() == "true" || value == "1";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static int GetInt(string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            int val = 0;
 | 
					 | 
				
			||||||
            int.TryParse(value, out val);
 | 
					 | 
				
			||||||
            return val;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static double GetDouble(string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            double val = 0;
 | 
					 | 
				
			||||||
            double.TryParse(value, out val);
 | 
					 | 
				
			||||||
            return val;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static bool IsInvalidLine(string line)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return string.IsNullOrEmpty(line) || line.StartsWith(K_SPLITTER_TAB.ToString());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static bool GetValue(string line, string key, out string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            value = ""; // default  
 | 
					 | 
				
			||||||
            if (line.StartsWith(key))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                value = line.Split(K_SPLITTER_TAB)[1];
 | 
					 | 
				
			||||||
                if (string.IsNullOrEmpty(value)) value = "empty";
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string[] GetStringArray(string line, int startIndex = 0, int length = 0, char spliter = K_SPLITTER_TAB)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (IsInvalidLine(line)) return null;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            var raw = line.Split(spliter);
 | 
					 | 
				
			||||||
            if (length == 0) length = raw.Length;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            var a = new List<string>(length);
 | 
					 | 
				
			||||||
            for (int i = startIndex; i < length + startIndex; i++)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (i < raw.Length)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    a.Add(raw[i]);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    a.Add("");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return a.ToArray();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Menu Items
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if GURU_SDK_DEV
 | 
					 | 
				
			||||||
        [MenuItem("Tools/Export Guru Service", false, 0 )]
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        private static void ExportJsonFile()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            string saveDir = Path.GetFullPath($"{Application.dataPath}/../output");
 | 
					 | 
				
			||||||
            string saveFile = Path.Combine(saveDir,$"guru-services____{DateTime.Now:yyyy-M-d-HH-mm}.json");
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if(!Directory.Exists(saveDir)) Directory.CreateDirectory(saveDir);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            string searchPath = "~/Downloads/";
 | 
					 | 
				
			||||||
            string tsvPath = EditorUtility.OpenFilePanel("Load Guru Service TSV", searchPath, ".tsv");
 | 
					 | 
				
			||||||
            if (!string.IsNullOrEmpty(tsvPath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ConvertFromTsvFile(tsvPath, saveFile);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [MenuItem("Guru/Guru-Service Json Builder...", false, 0)]
 | 
					 | 
				
			||||||
        private static void OpenWindow()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if(_instance != null ) _instance.Close();
 | 
					 | 
				
			||||||
            _instance = GetWindow<GuruServiceJsonBuilder>();
 | 
					 | 
				
			||||||
            _instance.Show();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Settings
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string GetRelativeDir()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var guids = AssetDatabase.FindAssets(nameof(GuruServiceJsonBuilder));
 | 
					 | 
				
			||||||
            if (guids.Length > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var path = AssetDatabase.GUIDToAssetPath(guids[0]);
 | 
					 | 
				
			||||||
                var rpath = Directory.GetParent(path).FullName;
 | 
					 | 
				
			||||||
                return rpath;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return Path.GetFullPath($"Assets/../Packages/Editor/GuruJsonBuilder");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static Dictionary<string, string> LoadProjectSettingsCfg()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var cfgPath = $"{GetRelativeDir()}/{GuruProjectSettingsName}";
 | 
					 | 
				
			||||||
            if (File.Exists(cfgPath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var lines = File.ReadAllLines(cfgPath);
 | 
					 | 
				
			||||||
                int len = lines?.Length ?? -1;
 | 
					 | 
				
			||||||
                if (len > 0)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Dictionary<string, string> dict = new Dictionary<string, string>(lines.Length);
 | 
					 | 
				
			||||||
                    int i = 0;
 | 
					 | 
				
			||||||
                    string[] raw;
 | 
					 | 
				
			||||||
                    string line, key, value;
 | 
					 | 
				
			||||||
                    while (i < len)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        line = lines[i];
 | 
					 | 
				
			||||||
                        if(string.IsNullOrEmpty(line)) continue;
 | 
					 | 
				
			||||||
                        raw = lines[i].Split(',');
 | 
					 | 
				
			||||||
                        value = "";
 | 
					 | 
				
			||||||
                        key = raw[0];
 | 
					 | 
				
			||||||
                        if(string.IsNullOrEmpty(key)) continue; 
 | 
					 | 
				
			||||||
                        if(raw.Length > 1) value = raw[1];
 | 
					 | 
				
			||||||
                        dict[key] = value;
 | 
					 | 
				
			||||||
                        i++;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    return dict;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Window
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void Awake()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Debug.Log($"------- Awake -------");
 | 
					 | 
				
			||||||
            this.titleContent = new GUIContent("Json Builder");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _projectNames = new List<string>(20);
 | 
					 | 
				
			||||||
            string[] names = PublishLinks.Keys.ToArray();
 | 
					 | 
				
			||||||
            string name = "";
 | 
					 | 
				
			||||||
            for(int i = 0; i < names.Length; i++)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                name = names[i];
 | 
					 | 
				
			||||||
                if (name == "Default")
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _projectNames.Insert(0, NoSelectionName);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _projectNames.Add(name);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            _selectedProjectIndex = 0;
 | 
					 | 
				
			||||||
            _state = STATE_IDLE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var json = LoadProjectSettingsCfg();
 | 
					 | 
				
			||||||
            Debug.Log(json);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void OnEnable()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // Debug.Log($"------- OnEnable -------");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private void OnGUI()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            GUI_Title();
 | 
					 | 
				
			||||||
            switch (_state)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                case STATE_IDLE:
 | 
					 | 
				
			||||||
                    GUI_Projects();
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                case STATE_LOADING:
 | 
					 | 
				
			||||||
                    GUI_Loading();
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region GUI
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private string _state = "";
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private void GUI_Title()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var s = new GUIStyle("box")
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                fontSize = 20,
 | 
					 | 
				
			||||||
                fontStyle = FontStyle.Bold,
 | 
					 | 
				
			||||||
                stretchWidth = true,
 | 
					 | 
				
			||||||
                alignment = TextAnchor.MiddleCenter,
 | 
					 | 
				
			||||||
                padding = new RectOffset(4, 4, 4, 4),
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
            GUILayout.Label("Guru-Service Json Builder", s, GUILayout.Height(60));
 | 
					 | 
				
			||||||
            s.fontSize = 14;
 | 
					 | 
				
			||||||
            GUILayout.Label($"Version: {Version}", s);
 | 
					 | 
				
			||||||
            GUILayout.Space(10);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private void GUI_Loading()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var s = new GUIStyle("box")
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                fontSize = 16,
 | 
					 | 
				
			||||||
                alignment = TextAnchor.MiddleCenter,
 | 
					 | 
				
			||||||
                stretchWidth = true,
 | 
					 | 
				
			||||||
                padding = new RectOffset(4, 4, 4, 4),
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            GUILayout.Label("Loading...", s);
 | 
					 | 
				
			||||||
            GUILayout.Space(10);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void GUI_Projects()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _selectedProjectIndex = EditorGUILayout.Popup("选择生成项目", _selectedProjectIndex, _projectNames.ToArray());
 | 
					 | 
				
			||||||
            GUILayout.Space(5);
 | 
					 | 
				
			||||||
            if (GUILayout.Button("生成 guru-services.json", GUILayout.Height(40)))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var id = _projectNames[_selectedProjectIndex];
 | 
					 | 
				
			||||||
                if (id == NoSelectionName)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    ShowDialog("选择错误", "请选择一个存在的项目");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    DownloadTsvAndBuild(_selectedProjectIndex, (success, txt) =>
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        if (success)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            ConvertFromTSV(txt);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        else
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            ShowDialog("网络错误", txt);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        _state = STATE_IDLE;
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    _state = STATE_LOADING;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void ShowDialog(string title, string content, string okName = "OK", Action onOKCallback = null,
 | 
					 | 
				
			||||||
            string cancelName = "", Action onCancelCallback = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (EditorUtility.DisplayDialog(title, content, okName, cancelName))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                onOKCallback?.Invoke();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                onCancelCallback?.Invoke();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Networking
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static string GetProjectTSVUrl(string pid)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (PublishLinks.TryGetValue(pid, out var id))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                string url = string.Format(TSVLink, id);
 | 
					 | 
				
			||||||
                return url;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return "";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private void DownloadTsvAndBuild(int projIndex, Action<bool, string> loadCompleted = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var pid = _projectNames[projIndex];
 | 
					 | 
				
			||||||
            var id = GetProjectTSVUrl(pid);
 | 
					 | 
				
			||||||
            string title, msg;
 | 
					 | 
				
			||||||
            if(string.IsNullOrEmpty(id))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                title = "参数错误";
 | 
					 | 
				
			||||||
                msg = $"项目 {pid} 不正确, 请重新选择...";
 | 
					 | 
				
			||||||
                ShowDialog(title, msg);
 | 
					 | 
				
			||||||
                Debug.LogError($"{title}\n{msg}");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            var www = UnityEngine.Networking.UnityWebRequest.Get(id);
 | 
					 | 
				
			||||||
            www.SendWebRequest().completed += ap =>
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (www.result == UnityWebRequest.Result.Success)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Debug.Log($"<color=#088ff00>--- Load Success ---</color>");
 | 
					 | 
				
			||||||
                    // Debug.Log(www.downloadHandler.text);
 | 
					 | 
				
			||||||
                    loadCompleted?.Invoke(true, www.downloadHandler.text);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    msg = $"Result {www.result}: {www.responseCode}\n\r{www.error}";
 | 
					 | 
				
			||||||
                    Debug.LogError(msg);
 | 
					 | 
				
			||||||
                    loadCompleted?.Invoke(false, msg);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region TEST
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if GURU_SDK_DEV
 | 
					 | 
				
			||||||
        // [MenuItem("Tools/Test/Fetch Config File", false, 1)]
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        private static void Test_FetchConfigFile()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var pid = "FindOut";
 | 
					 | 
				
			||||||
            var url = GetProjectTSVUrl(pid);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(string.IsNullOrEmpty(url))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.LogError($"Wrong ProjectId: {pid}");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var www = UnityEngine.Networking.UnityWebRequest.Get(url);
 | 
					 | 
				
			||||||
            www.SendWebRequest().completed += ap =>
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (www.result == UnityWebRequest.Result.Success)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Debug.Log($"<color=#088ff00>--- Load Success ---</color>");
 | 
					 | 
				
			||||||
                    Debug.Log(www.downloadHandler.text);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Debug.LogError($"Loading Failed: {www.error} : {www.result} : {www.responseCode}");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 8c7dc826ea0848f0a3bf05009fab6377
 | 
					 | 
				
			||||||
timeCreated: 1706856053
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,22 +0,0 @@
 | 
				
			||||||
Default
 | 
					 | 
				
			||||||
BallSort,2PACX-1vSPus7415l66-zScY1B1JOgLfSkm0yRPnRDG-BW0JgtnVZJziGDzvtBZr8D9oEZL2x478SdCS2yh0S_
 | 
					 | 
				
			||||||
BallSort2,2PACX-1vSZIZYbFuURK_ZMeMHV9ruL0SWBGMPA13er_J_DLRPVw5HBnU8_6c4mvek5UBFo1Ebbk63TMr-rsY6y
 | 
					 | 
				
			||||||
WaterSort,2PACX-1vTPxWbpP7KnT-e2xg9Uz-nukbLmqYc8SjwNL9MgycIhInNgmAjOmRnmyH6PWm3-hsEugKTJHKDcoKdI
 | 
					 | 
				
			||||||
WaterSort2,2PACX-1vQnZNtE7ZpT6eYagQDt686Be9Jr0tg22sRFg5cGiqFIsCVhWLu6jxDUg7qmyfIrX3iL5awat8FFnN7B
 | 
					 | 
				
			||||||
BallSortPlus,2PACX-1vTa324nQIwNmXNtHC5sZZXYBdpUryKXdb8rFbTAwBa4KVlQ5ZXnErx2IvifkRxD8qKcNupfO1Kv5pJw
 | 
					 | 
				
			||||||
TileBurst,2PACX-1vS5wvSZ5eSlK8TmYwVvgScJstaqN8cAB7Uxlq_nsbQwQ4VeD1BkhQuDbRdcO9ncuOCtoKjzSnviHVmc
 | 
					 | 
				
			||||||
Parking3D,2PACX-1vQY18hIrYjYNOqQNE12BcPtF4gtFMhBbfDrakBcIVQKoStmfPBY7C4O_Efj1Y8sNbytbKZr-0lPmBml
 | 
					 | 
				
			||||||
TileConnect,2PACX-1vRfSd9UbrLilele8Fw1BafFjlvHG5EMnODfbppPiUaacr7wZ62IvgM65SYSjGEXJmU9g9AczollaFCf
 | 
					 | 
				
			||||||
DOF,2PACX-1vSDpYZEUrCioCBRSkoZLra5nWk53Ks1f180TD1g2dnKcm-MZtAYvFSXDieAF4xromZCmxIoZuIfGyCJ
 | 
					 | 
				
			||||||
D2,2PACX-1vS1V35WTTPx2Jdeu6sVjPbaqFpjVwFu1Rn9tZkvxm8aEHbuRWibxt2pxPkLdDzwmCmrGBtU-PilABJm
 | 
					 | 
				
			||||||
FindOut,2PACX-1vQA3R66yWRHmUn6sneeIUU1qXEiaXv6h9QdzYzVRTEOg-yZf7WCJ6tuvXMWzwcOgGihiSnr9shMX__d
 | 
					 | 
				
			||||||
FindIt,2PACX-1vQs75JLsmTv1PESzOP6ANRqVk4zA3Y7PD5OV0yQbA5MM3wmp-hu4qE9gztZy7ETCP2nHJgDziwVpcFE
 | 
					 | 
				
			||||||
AP,2PACX-1vSvXTqbTcNCuHZYbKGA-fPKQj8XuixYUB9UXWqTQXz5QLXNurCtmBlziMbUxte_eqsGO0tB2GXe-sEC
 | 
					 | 
				
			||||||
Arrow,2PACX-1vS5w8rNcJycK-VYgum7gkrLAx2Ln_7wykXLBX4EulI8XNGnlPsVlENQ8LupDdtiIu-JlngJaTw5dIzR
 | 
					 | 
				
			||||||
ApFindDifference,2PACX-1vSmodKOvKjxmiNoYbrXFI6n1XC0aqvlnbYkEGyabLKqS6-C3Yi3nstmC89Hc31cdIzCk1FAl54beW2P
 | 
					 | 
				
			||||||
GoodsSort,2PACX-1vQrg6Ss2bNYAI7A250t3_zp10UJtbbQXb8I4LtDJ2Q3uopUG_PUadoJy7T4w0jlf4xcTqot2NWsfp1m
 | 
					 | 
				
			||||||
CookingBlitz,2PACX-1vShqtSSpwYQ8CvK-BVr-wQ5ygGeKKLdpaaWysSN_QumoDZYATemayAQIOdnFRzMP69nEwFYSx51oey8
 | 
					 | 
				
			||||||
WoodNuts,2PACX-1vQhtRg354eThBpWGCEk5f_2cLVbz1clQXv0n6w4Cyip0Knl6EQ4XwWMlcCec-legZdHU3E0-_cqKipc
 | 
					 | 
				
			||||||
MahjongSolitaire,2PACX-1vQpcaj8CpO__K1KGl-mg_940WOOIXBVzi0lmcjYTt1sqBI2PtK37s29McLTGU2I6N3fWM0ZepChedq7
 | 
					 | 
				
			||||||
NutsSort,2PACX-1vShdEGGEeMp51fMhg0UnMRHA51xfP8OkCdaD3gu-GTZCcJ6NvveP9Q0ahWDwSi1HS4Okyabn3gnLoLU
 | 
					 | 
				
			||||||
FindMaster,2PACX-1vRXB42qVcqE0nhWxPScs_qtaUkkVz_BXmdMiEZm580VC4a3I6Av8MrhOEhixVzg2NcPhQh_8HIHt7wk
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 88694fb6aaa64ea192e0bffb4cb5b744
 | 
					 | 
				
			||||||
timeCreated: 1719552889
 | 
					 | 
				
			||||||
| 
						 | 
					@ -10,15 +10,12 @@ namespace Guru.Editor
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    public class EditorGuruServiceIO
 | 
					    public class EditorGuruServiceIO
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        internal static readonly string SourceConfigFileName = "guru-service";
 | 
					        private static readonly string DefaultFileName = "guru-service";
 | 
				
			||||||
        internal const string LocalServicesConfigPath = "Guru/Resources";
 | 
					 | 
				
			||||||
        internal const string SourceConfigExtension = ".json";
 | 
					 | 
				
			||||||
        internal const string LocalConfigExtension = ".txt";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        internal static string DefaultFilePath =
 | 
					        private static string DefaultFilePath =
 | 
				
			||||||
            Path.GetFullPath(Path.Combine(Application.dataPath, $"{SourceConfigFileName}{SourceConfigExtension}"));
 | 
					            Path.GetFullPath(Path.Combine(Application.dataPath, $"{DefaultFileName}.json"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        internal static string SourceServiceFilePath = "";
 | 
					        internal static string EmbededServiceFilePath = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// 加载配置
 | 
					        /// 加载配置
 | 
				
			||||||
| 
						 | 
					@ -26,18 +23,18 @@ namespace Guru.Editor
 | 
				
			||||||
        /// <returns></returns>
 | 
					        /// <returns></returns>
 | 
				
			||||||
        public static GuruServicesConfig LoadConfig()
 | 
					        public static GuruServicesConfig LoadConfig()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var a = AssetDatabase.FindAssets($"*{SourceConfigFileName}* t:TextAsset", new []{"Assets"});
 | 
					            var a = AssetDatabase.FindAssets($"*{DefaultFileName}* t:TextAsset");
 | 
				
			||||||
            if (a == null || a.Length == 0)
 | 
					            if (a == null || a.Length == 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                UnityEngine.Debug.Log($"<color=orange>--- Can't find guru-services file</color>");
 | 
					                Debug.Log($"<color=orange>--- Can't ind guru-services file</color>");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var p = AssetDatabase.GUIDToAssetPath(a[0]);
 | 
					                var p = AssetDatabase.GUIDToAssetPath(a[0]);
 | 
				
			||||||
                var fp = Path.GetFullPath(p);
 | 
					                var fp = Path.GetFullPath(p);
 | 
				
			||||||
                if (File.Exists(fp)) SourceServiceFilePath = fp;
 | 
					                if (File.Exists(fp)) EmbededServiceFilePath = fp;
 | 
				
			||||||
                var t = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
 | 
					                var t = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
 | 
				
			||||||
                // UnityEngine.Debug.Log($"<color=#88ff00>--- find services file:{p} \n{t.text}</color>");
 | 
					                // Debug.Log($"<color=#88ff00>--- find services file:{p} \n{t.text}</color>");
 | 
				
			||||||
                return JsonMapper.ToObject<GuruServicesConfig>(t.text);
 | 
					                return JsonMapper.ToObject<GuruServicesConfig>(t.text);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
| 
						 | 
					@ -47,7 +44,7 @@ namespace Guru.Editor
 | 
				
			||||||
        /// 保存配置
 | 
					        /// 保存配置
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        /// <param name="config"></param>
 | 
					        /// <param name="config"></param>
 | 
				
			||||||
        internal static void SaveConfig(GuruServicesConfig config = null, string savePath = "")
 | 
					        internal static void SaveConfig(GuruServicesConfig config = null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (config == null)
 | 
					            if (config == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
| 
						 | 
					@ -59,13 +56,12 @@ namespace Guru.Editor
 | 
				
			||||||
                PrettyPrint = true,
 | 
					                PrettyPrint = true,
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            JsonMapper.ToJson(config, jw);
 | 
					            JsonMapper.ToJson(config, jw);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var json = jw.ToString();
 | 
					            var json = jw.ToString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (string.IsNullOrEmpty(savePath)) savePath = SourceServiceFilePath;
 | 
					            if (string.IsNullOrEmpty(EmbededServiceFilePath)) EmbededServiceFilePath = DefaultFilePath;
 | 
				
			||||||
            if (string.IsNullOrEmpty(savePath)) savePath = DefaultFilePath;
 | 
					            File.WriteAllText(EmbededServiceFilePath, json);
 | 
				
			||||||
            
 | 
					            Debug.Log($"Save config to {EmbededServiceFilePath}");
 | 
				
			||||||
            File.WriteAllText(savePath, json);
 | 
					 | 
				
			||||||
            UnityEngine.Debug.Log($"Save config to {savePath}");
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
| 
						 | 
					@ -80,7 +76,6 @@ namespace Guru.Editor
 | 
				
			||||||
            cfg.ad_settings = new GuruAdSettings();
 | 
					            cfg.ad_settings = new GuruAdSettings();
 | 
				
			||||||
            cfg.adjust_settings = new GuruAdjustSettings();
 | 
					            cfg.adjust_settings = new GuruAdjustSettings();
 | 
				
			||||||
            cfg.fb_settings = new GuruFbSettings();
 | 
					            cfg.fb_settings = new GuruFbSettings();
 | 
				
			||||||
            cfg.parameters = new GuruParameters();
 | 
					 | 
				
			||||||
            return cfg;
 | 
					            return cfg;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -93,14 +88,14 @@ namespace Guru.Editor
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static void DeployLocalServiceFile()
 | 
					        public static void DeployAppServiceFile()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var deployPath = Path.Combine(Application.dataPath, LocalServicesConfigPath);
 | 
					            var streamingPath = Application.streamingAssetsPath;
 | 
				
			||||||
            if(!Directory.Exists(deployPath)) Directory.CreateDirectory(deployPath);
 | 
					            if(!Directory.Exists(streamingPath)) Directory.CreateDirectory(streamingPath);
 | 
				
			||||||
            var path = Path.Combine(deployPath, $"{GuruSDK.ServicesConfigKey}{LocalConfigExtension}");
 | 
					            var path = Path.Combine(streamingPath, $"{GuruSDK.ServicesConfigKey}.{GuruSDK.ServicesConfigExtension}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var config = LoadConfig();
 | 
					            var config = LoadConfig();
 | 
				
			||||||
            var from = SourceServiceFilePath;
 | 
					            var from = EmbededServiceFilePath;
 | 
				
			||||||
            if (string.IsNullOrEmpty(from) || !File.Exists(from)) // 文件不存在
 | 
					            if (string.IsNullOrEmpty(from) || !File.Exists(from)) // 文件不存在
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
| 
						 | 
					@ -109,7 +104,7 @@ namespace Guru.Editor
 | 
				
			||||||
            if (null != config)
 | 
					            if (null != config)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (File.Exists(path)) File.Delete(path);
 | 
					                if (File.Exists(path)) File.Delete(path);
 | 
				
			||||||
                UnityEngine.Debug.Log($"<color=#88ff00> --- setup {GuruSDK.ServicesConfigKey} to local resources.</color>");
 | 
					                Debug.Log($"<color=#88ff00> --- setup {GuruSDK.ServicesConfigKey} file on streamingPath</color>");
 | 
				
			||||||
                File.Copy(from, path);
 | 
					                File.Copy(from, path);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,31 +0,0 @@
 | 
				
			||||||
allprojects {
 | 
					 | 
				
			||||||
    buildscript {
 | 
					 | 
				
			||||||
        repositories {**ARTIFACTORYREPOSITORY**
 | 
					 | 
				
			||||||
            google()
 | 
					 | 
				
			||||||
            jcenter()
 | 
					 | 
				
			||||||
            mavenCentral()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        dependencies {
 | 
					 | 
				
			||||||
            // If you are changing the Android Gradle Plugin version, make sure it is compatible with the Gradle version preinstalled with Unity
 | 
					 | 
				
			||||||
            // See which Gradle version is preinstalled with Unity here https://docs.unity3d.com/Manual/android-gradle-overview.html
 | 
					 | 
				
			||||||
            // See official Gradle and Android Gradle Plugin compatibility table here https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
 | 
					 | 
				
			||||||
            // To specify a custom Gradle version in Unity, go do "Preferences > External Tools", uncheck "Gradle Installed with Unity (recommended)" and specify a path to a custom Gradle version
 | 
					 | 
				
			||||||
//             classpath 'com.android.tools.build:gradle:4.0.1'
 | 
					 | 
				
			||||||
            classpath 'com.android.tools.build:gradle:7.4.2'
 | 
					 | 
				
			||||||
            **BUILD_SCRIPT_DEPS**
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    repositories {**ARTIFACTORYREPOSITORY**
 | 
					 | 
				
			||||||
        google()
 | 
					 | 
				
			||||||
        jcenter()
 | 
					 | 
				
			||||||
        flatDir {
 | 
					 | 
				
			||||||
            dirs "${project(':unityLibrary').projectDir}/libs"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
task clean(type: Delete) {
 | 
					 | 
				
			||||||
    delete rootProject.buildDir
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 210888b84fe449e88319129ae097514d
 | 
					 | 
				
			||||||
timeCreated: 1722245010
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
 | 
					org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
 | 
				
			||||||
org.gradle.parallel=true
 | 
					org.gradle.parallel=true
 | 
				
			||||||
org.gradle.daemon=true
 | 
					android.enableR8=**MINIFY_WITH_R_EIGHT**
 | 
				
			||||||
org.gradle.caching=true
 | 
					 | 
				
			||||||
unityStreamingAssets=**STREAMING_ASSETS**
 | 
					unityStreamingAssets=**STREAMING_ASSETS**
 | 
				
			||||||
# Android Resolver Properties Start
 | 
					# Android Resolver Properties Start
 | 
				
			||||||
android.useAndroidX=true
 | 
					android.useAndroidX=true
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,8 +5,7 @@ dependencies {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
android {
 | 
					android {
 | 
				
			||||||
    namespace "**NAMESPACE**"
 | 
					    compileSdkVersion **APIVERSION**
 | 
				
			||||||
    compileSdkVersion **TARGETSDKVERSION**
 | 
					 | 
				
			||||||
    buildToolsVersion '**BUILDTOOLS**'
 | 
					    buildToolsVersion '**BUILDTOOLS**'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    compileOptions {
 | 
					    compileOptions {
 | 
				
			||||||
| 
						 | 
					@ -23,7 +22,6 @@ android {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        versionCode **VERSIONCODE**
 | 
					        versionCode **VERSIONCODE**
 | 
				
			||||||
        versionName '**VERSIONNAME**'
 | 
					        versionName '**VERSIONNAME**'
 | 
				
			||||||
        multiDexEnabled true
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    aaptOptions {
 | 
					    aaptOptions {
 | 
				
			||||||
| 
						 | 
					@ -36,11 +34,6 @@ android {
 | 
				
			||||||
        checkReleaseBuilds false
 | 
					        checkReleaseBuilds false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dexOptions {
 | 
					 | 
				
			||||||
        incremental true
 | 
					 | 
				
			||||||
        javaMaxHeapSize "8g"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    buildTypes {
 | 
					    buildTypes {
 | 
				
			||||||
        debug {
 | 
					        debug {
 | 
				
			||||||
            minifyEnabled **MINIFY_DEBUG**
 | 
					            minifyEnabled **MINIFY_DEBUG**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,41 +0,0 @@
 | 
				
			||||||
apply plugin: 'com.android.library'
 | 
					 | 
				
			||||||
**APPLY_PLUGINS**
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dependencies {
 | 
					 | 
				
			||||||
    implementation fileTree(dir: 'libs', include: ['*.jar'])
 | 
					 | 
				
			||||||
**DEPS**}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
android {
 | 
					 | 
				
			||||||
    namespace "com.unity3d.player"
 | 
					 | 
				
			||||||
    ndkPath "**NDKPATH**"
 | 
					 | 
				
			||||||
    compileSdkVersion **APIVERSION**
 | 
					 | 
				
			||||||
    buildToolsVersion '**BUILDTOOLS**'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    compileOptions {
 | 
					 | 
				
			||||||
        sourceCompatibility JavaVersion.VERSION_11
 | 
					 | 
				
			||||||
        targetCompatibility JavaVersion.VERSION_11
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    defaultConfig {
 | 
					 | 
				
			||||||
        minSdkVersion **MINSDKVERSION**
 | 
					 | 
				
			||||||
        targetSdkVersion **TARGETSDKVERSION**
 | 
					 | 
				
			||||||
        ndk {
 | 
					 | 
				
			||||||
            abiFilters **ABIFILTERS**
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        versionCode **VERSIONCODE**
 | 
					 | 
				
			||||||
        versionName '**VERSIONNAME**'
 | 
					 | 
				
			||||||
        consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    lintOptions {
 | 
					 | 
				
			||||||
        abortOnError false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    aaptOptions {
 | 
					 | 
				
			||||||
        noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
 | 
					 | 
				
			||||||
        ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
 | 
					 | 
				
			||||||
    }**PACKAGING_OPTIONS**
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
**IL_CPP_BUILD_SETUP**
 | 
					 | 
				
			||||||
**SOURCE_BUILD_SETUP**
 | 
					 | 
				
			||||||
**EXTERNAL_SOURCES**
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,43 +0,0 @@
 | 
				
			||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					 | 
				
			||||||
<network-security-config>
 | 
					 | 
				
			||||||
    <base-config cleartextTrafficPermitted="true">
 | 
					 | 
				
			||||||
        <trust-anchors>
 | 
					 | 
				
			||||||
            <certificates src="system" />
 | 
					 | 
				
			||||||
        </trust-anchors>
 | 
					 | 
				
			||||||
    </base-config>
 | 
					 | 
				
			||||||
    <domain-config cleartextTrafficPermitted="true">
 | 
					 | 
				
			||||||
        <!-- For Facebook(Meta) -->
 | 
					 | 
				
			||||||
        <!-- In the Audience Network Android SDK,
 | 
					 | 
				
			||||||
            we use 127.0.0.1 (localhost) as a caching
 | 
					 | 
				
			||||||
            proxy to cache media files in the SDK.
 | 
					 | 
				
			||||||
            Since Android P, cleartext traffic (unencrypted HTTP)
 | 
					 | 
				
			||||||
            will be blocked by default, which will affect the
 | 
					 | 
				
			||||||
            functionality of media caching of the SDK and could
 | 
					 | 
				
			||||||
            affect user experience and ads revenue. -->
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">127.0.0.1</domain>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- For Amazon -->
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">amazon-adsystem.com</domain>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- For Unity -->
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">cdn-creatives-akamai-prd.unityads.unity3d.com</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">cdn-creatives-akamaistls-prd.unityads.unity3d.com</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">cdn-creatives-akamaistls-re-prd.unityads.unity3d.com</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">cdn-creatives-geocdn-prd.unityads.unity3d.com</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">cdn-creatives-prd.unityads.unity3d.com</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">cdn-creatives-highwinds-prd.unityads.unity3d.com</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">cdn-creatives-tencent-prd.unityads.unitychina.cn</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">cdn-store-icons-akamai-prd.unityads.unity3d.com</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">cdn-store-icons-highwinds-prd.unityads.unity3d.com</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">cdn-store-icons-tencent-prd.unityads.unitychina.cn</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">cdn-creatives-akamaistls-prd.acquire.unity3dusercontent.com</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">unityads.unity3d.com</domain>
 | 
					 | 
				
			||||||
        <domain includeSubdomains="true">unityads.unitychina.cn</domain>
 | 
					 | 
				
			||||||
    </domain-config>
 | 
					 | 
				
			||||||
    <debug-overrides>
 | 
					 | 
				
			||||||
        <trust-anchors>
 | 
					 | 
				
			||||||
            <!-- Trust user added CAs while debuggable only -->
 | 
					 | 
				
			||||||
            <certificates src="user" />
 | 
					 | 
				
			||||||
        </trust-anchors>
 | 
					 | 
				
			||||||
    </debug-overrides>
 | 
					 | 
				
			||||||
</network-security-config>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: e92a383bcfa548e2bdf0eeb8b0264205
 | 
					 | 
				
			||||||
timeCreated: 1630417512
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,134 +0,0 @@
 | 
				
			||||||
-keep class com.unity3d.plugin.* { *; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#proguard-adjust.pro
 | 
					 | 
				
			||||||
-keep public class com.adjust.sdk.** { *; }
 | 
					 | 
				
			||||||
-keep class com.amazon.device.ads.** { *; } 
 | 
					 | 
				
			||||||
-keep class com.amazon.aps.** { *; } 
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient {
 | 
					 | 
				
			||||||
    com.google.android.gms.ads.identifier.AdvertisingIdClient$Info getAdvertisingIdInfo(android.content.Context);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info {
 | 
					 | 
				
			||||||
    java.lang.String getId();
 | 
					 | 
				
			||||||
    boolean isLimitAdTrackingEnabled();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep public class com.android.installreferrer.** { *; }
 | 
					 | 
				
			||||||
-keepclassmembers class com.ironsource.sdk.controller.IronSourceWebView$JSInterface {
 | 
					 | 
				
			||||||
    public *;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keepclassmembers class * implements android.os.Parcelable {
 | 
					 | 
				
			||||||
    public static final android.os.Parcelable$Creator *;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep public class com.google.android.gms.ads.** {
 | 
					 | 
				
			||||||
    public *;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep class com.ironsource.adapters.** { *; }
 | 
					 | 
				
			||||||
-dontwarn com.ironsource.mediationsdk.**
 | 
					 | 
				
			||||||
-dontwarn com.ironsource.adapters.**
 | 
					 | 
				
			||||||
-keepattributes JavascriptInterface
 | 
					 | 
				
			||||||
-keepclassmembers class * {
 | 
					 | 
				
			||||||
    @android.webkit.JavascriptInterface <methods>;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-keep class dalvik.system.VMRuntime {
 | 
					 | 
				
			||||||
    java.lang.String getRuntime();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep class android.os.Build {
 | 
					 | 
				
			||||||
    java.lang.String[] SUPPORTED_ABIS;
 | 
					 | 
				
			||||||
    java.lang.String CPU_ABI;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep class android.content.res.Configuration {
 | 
					 | 
				
			||||||
    android.os.LocaleList getLocales();
 | 
					 | 
				
			||||||
    java.util.Locale locale;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep class android.os.LocaledList {
 | 
					 | 
				
			||||||
    java.util.Locale get(int);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#proguard-facebook.pro
 | 
					 | 
				
			||||||
-keep class com.facebook.** { *; }
 | 
					 | 
				
			||||||
-keepattributes Signature
 | 
					 | 
				
			||||||
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
 | 
					 | 
				
			||||||
-keep @com.facebook.common.internal.DoNotStrip class *
 | 
					 | 
				
			||||||
-keep class com.facebook.stetho.** { *; }
 | 
					 | 
				
			||||||
-keepclassmembers class * {
 | 
					 | 
				
			||||||
    @com.facebook.common.internal.DoNotStrip *;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keepclassmembers class * {
 | 
					 | 
				
			||||||
    native <methods>;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-dontwarn okio.**
 | 
					 | 
				
			||||||
-dontwarn javax.annotation.**
 | 
					 | 
				
			||||||
-dontwarn com.android.volley.toolbox.**
 | 
					 | 
				
			||||||
-keep class com.google.firebase.** { *; }
 | 
					 | 
				
			||||||
-dontwarn com.google.firebase.**
 | 
					 | 
				
			||||||
-keep class com.bytedance.sdk.** { *; }
 | 
					 | 
				
			||||||
-keep class com.pgl.sys.ces.* { *; }
 | 
					 | 
				
			||||||
-keep class com.facebook.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.play.core.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.games.bridge.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.games.leaderboard.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.games.snapshot.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.games.achievement.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.games.event.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.games.stats.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.games.video.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.games.* { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.common.api.ResultCallback { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.signin.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.dynamic.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.dynamite.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.tasks.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.security.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.base.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.actions.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.common.ConnectionResult { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.common.GooglePlayServicesUtil { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.common.api.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.common.data.DataBufferUtils { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.games.quest.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.nearby.** { *; }
 | 
					 | 
				
			||||||
-keep class com.google.android.gms.ads.** { *; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-keep class com.pubmatic.sdk.** { *; }
 | 
					 | 
				
			||||||
-keep class com.applovin.** { *; }
 | 
					 | 
				
			||||||
-keep class com.chartboost.** { *; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-keep class com.guru.** { *; }
 | 
					 | 
				
			||||||
-keep class guru.core.** { *; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-keep class com.onevcat.uniwebview.* { *; }
 | 
					 | 
				
			||||||
-keep class com.iab.omid.* { *; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-keep public class com.tradplus.** { *; }
 | 
					 | 
				
			||||||
-keep class com.tradplus.ads.** { *; }
 | 
					 | 
				
			||||||
-keep class com.applovin.mediation.adapters.** { *; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-keep public class com.applovin.sdk.AppLovinSdk{ *; }
 | 
					 | 
				
			||||||
-keep public class com.applovin.sdk.AppLovin* {
 | 
					 | 
				
			||||||
    public protected *;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep public class com.applovin.nativeAds.AppLovin* {
 | 
					 | 
				
			||||||
    public protected *;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep public class com.applovin.adview.* {
 | 
					 | 
				
			||||||
    public protected *;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep public class com.applovin.mediation.* {
 | 
					 | 
				
			||||||
    public protected *;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep public class com.applovin.mediation.ads.* {
 | 
					 | 
				
			||||||
    public protected *;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep public class com.applovin.impl.*.AppLovin {
 | 
					 | 
				
			||||||
    public protected *;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep public class com.applovin.impl.**.*Impl {
 | 
					 | 
				
			||||||
    public protected *;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keepclassmembers class com.applovin.sdk.AppLovinSdkSettings {
 | 
					 | 
				
			||||||
    private java.util.Map localSettings;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
-keep class com.applovin.mediation.adapters.** { *; }
 | 
					 | 
				
			||||||
-keep class com.applovin.mediation.adapter.** { *; }
 | 
					 | 
				
			||||||
-keep class com.applovin.mediation.unity.** { *; }
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: a08608d11bf7483fa4c49df80ebab657
 | 
					 | 
				
			||||||
timeCreated: 1722245439
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,60 +0,0 @@
 | 
				
			||||||
pluginManagement {
 | 
					 | 
				
			||||||
    repositories {
 | 
					 | 
				
			||||||
        **ARTIFACTORYREPOSITORY**
 | 
					 | 
				
			||||||
        gradlePluginPortal()
 | 
					 | 
				
			||||||
        google()
 | 
					 | 
				
			||||||
        mavenCentral()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
include ':launcher', ':unityLibrary'
 | 
					 | 
				
			||||||
**INCLUDES**
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dependencyResolutionManagement {
 | 
					 | 
				
			||||||
    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
 | 
					 | 
				
			||||||
    repositories {
 | 
					 | 
				
			||||||
        **ARTIFACTORYREPOSITORY**
 | 
					 | 
				
			||||||
        google()
 | 
					 | 
				
			||||||
        mavenCentral()
 | 
					 | 
				
			||||||
// Android Resolver Repos Start
 | 
					 | 
				
			||||||
        def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/")
 | 
					 | 
				
			||||||
        maven {
 | 
					 | 
				
			||||||
            url "https://verve.jfrog.io/artifactory/verve-gradle-release" // Packages/com.guru.unity.max/Mediation/Verve/Editor/Dependencies.xml:7
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        maven {
 | 
					 | 
				
			||||||
            url (unityProjectPath + "/Assets/GeneratedLocalRepo/Firebase/m2repository") // Packages/com.google.firebase.firestore/Firebase/Editor/FirestoreDependencies.xml:20, Packages/com.google.firebase.app/Firebase/Editor/AppDependencies.xml:22, Packages/com.google.firebase.auth/Firebase/Editor/AuthDependencies.xml:20, Packages/com.google.firebase.messaging/Firebase/Editor/MessagingDependencies.xml:24, Packages/com.google.firebase.crashlytics/Firebase/Editor/CrashlyticsDependencies.xml:20, Packages/com.google.firebase.dynamic-links/Firebase/Editor/DynamicLinksDependencies.xml:20, Packages/com.google.firebase.installations/Firebase/Editor/InstallationsDependencies.xml:20, Packages/com.google.firebase.remote-config/Firebase/Editor/RemoteConfigDependencies.xml:20, Packages/com.google.firebase.analytics/Firebase/Editor/AnalyticsDependencies.xml:18
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        maven {
 | 
					 | 
				
			||||||
            url "https://aws.oss.sonatype.org/content/repositories/releases/" // Packages/com.guru.unity.max/Amazon/Scripts/Editor/AmazonDependencies.xml:10
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        maven {
 | 
					 | 
				
			||||||
            url "https://artifactory.bidmachine.io/bidmachine" // Packages/com.guru.unity.max/Mediation/BidMachine/Editor/Dependencies.xml:8
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        maven {
 | 
					 | 
				
			||||||
            url "https://artifact.bytedance.com/repository/pangle" // Packages/com.guru.unity.max/Mediation/ByteDance/Editor/Dependencies.xml:8
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        maven {
 | 
					 | 
				
			||||||
            url "https://cboost.jfrog.io/artifactory/chartboost-ads/" // Packages/com.guru.unity.max/Mediation/Chartboost/Editor/Dependencies.xml:8
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        maven {
 | 
					 | 
				
			||||||
            url "https://android-sdk.is.com/" // Packages/com.guru.unity.max/Mediation/IronSource/Editor/Dependencies.xml:8
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        maven {
 | 
					 | 
				
			||||||
            url "https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea" // Packages/com.guru.unity.max/Mediation/Mintegral/Editor/Dependencies.xml:8
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        maven {
 | 
					 | 
				
			||||||
            url "https://maven.ogury.co" // Packages/com.guru.unity.max/Mediation/OguryPresage/Editor/Dependencies.xml:8
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        maven {
 | 
					 | 
				
			||||||
            url "https://s3.amazonaws.com/smaato-sdk-releases/" // Packages/com.guru.unity.max/Mediation/Smaato/Editor/Dependencies.xml:8
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        maven {
 | 
					 | 
				
			||||||
            url "https://repo.pubmatic.com/artifactory/public-repos" // Packages/com.guru.unity.max/OpenWrapSDK/Editor/OpenWrapSDKDependencies.xml:18, Packages/com.guru.unity.max/OpenWrapSDK/Mediation/AppLovinMAX/Editor/ALOpenWrapMediationDependencies.xml:7
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        mavenLocal()
 | 
					 | 
				
			||||||
// Android Resolver Repos End
 | 
					 | 
				
			||||||
        flatDir {
 | 
					 | 
				
			||||||
            dirs "${project(':unityLibrary').projectDir}/libs"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 4f7213bbbc8741bd89b3bd17a7bea43e
 | 
					 | 
				
			||||||
timeCreated: 1722240718
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,57 +0,0 @@
 | 
				
			||||||
namespace Guru.Editor
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using System.IO;
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Create androidlib assets
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public class AndroidLibHelper
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static readonly string PluginsRoot = "Plugins/Android";
 | 
					 | 
				
			||||||
        private static readonly string Extends = "androidlib";
 | 
					 | 
				
			||||||
        private static readonly string ProjectPropertiesName = "project.properties";
 | 
					 | 
				
			||||||
        private static readonly string ProjectPropertiesContent= "target=android-9\nandroid.library=true";
 | 
					 | 
				
			||||||
        private static readonly string AndroidManifestName = "AndroidManifest.xml";
 | 
					 | 
				
			||||||
        private static readonly string AndroidManifestContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"{0}\"\n          android:versionCode=\"1\"\n          android:versionName=\"1.0\">\n</manifest>";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static bool IsEmbeddedAndroidLibExists(string fileName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            string dir = Path.GetFullPath($"{Application.dataPath}/{PluginsRoot}/{fileName}.{Extends}");
 | 
					 | 
				
			||||||
            return Directory.Exists(dir);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static string CreateLibRoot(string packageName, string fileName = "")
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(packageName)) return "";
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if(string.IsNullOrEmpty(fileName)) fileName = packageName;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            string dir = Path.GetFullPath($"{Application.dataPath}/{PluginsRoot}/{fileName}.{Extends}");
 | 
					 | 
				
			||||||
            if (Directory.Exists(dir))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return dir;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Directory.CreateDirectory(dir);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            string path = "";
 | 
					 | 
				
			||||||
            string content = "";
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            //------ Create project.properties ------
 | 
					 | 
				
			||||||
            content = ProjectPropertiesContent;
 | 
					 | 
				
			||||||
            path = $"{dir}/{ProjectPropertiesName}";
 | 
					 | 
				
			||||||
            File.WriteAllText(path, content);
 | 
					 | 
				
			||||||
            // ------ Create AndroidManifest.xml ------
 | 
					 | 
				
			||||||
            content = AndroidManifestContent.Replace("{0}", packageName);
 | 
					 | 
				
			||||||
            path = $"{dir}/{AndroidManifestName}";
 | 
					 | 
				
			||||||
            File.WriteAllText(path, content);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            return dir;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: ee3d47ab5d544373ae6a43fdd36d96fb
 | 
					 | 
				
			||||||
timeCreated: 1711761343
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,389 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru.Editor
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using System.Xml;
 | 
					 | 
				
			||||||
    using System.IO;
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    using System.Collections.Generic;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Android 配置修改器
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public class AndroidManifestDoc
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private const string TargetPath = "Plugins/Android/AndroidManifest.xml";
 | 
					 | 
				
			||||||
        private const string XmlnsAndroid = "xmlns:android";
 | 
					 | 
				
			||||||
        private const string NamespaceAndroid = "http://schemas.android.com/apk/res/android";
 | 
					 | 
				
			||||||
        private const string XmlnsTools= "xmlns:tools";
 | 
					 | 
				
			||||||
        private const string NamespaceTools = "http://schemas.android.com/tools";
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private const string UserPermission = "uses-permission";
 | 
					 | 
				
			||||||
        private const string MetaData = "meta-data";
 | 
					 | 
				
			||||||
        private const string KName = "name";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private XmlDocument _doc;
 | 
					 | 
				
			||||||
        public XmlDocument Doc => _doc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private string _docPath;
 | 
					 | 
				
			||||||
        private bool _isReady = false;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private XmlElement _manifestNode;
 | 
					 | 
				
			||||||
        private XmlElement _applicationNode;
 | 
					 | 
				
			||||||
        private XmlElement _queriesNode;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Initiallize
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 加载文件
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="docPath"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static AndroidManifestDoc Load(string docPath = "")
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(docPath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                docPath = Path.GetFullPath(Path.Combine(Application.dataPath, TargetPath));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!File.Exists(docPath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.LogError($"--- File not found: {docPath}");
 | 
					 | 
				
			||||||
                return null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var mod = new AndroidManifestDoc();
 | 
					 | 
				
			||||||
            mod.ReadFromPath(docPath);
 | 
					 | 
				
			||||||
            return mod;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static AndroidManifestDoc Read(string xmlStr, string docPath = "")
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var mod = new AndroidManifestDoc();
 | 
					 | 
				
			||||||
            mod.ReadFromXml(xmlStr, docPath);
 | 
					 | 
				
			||||||
            return mod;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 从文件路径读取
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="docPath"></param>
 | 
					 | 
				
			||||||
        public void ReadFromPath(string docPath)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _isReady = false;
 | 
					 | 
				
			||||||
            if (File.Exists(docPath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var xmlStr = File.ReadAllText(docPath);
 | 
					 | 
				
			||||||
                ReadFromXml(xmlStr, docPath);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.LogError($"--- File not found: {docPath}");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void ReadFromXml(string xmlStr, string docPath = "")
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _doc = new XmlDocument();
 | 
					 | 
				
			||||||
            _doc.LoadXml(xmlStr);
 | 
					 | 
				
			||||||
            if(!string.IsNullOrEmpty(docPath)) _docPath = docPath;
 | 
					 | 
				
			||||||
            Init();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Initializes the Doc
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private void Init()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // --- Root Nodes ---
 | 
					 | 
				
			||||||
            _manifestNode = _doc.SelectSingleNode("manifest") as XmlElement;
 | 
					 | 
				
			||||||
            _applicationNode = _doc.SelectSingleNode("manifest/application") as XmlElement;
 | 
					 | 
				
			||||||
            _queriesNode = _doc.SelectSingleNode("manifest/queries") as XmlElement;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            AddXmlnsAndroid();
 | 
					 | 
				
			||||||
            AddXmlnsTools();
 | 
					 | 
				
			||||||
            _isReady = true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Save Doc
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public void Save(string docPath = "")
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_isReady)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (!string.IsNullOrEmpty(docPath)) _docPath = docPath;
 | 
					 | 
				
			||||||
                if (!string.IsNullOrEmpty(_docPath))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var dir = Directory.GetParent(_docPath);
 | 
					 | 
				
			||||||
                    if(!dir.Exists) dir.Create();
 | 
					 | 
				
			||||||
                    _doc.Save(_docPath);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Node Opreation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static bool AddAttribute(XmlElement node, string key, string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (node != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (node.HasAttribute(key))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    node.Attributes[key].Value = value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    node.SetAttribute(key, value);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region API
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool AddXmlnsAndroid()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return AddAttribute(_manifestNode, XmlnsAndroid, NamespaceAndroid);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public bool AddXmlnsTools()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return AddAttribute(_manifestNode, XmlnsTools, NamespaceTools);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Add Replace Item
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="item"></param>
 | 
					 | 
				
			||||||
        public void AddApplicationReplaceItem(string item)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_applicationNode != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                List<string> items = new List<string>(5);
 | 
					 | 
				
			||||||
                if (_applicationNode.HasAttribute("replace", NamespaceTools))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var arr = _applicationNode.GetAttribute("replace",NamespaceTools).Split(',');
 | 
					 | 
				
			||||||
                    if(arr != null && arr.Length > 0)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        items.AddRange(arr);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (!items.Contains(item)) items.Add(item);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                _applicationNode.SetAttribute("replace",  NamespaceTools, string.Join(",", items));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public void SetApplicationAttribute(string key, string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_applicationNode != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _applicationNode.SetAttribute(key, NamespaceAndroid, value);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Set metadata
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="key"></param>
 | 
					 | 
				
			||||||
        /// <param name="value"></param>
 | 
					 | 
				
			||||||
        /// <param name="valueName"></param>
 | 
					 | 
				
			||||||
        /// <param name="keyName"></param>
 | 
					 | 
				
			||||||
        public void SetMetadata(string key, string value, string valueName = "value", string keyName = KName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_doc == null || !_isReady) return;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            XmlElement node = null;
 | 
					 | 
				
			||||||
            if (!TryGetMetadata(key, out node, keyName))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                node = _doc.CreateElement(MetaData);
 | 
					 | 
				
			||||||
                _applicationNode?.AppendChild(node);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            node.SetAttribute(keyName, NamespaceAndroid, key);
 | 
					 | 
				
			||||||
            node.SetAttribute(valueName, NamespaceAndroid, value);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 添加权限
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="key"></param>
 | 
					 | 
				
			||||||
        /// <param name="keyName"></param>
 | 
					 | 
				
			||||||
        public void AddPermission(string key, string keyName = KName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_doc == null || !_isReady) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            XmlElement node = null;
 | 
					 | 
				
			||||||
            if(!TryGetPermission(key, out node, keyName))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                node = _doc.CreateElement(UserPermission);
 | 
					 | 
				
			||||||
                _manifestNode?.AppendChild(node);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            node.SetAttribute(keyName, NamespaceAndroid, key);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 删除 Permission
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="key"></param>
 | 
					 | 
				
			||||||
        /// <param name="value"></param>
 | 
					 | 
				
			||||||
        /// <param name="keyName"></param>
 | 
					 | 
				
			||||||
        /// <param name="valueName"></param>
 | 
					 | 
				
			||||||
        public void RemovePermission(string key, string value = "remove", string keyName = "name",  string valueName = "node")
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_doc == null || !_isReady) return;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            XmlElement node = null;
 | 
					 | 
				
			||||||
            if(!TryGetPermission(key, out node, keyName))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                node = _doc.CreateElement(UserPermission);
 | 
					 | 
				
			||||||
                _manifestNode?.AppendChild(node);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            node.SetAttribute(keyName, NamespaceAndroid, key);
 | 
					 | 
				
			||||||
            node.SetAttribute(valueName, NamespaceTools, value);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public bool SetPackageName(string packageName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_manifestNode != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _manifestNode.Attributes["package"].Value = packageName;
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 添加 Queries Intent
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="value"></param>
 | 
					 | 
				
			||||||
        /// <param name="keyName"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public void AddQueriesIntent(string value, string keyName = "name")
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_queriesNode == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _queriesNode = _doc.CreateElement("queries");
 | 
					 | 
				
			||||||
                _manifestNode?.AppendChild(_queriesNode);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var intentList = _queriesNode.SelectNodes("intent");
 | 
					 | 
				
			||||||
            if (intentList != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                foreach (XmlElement intent in intentList)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var action = intent?.SelectSingleNode("action") as XmlElement;
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                    if (action != null 
 | 
					 | 
				
			||||||
                        && action.GetAttribute(keyName, NamespaceAndroid) == value)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        return; // Has injected,skip ...
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Inject new intent node
 | 
					 | 
				
			||||||
            XmlElement intentNode = _doc.CreateElement("intent");
 | 
					 | 
				
			||||||
            _queriesNode.AppendChild(intentNode);
 | 
					 | 
				
			||||||
            XmlElement actionNode = _doc.CreateElement("action");
 | 
					 | 
				
			||||||
            intentNode.AppendChild(actionNode);
 | 
					 | 
				
			||||||
            actionNode.SetAttribute(keyName, NamespaceAndroid, value);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Data Opration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool TryGetMetadata(string name, out XmlElement node, string keyName = KName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            node = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(_applicationNode != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var list = _applicationNode.SelectNodes(MetaData);
 | 
					 | 
				
			||||||
                if (list != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    foreach (XmlElement e in list)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        if (e.GetAttribute(keyName, NamespaceAndroid) == name)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            node = e;
 | 
					 | 
				
			||||||
                            return true;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public bool TryGetPermission(string name, out XmlElement node, string keyName = KName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            node = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if(_manifestNode != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var list = _manifestNode.SelectNodes(UserPermission);
 | 
					 | 
				
			||||||
                if (list != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    foreach (XmlElement e in list)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        if (e.GetAttribute(keyName, NamespaceAndroid) == name)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            node = e;
 | 
					 | 
				
			||||||
                            return true;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public bool TryRootNode(string nodeName, out XmlElement node)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            node = null;
 | 
					 | 
				
			||||||
            if (_doc != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                node = _doc.SelectSingleNode(nodeName) as XmlElement;
 | 
					 | 
				
			||||||
                return node != null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Output
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override string ToString()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_doc != null) return _doc.InnerXml;
 | 
					 | 
				
			||||||
            return this.ToString();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 89a1c7f77fcf4982adbbaf6dc61bd62d
 | 
					 | 
				
			||||||
timeCreated: 1711505949
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,5 @@
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using Unity.EditorCoroutines.Editor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Guru.Editor
 | 
					namespace Guru.Editor
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -14,35 +16,6 @@ namespace Guru.Editor
 | 
				
			||||||
        private const string ValOptimizeInitialization = "com.google.android.gms.ads.flag.OPTIMIZE_INITIALIZATION";
 | 
					        private const string ValOptimizeInitialization = "com.google.android.gms.ads.flag.OPTIMIZE_INITIALIZATION";
 | 
				
			||||||
        private const string ValOptimizeAdLoading = "com.google.android.gms.ads.flag.OPTIMIZE_AD_LOADING";
 | 
					        private const string ValOptimizeAdLoading = "com.google.android.gms.ads.flag.OPTIMIZE_AD_LOADING";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private const string PermissionReadPostNotifications = "android.permission.POST_NOTIFICATIONS";
 | 
					 | 
				
			||||||
        private const string PermissionReadPhoneState = "android.permission.READ_PHONE_STATE";
 | 
					 | 
				
			||||||
        private const string PermissionAccessCoarseLocation = "android.permission.ACCESS_COARSE_LOCATION";
 | 
					 | 
				
			||||||
        private const string PermissionAccessFineLocation = "android.permission.ACCESS_FINE_LOCATION";
 | 
					 | 
				
			||||||
        private const string PermissionReadExternalStorage = "android.permission.READ_EXTERNAL_STORAGE";
 | 
					 | 
				
			||||||
        private const string PermissionReadLogs = "android.permission.READ_LOGS";
 | 
					 | 
				
			||||||
        private const string NetworkSecurityConfig = "networkSecurityConfig";
 | 
					 | 
				
			||||||
        private const string NetworkSecurityConfigValue = "@xml/network_security_config";
 | 
					 | 
				
			||||||
        private const string PermissionAdjustReadPermission = "com.adjust.preinstall.READ_PERMISSION"; // Adjust permission
 | 
					 | 
				
			||||||
        private const string AdjustQueriesActionValue = "com.attribution.REFERRAL_PROVIDER"; // Adjust action
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Add Permissions
 | 
					 | 
				
			||||||
        private static string[] addPermissions = new[]
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            PermissionReadPostNotifications,
 | 
					 | 
				
			||||||
            PermissionAdjustReadPermission,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Remove Permissions
 | 
					 | 
				
			||||||
        private static string[] removePermissions = new[]
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            PermissionReadPhoneState,
 | 
					 | 
				
			||||||
            PermissionAccessCoarseLocation,
 | 
					 | 
				
			||||||
            PermissionAccessFineLocation,
 | 
					 | 
				
			||||||
            PermissionReadExternalStorage,
 | 
					 | 
				
			||||||
            PermissionReadLogs,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string TargetFullPath = Path.Combine(Application.dataPath, TargetPath);
 | 
					        private static string TargetFullPath = Path.Combine(Application.dataPath, TargetPath);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        public static bool IsManifestExist() => File.Exists(TargetFullPath);
 | 
					        public static bool IsManifestExist() => File.Exists(TargetFullPath);
 | 
				
			||||||
| 
						 | 
					@ -52,58 +25,70 @@ namespace Guru.Editor
 | 
				
			||||||
            if (!IsManifestExist())
 | 
					            if (!IsManifestExist())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                CopyManifest();
 | 
					                CopyManifest();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            FixAndroidManifest();
 | 
					            var doc = new XmlDocument();
 | 
				
			||||||
 | 
					            doc.Load(TargetFullPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var rootNode = doc.SelectSingleNode("manifest/application");
 | 
				
			||||||
 | 
					            int item1 = 0;
 | 
				
			||||||
 | 
					            int item2 = 0;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            XmlNodeList metadatas = rootNode.SelectNodes("meta-data");
 | 
				
			||||||
 | 
					            if (metadatas != null && metadatas.Count > 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                bool isDirty = false;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					                foreach (XmlElement e in metadatas)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    if (e != null)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        if (e.HasAttribute("android:name"))
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            if (e.Attributes["android:name"].Value == ValOptimizeInitialization) item1 = 1;
 | 
				
			||||||
 | 
					                            if (e.Attributes["android:name"].Value == ValOptimizeAdLoading) item2 = 1;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            string androidSP = "http://schemas.android.com/apk/res/android";
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (item1 == 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					               var e =  doc.CreateElement("meta-data");
 | 
				
			||||||
 | 
					               e.SetAttribute("name",androidSP, ValOptimizeInitialization);
 | 
				
			||||||
 | 
					               e.SetAttribute("value",androidSP, "true");
 | 
				
			||||||
 | 
					               rootNode.AppendChild(e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (item2 == 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var e =  doc.CreateElement("meta-data");
 | 
				
			||||||
 | 
					                e.SetAttribute("name",androidSP,ValOptimizeAdLoading);
 | 
				
			||||||
 | 
					                e.SetAttribute("value",androidSP, "true");
 | 
				
			||||||
 | 
					                rootNode.AppendChild(e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var rootE = doc.SelectSingleNode("manifest") as XmlElement;
 | 
				
			||||||
 | 
					            if (rootE != null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                rootE.Attributes["package"].Value = PlayerSettings.applicationIdentifier; // 写入包名
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            doc.Save(TargetFullPath);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Fix Android Manifest
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private static void FixAndroidManifest()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var doc = AndroidManifestDoc.Load(TargetFullPath);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // --- network_security_config ---
 | 
					 | 
				
			||||||
            doc.SetApplicationAttribute(NetworkSecurityConfig, NetworkSecurityConfigValue);
 | 
					 | 
				
			||||||
            doc.AddApplicationReplaceItem($"android:{NetworkSecurityConfig}");
 | 
					 | 
				
			||||||
            // ---- Metadata ---
 | 
					 | 
				
			||||||
            doc.SetMetadata(ValOptimizeInitialization, "true");
 | 
					 | 
				
			||||||
            doc.SetMetadata(ValOptimizeAdLoading, "true");
 | 
					 | 
				
			||||||
            // ---- Permission ---
 | 
					 | 
				
			||||||
            // Add needed permissions
 | 
					 | 
				
			||||||
            foreach (var p in addPermissions)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                doc.AddPermission(p);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            // Remove sensitive permissions
 | 
					 | 
				
			||||||
            foreach (var p in removePermissions)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                doc.RemovePermission(p);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // --- Bundle Id ---
 | 
					 | 
				
			||||||
            doc.SetPackageName(PlayerSettings.applicationIdentifier);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // --- Adjust Preinstall (Content provider) ---
 | 
					 | 
				
			||||||
            doc.AddQueriesIntent(AdjustQueriesActionValue);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            doc.Save();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 拷贝 AndroidManifest
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private static void CopyManifest()
 | 
					        private static void CopyManifest()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (File.Exists(TargetFullPath)) return;
 | 
					            if (File.Exists(TargetFullPath)) return;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            var path = GuruEditorHelper.GetAssetPath(nameof(AndroidManifestMod), "Script", true);
 | 
					            var path = GuruEditorHelper.GetFilePath($"{nameof(AndroidManifestMod)}.cs t:Script");
 | 
				
			||||||
            if (!string.IsNullOrEmpty(path))
 | 
					            if (!string.IsNullOrEmpty(path))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var from = Path.GetFullPath($"{path}/../../Files/AndroidManifest.txt");
 | 
					                var files = Path.GetFullPath($"{path}/../Files");
 | 
				
			||||||
 | 
					                var from = $"{files}/AndroidManifest.txt";
 | 
				
			||||||
                if (File.Exists(from))
 | 
					                if (File.Exists(from))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    File.Copy(from, TargetFullPath);
 | 
					                    File.Copy(from, TargetFullPath);
 | 
				
			||||||
| 
						 | 
					@ -111,6 +96,9 @@ namespace Guru.Editor
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #region Testing
 | 
					        #region Testing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Test]
 | 
					        [Test]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,178 +0,0 @@
 | 
				
			||||||
using System.Collections;
 | 
					 | 
				
			||||||
using Unity.EditorCoroutines.Editor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru.Editor
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using NUnit.Framework;
 | 
					 | 
				
			||||||
    using UnityEditor;
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using System.IO;
 | 
					 | 
				
			||||||
    using System.Xml;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public static class AndroidManifestMod
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private const string TargetPath = "Plugins/Android/AndroidManifest.xml";
 | 
					 | 
				
			||||||
        private const string ValOptimizeInitialization = "com.google.android.gms.ads.flag.OPTIMIZE_INITIALIZATION";
 | 
					 | 
				
			||||||
        private const string ValOptimizeAdLoading = "com.google.android.gms.ads.flag.OPTIMIZE_AD_LOADING";
 | 
					 | 
				
			||||||
        private const string NamespaceAndroid = "http://schemas.android.com/apk/res/android";
 | 
					 | 
				
			||||||
        private const string NamespaceTools = "http://schemas.android.com/tools";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private const string PermissionReadPhoneState = "android.permission.READ_PHONE_STATE";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string TargetFullPath = Path.Combine(Application.dataPath, TargetPath);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static bool IsManifestExist() => File.Exists(TargetFullPath);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static void Apply()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!IsManifestExist())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                CopyManifest();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            var doc = new XmlDocument();
 | 
					 | 
				
			||||||
            doc.Load(TargetFullPath);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            SetApplicationMod(doc);
 | 
					 | 
				
			||||||
            SetPermissionMod(doc);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            doc.Save(TargetFullPath);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Fix all Elements in <Applicaiton>
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="doc"></param>
 | 
					 | 
				
			||||||
        private static void SetApplicationMod(XmlDocument doc)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            string rootName = "manifest/application";
 | 
					 | 
				
			||||||
            var rootNode = doc.SelectSingleNode(rootName);
 | 
					 | 
				
			||||||
            int item1 = 0;
 | 
					 | 
				
			||||||
            int item2 = 0;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (rootNode == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.LogError($"Can't find root with name {rootName} ...");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            XmlNodeList metadatas = rootNode.SelectNodes("meta-data");
 | 
					 | 
				
			||||||
            if (metadatas != null && metadatas.Count > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                bool isDirty = false;
 | 
					 | 
				
			||||||
      
 | 
					 | 
				
			||||||
                foreach (XmlElement e in metadatas)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (e != null)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        if (e.HasAttribute("name", NamespaceAndroid))
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            if (e.Attributes["android:name"].Value == ValOptimizeInitialization) item1 = 1;
 | 
					 | 
				
			||||||
                            if (e.Attributes["android:name"].Value == ValOptimizeAdLoading) item2 = 1;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (item1 == 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var e =  doc.CreateElement("meta-data");
 | 
					 | 
				
			||||||
                e.SetAttribute("name",NamespaceAndroid, ValOptimizeInitialization);
 | 
					 | 
				
			||||||
                e.SetAttribute("value",NamespaceAndroid, "true");
 | 
					 | 
				
			||||||
                rootNode.AppendChild(e);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (item2 == 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var e =  doc.CreateElement("meta-data");
 | 
					 | 
				
			||||||
                e.SetAttribute("name",NamespaceAndroid,ValOptimizeAdLoading);
 | 
					 | 
				
			||||||
                e.SetAttribute("value",NamespaceAndroid, "true");
 | 
					 | 
				
			||||||
                rootNode.AppendChild(e);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var rootE = doc.SelectSingleNode("manifest") as XmlElement;
 | 
					 | 
				
			||||||
            if (rootE != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                rootE.Attributes["package"].Value = PlayerSettings.applicationIdentifier; // 写入包名
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Fix all permissions 
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="doc"></param>
 | 
					 | 
				
			||||||
        private static void SetPermissionMod(XmlDocument doc)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            string attName = "uses-permission";
 | 
					 | 
				
			||||||
            string rootName = "manifest";
 | 
					 | 
				
			||||||
            bool isBuild = false;
 | 
					 | 
				
			||||||
            var rootNode = doc.SelectSingleNode(rootName);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            XmlElement item1 = null;
 | 
					 | 
				
			||||||
            if (rootNode == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.LogError($"Can't find root with name {rootName} ...");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            XmlNodeList permissions = rootNode.SelectNodes(attName);
 | 
					 | 
				
			||||||
            if (permissions != null && permissions.Count > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                foreach (XmlElement e in permissions)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (e != null)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        if (e.HasAttribute("android:name"))
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            if (e.Attributes["android:name"].Value == PermissionReadPhoneState) item1 = e;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            isBuild = false;
 | 
					 | 
				
			||||||
            if (item1 == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                isBuild = true;
 | 
					 | 
				
			||||||
                item1 =  doc.CreateElement(attName);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            item1.SetAttribute("name",NamespaceAndroid, PermissionReadPhoneState);
 | 
					 | 
				
			||||||
            item1.SetAttribute("node",NamespaceTools, "remove");
 | 
					 | 
				
			||||||
            if (isBuild) rootNode.AppendChild(item1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void CopyManifest()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (File.Exists(TargetFullPath)) return;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            var path = GuruEditorHelper.GetFilePath($"{nameof(AndroidManifestMod)} t:Script");
 | 
					 | 
				
			||||||
            if (!string.IsNullOrEmpty(path))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var from = Path.GetFullPath($"{path}/../../Files/AndroidManifest.txt");
 | 
					 | 
				
			||||||
                if (File.Exists(from))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    File.Copy(from, TargetFullPath);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Testing
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Test]
 | 
					 | 
				
			||||||
        public static void Test_Injection()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Apply();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,7 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: f4547abfcddf84bc6b61e884ebfb30e2
 | 
					 | 
				
			||||||
DefaultImporter:
 | 
					 | 
				
			||||||
  externalObjects: {}
 | 
					 | 
				
			||||||
  userData: 
 | 
					 | 
				
			||||||
  assetBundleName: 
 | 
					 | 
				
			||||||
  assetBundleVariant: 
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Guru.Editor
 | 
					namespace Guru.Editor
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -6,46 +6,29 @@ namespace Guru.Editor
 | 
				
			||||||
    using UnityEngine;
 | 
					    using UnityEngine;
 | 
				
			||||||
    using System;
 | 
					    using System;
 | 
				
			||||||
    using System.IO;
 | 
					    using System.IO;
 | 
				
			||||||
    using System.Linq;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public class AndroidProjectMod
 | 
					    public class AndroidProjectMod
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private const int TargetSDKVersion = 34;
 | 
					        private static readonly int TargetSDKVersion = 33;
 | 
				
			||||||
        private const string K_ANDROID_PLUGINS_NAME = "Plugins/Android";
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        private const string LauncherName = "launcherTemplate";
 | 
					        private static readonly string LauncherName = "launcherTemplate";
 | 
				
			||||||
        private static readonly string LauncherFullPath = Path.Combine(Application.dataPath, $"{K_ANDROID_PLUGINS_NAME}/{LauncherName}.gradle");
 | 
					        private static string LauncherFullPath = Path.Combine(Application.dataPath, $"Plugins/Android/{LauncherName}.gradle");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        private const string MainName = "mainTemplate";
 | 
					        private static readonly string MainName = "mainTemplate";
 | 
				
			||||||
        private static readonly string MainFullPath = Path.Combine(Application.dataPath,  $"{K_ANDROID_PLUGINS_NAME}/{MainName}.gradle");
 | 
					        private static string MainFullPath = Path.Combine(Application.dataPath,  $"Plugins/Android/{MainName}.gradle");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        private const string BaseProjectName = "baseProjectTemplate";
 | 
					        private static readonly string PropertiesName = "gradleTemplate";
 | 
				
			||||||
        private static readonly string BaseProjectFullPath = Path.Combine(Application.dataPath,  $"{K_ANDROID_PLUGINS_NAME}/{BaseProjectName}.gradle");
 | 
					        private static string PropertiesFullPath = Path.Combine(Application.dataPath,  $"Plugins/Android/{PropertiesName}.properties");
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private const string PropertiesName = "gradleTemplate";
 | 
					 | 
				
			||||||
        private const string K_ENABLE_R8 = "android.enableR8";
 | 
					 | 
				
			||||||
        private static readonly string PropertiesFullPath = Path.Combine(Application.dataPath,  $"{K_ANDROID_PLUGINS_NAME}/{PropertiesName}.properties");
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private const string SettingsName = "settingsTemplate";
 | 
					 | 
				
			||||||
        private static readonly string SettingsFullPath = Path.Combine(Application.dataPath,  $"Plugins/Android/{SettingsName}.gradle");
 | 
					 | 
				
			||||||
        private const string K_LINE_UNITY_PROJECT = "def unityProjectPath";
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private const string ProguardUserName = "proguard-user";
 | 
					 | 
				
			||||||
        private static readonly string ProguardUserFullPath = Path.Combine(Application.dataPath,  $"{K_ANDROID_PLUGINS_NAME}/{ProguardUserName}.txt");
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        public static void Apply()
 | 
					        public static void Apply()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            ApplyLauncher();
 | 
					            FixLauncher();
 | 
				
			||||||
            ApplyBaseProjectTemplates();
 | 
					            FixMain();
 | 
				
			||||||
            ApplyMainTemplates();
 | 
					            FixProperties();
 | 
				
			||||||
            ApplyGradleTemplate();
 | 
					            CheckTargetSDKVersion();
 | 
				
			||||||
            ApplySettings();
 | 
					 | 
				
			||||||
            ApplyProguardUser();
 | 
					 | 
				
			||||||
            CheckTargetSDKVersion();  // 强制修复构建版本号
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static void ApplyLauncher()
 | 
					
 | 
				
			||||||
 | 
					        private static void FixLauncher()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (!File.Exists(LauncherFullPath))
 | 
					            if (!File.Exists(LauncherFullPath))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
| 
						 | 
					@ -58,9 +41,10 @@ namespace Guru.Editor
 | 
				
			||||||
            var ptn2 = "abortOnError false";
 | 
					            var ptn2 = "abortOnError false";
 | 
				
			||||||
            var lines = File.ReadAllLines(LauncherFullPath);
 | 
					            var lines = File.ReadAllLines(LauncherFullPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            string line = "";
 | 
				
			||||||
            for (int i = 0; i < lines.Length; i++)
 | 
					            for (int i = 0; i < lines.Length; i++)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var line = lines[i];
 | 
					                line = lines[i];
 | 
				
			||||||
                if (line.Contains(ptn1))
 | 
					                if (line.Contains(ptn1))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    lines[i] = line.Replace(ptn1, "\n\n\tpackagingOptions {\n\t\texclude(\"META-INF/*.kotlin_module\")\n\t}\n\n");
 | 
					                    lines[i] = line.Replace(ptn1, "\n\n\tpackagingOptions {\n\t\texclude(\"META-INF/*.kotlin_module\")\n\t}\n\n");
 | 
				
			||||||
| 
						 | 
					@ -78,7 +62,7 @@ namespace Guru.Editor
 | 
				
			||||||
            File.WriteAllLines(LauncherFullPath, lines);
 | 
					            File.WriteAllLines(LauncherFullPath, lines);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        private static void ApplyMainTemplates()
 | 
					        private static void FixMain()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (!File.Exists(MainFullPath))
 | 
					            if (!File.Exists(MainFullPath))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
| 
						 | 
					@ -86,100 +70,13 @@ namespace Guru.Editor
 | 
				
			||||||
                CopyFile($"{MainName}.txt", MainFullPath);
 | 
					                CopyFile($"{MainName}.txt", MainFullPath);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        private static void FixProperties()
 | 
				
			||||||
        private static void ApplyBaseProjectTemplates()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!File.Exists(BaseProjectFullPath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.Log($"[MOD] --- Copy file to: {BaseProjectFullPath}");
 | 
					 | 
				
			||||||
                CopyFile($"{BaseProjectName}.txt", BaseProjectFullPath);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static void ApplyGradleTemplate()
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (!File.Exists(PropertiesFullPath))
 | 
					            if (!File.Exists(PropertiesFullPath))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Debug.Log($"[MOD] --- Copy file to: {PropertiesFullPath}");
 | 
					                Debug.Log($"[MOD] --- Copy file to: {PropertiesFullPath}");
 | 
				
			||||||
                CopyFile($"{PropertiesName}.txt", PropertiesFullPath);
 | 
					                CopyFile($"{PropertiesName}.txt", PropertiesFullPath);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (TargetSDKVersion > 33)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                FixGradleTemplate(PropertiesFullPath);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 该版本中不再使用 R8
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="filePath"></param>
 | 
					 | 
				
			||||||
        private static void FixGradleTemplate(string filePath)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (File.Exists(filePath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                bool isDirty = false;
 | 
					 | 
				
			||||||
                var lines = File.ReadAllLines(filePath);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                for (int i = 0; i < lines.Length; i++)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (lines[i].Contains(K_ENABLE_R8))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        lines[i] = $"# {lines[i]}"; // 禁用R8
 | 
					 | 
				
			||||||
                        isDirty = true;
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (isDirty) File.WriteAllLines(filePath, lines);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 写入 settings.gradle 配置文件
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private static void ApplySettings()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!File.Exists(SettingsFullPath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                CopyFile($"{SettingsName}.txt", SettingsFullPath);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            FixProjectPathInSettings(SettingsFullPath);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static void FixProjectPathInSettings(string settingsPath)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            bool isDirty = false;
 | 
					 | 
				
			||||||
            if (File.Exists(settingsPath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                string projectPath = Path.GetFullPath($"{Application.dataPath}/../").Replace("\\", "/");
 | 
					 | 
				
			||||||
                var lines = File.ReadAllLines(settingsPath);
 | 
					 | 
				
			||||||
                for (int i = 0; i < lines.Length; i++)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    if (lines[i].Contains(K_LINE_UNITY_PROJECT))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        lines[i] = $"        def unityProjectPath = $/file:////{projectPath}/$.replace(\"\\\\\", \"/\")";
 | 
					 | 
				
			||||||
                        isDirty = true;
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                if(isDirty)
 | 
					 | 
				
			||||||
                    File.WriteAllLines(settingsPath, lines);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 写入所有的配置文件
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private static void ApplyProguardUser()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!File.Exists(ProguardUserFullPath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                CopyFile($"{ProguardUserName}.txt", ProguardUserFullPath);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static void CheckTargetSDKVersion()
 | 
					        private static void CheckTargetSDKVersion()
 | 
				
			||||||
| 
						 | 
					@ -196,8 +93,8 @@ namespace Guru.Editor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static string GetMoveFilePath(string fileName)
 | 
					        private static string GetMoveFilePath(string fileName)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var path = GuruEditorHelper.GetAssetPath(nameof(AndroidProjectMod), "Script", true);
 | 
					            var path = GuruEditorHelper.GetFilePath($"{nameof(AndroidProjectMod)}.cs t:Script");
 | 
				
			||||||
            var files = Path.GetFullPath($"{path}/../../Files");
 | 
					            var files = Path.GetFullPath($"{path}/../Files");
 | 
				
			||||||
            return $"{files}/{fileName}";
 | 
					            return $"{files}/{fileName}";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        private static void CopyFile(string fileName, string toPath)
 | 
					        private static void CopyFile(string fileName, string toPath)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,159 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
using System.Net;
 | 
					 | 
				
			||||||
using UnityEditor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru.Editor
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    using System.IO;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public class AndroidPushIconHelper
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public static readonly int targetWidth = 96; // 目标宽度
 | 
					 | 
				
			||||||
        public static readonly int targetHeight = 96;
 | 
					 | 
				
			||||||
        private static readonly string LibName = "SDKRes";
 | 
					 | 
				
			||||||
        private static readonly string PackageName = "com.guru.unity.res";
 | 
					 | 
				
			||||||
        private static readonly string IconName = "ic_notification.png";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static readonly string ColorContent =
 | 
					 | 
				
			||||||
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorAccent\">#{0}</color>\n</resources>";
 | 
					 | 
				
			||||||
        private static readonly string ValueContent =
 | 
					 | 
				
			||||||
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"default_notification_channel_id\" translatable=\"false\">{0}</string>\n</resources>";
 | 
					 | 
				
			||||||
        private static readonly string[] iconNames = new string[]
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            "drawable-mdpi",
 | 
					 | 
				
			||||||
            "drawable-hdpi",
 | 
					 | 
				
			||||||
            "drawable-xhdpi",
 | 
					 | 
				
			||||||
            "drawable-xxhdpi",
 | 
					 | 
				
			||||||
            "drawable-xxxhdpi"
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static string FcmChannelId = "fcm_default_channel";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 设置推送图标
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="source"></param>
 | 
					 | 
				
			||||||
        /// <param name="color"></param>
 | 
					 | 
				
			||||||
        public static bool SetPushIconAssets(Texture2D source, Color color = default(Color))
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (source == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.LogError($"=== No Texture2D found ===");
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return DeployAllIcons(source, color);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            MakeTextureReadable(source);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Texture2D result = new Texture2D(newWidth, newHeight);
 | 
					 | 
				
			||||||
            // Color[] newColors = new Color[newWidth * newHeight];
 | 
					 | 
				
			||||||
            //
 | 
					 | 
				
			||||||
            // for (int y = 0; y < newHeight; y++)
 | 
					 | 
				
			||||||
            // {
 | 
					 | 
				
			||||||
            //     for (int x = 0; x < newWidth; x++)
 | 
					 | 
				
			||||||
            //     {
 | 
					 | 
				
			||||||
            //         // 应用一些缩放逻辑来获取新的颜色值
 | 
					 | 
				
			||||||
            //         newColors[x + y * newWidth] = source.GetPixelBilinear((float)x / newWidth * source.width, (float)y / newHeight * source.height);
 | 
					 | 
				
			||||||
            //     }
 | 
					 | 
				
			||||||
            // }
 | 
					 | 
				
			||||||
            //
 | 
					 | 
				
			||||||
            // result.SetPixels(newColors);
 | 
					 | 
				
			||||||
            // result.Apply();
 | 
					 | 
				
			||||||
            // return result;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            RenderTexture rt=new RenderTexture(newWidth, newHeight,24);
 | 
					 | 
				
			||||||
            RenderTexture.active = rt;
 | 
					 | 
				
			||||||
            Graphics.Blit(source,rt);
 | 
					 | 
				
			||||||
            Texture2D result=new Texture2D(newWidth,newHeight);
 | 
					 | 
				
			||||||
            result.ReadPixels(new Rect(0,0,newWidth,newHeight),0,0);
 | 
					 | 
				
			||||||
            result.Apply();
 | 
					 | 
				
			||||||
            return result;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static void MakeTextureReadable(Texture2D source)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (source.isReadable) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var path = AssetDatabase.GetAssetPath(source);
 | 
					 | 
				
			||||||
            TextureImporter ti = (TextureImporter)AssetImporter.GetAtPath(path);
 | 
					 | 
				
			||||||
            if (!ti.isReadable)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ti.isReadable = true;
 | 
					 | 
				
			||||||
                ti.SaveAndReimport();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static string ColorToHex(Color color)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return string.Format("{0:X2}{1:X2}{2:X2}", (int)(color.r * 255), (int)(color.g * 255), (int)(color.b * 255));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static bool DeployAllIcons(Texture2D source, Color color)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var dir = AndroidLibHelper.CreateLibRoot(PackageName, LibName);
 | 
					 | 
				
			||||||
            string path = "";
 | 
					 | 
				
			||||||
            string content = "";
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            var result = ResizeTexture(source, targetWidth, targetHeight);
 | 
					 | 
				
			||||||
            byte[] bytes = result.EncodeToPNG();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var resPath = $"{dir}/res";
 | 
					 | 
				
			||||||
            if (!Directory.Exists(resPath))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Directory.CreateDirectory(resPath);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            File.WriteAllBytes($"{resPath}/{IconName}", bytes); // Base Icon;
 | 
					 | 
				
			||||||
            // ----- Build all  Icons ------
 | 
					 | 
				
			||||||
            foreach (var iconName in iconNames)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var iconPath = $"{resPath}/{iconName}";
 | 
					 | 
				
			||||||
                if (!Directory.Exists(iconPath))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Directory.CreateDirectory(iconPath);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                File.WriteAllBytes($"{iconPath}/{IconName}", bytes);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var valuesPath = $"{resPath}/values";
 | 
					 | 
				
			||||||
            if (!Directory.Exists(valuesPath)) Directory.CreateDirectory(valuesPath);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // ----- Build colors.xml ------
 | 
					 | 
				
			||||||
            path = $"{valuesPath}/colors.xml";
 | 
					 | 
				
			||||||
            content = ColorContent.Replace("{0}", ColorToHex(color));
 | 
					 | 
				
			||||||
            File.WriteAllText(path, content);
 | 
					 | 
				
			||||||
            // ----- Build strings.xml ------
 | 
					 | 
				
			||||||
            path = $"{valuesPath}/strings.xml";
 | 
					 | 
				
			||||||
            content = ValueContent.Replace("{0}", FcmChannelId);
 | 
					 | 
				
			||||||
            File.WriteAllText(path, content);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // ----- Inject AndroidManifest.xml ------
 | 
					 | 
				
			||||||
            var doc = AndroidManifestDoc.Load();
 | 
					 | 
				
			||||||
            if (doc != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                doc.SetMetadata("com.google.firebase.messaging.default_notification_icon", "@drawable/ic_notification", valueName:"resource");
 | 
					 | 
				
			||||||
                doc.SetMetadata("com.google.firebase.messaging.default_notification_color", "@color/colorAccent", valueName:"resource");
 | 
					 | 
				
			||||||
                doc.SetMetadata("com.google.firebase.messaging.default_notification_channel_id", "@string/default_notification_channel_id");
 | 
					 | 
				
			||||||
                doc.Save();
 | 
					 | 
				
			||||||
                Debug.Log("<color=#88ff00> --- Push Icon Build Success --- </color>");
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                AssetDatabase.Refresh();
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            Debug.LogError("AndroidManifest.xml not found ...");
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 39d65f42b9694641b0c3509262aca1d4
 | 
					 | 
				
			||||||
timeCreated: 1711724404
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,51 +0,0 @@
 | 
				
			||||||
namespace Guru.Editor
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using System.IO;
 | 
					 | 
				
			||||||
    using UnityEditor;
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public class AndroidResMod
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private static readonly string NetworkSecurityXmlName = "network_security_config.xml";
 | 
					 | 
				
			||||||
        private static readonly string LibNetworkSecurity = "GuruNetworkSecurity";
 | 
					 | 
				
			||||||
        private static readonly string LibNetworkSecurityPackageName = "com.guru.unity.sdk.android.res.network.security";
 | 
					 | 
				
			||||||
        private static string OldSecurityXml = Path.Combine(Application.dataPath, $"Plugins/Android/res/xml/{NetworkSecurityXmlName}");
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 应用补丁
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static void Apply()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            DeployNetworkSecurity();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 部署网络安全配置
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private static void DeployNetworkSecurity()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if(File.Exists(OldSecurityXml)) File.Delete(OldSecurityXml); // 清理旧文件
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!AndroidLibHelper.IsEmbeddedAndroidLibExists(LibNetworkSecurity))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                string dir = AndroidLibHelper.CreateLibRoot(LibNetworkSecurityPackageName, LibNetworkSecurity);
 | 
					 | 
				
			||||||
                var d = GuruEditorHelper.GetAssetPath(nameof(AndroidResMod), "Script", true);
 | 
					 | 
				
			||||||
                if (!string.IsNullOrEmpty(d))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var from = $"{Directory.GetParent(d)?.FullName ?? ""}/../Files/{NetworkSecurityXmlName}";
 | 
					 | 
				
			||||||
                    if (File.Exists(from))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        string toDir = $"{dir}/res/xml";
 | 
					 | 
				
			||||||
                        if(!Directory.Exists(toDir))Directory.CreateDirectory(toDir);
 | 
					 | 
				
			||||||
                        string to = $"{toDir}/{NetworkSecurityXmlName}";
 | 
					 | 
				
			||||||
                        FileUtil.CopyFileOrDirectory(from, to);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: c06b1730aac144929ca73fba2e481508
 | 
					 | 
				
			||||||
timeCreated: 1706058636
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,46 +1,17 @@
 | 
				
			||||||
namespace Guru.Editor
 | 
					namespace Guru.Editor
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using UnityEditor;
 | 
					    using UnityEditor;
 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using System.IO;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public class GuruEditorHelper
 | 
					    public class GuruEditorHelper
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static string GetAssetPath(string filter, bool useFullPath = false)
 | 
					        public static string GetFilePath(string filter)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var guids = AssetDatabase.FindAssets(filter);
 | 
					            var guids = AssetDatabase.FindAssets(filter);
 | 
				
			||||||
            string path = "";
 | 
					 | 
				
			||||||
            string fullPath = "";
 | 
					 | 
				
			||||||
            if (guids != null && guids.Length > 0)
 | 
					            if (guids != null && guids.Length > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                path = AssetDatabase.GUIDToAssetPath(guids[0]);
 | 
					                var path = AssetDatabase.GUIDToAssetPath(guids[0]);
 | 
				
			||||||
                fullPath = path.Replace("Assets", Application.dataPath);
 | 
					                return path;
 | 
				
			||||||
                if (File.Exists(fullPath))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    return useFullPath? fullPath : path;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return "";
 | 
					            return "";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static string GetAssetPath(string fileName, string typeName = "", bool useFullPath = false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var filter = fileName;
 | 
					 | 
				
			||||||
            if(!string.IsNullOrEmpty(typeName)) filter = $"{fileName} t:{typeName}";
 | 
					 | 
				
			||||||
            return GetAssetPath(filter, useFullPath);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static void OpenPath(string path)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
#if UNITY_EDITOR_OSX
 | 
					 | 
				
			||||||
            EditorUtility.RevealInFinder(path);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
            Application.OpenURL($"file://{path}");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -37,7 +37,7 @@ namespace Guru.Editor
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        static BoostOnLoad()
 | 
					        static BoostOnLoad()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            EditorGuruServiceIO.DeployLocalServiceFile();
 | 
					            EditorGuruServiceIO.DeployAppServiceFile();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 7f9d1111a9d94187bf583ae71d9192f0
 | 
					 | 
				
			||||||
timeCreated: 1711858346
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,116 +0,0 @@
 | 
				
			||||||
namespace Guru.Editor
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using System.Collections.Generic;
 | 
					 | 
				
			||||||
    using System.IO;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public class EaseConfigFile
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private Dictionary<string, string> _dataDict;
 | 
					 | 
				
			||||||
        private string _filePath;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        protected bool ReadFile(string path)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _filePath = path;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (File.Exists(path))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var lines = File.ReadAllLines(path);
 | 
					 | 
				
			||||||
                int len = lines.Length;
 | 
					 | 
				
			||||||
                _dataDict = new Dictionary<string, string>(len);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                string key = "";
 | 
					 | 
				
			||||||
                string value = "";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                for (int i=0; i< len; i++)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var line = lines[i];
 | 
					 | 
				
			||||||
                    if (line.Contains("="))
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        key = "";
 | 
					 | 
				
			||||||
                        value = "";
 | 
					 | 
				
			||||||
                        var kv = line.Split('=');
 | 
					 | 
				
			||||||
                        if(kv.Length > 0) key = kv[0];
 | 
					 | 
				
			||||||
                        if(kv.Length > 1) value = kv[1];
 | 
					 | 
				
			||||||
                        if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            _dataDict[key] = value;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            _dataDict = new Dictionary<string, string>(10);
 | 
					 | 
				
			||||||
            var dir = Directory.GetParent(path);
 | 
					 | 
				
			||||||
            if(dir is { Exists: false }) dir.Create();
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void Save()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_dataDict == null || _dataDict.Count < 1) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            List<string> lines = new List<string>(_dataDict.Count);
 | 
					 | 
				
			||||||
            foreach (var key in _dataDict.Keys)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                lines.Add($"{key}={_dataDict[key].ToString()}");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if(!string.IsNullOrEmpty(_filePath))
 | 
					 | 
				
			||||||
                File.WriteAllLines(_filePath, lines);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public void Set(string key, object value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_dataDict == null) _dataDict = new Dictionary<string, string>(10);
 | 
					 | 
				
			||||||
            _dataDict[key] = value.ToString();
 | 
					 | 
				
			||||||
            Save();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public string Get(string key) => _dataDict.ContainsKey(key) ? _dataDict[key] : "";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool TryGet(string key, out string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            value = "";
 | 
					 | 
				
			||||||
            return _dataDict?.TryGetValue(key, out value) ?? false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool GetBool(string key, bool defaultVal = false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (TryGet(key, out var str))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return (str.ToLower() == "true" || str == "1");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return defaultVal;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public int GetInt(string key, int defaultVal = 0)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (TryGet(key, out var str))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var inVal = 0;
 | 
					 | 
				
			||||||
                if (int.TryParse(str, out inVal))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    return inVal;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return defaultVal;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public float GetFloat(string key, float defaultVal = 0)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (TryGet(key, out var str))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                float val = 0;
 | 
					 | 
				
			||||||
                if (float.TryParse(str, out val))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    return val;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return defaultVal;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 58ea378c89d742008a18f019afd54a27
 | 
					 | 
				
			||||||
timeCreated: 1711858357
 | 
					 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,7 @@
 | 
				
			||||||
        "GuruSDK",
 | 
					        "GuruSDK",
 | 
				
			||||||
        "Guru.LitJson",
 | 
					        "Guru.LitJson",
 | 
				
			||||||
        "Guru.Runtime",
 | 
					        "Guru.Runtime",
 | 
				
			||||||
        "MaxSdk.Scripts.IntegrationManager.Editor",
 | 
					        "MaxSdk.Scripts.IntegrationManager.Editor"
 | 
				
			||||||
        "Guru.Editor",
 | 
					 | 
				
			||||||
        "GuruAdjust.Editor"
 | 
					 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "includePlatforms": [
 | 
					    "includePlatforms": [
 | 
				
			||||||
        "Editor"
 | 
					        "Editor"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 1d20da1a87484f37bc53360cc2414030
 | 
					 | 
				
			||||||
timeCreated: 1712622721
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,23 +0,0 @@
 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// 更新器
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public interface IUpdater
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        UpdaterState State { get; }
 | 
					 | 
				
			||||||
        void Start();
 | 
					 | 
				
			||||||
        void OnUpdate();
 | 
					 | 
				
			||||||
        void Pause(bool pause);
 | 
					 | 
				
			||||||
        void Kill();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public enum UpdaterState
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        Prepare,
 | 
					 | 
				
			||||||
        Running,
 | 
					 | 
				
			||||||
        Pause,
 | 
					 | 
				
			||||||
        Kill,
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 6ab7dd209c3a494eab0dfbedce0700d8
 | 
					 | 
				
			||||||
timeCreated: 1712622736
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,82 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using System.Collections;
 | 
					 | 
				
			||||||
    using System.Collections.Generic;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public class ThreadHandler: IUpdater
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private Queue<Action> _actions;
 | 
					 | 
				
			||||||
        public Queue<Action> Actions
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if(_actions == null) 
 | 
					 | 
				
			||||||
                    _actions = new Queue<Action>(10);
 | 
					 | 
				
			||||||
                return _actions;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            set
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (value != null) _actions = value;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private UpdaterState _state;
 | 
					 | 
				
			||||||
        public UpdaterState State => _state;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 启动 Updater
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public void Start()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _state = UpdaterState.Running;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 执行方案
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public void OnUpdate()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (Actions.Count > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // 消耗对垒
 | 
					 | 
				
			||||||
                while (Actions.Count > 0)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Actions.Dequeue()?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public void Pause(bool pause = true)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _state = pause ? UpdaterState.Pause : UpdaterState.Running;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 删除对象
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public void Kill()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _state = UpdaterState.Kill;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void Dispose()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _actions.Clear();
 | 
					 | 
				
			||||||
            _state = UpdaterState.Kill;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public void AddAction(Action action)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (action == null) return;
 | 
					 | 
				
			||||||
            Actions.Enqueue(action);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: dc05f1b1ff5947c19ce6af1db301398d
 | 
					 | 
				
			||||||
timeCreated: 1712624042
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,213 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using System.Collections.Generic;
 | 
					 | 
				
			||||||
    using System.Text;
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// 启动参数配置
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public class GuruSDKInitConfig
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region Properties
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 使用自定义的ConsentFlow启动流程
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public bool UseCustomConsent = false;
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// SDK初始化完成后自动加载广告
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public bool AutoLoadWhenAdsReady = true;
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 使用IAP支付插件功能
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public bool IAPEnabled = true;
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 自动申请推送授权信息
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public bool AutoNotificationPermission = true;
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 自动记录完成的关卡
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        [Obsolete("Will be removed from InitConfig in next version. Use the <b_level> and <b_play> data from the GameUserData from game itself instead!")]
 | 
					 | 
				
			||||||
        public bool AutoRecordFinishedLevels = false;
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 自定义 Service 云控 Key
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public string CustomServiceKey = "";
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Banner 背景颜色 Hex 值
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public string BannerBackgroundColor = "#00000040";
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 已购买去广告道具
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public bool IsBuyNoAds = false;
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Debug模式(默认关闭)
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public bool DebugMode = false;
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Debug模式下开启打点(默认关闭)
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public bool EnableDebugLogEvent = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private Dictionary<string, object> _defaultRemoteData = new Dictionary<string, object>();
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 云控参数的默认配置
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public Dictionary<string, object> DefaultRemoteData
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            set
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (value != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _defaultRemoteData = value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            get => _defaultRemoteData;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 启用 AdjustDeeplink
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public Action<string> OnAdjustDeeplinkCallback = null;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 支付初始化Keys
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public byte[] GoogleKeys;       // 数据取自 GooglePlayTangle.Data();
 | 
					 | 
				
			||||||
        public byte[] AppleRootCerts;   // 数据取自 AppleTangle.Data();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Print
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public override string ToString()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            StringBuilder sb = new StringBuilder();
 | 
					 | 
				
			||||||
            sb.AppendLine($"------- Custom InitConfig -------");
 | 
					 | 
				
			||||||
            sb.AppendLine($"\t  UseCustomConsent: {UseCustomConsent}");
 | 
					 | 
				
			||||||
            sb.AppendLine($"\t  AutoLoadWhenAdsReady: {AutoLoadWhenAdsReady}");
 | 
					 | 
				
			||||||
            sb.AppendLine($"\t  IAPEnabled: {IAPEnabled}");
 | 
					 | 
				
			||||||
            sb.AppendLine($"\t  AutoNotificationPermission: {AutoNotificationPermission}");
 | 
					 | 
				
			||||||
            sb.AppendLine($"\t  AutoRecordFinishedLevels: {AutoRecordFinishedLevels}");
 | 
					 | 
				
			||||||
            sb.AppendLine($"\t  CustomServiceKey: {CustomServiceKey}");
 | 
					 | 
				
			||||||
            sb.AppendLine($"\t  BannerBackgroundColor: {BannerBackgroundColor}");
 | 
					 | 
				
			||||||
            sb.AppendLine($"\t  IsBuyNoAds: {IsBuyNoAds}");
 | 
					 | 
				
			||||||
            sb.AppendLine($"\t  DebugMode: {DebugMode}");
 | 
					 | 
				
			||||||
            sb.AppendLine($"\t  DefaultRemote: Count: {DefaultRemoteData.Count}");
 | 
					 | 
				
			||||||
            sb.AppendLine($"------- Custom InitConfig -------");
 | 
					 | 
				
			||||||
            return sb.ToString();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Builder
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 构造器
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static GuruSDKInitConfigBuilder Builder() => new GuruSDKInitConfigBuilder();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// 构建器
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public class GuruSDKInitConfigBuilder
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private GuruSDKInitConfig _config = new GuruSDKInitConfig();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 构建配置
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public GuruSDKInitConfig Build()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return _config;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetUseCustomConsent(bool value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.UseCustomConsent = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetAutoLoadWhenAdsReady(bool value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.AutoLoadWhenAdsReady = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetIAPEnabled(bool value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.IAPEnabled = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetAutoRecordFinishedLevels(bool value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.AutoRecordFinishedLevels = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetIsBuyNoAds(bool value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.IsBuyNoAds = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetBannerBackgroundColor(string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.BannerBackgroundColor = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetDebugMode(bool value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.DebugMode = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetOnAdjustDeeplinkCallback(Action<string> callback)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.OnAdjustDeeplinkCallback = callback;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetGoogleKeys(byte[] value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.GoogleKeys = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetAppleRootCerts(byte[]  value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.AppleRootCerts = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetDefaultRemoteData(Dictionary<string, object> value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.DefaultRemoteData = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetEnableDebugLogEvent(bool value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.EnableDebugLogEvent = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetCustomServiceKey(string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.CustomServiceKey = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public GuruSDKInitConfigBuilder SetAutoNotificationPermission(bool value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _config.AutoNotificationPermission = value;
 | 
					 | 
				
			||||||
            return this;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,16 +1,15 @@
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using UnityEngine.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Guru
 | 
					namespace Guru
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using UnityEngine.Serialization;
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    [Serializable]
 | 
					    [Serializable]
 | 
				
			||||||
    public class GuruServicesConfig
 | 
					    public class GuruServicesConfig
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public long version = 0;
 | 
					        public long version = 0;
 | 
				
			||||||
        public GuruAppSettings app_settings;
 | 
					        public GuruAppSettings app_settings;
 | 
				
			||||||
        public GuruParameters parameters;
 | 
					 | 
				
			||||||
        public GuruAdjustSettings adjust_settings;
 | 
					        public GuruAdjustSettings adjust_settings;
 | 
				
			||||||
        public GuruFbSettings fb_settings;
 | 
					        public GuruFbSettings fb_settings;
 | 
				
			||||||
        public GuruAdSettings ad_settings;
 | 
					        public GuruAdSettings ad_settings;
 | 
				
			||||||
| 
						 | 
					@ -35,40 +34,10 @@ namespace Guru
 | 
				
			||||||
        public bool IsMolocoIOSEnabled() => ad_settings != null && 
 | 
					        public bool IsMolocoIOSEnabled() => ad_settings != null && 
 | 
				
			||||||
                                                ad_settings.moloco_ids_ios != null &&
 | 
					                                                ad_settings.moloco_ids_ios != null &&
 | 
				
			||||||
                                                ad_settings.moloco_ids_ios.Length > 0;
 | 
					                                                ad_settings.moloco_ids_ios.Length > 0;
 | 
				
			||||||
        public bool IsTradplusAndroidEnabled() => ad_settings != null && 
 | 
					 | 
				
			||||||
                                            ad_settings.tradplus_ids_android != null &&
 | 
					 | 
				
			||||||
                                            ad_settings.tradplus_ids_android.Length > 0;
 | 
					 | 
				
			||||||
        public bool IsTradplusIOSEnabled() => ad_settings != null && 
 | 
					 | 
				
			||||||
                                              ad_settings.tradplus_ids_ios != null &&
 | 
					 | 
				
			||||||
                                              ad_settings.tradplus_ids_ios.Length > 0;
 | 
					 | 
				
			||||||
        public bool IsIAPEnabled() => app_settings != null && app_settings.enable_iap 
 | 
					        public bool IsIAPEnabled() => app_settings != null && app_settings.enable_iap 
 | 
				
			||||||
                                                           && products != null && products.Length > 0;
 | 
					                                                           && products != null && products.Length > 0;
 | 
				
			||||||
        public bool UseCustomKeystore() => app_settings?.custom_keystore ?? false;
 | 
					        public bool IsKeywordsEnabled() => app_settings != null && app_settings.enable_keywords; 
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public bool IsFirebaseEnabled() => app_settings?.enable_firebase ?? true;
 | 
					 | 
				
			||||||
        public bool IsFacebookEnabled() => app_settings?.enable_facebook ?? true;
 | 
					 | 
				
			||||||
        public bool IsAdjustEnabled() => app_settings?.enable_adjust ?? true;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        //-------------------------------- 配置检测 -------------------------------
 | 
					        //-------------------------------- 配置检测 -------------------------------
 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        //-------------------------------- Parameters --------------------------------
 | 
					 | 
				
			||||||
        public double Tch02Value() => parameters?.tch_020 ?? 0;
 | 
					 | 
				
			||||||
        public bool IsAppReview() => parameters?.apple_review ?? false;
 | 
					 | 
				
			||||||
        public bool EnableAnaErrorLog() => parameters?.enable_errorlog ?? false;
 | 
					 | 
				
			||||||
        public bool IsAdsCompliance() => parameters?.ads_compliance ?? false;
 | 
					 | 
				
			||||||
        public bool DMACountryCheck() => parameters?.dma_country_check ?? false;
 | 
					 | 
				
			||||||
        public string DMAMapRule() => parameters?.dma_map_rule ?? "";
 | 
					 | 
				
			||||||
        public bool UseUUID() => parameters?.using_uuid ?? false;
 | 
					 | 
				
			||||||
        public bool KeywordsEnabled() => parameters?.enable_keywords ?? false; 
 | 
					 | 
				
			||||||
        public int TokenValidTime() => parameters?.token_valid_time ?? 604800;
 | 
					 | 
				
			||||||
        public int LevelEndSuccessNum() => parameters?.level_end_success_num ?? 50;
 | 
					 | 
				
			||||||
        public string CdnHost() => parameters?.cdn_host ?? "";
 | 
					 | 
				
			||||||
        public bool UsingUUID() => parameters?.using_uuid ?? true;
 | 
					 | 
				
			||||||
        //-------------------------------- Parameters --------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    [Serializable]    
 | 
					    [Serializable]    
 | 
				
			||||||
| 
						 | 
					@ -82,27 +51,13 @@ namespace Guru
 | 
				
			||||||
        public string terms_url;
 | 
					        public string terms_url;
 | 
				
			||||||
        public string android_store;
 | 
					        public string android_store;
 | 
				
			||||||
        public string ios_store;
 | 
					        public string ios_store;
 | 
				
			||||||
 | 
					        public int token_vaild_time = 604800;
 | 
				
			||||||
 | 
					        public int level_end_success_num = 50;
 | 
				
			||||||
 | 
					        public bool enable_keywords = true;
 | 
				
			||||||
        public bool enable_firebase = true;
 | 
					        public bool enable_firebase = true;
 | 
				
			||||||
        public bool enable_facebook = true;
 | 
					        public bool enable_facebook = true;
 | 
				
			||||||
        public bool enable_adjust = true;
 | 
					        public bool enable_adjust = true;
 | 
				
			||||||
        public bool enable_iap = false;
 | 
					        public bool enable_iap = false;
 | 
				
			||||||
        public bool custom_keystore = false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [Serializable]
 | 
					 | 
				
			||||||
    public class GuruParameters
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public int token_valid_time = 604800;
 | 
					 | 
				
			||||||
        public int level_end_success_num = 50;
 | 
					 | 
				
			||||||
        public bool enable_keywords = false;
 | 
					 | 
				
			||||||
        public double tch_020 = 0;
 | 
					 | 
				
			||||||
        public bool using_uuid = false;
 | 
					 | 
				
			||||||
        public string dma_map_rule = "";
 | 
					 | 
				
			||||||
        public bool dma_country_check = false;
 | 
					 | 
				
			||||||
        public bool apple_review = false; // 苹果审核标志位
 | 
					 | 
				
			||||||
        public bool enable_errorlog = false;
 | 
					 | 
				
			||||||
        public bool ads_compliance = false;
 | 
					 | 
				
			||||||
        public string cdn_host = "";
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    [Serializable]
 | 
					    [Serializable]
 | 
				
			||||||
| 
						 | 
					@ -111,15 +66,15 @@ namespace Guru
 | 
				
			||||||
        public string[] app_token;
 | 
					        public string[] app_token;
 | 
				
			||||||
        public string[] events;
 | 
					        public string[] events;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string AndroidToken() => app_token != null && app_token.Length > 0 ? app_token[0] : "";
 | 
					        public string AndroidToken => app_token != null && app_token.Length > 0 ? app_token[0] : "";
 | 
				
			||||||
        public string iOSToken() => app_token != null && app_token.Length > 1 ? app_token[1] : "";
 | 
					        public string iOSToken => app_token != null && app_token.Length > 1 ? app_token[1] : "";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    [Serializable]
 | 
					    [Serializable]
 | 
				
			||||||
    public class GuruFbSettings
 | 
					    public class GuruFbSettings
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public string fb_app_id;
 | 
					        public string app_id;
 | 
				
			||||||
        public string fb_client_token;
 | 
					        public string client_token;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Serializable]
 | 
					    [Serializable]
 | 
				
			||||||
| 
						 | 
					@ -135,8 +90,8 @@ namespace Guru
 | 
				
			||||||
        public string[] pubmatic_ids_ios;
 | 
					        public string[] pubmatic_ids_ios;
 | 
				
			||||||
        public string[] moloco_ids_android;
 | 
					        public string[] moloco_ids_android;
 | 
				
			||||||
        public string[] moloco_ids_ios;
 | 
					        public string[] moloco_ids_ios;
 | 
				
			||||||
        public string[] tradplus_ids_android;
 | 
					 | 
				
			||||||
        public string[] tradplus_ids_ios;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,227 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Guru
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    using UnityEngine;
 | 
				
			||||||
 | 
					    using System;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public partial class GuruSDK
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 启动广告服务
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static void StartAds()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (InitConfig.UseCustomConsent)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Debug.Log($"{Tag} --- Call <color=orange>StartAdsWithCustomConsent</color> when you use custom consent, and pass the result (boolean) to the method.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // 默认的启动顺序是先启动Consent后, 根据用户回调的结果来启动广告
 | 
				
			||||||
 | 
					                Instance.StartConsentFlow();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 使用自定义的Consent, 获取用户授权后, 调用此方法
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="userAllow"></param>
 | 
				
			||||||
 | 
					        public static void StartAdsWithCustomConsent(bool userAllow = true)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (userAllow)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                StartAdService();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Debug.Log($"{Tag} --- User refuse to provide ads Id, Ads Service will be cancelled");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region Guru Consent
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 启动Consent流程
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        private void StartConsentFlow()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogI($"StartConsentFlow");
 | 
				
			||||||
 | 
					            GuruConsent.StartConsent(OnConsentOver);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnConsentOver(int code)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Callbacks.ConsentFlow._onConsentResult?.Invoke(code);
 | 
				
			||||||
 | 
					            switch(code)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                case GuruConsent.StatusCode.OBTAINED:
 | 
				
			||||||
 | 
					                case GuruConsent.StatusCode.NOT_AVAILABLE:
 | 
				
			||||||
 | 
					                    // 已获取授权, 或者地区不可用
 | 
				
			||||||
 | 
					#if UNITY_IOS
 | 
				
			||||||
 | 
					                    CheckATTStatus();
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					                    StartAdService();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if UNITY_IOS
 | 
				
			||||||
 | 
					         /// <summary>
 | 
				
			||||||
 | 
					        /// iOS 平台检查 ATT 状态
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        private void CheckATTStatus()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            AttManager.Instance.CheckATTStatus(OnATTStatus);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region Ad Services
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static bool _initAdsCompleted = false;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 启动广告服务
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static void StartAdService()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogI($"StartAdService");
 | 
				
			||||||
 | 
					            ADService.Instance.StartService(OnAdsInitComplete, 
 | 
				
			||||||
 | 
					                InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            //--------- Callbacks -----------
 | 
				
			||||||
 | 
					            ADService.OnInterstitialLoaded = OnInterstitialLoaded;
 | 
				
			||||||
 | 
					            ADService.OnInterstitialFailed = OnInterstitialFailed;
 | 
				
			||||||
 | 
					            ADService.OnRewardLoaded = OnRewardLoaded;
 | 
				
			||||||
 | 
					            ADService.OnRewardFailed = OnRewardFailed;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static void OnInterstitialLoaded() 
 | 
				
			||||||
 | 
					            => Callbacks.Ads._onInterstitialADLoaded?.Invoke();
 | 
				
			||||||
 | 
					        private static void OnInterstitialFailed()
 | 
				
			||||||
 | 
					            => Callbacks.Ads._onInterstitialADFailed?.Invoke();
 | 
				
			||||||
 | 
					        private static void OnRewardLoaded()
 | 
				
			||||||
 | 
					            => Callbacks.Ads._onRewardedADLoaded?.Invoke(); 
 | 
				
			||||||
 | 
					        private static void OnRewardFailed()
 | 
				
			||||||
 | 
					            => Callbacks.Ads._onRewardADFailed?.Invoke();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static void OnAdsInitComplete()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _initAdsCompleted = true;
 | 
				
			||||||
 | 
					            Callbacks.Ads._onAdsInitComplete?.Invoke();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static bool CheckAdsReady()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!_initAdsCompleted)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                LogE("Ads is not ready. Call <GuruSDk.StartAdService> first.");
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 显示Banner广告
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="placement"></param>
 | 
				
			||||||
 | 
					        public static void ShowBanner(string placement = "")
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!CheckAdsReady()) return;
 | 
				
			||||||
 | 
					            ADService.Instance.ShowBanner(placement);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 隐藏Banner广告
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static void HideBanner()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!CheckAdsReady()) return;
 | 
				
			||||||
 | 
					            ADService.Instance.HideBanner();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static void LoadInterstitialAd()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!CheckAdsReady()) return;
 | 
				
			||||||
 | 
					            ADService.Instance.RequestInterstitialAD();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static bool IsInterstitialAdReady => ADService.Instance.IsInterstitialADReady();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 显示插屏广告
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="placement"></param>
 | 
				
			||||||
 | 
					        /// <param name="onDismissed"></param>
 | 
				
			||||||
 | 
					        public static void ShowInterstitialAd(string placement = "", Action onDismissed = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!CheckAdsReady()) return;
 | 
				
			||||||
 | 
					            if (!ADService.Instance.IsInterstitialADReady())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                LogE("Interstitial is not ready. Call <GuruSDk.ShowInterstitialAd> again.");
 | 
				
			||||||
 | 
					                LoadInterstitialAd();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ADService.Instance.ShowInterstitialAD(placement, onDismissed);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static void LoadRewardAd()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!CheckAdsReady()) return;
 | 
				
			||||||
 | 
					            ADService.Instance.RequestRewardedAD();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static bool IsRewardAdReady => ADService.Instance.IsRewardedADReady();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 显示激励视频广告
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="placement"></param>
 | 
				
			||||||
 | 
					        /// <param name="onRewarded"></param>
 | 
				
			||||||
 | 
					        /// <param name="onFailed"></param>
 | 
				
			||||||
 | 
					        public static void ShowRewardAd(string placement = "", Action onRewarded = null, Action<string> onFailed = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!CheckAdsReady()) return;
 | 
				
			||||||
 | 
					            if (!ADService.Instance.IsRewardedADReady())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                LogE("RewardAd is not ready. Call <GuruSDk.LoadRewardAd> again.");
 | 
				
			||||||
 | 
					                LoadRewardAd();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ADService.Instance.ShowRewardedAD(placement, onRewarded, onFailed);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region MaxServices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 显示Max调试菜单
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static void ShowMaxDebugPanel()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					#if UNITY_EDITOR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LogI($"Can not show Max Debug Panel in Editor, skipped.");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					            if (!ADService.Instance.IsInitialized)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                LogI($"ADService is not initialized, call <GuruSDK.StartAds> first.");
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ADService.Instance.ShowMaxDebugPanel();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,216 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Guru
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    using System.Collections.Generic;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    /// 打点管理
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
 | 
					    public partial class GuruSDK
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        //----------------- 关卡开始类型 ---------------------
 | 
				
			||||||
 | 
					        public const string EventLevelStartModePlay = "play";
 | 
				
			||||||
 | 
					        public const string EventLevelStartModeReplay = "replay";
 | 
				
			||||||
 | 
					        public const string EventLevelStartModeContinue= "continue";
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        //----------------- 关卡结束类型 ---------------------
 | 
				
			||||||
 | 
					        public const string EventLevelEndSuccess = "success";
 | 
				
			||||||
 | 
					        public const string EventLevelEndFail = "fail";
 | 
				
			||||||
 | 
					        public const string EventLevelEndExit = "exit";
 | 
				
			||||||
 | 
					        public const string EventLevelEndTimeout = "timeout";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region 通用接口
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 自定义事件打点
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="eventName"></param>
 | 
				
			||||||
 | 
					        /// <param name="data"></param>
 | 
				
			||||||
 | 
					        public static void LogEvent(string eventName, Dictionary<string, dynamic> data = null)
 | 
				
			||||||
 | 
					            => Analytics.Track(eventName, data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static void SetScreen(string screen, string extra = "") 
 | 
				
			||||||
 | 
					            => Analytics.SetCurrentScreen(screen, extra);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #region 游戏打点
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 游戏启动打点
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="level"></param>
 | 
				
			||||||
 | 
					        /// <param name="levelName"></param>
 | 
				
			||||||
 | 
					        /// <param name="levelCategory"></param>
 | 
				
			||||||
 | 
					        /// <param name="levelID"></param>
 | 
				
			||||||
 | 
					        /// <param name="startType"></param>
 | 
				
			||||||
 | 
					        /// <param name="isReplay"></param>
 | 
				
			||||||
 | 
					        public static void LogLevelStart(int level, string startType = EventLevelStartModePlay, 
 | 
				
			||||||
 | 
					            string levelCategory = "main", string levelName = "", string levelID = "",
 | 
				
			||||||
 | 
					            bool isReplay = false)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Analytics.LogLevelStart(level, levelName, levelCategory, levelID, startType, isReplay);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 游戏点击 Continue 重开始游戏
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="level"></param>
 | 
				
			||||||
 | 
					        /// <param name="levelCategory"></param>
 | 
				
			||||||
 | 
					        /// <param name="levelName"></param>
 | 
				
			||||||
 | 
					        /// <param name="levelID"></param>
 | 
				
			||||||
 | 
					        public static void LogLevelContinue(int level, string levelCategory = "main",
 | 
				
			||||||
 | 
					            string levelName = "", string levelID = "")
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogLevelStart(level, EventLevelStartModeContinue, levelCategory, levelName, levelID,  true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 游戏点击 Continue 重开始游戏
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="level"></param>
 | 
				
			||||||
 | 
					        /// <param name="levelCategory"></param>
 | 
				
			||||||
 | 
					        /// <param name="levelName"></param>
 | 
				
			||||||
 | 
					        /// <param name="levelID"></param>
 | 
				
			||||||
 | 
					        public static void LogLevelReplay(int level, string levelCategory = "main",
 | 
				
			||||||
 | 
					            string levelName = "", string levelID = "")
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogLevelStart(level, EventLevelStartModeReplay,levelCategory, levelName, levelID,  true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 游戏胜利打点
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static void LogLevelEnd(int level,  string result = EventLevelEndSuccess,
 | 
				
			||||||
 | 
					            string levelCategory = "main", string levelName = "", string levelID = "",
 | 
				
			||||||
 | 
					            int? duration = null, int? step = null, int? score = null )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (InitConfig.AutoRecordFinishedLevels)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if(result == EventLevelEndSuccess) Model.SuccessLevelCount++; // 自动记录关卡完成次数
 | 
				
			||||||
 | 
					                Model.TotalPlayedCount++; // 自动记录关卡总次数
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Analytics.BLevel = Model.SuccessLevelCount; // 记录 BLevel
 | 
				
			||||||
 | 
					                Analytics.BPlay = Model.TotalPlayedCount; // 记录 BPlay
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Analytics.LogLevelEnd(level, result, 
 | 
				
			||||||
 | 
					                levelName, levelCategory, levelCategory,
 | 
				
			||||||
 | 
					                duration, step, score);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 游戏失败打点
 | 
				
			||||||
 | 
					        /// 需要为游戏记录详细的失败原因
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static void LogLevelFail(int level,
 | 
				
			||||||
 | 
					            string levelCategory = "main",string levelName = "", string levelID = "",
 | 
				
			||||||
 | 
					            int? duration = null, int? step = null, int? score = null )
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogLevelEnd(level, EventLevelEndFail, levelCategory, levelName, levelID, duration, step, score);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 因退出关卡导致游戏失败
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static void LogLevelFailExit(int level,
 | 
				
			||||||
 | 
					            string levelCategory = "main", string levelName = "", string levelID = "",
 | 
				
			||||||
 | 
					            int? duration = null, int? step = null, int? score = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogLevelEnd(level, EventLevelEndExit, levelCategory, levelName, levelID, duration, step, score);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 因关卡超时导致游戏失败
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static void LogLevelFailTimeout(int level,
 | 
				
			||||||
 | 
					            string levelCategory = "main", string levelName = "", string levelID = "",
 | 
				
			||||||
 | 
					            int? duration = null, int? step = null, int? score = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogLevelEnd(level, EventLevelEndTimeout, levelCategory, levelName, levelID, duration, step, score);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 玩家(角色)升级事件
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="playerLevel"></param>
 | 
				
			||||||
 | 
					        /// <param name="playerName"></param>
 | 
				
			||||||
 | 
					        public static void LogLevelUp(int playerLevel, string playerName)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Analytics.LevelUp(playerLevel, playerName);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 玩家解锁成就
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="achievementName"></param>
 | 
				
			||||||
 | 
					        public static void LogAchievement(string achievementName)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Analytics.UnlockAchievement(achievementName);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region 用户属性
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 设置用户属性
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="key"></param>
 | 
				
			||||||
 | 
					        /// <param name="value"></param>
 | 
				
			||||||
 | 
					        public static void SetUserProperty(string key, string value)
 | 
				
			||||||
 | 
					            => Analytics.SetUserProperty(key, value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static void SetUID(string uid)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetUserProperty(Analytics.PropertyUserID, uid);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static void SetUserBLevel(int blevel)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetUserProperty(Analytics.PropertyLevel, $"{blevel}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static void SetUserBPlay(int bplay)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetUserProperty(Analytics.PropertyPlay, $"{bplay}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static void SetUserTotalCoins(int totalCoins)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetUserProperty(Analytics.PropertyCoin, $"{totalCoins}");        
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static void SetUserCoins(int coins)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetUserProperty(Analytics.PropertyNonIAPCoin, $"{coins}");        
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static void SetUserPaidCoins(int paidCoins)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetUserProperty(Analytics.PropertyIAPCoin, $"{paidCoins}");        
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static void SetUserExp(int exp)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetUserProperty(Analytics.PropertyExp, $"{exp}");        
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static void SetUserHp(int hp)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetUserProperty(Analytics.PropertyHp, $"{hp}");        
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static void SetUserGrade(int grade)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetUserProperty(Analytics.PropertyGrade, $"{grade}");        
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,157 @@
 | 
				
			||||||
 | 
					namespace Guru
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    using System;
 | 
				
			||||||
 | 
					    using UnityEngine;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public partial class GuruSDK
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 回调参数类
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public class Callbacks
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            /// <summary>
 | 
				
			||||||
 | 
					            /// APP 事件
 | 
				
			||||||
 | 
					            /// </summary>
 | 
				
			||||||
 | 
					            public static class App
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                internal static Action<bool> _onAppPaused;
 | 
				
			||||||
 | 
					                public static event Action<bool> OnAppPaused
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onAppPaused += value;
 | 
				
			||||||
 | 
					                    remove => _onAppPaused -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                internal static Action _onAppQuit;
 | 
				
			||||||
 | 
					                public static event Action OnAppQuit
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onAppQuit += value;
 | 
				
			||||||
 | 
					                    remove => _onAppQuit -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /// <summary>
 | 
				
			||||||
 | 
					            /// GDPR Consent
 | 
				
			||||||
 | 
					            /// </summary>
 | 
				
			||||||
 | 
					            public static class ConsentFlow
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                /// <summary>
 | 
				
			||||||
 | 
					                /// 当Consent启动结束后返回状态码
 | 
				
			||||||
 | 
					                /// </summary>
 | 
				
			||||||
 | 
					                public static event Action<int> OnConsentResult
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onConsentResult += value;
 | 
				
			||||||
 | 
					                    remove => _onConsentResult -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                internal static Action<int> _onConsentResult;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                /// <summary>
 | 
				
			||||||
 | 
					                /// ATT 状态返回
 | 
				
			||||||
 | 
					                /// </summary>
 | 
				
			||||||
 | 
					                public static event Action<int> OnAttResult
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onAttResult += value;
 | 
				
			||||||
 | 
					                    remove => _onAttResult -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                internal static Action<int> _onAttResult;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /// <summary>
 | 
				
			||||||
 | 
					            /// 广告回调
 | 
				
			||||||
 | 
					            /// </summary>
 | 
				
			||||||
 | 
					            public static class Ads
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                internal static Action _onAdsInitComplete;
 | 
				
			||||||
 | 
					                public static event Action OnAdsInitComplete
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onAdsInitComplete += value;
 | 
				
			||||||
 | 
					                    remove => _onAdsInitComplete -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                internal static Action _onInterstitialADLoaded;
 | 
				
			||||||
 | 
					                public static event Action OnInterstitialADLoaded
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onInterstitialADLoaded += value;
 | 
				
			||||||
 | 
					                    remove => _onInterstitialADLoaded -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                internal static Action _onInterstitialADFailed;
 | 
				
			||||||
 | 
					                public static event Action OnInterstitialADFailed
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onInterstitialADFailed += value;
 | 
				
			||||||
 | 
					                    remove => _onInterstitialADFailed -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                internal static Action _onRewardedADLoaded;
 | 
				
			||||||
 | 
					                public static event Action OnRewardedADLoaded
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onRewardedADLoaded += value;
 | 
				
			||||||
 | 
					                    remove => _onRewardedADLoaded -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                internal static Action _onRewardADFailed;
 | 
				
			||||||
 | 
					                public static event Action OnRewardADFailed
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onRewardADFailed += value;
 | 
				
			||||||
 | 
					                    remove => _onRewardADFailed -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /// <summary>
 | 
				
			||||||
 | 
					            /// 云控参数
 | 
				
			||||||
 | 
					            /// </summary>
 | 
				
			||||||
 | 
					            public static class Remote
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                internal static Action _onRemoteInitComplete;
 | 
				
			||||||
 | 
					                public static event Action OnRemoteInitComplete
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onRemoteInitComplete += value;
 | 
				
			||||||
 | 
					                    remove => _onRemoteInitComplete -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                internal static Action<bool> _onRemoteFetchComplete;
 | 
				
			||||||
 | 
					                public static event Action<bool> OnRemoteFetchComplete
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onRemoteFetchComplete += value;
 | 
				
			||||||
 | 
					                    remove => _onRemoteFetchComplete -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /// <summary>
 | 
				
			||||||
 | 
					            /// 支付回调
 | 
				
			||||||
 | 
					            /// </summary>
 | 
				
			||||||
 | 
					            public static class IAP
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                internal static Action<bool> _onIAPInitComplete;
 | 
				
			||||||
 | 
					                public static event Action<bool> OnIAPInitComplete
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onIAPInitComplete += value;
 | 
				
			||||||
 | 
					                    remove => _onIAPInitComplete -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                internal static Action<string> _onPurchaseStart;
 | 
				
			||||||
 | 
					                public static event Action<string> OnPurchaseStart
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onPurchaseStart += value;
 | 
				
			||||||
 | 
					                    remove => _onPurchaseStart -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                internal static Action<string, string> _onPurchaseFailed;
 | 
				
			||||||
 | 
					                public static event Action<string, string> OnPurchaseFailed
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    add => _onPurchaseFailed += value;
 | 
				
			||||||
 | 
					                    remove => _onPurchaseFailed -= value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,171 @@
 | 
				
			||||||
 | 
					namespace Guru
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    using UnityEngine;
 | 
				
			||||||
 | 
					    using System;
 | 
				
			||||||
 | 
					    using System.Linq;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public partial class GuruSDK
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static bool IsIAPReady = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //---------- 支付失败原因 ----------
 | 
				
			||||||
 | 
					        public const string BuyFail_PurchasingUnavailable = "PurchasingUnavailable";
 | 
				
			||||||
 | 
					        public const string BuyFail_Pending = "ExistingPurchasePending";
 | 
				
			||||||
 | 
					        public const string BuyFail_ProductUnavailable = "ProductUnavailable";
 | 
				
			||||||
 | 
					        public const string BuyFail_SignatureInvalid = "SignatureInvalid";
 | 
				
			||||||
 | 
					        public const string BuyFail_UserCancelled = "UserCancelled";
 | 
				
			||||||
 | 
					        public const string BuyFail_PaymentDeclined = "PaymentDeclined";
 | 
				
			||||||
 | 
					        public const string BuyFail_DuplicateTransaction = "DuplicateTransaction";
 | 
				
			||||||
 | 
					        public const string BuyFail_Unknown = "Unknown";
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 初始化IAP 功能
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static void InitIAP(byte[] googleKey, byte[] appleRootCerts)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            GuruIAP.Instance.OnInitResult += OnIAPInitResult;
 | 
				
			||||||
 | 
					            GuruIAP.Instance.OnRestored += OnRestored;
 | 
				
			||||||
 | 
					            GuruIAP.Instance.OnBuyStart += OnBuyStart;
 | 
				
			||||||
 | 
					            GuruIAP.Instance.OnBuyEnd += OnBuyEnd;
 | 
				
			||||||
 | 
					            GuruIAP.Instance.OnBuyFailed += OnBuyFailed;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            GuruIAP.Instance.InitWithKeys(googleKey, appleRootCerts, IsDebugMode);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 初始化结果
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="success"></param>
 | 
				
			||||||
 | 
					        private static void OnIAPInitResult(bool success)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogI($"IAP init result: {success}");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            IsIAPReady = success;
 | 
				
			||||||
 | 
					            Callbacks.IAP._onIAPInitComplete?.Invoke(success);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static bool CheckIAPReady()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!IsIAPReady)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                LogE("IAP is not ready, call <GuruSDK.InitIAP> first.");
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #region Purchase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static ProductSetting GetProductSettingById(string productId)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var products = GuruSettings.Instance.Products;
 | 
				
			||||||
 | 
					            if (products != null && products.Length > 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return products.FirstOrDefault(p => p.ProductId == productId);   
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static ProductSetting GetProductSetting(string productName)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var products = GuruSettings.Instance.Products;
 | 
				
			||||||
 | 
					            if (products != null && products.Length > 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return products.FirstOrDefault(p => p.ProductName == productName);   
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        private static Action<string, bool> _onPurchaseCallback;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 购买商品, 通过商品Name
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="productName"></param>
 | 
				
			||||||
 | 
					        /// <param name="purchaseCallback"></param>
 | 
				
			||||||
 | 
					        public static void Purchase(string productName, Action<string, bool> purchaseCallback = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (CheckIAPReady())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _onPurchaseCallback = purchaseCallback;
 | 
				
			||||||
 | 
					                GuruIAP.Instance.Buy(productName);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 购买商品, 通过商品ID
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="productId"></param>
 | 
				
			||||||
 | 
					        /// <param name="purchaseCallback"></param>
 | 
				
			||||||
 | 
					        public static bool PurchaseById(string productId, Action<string, bool> purchaseCallback = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var productName = GetProductSettingById(productId)?.ProductName ?? "";
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (CheckIAPReady() && !string.IsNullOrEmpty(productName))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Purchase(productName, purchaseCallback);
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 支付回调
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="productId"></param>
 | 
				
			||||||
 | 
					        /// <param name="success"></param>
 | 
				
			||||||
 | 
					        private static void OnBuyEnd(string productName, bool success)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _onPurchaseCallback?.Invoke(productName, success);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 支付开始
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="productName"></param>
 | 
				
			||||||
 | 
					        private static void OnBuyStart(string productName)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Callbacks.IAP._onPurchaseStart?.Invoke(productName);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 支付失败
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="productName"></param>
 | 
				
			||||||
 | 
					        /// <param name="reason"></param>
 | 
				
			||||||
 | 
					        private static void OnBuyFailed(string productName, string reason)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Callbacks.IAP._onPurchaseFailed?.Invoke(productName, reason);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #region Restore Purchase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static Action<bool> _onRestored;
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 恢复购买
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="restoreCallback"></param>
 | 
				
			||||||
 | 
					        public static void Restore(Action<bool> restoreCallback)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (CheckIAPReady())
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _onRestored = restoreCallback;
 | 
				
			||||||
 | 
					                GuruIAP.Instance.Restore();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        private static void OnRestored(bool success)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _onRestored?.Invoke(success);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Firebase.RemoteConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Guru
 | 
					namespace Guru
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using System.Collections.Generic;
 | 
					 | 
				
			||||||
    using Firebase.RemoteConfig;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public partial class GuruSDK
 | 
					    public partial class GuruSDK
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,45 +30,27 @@ namespace Guru
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return RemoteConfigManager.GetConfig<T>(key);
 | 
					            return RemoteConfigManager.GetConfig<T>(key);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        public static string GetRemoteString(string key, string defaultValue = "") => RemoteConfigManager.GetString(key, defaultValue);
 | 
					        public static string GetRemoteString(string key) => RemoteConfigManager.GetString(key);
 | 
				
			||||||
        public static int GetRemoteInt(string key, int defaultValue = 0) => RemoteConfigManager.GetInt(key, defaultValue);
 | 
					        public static int GetRemoteInt(string key) => RemoteConfigManager.GetInt(key);
 | 
				
			||||||
        public static long GetRemoteLong(string key, long defaultValue = 0 ) => RemoteConfigManager.GetLong(key, defaultValue);
 | 
					        public static long GetRemoteLong(string key) => RemoteConfigManager.GetLong(key);
 | 
				
			||||||
        public static double GetRemoteDouble(string key, double defaultValue = 0) => RemoteConfigManager.GetDouble(key, defaultValue);
 | 
					        public static double GetRemoteDouble(string key) => RemoteConfigManager.GetDouble(key);
 | 
				
			||||||
        public static float GetRemoteFloat(string key, float defaultValue = 0) => RemoteConfigManager.GetFloat(key, defaultValue);
 | 
					        public static float GetRemoteFloat(string key) => RemoteConfigManager.GetFloat(key);
 | 
				
			||||||
        public static bool GetRemoteBool(string key, bool defaultValue = false) => RemoteConfigManager.GetBool(key, defaultValue);
 | 
					        public static bool GetRemoteBool(string key) => RemoteConfigManager.GetBool(key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 注册监听某个 Key 的变化
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="key"></param>
 | 
					 | 
				
			||||||
        /// <param name="onValueChanged"></param>
 | 
					 | 
				
			||||||
        public static void RegisterOnValueChanged(string key, Action<string,string> onValueChanged)
 | 
					        public static void RegisterOnValueChanged(string key, Action<string,string> onValueChanged)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            RemoteConfigManager.RegisterOnValueChanged(key, onValueChanged);
 | 
					            RemoteConfigManager.RegisterOnValueChanged(key, onValueChanged);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 注销监听某个 Key 的变化
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="key"></param>
 | 
					 | 
				
			||||||
        /// <param name="onValueChanged"></param>
 | 
					 | 
				
			||||||
        public static void UnRegisterOnValueChanged(string key, Action<string,string> onValueChanged)
 | 
					        public static void UnRegisterOnValueChanged(string key, Action<string,string> onValueChanged)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            RemoteConfigManager.UnRegisterOnValueChanged(key, onValueChanged);
 | 
					            RemoteConfigManager.UnRegisterOnValueChanged(key, onValueChanged);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 获取所有云控配置
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static Dictionary<string, ConfigValue> GetRemoteAllValues() => RemoteConfigManager.GetAllValues();
 | 
					        public static Dictionary<string, ConfigValue> GetRemoteAllValues() => RemoteConfigManager.GetAllValues();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        /// <summary>
 | 
					        
 | 
				
			||||||
        /// 
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="key"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static string GetRemoteStaticValue(string key) => RemoteConfigManager.GetStaticValue(key);
 | 
					        public static string GetRemoteStaticValue(string key) => RemoteConfigManager.GetStaticValue(key);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,355 @@
 | 
				
			||||||
 | 
					namespace Guru
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    using UnityEngine;
 | 
				
			||||||
 | 
					    using System;
 | 
				
			||||||
 | 
					    using System.Collections;
 | 
				
			||||||
 | 
					    using System.Collections.Generic;
 | 
				
			||||||
 | 
					    using System.IO;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public partial class GuruSDK: MonoBehaviour
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public const string Version = "1.0.0";
 | 
				
			||||||
 | 
					        public const string Tag = "[Guru]";
 | 
				
			||||||
 | 
					        public const string ServicesConfigKey = "guru_services";
 | 
				
			||||||
 | 
					        public const string ServicesConfigExtension = "cfg";
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        private static GuruSDK _instance;
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 单利引用
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static GuruSDK Instance
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if(null == _instance)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    _instance = CreateInstance();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return _instance;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private GuruSDKInitConfig _initConfig;
 | 
				
			||||||
 | 
					        private Action<bool> _onCompleteCallback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static GuruSDKModel _model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        internal static GuruSDKInitConfig InitConfig => Instance._initConfig;
 | 
				
			||||||
 | 
					        internal static GuruSDKModel Model => GuruSDKModel.Instance;
 | 
				
			||||||
 | 
					        private static GuruServicesConfig _appServicesConfig;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Debug Mode
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static bool IsDebugMode
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					#if UNITY_EDITOR || DEBUG
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #region 初始化
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static GuruSDK CreateInstance()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var go = new GameObject(nameof(GuruSDK));
 | 
				
			||||||
 | 
					            DontDestroyOnLoad(go);
 | 
				
			||||||
 | 
					            _instance = go.AddComponent<GuruSDK>();
 | 
				
			||||||
 | 
					            return _instance;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static GuruSDKInitConfig BuildConfig(
 | 
				
			||||||
 | 
					            bool useCustomConsent = false, 
 | 
				
			||||||
 | 
					            bool autoLoadAds = true, 
 | 
				
			||||||
 | 
					            bool iapEnabled = true, 
 | 
				
			||||||
 | 
					            bool autoRecordFinishedLevels = true, 
 | 
				
			||||||
 | 
					            bool showDebugLog = false,
 | 
				
			||||||
 | 
					            Dictionary<string, object> defaultRemoteData = null,
 | 
				
			||||||
 | 
					            byte[] googleKeys = null,
 | 
				
			||||||
 | 
					            byte[] appleRootCerts = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var config = GuruSDKInitConfig.Build(useCustomConsent, autoLoadAds, iapEnabled, 
 | 
				
			||||||
 | 
					                autoRecordFinishedLevels, showDebugLog, defaultRemoteData, googleKeys, appleRootCerts);
 | 
				
			||||||
 | 
					            return config;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static void Init(Action<bool> onComplete)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Init(GuruSDKInitConfig.Build(), onComplete);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static void Init(GuruSDKInitConfig config, Action<bool> onComplete)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogI($"---- Guru SDK init ----");
 | 
				
			||||||
 | 
					            LogI(config.ToString());
 | 
				
			||||||
 | 
					            Instance.StartWithConfig(config, onComplete);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 启动SDK
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="config"></param>
 | 
				
			||||||
 | 
					        /// <param name="onComplete"></param>
 | 
				
			||||||
 | 
					        private void StartWithConfig(GuruSDKInitConfig config, Action<bool> onComplete)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Model.PropBLevel.OnValueChanged += OnBLevelChanged;
 | 
				
			||||||
 | 
					            Model.PropBPlay.OnValueChanged += OnBPlayChanged;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            _initConfig = config;
 | 
				
			||||||
 | 
					            _onCompleteCallback = onComplete;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            //---------- Start Firebase ------------
 | 
				
			||||||
 | 
					            FirebaseUtil.InitFirebase(OnFirebaseReady);
 | 
				
			||||||
 | 
					            // FirebaseUtil.OnFetchRemoteSuccess+= OnFetchRemoteCallback;
 | 
				
			||||||
 | 
					            //---------- Start Facebook ------------
 | 
				
			||||||
 | 
					            FBService.Instance.StartService();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 开始各种组件初始化
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        private void OnFirebaseReady()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (!InitConfig.UseCustomConsent)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                // LogI($"--- #3 Start Consent Flow ---");
 | 
				
			||||||
 | 
					                StartConsentFlow();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if(!string.IsNullOrEmpty(IPMConfig.IPM_UID)) SetUID(IPMConfig.IPM_UID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 开始Remote Manager初始化 
 | 
				
			||||||
 | 
					            RemoteConfigManager.Init(BuildDefaultRemoteData(_initConfig.DefaultRemoteData));
 | 
				
			||||||
 | 
					            RemoteConfigManager.OnFetchCompleted += OnFetchRemoteCallback;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // 根据上次的云控配置来初始化参数
 | 
				
			||||||
 | 
					            SetupServicesConfig();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            _onCompleteCallback?.Invoke(true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 注入云控参数基础数据
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="dict"></param>
 | 
				
			||||||
 | 
					        /// <returns></returns>
 | 
				
			||||||
 | 
					        private Dictionary<string, object> BuildDefaultRemoteData(Dictionary<string, object> dict)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            string json = Model.LoadAppServicesConfigJson(); // 注入默认的Services 配置值
 | 
				
			||||||
 | 
					            if (!string.IsNullOrEmpty(json))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (dict == null) dict = new Dictionary<string, object>(3);
 | 
				
			||||||
 | 
					                dict[ServicesConfigKey] = json;
 | 
				
			||||||
 | 
					                return dict;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 拉取云控参数完成
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="success"></param>
 | 
				
			||||||
 | 
					        private void OnFetchRemoteCallback(bool success)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogI($"--- Remote fetch complete: {success} ---");
 | 
				
			||||||
 | 
					            ABTestManager.Init(); // 启动AB测试解析器
 | 
				
			||||||
 | 
					            Callbacks.Remote._onRemoteFetchComplete?.Invoke(success);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region App Remote Update
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void SetupServicesConfig()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            bool useKeywords = true;
 | 
				
			||||||
 | 
					            bool useIAP = true;
 | 
				
			||||||
 | 
					            var guruSettings = GuruSettings.Instance;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            var services = GetRemoteServicesConfig();
 | 
				
			||||||
 | 
					            if (services != null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _appServicesConfig = services;
 | 
				
			||||||
 | 
					                useKeywords = _appServicesConfig.IsKeywordsEnabled();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (null != guruSettings)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    if(_appServicesConfig.adjust_settings != null)
 | 
				
			||||||
 | 
					                    {    
 | 
				
			||||||
 | 
					                        // 更新 Adjust Tokens
 | 
				
			||||||
 | 
					                        guruSettings.UpdateAdjustTokens(_appServicesConfig.adjust_settings.AndroidToken
 | 
				
			||||||
 | 
					                            ,_appServicesConfig.adjust_settings.iOSToken);
 | 
				
			||||||
 | 
					                        // 更新 Adjust Events
 | 
				
			||||||
 | 
					                        guruSettings.UpdateAdjustEvents(_appServicesConfig.adjust_settings.events);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                useIAP = _appServicesConfig.IsIAPEnabled();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            AdjustService.StartService();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if(useIAP) {
 | 
				
			||||||
 | 
					                InitIAP(_initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if(useKeywords) {
 | 
				
			||||||
 | 
					                KeywordsManager.Install(Model.IsIAPUser, Model.SuccessLevelCount); // 启动Keyword管理器
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private GuruServicesConfig GetRemoteServicesConfig()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var json = GetRemoteString(ServicesConfigKey);
 | 
				
			||||||
 | 
					            if (!string.IsNullOrEmpty(json))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return JsonParser.ToObject<GuruServicesConfig>(json);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region 数据
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnBLevelChanged(int blevel)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetUserBLevel(blevel);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnBPlayChanged(int bplay)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetUserBPlay(bplay);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #region Misc
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 打开页面
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="url"></param>
 | 
				
			||||||
 | 
					        public static void OpenURL(string url)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            GuruWebview.OpenPage(url);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region Logging
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static void LogI(object message)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Debug.Log($"{Tag} {message}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static void LogW(object message)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Debug.LogWarning($"{Tag} {message}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static void LogE(object message)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Debug.LogError($"{Tag} {message}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static void LogException(string message)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            LogException( new Exception($"{Tag} {message}"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public static void LogException(Exception e)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Debug.LogException(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region 生命周期
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 暂停时处理
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="paused"></param>
 | 
				
			||||||
 | 
					        private void OnAppPauseHandler(bool paused)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if(paused) Model.Save(true); // 强制保存数据
 | 
				
			||||||
 | 
					            Callbacks.App._onAppPaused?.Invoke(paused);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        private void OnApplicationPause(bool paused)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            OnAppPauseHandler(paused);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnApplicationFocus(bool hasFocus)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            OnAppPauseHandler(!hasFocus);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnApplicationQuit()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Model.Save(true);
 | 
				
			||||||
 | 
					            Callbacks.App._onAppQuit?.Invoke();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region 延迟处理
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 启动协程
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="enumerator"></param>
 | 
				
			||||||
 | 
					        /// <returns></returns>
 | 
				
			||||||
 | 
					        public static Coroutine DoCoroutine(IEnumerator enumerator)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return Instance != null ? Instance.StartCoroutine(enumerator) : null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static void KillCoroutine(Coroutine coroutine)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if(coroutine != null)
 | 
				
			||||||
 | 
					                Instance.StopCoroutine(coroutine);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 延时执行
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="seconds"></param>
 | 
				
			||||||
 | 
					        /// <param name="callback"></param>
 | 
				
			||||||
 | 
					        public static void Delay(float seconds, Action callback)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            DoCoroutine(Instance.OnDelayCall(seconds, callback));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private IEnumerator OnDelayCall(float delay, Action callback)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (delay > 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                yield return new WaitForSeconds(delay);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                yield return null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            callback?.Invoke();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,97 @@
 | 
				
			||||||
 | 
					namespace Guru
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    using System.Collections.Generic;
 | 
				
			||||||
 | 
					    using System.Text;
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    /// 启动参数配置
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
 | 
					    public partial class GuruSDKInitConfig
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 使用自定义的ConsentFlow启动流程
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public bool UseCustomConsent = false;
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// SDK初始化完成后自动加载广告
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public bool AutoLoadWhenAdsReady = true;
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 使用IAP支付插件功能
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public bool IAPEnabled = true;
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 自动记录完成的关卡
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public bool AutoRecordFinishedLevels = true;
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 显示Debug日志
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public bool ShowDebugLog = false;
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 云控参数的默认配置
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <returns></returns>
 | 
				
			||||||
 | 
					        public Dictionary<string, object> DefaultRemoteData = new Dictionary<string, object>();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 支付初始化Keys
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public byte[] GoogleKeys;
 | 
				
			||||||
 | 
					        public byte[] AppleRootCerts;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #region Initialization
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 构建启动配置
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <returns></returns>
 | 
				
			||||||
 | 
					        public static GuruSDKInitConfig Build(
 | 
				
			||||||
 | 
					            bool useCustomConsent = false, 
 | 
				
			||||||
 | 
					            bool autoLoadAds = true, 
 | 
				
			||||||
 | 
					            bool iapEnabled = true, 
 | 
				
			||||||
 | 
					            bool autoRecordFinishedLevels = true, 
 | 
				
			||||||
 | 
					            bool showDebugLog = false,
 | 
				
			||||||
 | 
					            Dictionary<string, object> defaultRemoteData = null,
 | 
				
			||||||
 | 
					            byte[] googleKeys = null,
 | 
				
			||||||
 | 
					            byte[] appleRootCerts = null)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // 创建启动用参数
 | 
				
			||||||
 | 
					            GuruSDKInitConfig config = new GuruSDKInitConfig()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                UseCustomConsent = useCustomConsent,
 | 
				
			||||||
 | 
					                AutoLoadWhenAdsReady = autoLoadAds,
 | 
				
			||||||
 | 
					                IAPEnabled = iapEnabled,
 | 
				
			||||||
 | 
					                AutoRecordFinishedLevels = autoRecordFinishedLevels,
 | 
				
			||||||
 | 
					                ShowDebugLog = showDebugLog,
 | 
				
			||||||
 | 
					                GoogleKeys = googleKeys,
 | 
				
			||||||
 | 
					                AppleRootCerts = appleRootCerts,
 | 
				
			||||||
 | 
					                DefaultRemoteData = defaultRemoteData ?? new Dictionary<string, object>(),
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					#if UNITY_EDITOR
 | 
				
			||||||
 | 
					            config.ShowDebugLog = true;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					            return config;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region Print
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public override string ToString()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            StringBuilder sb = new StringBuilder();
 | 
				
			||||||
 | 
					            sb.AppendLine($"------- Custom init Config -------");
 | 
				
			||||||
 | 
					            sb.AppendLine($"\tUseCustomConsent: {UseCustomConsent}");
 | 
				
			||||||
 | 
					            sb.AppendLine($"\tAutoLoadWhenAdsReady: {AutoLoadWhenAdsReady}");
 | 
				
			||||||
 | 
					            sb.AppendLine($"\tIAPEnabled: {IAPEnabled}");
 | 
				
			||||||
 | 
					            sb.AppendLine($"\tShowDebugLog: {ShowDebugLog}");
 | 
				
			||||||
 | 
					            sb.AppendLine($"------- Custom init Config -------");
 | 
				
			||||||
 | 
					            return sb.ToString();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3,21 +3,20 @@ namespace Guru
 | 
				
			||||||
    internal class GuruIAP: IAPServiceBase<GuruIAP>
 | 
					    internal class GuruIAP: IAPServiceBase<GuruIAP>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// 获取BLevel
 | 
					        /// 获取BLevel
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        /// <returns></returns>
 | 
					        /// <returns></returns>
 | 
				
			||||||
        protected override int GetBLevel() => Model.BLevel; // BLevel
 | 
					        protected override int GetBLevel() => GuruSDKModel.Instance.SuccessLevelCount; // BLevel
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private GuruSDKModel Model => GuruSDKModel.Instance;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected override void OnPurchaseOver(bool success, string productName)
 | 
					        protected override void OnPurchaseOver(bool success, string productName)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
        }
 | 
					            if (success)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
        public void ClearData()
 | 
					                GuruSDKModel.Instance.PurchasedCount++; // 记录成功购买次数
 | 
				
			||||||
        {
 | 
					            }
 | 
				
			||||||
            _model.ClearData();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -12,17 +12,27 @@ namespace Guru
 | 
				
			||||||
            get => _value;
 | 
					            get => _value;
 | 
				
			||||||
            set
 | 
					            set
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (_value.Equals(value)) return;
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                _value = value;
 | 
					                _value = value;
 | 
				
			||||||
                OnValueChanged?.Invoke(value);
 | 
					                OnValueChanged?.Invoke(value);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        public event Action<T> OnValueChanged;
 | 
					        public event Action<T> OnValueChanged;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        public BindableProperty(T initValue)
 | 
					        
 | 
				
			||||||
 | 
					        public BindableProperty() 
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public BindableProperty(Action<T> onChanged)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            OnValueChanged = onChanged;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public BindableProperty(T initValue, Action<T> onChanged)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _value = initValue;
 | 
					            _value = initValue;
 | 
				
			||||||
 | 
					            OnValueChanged = onChanged;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,46 +1,16 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Guru
 | 
					namespace Guru
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using System;
 | 
					    using System;
 | 
				
			||||||
    using UnityEngine;
 | 
					    using UnityEngine;
 | 
				
			||||||
    using System.Collections.Generic;
 | 
					    using System.IO;
 | 
				
			||||||
    using System.Linq;
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    [Serializable]
 | 
					 | 
				
			||||||
    class PurchasedProduct
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public string productName;
 | 
					 | 
				
			||||||
        public string productId;
 | 
					 | 
				
			||||||
        public string receipt;
 | 
					 | 
				
			||||||
        public bool appleProductIsRestored;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [Serializable]
 | 
					 | 
				
			||||||
    class GuruSDKSerializedModel
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        //-------------- data ---------------
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public string uid = "";
 | 
					 | 
				
			||||||
        public int b_level = 0;
 | 
					 | 
				
			||||||
        public int b_play = 0;
 | 
					 | 
				
			||||||
        public bool no_ads = false;
 | 
					 | 
				
			||||||
        public List<PurchasedProduct> purchased = new List<PurchasedProduct>(10);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        //-------------- data ---------------
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    [Serializable]
 | 
					    [Serializable]
 | 
				
			||||||
    internal class GuruSDKModel
 | 
					    internal class GuruSDKModel
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private const float SaveInterval = 3;
 | 
					        private const float SaveInterval = 3;
 | 
				
			||||||
        private const string SaveKey = "com.guru.sdk.model.save";
 | 
					        private const string SaveKey = "com.guru.sdk.model.save";
 | 
				
			||||||
        private DateTime _lastSavedTime = new DateTime(1970,1,1);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private bool _noAds = false;
 | 
					 | 
				
			||||||
        private int _bLevel;
 | 
					 | 
				
			||||||
        private int _bPlay;
 | 
					 | 
				
			||||||
        private string _uid;
 | 
					 | 
				
			||||||
        private List<PurchasedProduct> _purchased;
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        private static GuruSDKModel _instance;
 | 
					        private static GuruSDKModel _instance;
 | 
				
			||||||
| 
						 | 
					@ -48,77 +18,67 @@ namespace Guru
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get
 | 
					            get
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (null == _instance) _instance = new GuruSDKModel();
 | 
					                if (null == _instance) _instance = Load();
 | 
				
			||||||
                return _instance;
 | 
					                return _instance;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public GuruSDKModel()
 | 
					        //-------------- data ---------------
 | 
				
			||||||
        {
 | 
					        public int b_level = 0;
 | 
				
			||||||
            // 读取内存值
 | 
					        public int b_play = 0;
 | 
				
			||||||
            GuruSDKSerializedModel model = LoadModel();
 | 
					        public int buy_count = 0;
 | 
				
			||||||
            _uid = model.uid;
 | 
					        //-------------- data ---------------
 | 
				
			||||||
            _noAds = model.no_ads;
 | 
					 | 
				
			||||||
            _bLevel = model.b_level;
 | 
					 | 
				
			||||||
            _bPlay = model.b_play;
 | 
					 | 
				
			||||||
            _purchased = model.purchased;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public int BLevel
 | 
					        private float _lastSavedTime = 0;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public int SuccessLevelCount
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get => _bLevel;
 | 
					            get
 | 
				
			||||||
            set
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (value < _bLevel)
 | 
					                if(_successLevel == null) InitProperties();
 | 
				
			||||||
                {
 | 
					                return _successLevel.Value;
 | 
				
			||||||
                    // b_level 必须比上一次的值大
 | 
					 | 
				
			||||||
                    Debug.LogWarning($"[SDK] :: Set b_level [{value}] should not be less than original value [{_bLevel}]");
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                _bLevel = value;
 | 
					 | 
				
			||||||
                Save();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            set => _successLevel.Value = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public int BPlay
 | 
					        public int TotalPlayedCount
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get => _bPlay;
 | 
					            get
 | 
				
			||||||
            set
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _bPlay = value;
 | 
					                if(_totalPlayed == null) InitProperties();
 | 
				
			||||||
                Save();
 | 
					                return _totalPlayed.Value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            set => _totalPlayed.Value = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string UserId
 | 
					        public int PurchasedCount
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            get => _uid;
 | 
					            get
 | 
				
			||||||
            set
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _uid = value;
 | 
					                if(_purchasedCount == null) InitProperties();
 | 
				
			||||||
                Save();
 | 
					                return _purchasedCount.Value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            set => _purchasedCount.Value = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public bool IsIAPUser => PurchasedCount > 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool IsIapUser => _purchased.Count > 0;
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        public bool IsNoAds
 | 
					        private BindableProperty<int> _successLevel;
 | 
				
			||||||
        {
 | 
					        private BindableProperty<int> _totalPlayed;
 | 
				
			||||||
            get => _noAds;
 | 
					        private BindableProperty<int> _purchasedCount;
 | 
				
			||||||
            set
 | 
					        
 | 
				
			||||||
            {
 | 
					        public BindableProperty<int> PropBLevel => _successLevel;
 | 
				
			||||||
                _noAds = value;
 | 
					        public BindableProperty<int> PropBPlay => _totalPlayed;
 | 
				
			||||||
                Save();
 | 
					        public BindableProperty<int> PropBuyCount => _purchasedCount;
 | 
				
			||||||
            }
 | 
					
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #region 初始化
 | 
					        #region 初始化
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        private GuruSDKSerializedModel LoadModel()
 | 
					        public static GuruSDKModel Load()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            GuruSDKSerializedModel model = null;
 | 
					            GuruSDKModel model = null;
 | 
				
			||||||
            if (PlayerPrefs.HasKey(SaveKey))
 | 
					            if (PlayerPrefs.HasKey(SaveKey))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var json = PlayerPrefs.GetString(SaveKey, "");
 | 
					                var json = PlayerPrefs.GetString(SaveKey, "");
 | 
				
			||||||
| 
						 | 
					@ -126,7 +86,7 @@ namespace Guru
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    try
 | 
					                    try
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        model =  JsonUtility.FromJson<GuruSDKSerializedModel>(json);
 | 
					                        model =  JsonUtility.FromJson<GuruSDKModel>(json);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    catch (Exception e)
 | 
					                    catch (Exception e)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
| 
						 | 
					@ -134,106 +94,95 @@ namespace Guru
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if(model == null) model = new GuruSDKSerializedModel();
 | 
					            if(model == null) model = new GuruSDKModel();
 | 
				
			||||||
 | 
					            model.InitProperties();
 | 
				
			||||||
            return model;
 | 
					            return model;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// 保存至 PlayerPrefs 数据
 | 
					        /// 保存至数据
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        private void SetToMemory()
 | 
					        private void SaveToPlayerPrefs()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var model = new GuruSDKSerializedModel()
 | 
					            var json = JsonUtility.ToJson(this);
 | 
				
			||||||
            {
 | 
					            PlayerPrefs.SetString(SaveKey, json);
 | 
				
			||||||
                uid = _uid,
 | 
					        }
 | 
				
			||||||
                b_level = _bLevel,
 | 
					 | 
				
			||||||
                b_play = _bPlay,
 | 
					 | 
				
			||||||
                no_ads = _noAds,
 | 
					 | 
				
			||||||
                purchased = _purchased,
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var json = JsonUtility.ToJson(model);
 | 
					        public void InitProperties()
 | 
				
			||||||
            if (!string.IsNullOrEmpty(json))
 | 
					        {
 | 
				
			||||||
            {
 | 
					            _successLevel = new BindableProperty<int>(b_level, OnLevelChanged);
 | 
				
			||||||
                PlayerPrefs.SetString(SaveKey, json);
 | 
					            _totalPlayed = new BindableProperty<int>(b_play, OnPlayedChanged);
 | 
				
			||||||
            }
 | 
					            _purchasedCount = new BindableProperty<int>(buy_count, OnPurchasedNumChanged);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// 保存数据
 | 
					        /// 保存数据
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        /// <param name="forceSave"></param>
 | 
					        /// <param name="force"></param>
 | 
				
			||||||
        public void Save(bool forceSave = false)
 | 
					        public void Save(bool force = false)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            SetToMemory(); // 每次保存都要设置到 PlayerPrefs 内
 | 
					            bool save = force || (Time.realtimeSinceStartup - _lastSavedTime>= SaveInterval);
 | 
				
			||||||
 | 
					            if (save)
 | 
				
			||||||
            bool shouldWriteToDisk = forceSave || (DateTime.Now - _lastSavedTime)>= TimeSpan.FromSeconds(SaveInterval);
 | 
					 | 
				
			||||||
            if (!shouldWriteToDisk) return;
 | 
					 | 
				
			||||||
            _lastSavedTime = DateTime.Now; // 更新最后保存时间
 | 
					 | 
				
			||||||
            PlayerPrefs.Save(); // 写入到磁盘
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region 订单记录
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public bool HasPurchasedProduct(string receipt)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if(_purchased.Count == 0) return false;
 | 
					 | 
				
			||||||
            return _purchased.Exists(p => p.receipt == receipt);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 添加已支付订单
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="receipt"></param>
 | 
					 | 
				
			||||||
        /// <param name="productName"></param>
 | 
					 | 
				
			||||||
        /// <param name="productId"></param>
 | 
					 | 
				
			||||||
        /// <param name="appleProductIsRestored"></param>
 | 
					 | 
				
			||||||
        public void AddReceipt(string receipt, string productName, string productId, bool appleProductIsRestored = false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!HasPurchasedProduct(receipt))
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                _purchased.Add(new PurchasedProduct()
 | 
					                _lastSavedTime = Time.realtimeSinceStartup;
 | 
				
			||||||
                {
 | 
					                SaveToPlayerPrefs();
 | 
				
			||||||
                    receipt = receipt,
 | 
					 | 
				
			||||||
                    productName = productName,
 | 
					 | 
				
			||||||
                    productId = productId,
 | 
					 | 
				
			||||||
                    appleProductIsRestored = appleProductIsRestored
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
               Save();
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string[] GetReceipts(string productName)
 | 
					
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region 数据绑定变化
 | 
				
			||||||
 | 
					        private void OnLevelChanged(int value)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var receipts = new List<string>();
 | 
					            b_level = value;
 | 
				
			||||||
            receipts.AddRange(from purchasedProduct in _purchased where purchasedProduct.productName == productName select purchasedProduct.receipt);
 | 
					            Save();
 | 
				
			||||||
            return receipts.ToArray();
 | 
					        }
 | 
				
			||||||
 | 
					        private void OnPlayedChanged(int value)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            b_play = value;
 | 
				
			||||||
 | 
					            Save();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnPurchasedNumChanged(int value)
 | 
				
			||||||
        public string[] GetReceiptsById(string productId)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var receipts = new List<string>();
 | 
					            buy_count = value;
 | 
				
			||||||
            receipts.AddRange(from purchasedProduct in _purchased where purchasedProduct.productId == productId select purchasedProduct.receipt);
 | 
					            Save();
 | 
				
			||||||
            return receipts.ToArray();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region 启动配置
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// 从 Streaming 加载 AppServices 配置
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <returns></returns>
 | 
				
			||||||
 | 
					        public string LoadAppServicesConfigJson()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                string path = Path.Combine(Application.streamingAssetsPath, $"{GuruSDK.ServicesConfigKey}.{GuruSDK.ServicesConfigExtension}");
 | 
				
			||||||
 | 
					                return File.ReadAllText(path);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception e)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Log.Exception(e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return "";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					        #endregion
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #region 清除数据
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void ClearData()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            PlayerPrefs.DeleteKey(SaveKey);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #endregion
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
| 
						 | 
					@ -243,4 +192,5 @@ namespace Guru
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,556 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using Guru.Notification;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public partial class GuruSDK
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private const float CONSENT_FLOW_TIMEOUT = 10; // Consent Flow 超时时间(秒)   
 | 
					 | 
				
			||||||
        private static AdsInitSpec _adInitSpec;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 启动广告服务
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static void StartAds(AdsInitSpec spec = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _adInitSpec = spec;
 | 
					 | 
				
			||||||
            if (InitConfig.UseCustomConsent)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.Log($"{Tag} --- Call <color=orange>StartAdsWithCustomConsent</color> when you use custom consent, and pass the result (boolean) to the method.");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // 默认的启动顺序是先启动Consent后, 根据用户回调的结果来启动广告
 | 
					 | 
				
			||||||
            Instance.StartConsentFlow();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 是否已经购买了去广告
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="buyNoAds"></param>
 | 
					 | 
				
			||||||
        public static void StartAds(bool buyNoAds = false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            StartAds(AdsInitSpec.BuildWithNoAds());  // 按照默认的去广告来生成广告启动配置
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 使用自定义的Consent, 获取用户授权后, 调用此方法
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="userAllow"></param>
 | 
					 | 
				
			||||||
        /// <param name="consentName">Consent 引导的类型, 如果使用了 MAX 的 consent 请填写 max </param>
 | 
					 | 
				
			||||||
        /// <param name="spec">广告启动配置</param>
 | 
					 | 
				
			||||||
        public static void StartAdsWithCustomConsent(bool userAllow = true, 
 | 
					 | 
				
			||||||
            string consentName = "custom", AdsInitSpec spec = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
#if UNITY_IOS
 | 
					 | 
				
			||||||
            _attType = consentName;
 | 
					 | 
				
			||||||
            InitAttStatus();
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
            if (userAllow)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
#if UNITY_IOS
 | 
					 | 
				
			||||||
                Instance.CheckAttStatus();
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
                StartAdService(spec);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.Log($"{Tag} --- User refuse to provide ads Id, Ads Service will be cancelled");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 使用自定义的Consent, 获取用户授权后, 调用此方法
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="userAllow">自定义 Consent 的用户授权结果</param>
 | 
					 | 
				
			||||||
        /// <param name="consentName">Consent引导名称</param>
 | 
					 | 
				
			||||||
        /// <param name="buyNoAds">是否已经购买了去广告</param>
 | 
					 | 
				
			||||||
        public static void StartAdsWithCustomConsent(bool userAllow = true, string consentName = "custom",
 | 
					 | 
				
			||||||
            bool buyNoAds = false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            StartAdsWithCustomConsent(userAllow, consentName, AdsInitSpec.BuildWithNoAds());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Guru Consent
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private bool _hasConsentCalled = false;
 | 
					 | 
				
			||||||
        private bool _adServiceHasStarted = false;
 | 
					 | 
				
			||||||
        private string _notiStatue = "";
 | 
					 | 
				
			||||||
        private bool _hasNotiGranted = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 启动Consent流程
 | 
					 | 
				
			||||||
        /// 因为之后规划广告流程会放在 Consent 初始化之后, 因此请求广告的时候会需要先请求 Consent
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private void StartConsentFlow()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            LogI($"#4.5 ---  StartConsentFlow ---");
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            float time = 1;
 | 
					 | 
				
			||||||
            if (!_adServiceHasStarted && _appServicesConfig != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                time = _appServicesConfig.IsAdsCompliance() ? CONSENT_FLOW_TIMEOUT : 1f; // 启动合规判定后, 延迟最多 10 秒后启动广告
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Delay(time, AdServiceHandler); // 广告延迟启动
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (_hasConsentCalled) return;
 | 
					 | 
				
			||||||
            _hasConsentCalled = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            bool enableCountryCheck = false;
 | 
					 | 
				
			||||||
            string dmaMapRule = "";
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (_appServicesConfig != null && _appServicesConfig.parameters != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                enableCountryCheck = _appServicesConfig.DMACountryCheck();
 | 
					 | 
				
			||||||
                dmaMapRule = _appServicesConfig.DMAMapRule();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if UNITY_IOS
 | 
					 | 
				
			||||||
            InitAttStatus(); // Consent 启动前记录 ATT 初始值
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
            UnityEngine.Debug.Log($"{Tag}  --- Call:StartConsentFlow ---");
 | 
					 | 
				
			||||||
            GuruConsent.StartConsent(OnGuruConsentOver, dmaMapRule:dmaMapRule, enableCountryCheck:enableCountryCheck);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Guru Consent flow is Over
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="code"></param>
 | 
					 | 
				
			||||||
        private void OnGuruConsentOver(int code)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // 无论状态如何, 都在回调内启动广告初始化
 | 
					 | 
				
			||||||
            AdServiceHandler();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 调用回调
 | 
					 | 
				
			||||||
            Callbacks.ConsentFlow.InvokeOnConsentResult(code);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
#if UNITY_IOS
 | 
					 | 
				
			||||||
            CheckAttStatus();  // [iOS] Consent 启动后检查 ATT 初始值
 | 
					 | 
				
			||||||
#elif UNITY_ANDROID
 | 
					 | 
				
			||||||
            CheckNotiPermission(); // Consent 回调后检查 Notification 权限
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // 内部处理后继逻辑
 | 
					 | 
				
			||||||
            switch(code)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                case GuruConsent.StatusCode.OBTAINED:
 | 
					 | 
				
			||||||
                case GuruConsent.StatusCode.NOT_AVAILABLE:
 | 
					 | 
				
			||||||
                    // 已获取授权, 或者地区不可用, ATT 尚未启动
 | 
					 | 
				
			||||||
                    // TODO: 添加后继处理逻辑
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 启动广告服务
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private void AdServiceHandler()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_adServiceHasStarted) return;
 | 
					 | 
				
			||||||
            _adServiceHasStarted = true;
 | 
					 | 
				
			||||||
            StartAdService(_adInitSpec);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region IOS ATT 广告授权流程
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
#if UNITY_IOS
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static string _initialAttStatus;
 | 
					 | 
				
			||||||
        private static String _attType = "admob";
 | 
					 | 
				
			||||||
        private static bool _autoReCallAtt = false;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 显示系统的 ATT 弹窗
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public void RequestAttDialog()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            LogI($"RequestATTDialog");
 | 
					 | 
				
			||||||
            ATTManager.RequestATTDailog(ReportAttStatus);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 初始化 ATT 状态
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static void InitAttStatus()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _attType = InitConfig.UseCustomConsent ? ATTManager.GUIDE_TYPE_CUSTOM : ATTManager.GUIDE_TYPE_ADMOB; // 点位属性确定
 | 
					 | 
				
			||||||
            _initialAttStatus = ATTManager.GetStatus();
 | 
					 | 
				
			||||||
            SetATTStatus(_initialAttStatus); // 上报一个初始的状态
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// iOS 平台检查 ATT 状态
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private void CheckAttStatus(bool autoReCall = false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _autoReCallAtt = autoReCall;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Delay 1s to get the user choice
 | 
					 | 
				
			||||||
            Delay(1, () => ATTManager.CheckStatus(ReportAttStatus));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void ReportAttStatus(string status)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            LogI($"{Tag} --- Get Att status:{status}  att Type:{_attType}  recall:{_autoReCallAtt}");
 | 
					 | 
				
			||||||
            SetATTStatus(_initialAttStatus); // 上报一个初始的状态
 | 
					 | 
				
			||||||
            // SetUserProperty(Analytics.ParameterATTStatus, status); // 当前的状态
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (!string.IsNullOrEmpty(status) 
 | 
					 | 
				
			||||||
                && status != _initialAttStatus 
 | 
					 | 
				
			||||||
                && status !=  ATTManager.ATT_STATUS_NOT_DETERMINED)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // 上报点位:
 | 
					 | 
				
			||||||
                SetATTStatus(_initialAttStatus);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            switch(status)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                case ATTManager.ATT_STATUS_NOT_DETERMINED:
 | 
					 | 
				
			||||||
                    // ATT 状态未知, 请求弹窗
 | 
					 | 
				
			||||||
                    if(_autoReCallAtt) RequestAttDialog();
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                case ATTManager.ATT_STATUS_RESTRICTED:
 | 
					 | 
				
			||||||
                case ATTManager.ATT_STATUS_DENIED:
 | 
					 | 
				
			||||||
                    // ATT 状态受限, 或者被拒绝
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                case ATTManager.ATT_STATUS_AUTHORIZED:
 | 
					 | 
				
			||||||
                    // ATT 状态已授权
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            CheckNotiPermission(); // Consent 回调后检查 Notification 权限
 | 
					 | 
				
			||||||
        } 
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Notification Permission Check
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 初始化 Noti Service
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private void InitNotiPermission()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // bool hasNotiGranted = false;
 | 
					 | 
				
			||||||
            _notiStatue = "no_determined";
 | 
					 | 
				
			||||||
            NotificationService.Initialize(); // 初始化 Noti 服务
 | 
					 | 
				
			||||||
            Analytics.SetNotiPerm(NotificationService.GetStatus());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 检查 Noti 状态
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private void CheckNotiPermission()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var status = NotificationService.GetStatus();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // 如果未启用自动 Noti 授权,则直接上报状态
 | 
					 | 
				
			||||||
            if (!_initConfig.AutoNotificationPermission)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.LogWarning($"[SDK] ---- AutoNotificationPermission is OFF, Project should request permission own.");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            bool isGranted = NotificationService.IsPermissionGranted();
 | 
					 | 
				
			||||||
            Debug.Log($"[SDK] ---- Check Noti Permission: {isGranted}");
 | 
					 | 
				
			||||||
            if (isGranted)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.Log($"[SDK] ---- Set Notification Permission: {status}");
 | 
					 | 
				
			||||||
                Analytics.SetNotiPerm(status);
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            RequestNotificationPermission(); // 请求授权
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 请求推送授权
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="callback"></param>
 | 
					 | 
				
			||||||
        public static void RequestNotificationPermission(Action<string> callback = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            FirebaseUtil.StartFetchFcmToken();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            Debug.Log($"[SDK] ---- RequestNotificationPermission");
 | 
					 | 
				
			||||||
            NotificationService.RequestPermission(status =>
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.Log($"[SDK] ---- Set Notification Permission: {status}");
 | 
					 | 
				
			||||||
                if(!string.IsNullOrEmpty(status)) Analytics.SetNotiPerm(status);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                callback?.Invoke(status);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 获取 Notification 状态值
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static string GetNotificationStatus()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return NotificationService.GetStatus();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 用户是否已经获取了 Notification 授权了
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static bool IsNotificationPermissionGranted()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return NotificationService.IsPermissionGranted();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Ad Services
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static bool _initAdsCompleted = false;
 | 
					 | 
				
			||||||
        public static bool IsAdsReady => _initAdsCompleted;
 | 
					 | 
				
			||||||
        private static int _preBannerAction = 0;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static AdsInitSpec GetDefaultAdsSpec()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return AdsInitSpec.BuildDefault(InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 启动广告服务
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private static void StartAdService(AdsInitSpec spec = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            //---------- Using InitConfig ----------
 | 
					 | 
				
			||||||
            if (InitConfig is { IsBuyNoAds: true }) SetBuyNoAds(true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            LogI($"StartAdService");
 | 
					 | 
				
			||||||
            if (spec == null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                spec = AdsInitSpec.BuildDefault(InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
 | 
					 | 
				
			||||||
                if (ADService.Instance.IsBuyNoAds)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    spec = AdsInitSpec.BuildWithNoAds(InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if(InitConfig != null && !string.IsNullOrEmpty(InitConfig.BannerBackgroundColor)) 
 | 
					 | 
				
			||||||
                spec.bannerColorHex = InitConfig.BannerBackgroundColor;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            //--------- Add Callbacks -----------
 | 
					 | 
				
			||||||
            // BADS
 | 
					 | 
				
			||||||
            ADService.Instance.OnBannerStartLoad = OnBannerStartLoad;
 | 
					 | 
				
			||||||
            ADService.Instance.OnBannerLoaded = OnBannerLoaded;
 | 
					 | 
				
			||||||
            // IADS
 | 
					 | 
				
			||||||
            ADService.Instance.OnInterstitialStartLoad = OnInterstitialStartLoad;
 | 
					 | 
				
			||||||
            ADService.Instance.OnInterstitialLoaded = OnInterstitialLoaded;
 | 
					 | 
				
			||||||
            ADService.Instance.OnInterstitialFailed = OnInterstitialFailed;
 | 
					 | 
				
			||||||
            ADService.Instance.OnInterstitialClosed = OnInterstitialClosed;
 | 
					 | 
				
			||||||
            // RADS
 | 
					 | 
				
			||||||
            ADService.Instance.OnRewardedStartLoad = OnRewardStartLoad;
 | 
					 | 
				
			||||||
            ADService.Instance.OnRewardLoaded = OnRewardLoaded;
 | 
					 | 
				
			||||||
            ADService.Instance.OnRewardFailed = OnRewardFailed;
 | 
					 | 
				
			||||||
            ADService.Instance.OnRewardClosed = OnRewardClosed;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // ---------- Start Services ----------
 | 
					 | 
				
			||||||
            ADService.Instance.StartService(OnAdsInitComplete, spec);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // ---------- Life Cycle ----------
 | 
					 | 
				
			||||||
            Callbacks.App.OnAppPaused += OnAppPaused;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 生命周期回调
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="paused"></param>
 | 
					 | 
				
			||||||
        private static void OnAppPaused(bool paused)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if(ADService.Instance.IsInitialized)
 | 
					 | 
				
			||||||
                ADService.Instance.OnAppPaused(paused);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void OnBannerStartLoad(string adUnitId)
 | 
					 | 
				
			||||||
            => Callbacks.Ads.InvokeOnBannerADStartLoad(adUnitId);
 | 
					 | 
				
			||||||
        private static void OnBannerLoaded() 
 | 
					 | 
				
			||||||
            => Callbacks.Ads.InvokeOnBannerADLoaded();
 | 
					 | 
				
			||||||
        private static void OnInterstitialStartLoad(string adUnitId) 
 | 
					 | 
				
			||||||
            => Callbacks.Ads.InvokeOnInterstitialADStartLoad(adUnitId);
 | 
					 | 
				
			||||||
        private static void OnInterstitialLoaded() 
 | 
					 | 
				
			||||||
            => Callbacks.Ads.InvokeOnInterstitialADLoaded();
 | 
					 | 
				
			||||||
        private static void OnInterstitialFailed()
 | 
					 | 
				
			||||||
            => Callbacks.Ads.InvokeOnInterstitialADFailed();
 | 
					 | 
				
			||||||
        private static void OnInterstitialClosed()
 | 
					 | 
				
			||||||
            => Callbacks.Ads.InvokeOnInterstitialADClosed();
 | 
					 | 
				
			||||||
        private static void OnRewardStartLoad(string adUnitId)
 | 
					 | 
				
			||||||
            => Callbacks.Ads.InvokeOnRewardedADStartLoad(adUnitId); 
 | 
					 | 
				
			||||||
        private static void OnRewardLoaded()
 | 
					 | 
				
			||||||
            => Callbacks.Ads.InvokeOnRewardedADLoaded(); 
 | 
					 | 
				
			||||||
        private static void OnRewardFailed()
 | 
					 | 
				
			||||||
            => Callbacks.Ads.InvokeOnRewardADFailed();
 | 
					 | 
				
			||||||
        private static void OnRewardClosed()
 | 
					 | 
				
			||||||
            => Callbacks.Ads.InvokeOnRewardADClosed();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void OnAdsInitComplete()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _initAdsCompleted = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (_adInitSpec != null && _adInitSpec.loadBanner)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // 预制动作处理
 | 
					 | 
				
			||||||
                if (_preBannerAction == 1)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _preBannerAction = 0;
 | 
					 | 
				
			||||||
                    ShowBanner();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else if (_preBannerAction == -1)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _preBannerAction = 0;
 | 
					 | 
				
			||||||
                    HideBanner();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Callbacks.Ads.InvokeOnAdsInitComplete();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static bool CheckAdsReady()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!IsAdsReady)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogW("[SDK] Ads is not ready. Call <GuruSDk.StartAdService> first.");
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 显示Banner广告
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="placement"></param>
 | 
					 | 
				
			||||||
        public static void ShowBanner(string placement = "")
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!CheckAdsReady())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _preBannerAction = 1;
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            ADService.Instance.ShowBanner(placement);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 设置 Banner 背景颜色
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="color"></param>
 | 
					 | 
				
			||||||
        public static void SetBannerBackgroundColor(Color color)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // if (!CheckAdsReady()) return;
 | 
					 | 
				
			||||||
            ADService.Instance.SetBannerBackgroundColor(color);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static void SetBannerAutoRefresh(bool value = true)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!CheckAdsReady()) return;
 | 
					 | 
				
			||||||
            ADService.Instance.SetBannerAutoRefresh(value);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 隐藏Banner广告
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static void HideBanner()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!CheckAdsReady())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _preBannerAction = -1;
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            ADService.Instance.HideBanner();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static void LoadInterstitialAd()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!CheckAdsReady()) return;
 | 
					 | 
				
			||||||
            ADService.Instance.RequestInterstitialAD();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static bool IsInterstitialAdReady => ADService.Instance.IsInterstitialADReady();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 显示插屏广告
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="placement"></param>
 | 
					 | 
				
			||||||
        /// <param name="onDismissed"></param>
 | 
					 | 
				
			||||||
        public static void ShowInterstitialAd(string placement = "", Action onDismissed = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!CheckAdsReady()) return;
 | 
					 | 
				
			||||||
            if (!ADService.Instance.IsInterstitialADReady())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogE("Interstitial is not ready. Call <GuruSDk.ShowInterstitialAd> again.");
 | 
					 | 
				
			||||||
                LoadInterstitialAd();
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            ADService.Instance.ShowInterstitialAD(placement, onDismissed);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static void LoadRewardAd()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!CheckAdsReady()) return;
 | 
					 | 
				
			||||||
            ADService.Instance.RequestRewardedAD();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static bool IsRewardAdReady => ADService.Instance.IsRewardedADReady();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 显示激励视频广告
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="placement"></param>
 | 
					 | 
				
			||||||
        /// <param name="onRewarded"></param>
 | 
					 | 
				
			||||||
        /// <param name="onFailed"></param>
 | 
					 | 
				
			||||||
        public static void ShowRewardAd(string placement = "", Action onRewarded = null, Action<string> onFailed = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!CheckAdsReady()) return;
 | 
					 | 
				
			||||||
            if (!ADService.Instance.IsRewardedADReady())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogE("RewardAd is not ready. Call <GuruSDk.LoadRewardAd> again.");
 | 
					 | 
				
			||||||
                LoadRewardAd();
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            ADService.Instance.ShowRewardedAD(placement, onRewarded, onFailed);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region MaxServices
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 显示Max调试菜单
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static void ShowMaxDebugPanel()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
#if UNITY_EDITOR
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            LogI($"Can not show Max Debug Panel in Editor, skipped.");
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
            if (!ADService.Instance.IsInitialized)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogI($"ADService is not initialized, call <GuruSDK.StartAds> first.");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            ADService.Instance.ShowMaxDebugPanel();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -1,378 +0,0 @@
 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public partial class GuruSDK
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 回调参数类
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public class Callbacks
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            /// <summary>
 | 
					 | 
				
			||||||
            /// APP 事件
 | 
					 | 
				
			||||||
            /// </summary>
 | 
					 | 
				
			||||||
            public static class App
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                private static Action<bool> _onAppPaused;
 | 
					 | 
				
			||||||
                public static event Action<bool> OnAppPaused
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onAppPaused += value;
 | 
					 | 
				
			||||||
                    remove => _onAppPaused -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnAppPaused(bool isPaused)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onAppPaused?.Invoke(isPaused);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action _onAppQuit;
 | 
					 | 
				
			||||||
                public static event Action OnAppQuit
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onAppQuit += value;
 | 
					 | 
				
			||||||
                    remove => _onAppQuit -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnAppQuit()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onAppQuit?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            /// <summary>
 | 
					 | 
				
			||||||
            /// GDPR Consent
 | 
					 | 
				
			||||||
            /// </summary>
 | 
					 | 
				
			||||||
            public static class ConsentFlow
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                /// <summary>
 | 
					 | 
				
			||||||
                /// 当Consent启动结束后返回状态码
 | 
					 | 
				
			||||||
                /// </summary>
 | 
					 | 
				
			||||||
                public static event Action<int> OnConsentResult
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onConsentResult += value;
 | 
					 | 
				
			||||||
                    remove => _onConsentResult -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                private static Action<int> _onConsentResult;
 | 
					 | 
				
			||||||
                internal static void InvokeOnConsentResult(int code)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onConsentResult?.Invoke(code);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                /// <summary>
 | 
					 | 
				
			||||||
                /// ATT 状态返回
 | 
					 | 
				
			||||||
                /// </summary>
 | 
					 | 
				
			||||||
                public static event Action<int> OnAttResult
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onAttResult += value;
 | 
					 | 
				
			||||||
                    remove => _onAttResult -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                private static Action<int> _onAttResult;
 | 
					 | 
				
			||||||
                internal static void InvokeOnAttResultCallback(int code)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onAttResult?.Invoke(code);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /// <summary>
 | 
					 | 
				
			||||||
            /// 广告回调
 | 
					 | 
				
			||||||
            /// </summary>
 | 
					 | 
				
			||||||
            public static class Ads
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                private static Action _onAdsInitComplete;
 | 
					 | 
				
			||||||
                public static event Action OnAdsInitComplete
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onAdsInitComplete += value;
 | 
					 | 
				
			||||||
                    remove => _onAdsInitComplete -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnAdsInitComplete()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onAdsInitComplete?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                //------------ BANNER -----------------
 | 
					 | 
				
			||||||
                private static Action<string> _onBannerADStartLoad;
 | 
					 | 
				
			||||||
                public static event Action<string> OnBannerADStartLoad
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onBannerADStartLoad += value;
 | 
					 | 
				
			||||||
                    remove => _onBannerADStartLoad -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnBannerADStartLoad(string adUnitId)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onBannerADStartLoad?.Invoke(adUnitId);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                private static Action _onBannerADLoaded;
 | 
					 | 
				
			||||||
                public static event Action OnBannerADLoaded
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onBannerADLoaded += value;
 | 
					 | 
				
			||||||
                    remove => _onBannerADLoaded -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnBannerADLoaded()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onBannerADLoaded?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                //------------ INTER -----------------
 | 
					 | 
				
			||||||
                private static Action<string> _onInterstitialADStartLoad;
 | 
					 | 
				
			||||||
                public static event Action<string> OnInterstitialADStartLoad
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onInterstitialADStartLoad += value;
 | 
					 | 
				
			||||||
                    remove => _onInterstitialADStartLoad -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnInterstitialADStartLoad(string adUnitId)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onInterstitialADStartLoad?.Invoke(adUnitId);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action _onInterstitialADLoaded;
 | 
					 | 
				
			||||||
                public static event Action OnInterstitialADLoaded
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onInterstitialADLoaded += value;
 | 
					 | 
				
			||||||
                    remove => _onInterstitialADLoaded -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnInterstitialADLoaded()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onInterstitialADLoaded?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action _onInterstitialADFailed;
 | 
					 | 
				
			||||||
                public static event Action OnInterstitialADFailed
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onInterstitialADFailed += value;
 | 
					 | 
				
			||||||
                    remove => _onInterstitialADFailed -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnInterstitialADFailed()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onInterstitialADFailed?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action _onInterstitialADClosed;
 | 
					 | 
				
			||||||
                public static event Action OnInterstitialADClosed
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onInterstitialADClosed += value;
 | 
					 | 
				
			||||||
                    remove => _onInterstitialADClosed -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnInterstitialADClosed()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onInterstitialADClosed?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                //------------ REWARD -----------------
 | 
					 | 
				
			||||||
                private static Action<string> _onRewardedADStartLoad;
 | 
					 | 
				
			||||||
                public static event Action<string> OnRewardedADStartLoad
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onRewardedADStartLoad += value;
 | 
					 | 
				
			||||||
                    remove => _onRewardedADStartLoad -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnRewardedADStartLoad(string adUnitId)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onRewardedADStartLoad?.Invoke(adUnitId);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action _onRewardedADLoaded;
 | 
					 | 
				
			||||||
                public static event Action OnRewardedADLoaded
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onRewardedADLoaded += value;
 | 
					 | 
				
			||||||
                    remove => _onRewardedADLoaded -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnRewardedADLoaded()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onRewardedADLoaded?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action _onRewardADClosed;
 | 
					 | 
				
			||||||
                public static event Action OnRewardedADClosed
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onRewardADClosed += value;
 | 
					 | 
				
			||||||
                    remove => _onRewardADClosed -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnRewardADClosed()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onRewardADClosed?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action _onRewardADFailed;
 | 
					 | 
				
			||||||
                public static event Action OnRewardADFailed
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onRewardADFailed += value;
 | 
					 | 
				
			||||||
                    remove => _onRewardADFailed -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnRewardADFailed()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onRewardADFailed?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /// <summary>
 | 
					 | 
				
			||||||
            /// 云控参数
 | 
					 | 
				
			||||||
            /// </summary>
 | 
					 | 
				
			||||||
            public static class Remote
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                private static Action<bool> _onRemoteFetchComplete;
 | 
					 | 
				
			||||||
                public static event Action<bool> OnRemoteFetchComplete
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onRemoteFetchComplete += value;
 | 
					 | 
				
			||||||
                    remove => _onRemoteFetchComplete -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnRemoteFetchComplete(bool success)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onRemoteFetchComplete?.Invoke(success);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /// <summary>
 | 
					 | 
				
			||||||
            /// 支付回调
 | 
					 | 
				
			||||||
            /// </summary>
 | 
					 | 
				
			||||||
            public static class IAP
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                private static Action _onIAPInitStart;
 | 
					 | 
				
			||||||
                public static event Action OnIAPInitStart
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onIAPInitStart += value;
 | 
					 | 
				
			||||||
                    remove => _onIAPInitStart -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnIAPInitStart()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onIAPInitStart?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action<bool> _onIAPInitComplete;
 | 
					 | 
				
			||||||
                public static event Action<bool> OnIAPInitComplete
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onIAPInitComplete += value;
 | 
					 | 
				
			||||||
                    remove => _onIAPInitComplete -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnIAPInitComplete(bool success)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onIAPInitComplete?.Invoke(success);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action<string> _onPurchaseStart;
 | 
					 | 
				
			||||||
                public static event Action<string> OnPurchaseStart
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onPurchaseStart += value;
 | 
					 | 
				
			||||||
                    remove => _onPurchaseStart -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnPurchaseStart(string productId)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onPurchaseStart?.Invoke(productId);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action<string, bool> _onPurchaseEnd;
 | 
					 | 
				
			||||||
                public static event Action<string, bool> OnPurchaseEnd
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onPurchaseEnd += value;
 | 
					 | 
				
			||||||
                    remove => _onPurchaseEnd -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnPurchaseEnd(string productId, bool success)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onPurchaseEnd?.Invoke(productId, success);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action<string, string> _onPurchaseFailed;
 | 
					 | 
				
			||||||
                public static event Action<string, string> OnPurchaseFailed
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onPurchaseFailed += value;
 | 
					 | 
				
			||||||
                    remove => _onPurchaseFailed -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnPurchaseFailed(string productId, string error)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onPurchaseFailed?.Invoke(productId, error);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
       
 | 
					 | 
				
			||||||
                private static Action<bool, string> _onIAPRestored;
 | 
					 | 
				
			||||||
                public static event Action<bool, string> OnIAPRestored
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onIAPRestored += value;
 | 
					 | 
				
			||||||
                    remove => _onIAPRestored -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnIAPRestored(bool success, string productId)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onIAPRestored?.Invoke(success, productId);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            public static class SDK
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                private static Action<bool> _onFirebaseReady;
 | 
					 | 
				
			||||||
                public static event Action<bool> OnFirebaseReady
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onFirebaseReady += value;
 | 
					 | 
				
			||||||
                    remove => _onFirebaseReady -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnFirebaseReady(bool success)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onFirebaseReady?.Invoke(success);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action _onGuruServiceReady;
 | 
					 | 
				
			||||||
                public static event Action OnGuruServiceReady
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onGuruServiceReady += value;
 | 
					 | 
				
			||||||
                    remove => _onGuruServiceReady -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnGuruServiceReady()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onGuruServiceReady?.Invoke();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                private static Action<bool> _onDebuggerDisplayed;
 | 
					 | 
				
			||||||
                public static event Action<bool> OnDisplayDebugger
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onDebuggerDisplayed += value;
 | 
					 | 
				
			||||||
                    remove => _onDebuggerDisplayed -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnDebuggerDisplayed(bool success)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onDebuggerDisplayed?.Invoke(success);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                private static Action<bool> _onUserAuthResult;
 | 
					 | 
				
			||||||
                public static event Action<bool> OnGuruUserAuthResult
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onUserAuthResult += value;
 | 
					 | 
				
			||||||
                    remove => _onUserAuthResult -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnGuruUserAuthResult(bool success)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onUserAuthResult?.Invoke(success);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                // DeepLink 回调 
 | 
					 | 
				
			||||||
                private static Action<string> _onDeeplinkCallback;
 | 
					 | 
				
			||||||
                public static event Action<string> OnDeeplinkCallback
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onDeeplinkCallback += value;
 | 
					 | 
				
			||||||
                    remove => _onDeeplinkCallback -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeDeeplinkCallback(string deeplink)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onDeeplinkCallback?.Invoke(deeplink);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                // TODO: 之后需要添加 define 宏来控制是否可用
 | 
					 | 
				
			||||||
                // Firebase Auth 回调
 | 
					 | 
				
			||||||
                private static Action<bool, Firebase.Auth.FirebaseUser> _onFirebaseUserAuthResult;
 | 
					 | 
				
			||||||
                public static event Action<bool, Firebase.Auth.FirebaseUser> OnFirebaseUserAuthResult
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    add => _onFirebaseUserAuthResult += value;
 | 
					 | 
				
			||||||
                    remove => _onFirebaseUserAuthResult -= value;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                internal static void InvokeOnFirebaseAuthResult(bool success, Firebase.Auth.FirebaseUser firebaseUser = null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _onFirebaseUserAuthResult?.Invoke(success, firebaseUser);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,282 +0,0 @@
 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public partial class GuruSDK
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
	    
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Consts values
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static class Consts
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
	        #region Firebase Defines
 | 
					 | 
				
			||||||
	        
 | 
					 | 
				
			||||||
	        public const string EventAdImpression = "ad_impression";
 | 
					 | 
				
			||||||
			public const string EventAddPaymentInfo = "add_payment_info";
 | 
					 | 
				
			||||||
			public const string EventAddShippingInfo = "add_shipping_info";
 | 
					 | 
				
			||||||
			public const string EventAddToCart = "add_to_cart";
 | 
					 | 
				
			||||||
			public const string EventAddToWishlist = "add_to_wishlist";
 | 
					 | 
				
			||||||
			public const string EventAppOpen = "app_open";
 | 
					 | 
				
			||||||
			public const string EventBeginCheckout = "begin_checkout";
 | 
					 | 
				
			||||||
			public const string EventCampaignDetails = "campaign_details";
 | 
					 | 
				
			||||||
			public const string EventEarnVirtualCurrency = "earn_virtual_currency";
 | 
					 | 
				
			||||||
			public const string EventGenerateLead = "generate_lead";
 | 
					 | 
				
			||||||
			public const string EventJoinGroup = "join_group";
 | 
					 | 
				
			||||||
			public const string EventLevelEnd = "level_end";
 | 
					 | 
				
			||||||
			public const string EventLevelStart = "level_start";
 | 
					 | 
				
			||||||
			public const string EventLevelEndSuccessPrefix = "level_end_success_";
 | 
					 | 
				
			||||||
			public const string EventLevelUp = "level_up";
 | 
					 | 
				
			||||||
			public const string EventLogin = "login";
 | 
					 | 
				
			||||||
			public const string EventPostScore = "post_score";
 | 
					 | 
				
			||||||
			public const string EventPurchase = "purchase";
 | 
					 | 
				
			||||||
			public const string EventRefund = "refund";
 | 
					 | 
				
			||||||
			public const string EventRemoveFromCart = "remove_from_cart";
 | 
					 | 
				
			||||||
			public const string EventScreenView = "screen_view";
 | 
					 | 
				
			||||||
			public const string EventSearch = "search";
 | 
					 | 
				
			||||||
			public const string EventSelectContent = "select_content";
 | 
					 | 
				
			||||||
			public const string EventSelectItem = "select_item";
 | 
					 | 
				
			||||||
			public const string EventSelectPromotion = "select_promotion";
 | 
					 | 
				
			||||||
			public const string EventShare = "share";
 | 
					 | 
				
			||||||
			public const string EventSignUp = "sign_up";
 | 
					 | 
				
			||||||
			public const string EventSpendVirtualCurrency = "spend_virtual_currency";
 | 
					 | 
				
			||||||
			public const string EventUnlockAchievement = "unlock_achievement";
 | 
					 | 
				
			||||||
			public const string EventHpPoints = "hp_points";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			public const string EventAttReguideImp = "att_reguide_imp";
 | 
					 | 
				
			||||||
			public const string EventAttReguideClk = "att_reguide_clk";
 | 
					 | 
				
			||||||
			public const string EventAttReguideResult = "att_reguide_result";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			public const string EventTutorialBegin = "tutorial_begin";
 | 
					 | 
				
			||||||
			public const string EventTutorialImp= "tutorial_{0}_imp";
 | 
					 | 
				
			||||||
			public const string EventTutorialNextClick= "tutorial_{0}_next_clk";
 | 
					 | 
				
			||||||
			public const string EventTutorialComplete= "tutorial_complete";
 | 
					 | 
				
			||||||
			public const string EventTutorialClose = "tutorial_close";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			public const string EventNotiPermImp = "noti_perm_imp";
 | 
					 | 
				
			||||||
			public const string EventNotiPermResult = "noti_perm_result";
 | 
					 | 
				
			||||||
			public const string EventNotiPermRationaleImp = "noti_perm_rationale_imp";
 | 
					 | 
				
			||||||
			public const string EventNotiPermRationaleResult = "noti_perm_rationale_result";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			public const string EventDevAudit = "dev_audit";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			public const string EventViewCart = "view_cart";
 | 
					 | 
				
			||||||
			public const string EventViewItem = "view_item";
 | 
					 | 
				
			||||||
			public const string EventViewItemList = "view_item_list";
 | 
					 | 
				
			||||||
			public const string EventViewPromotion = "view_promotion";
 | 
					 | 
				
			||||||
			public const string EventViewSearchResults = "view_search_results";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			public const string ParameterAchievementId = "achievement_id";
 | 
					 | 
				
			||||||
			public const string ParameterAdFormat = "ad_format";
 | 
					 | 
				
			||||||
			public const string ParameterAdNetworkClickID = "aclid";
 | 
					 | 
				
			||||||
			public const string ParameterAdPlatform = "ad_platform";
 | 
					 | 
				
			||||||
			public const string ParameterAdSource = "ad_source";
 | 
					 | 
				
			||||||
			public const string ParameterAdUnitName = "ad_unit_name";
 | 
					 | 
				
			||||||
			public const string ParameterAffiliation = "affiliation";
 | 
					 | 
				
			||||||
			public const string ParameterCP1 = "cp1";
 | 
					 | 
				
			||||||
			public const string ParameterCampaign = "campaign";
 | 
					 | 
				
			||||||
			public const string ParameterCharacter = "character";
 | 
					 | 
				
			||||||
			public const string ParameterContent = "content";
 | 
					 | 
				
			||||||
			public const string ParameterContentType = "content_type";
 | 
					 | 
				
			||||||
			public const string ParameterCoupon = "coupon";
 | 
					 | 
				
			||||||
			public const string ParameterCreativeName = "creative_name";
 | 
					 | 
				
			||||||
			public const string ParameterCreativeSlot = "creative_slot";
 | 
					 | 
				
			||||||
			public const string ParameterCurrency = "currency";
 | 
					 | 
				
			||||||
			public const string ParameterDestination = "destination";
 | 
					 | 
				
			||||||
			public const string ParameterDiscount = "discount";
 | 
					 | 
				
			||||||
			public const string ParameterEndDate = "end_date";
 | 
					 | 
				
			||||||
			public const string ParameterExtendSession = "extend_session";
 | 
					 | 
				
			||||||
			public const string ParameterFlightNumber = "flight_number";
 | 
					 | 
				
			||||||
			public const string ParameterGroupId = "group_id";
 | 
					 | 
				
			||||||
			public const string ParameterIndex = "index";
 | 
					 | 
				
			||||||
			public const string ParameterItemBrand = "item_brand";
 | 
					 | 
				
			||||||
			public const string ParameterItemCategory = "item_category";
 | 
					 | 
				
			||||||
			public const string ParameterItemCategory2 = "item_category2";
 | 
					 | 
				
			||||||
			public const string ParameterItemCategory3 = "item_category3";
 | 
					 | 
				
			||||||
			public const string ParameterItemCategory4 = "item_category4";
 | 
					 | 
				
			||||||
			public const string ParameterItemCategory5 = "item_category5";
 | 
					 | 
				
			||||||
			public const string ParameterItemId = "item_id";
 | 
					 | 
				
			||||||
			public const string ParameterItemList = "item_list";
 | 
					 | 
				
			||||||
			public const string ParameterItemListID = "item_list_id";
 | 
					 | 
				
			||||||
			public const string ParameterItemListName = "item_list_name";
 | 
					 | 
				
			||||||
			public const string ParameterItemName = "item_name";
 | 
					 | 
				
			||||||
			public const string ParameterLevel = "level";
 | 
					 | 
				
			||||||
			public const string ParameterLevelName = "level_name";
 | 
					 | 
				
			||||||
			public const string ParameterLocation = "location";
 | 
					 | 
				
			||||||
			public const string ParameterLocationID = "location_id";
 | 
					 | 
				
			||||||
			public const string ParameterMedium = "medium";
 | 
					 | 
				
			||||||
			public const string ParameterMethod = "method";
 | 
					 | 
				
			||||||
			public const string ParameterNumberOfNights = "number_of_nights";
 | 
					 | 
				
			||||||
			public const string ParameterNumberOfPassengers = "number_of_passengers";
 | 
					 | 
				
			||||||
			public const string ParameterNumberOfRooms = "number_of_rooms";
 | 
					 | 
				
			||||||
			public const string ParameterOrigin = "origin";
 | 
					 | 
				
			||||||
			public const string ParameterPaymentType = "payment_type";
 | 
					 | 
				
			||||||
			public const string ParameterPrice = "price";
 | 
					 | 
				
			||||||
			public const string ParameterPromotionID = "promotion_id";
 | 
					 | 
				
			||||||
			public const string ParameterPromotionName = "promotion_name";
 | 
					 | 
				
			||||||
			public const string ParameterQuantity = "quantity";
 | 
					 | 
				
			||||||
			public const string ParameterScore = "score";
 | 
					 | 
				
			||||||
			public const string ParameterScreenClass = "screen_class";
 | 
					 | 
				
			||||||
			public const string ParameterScreenName = "screen_name";
 | 
					 | 
				
			||||||
			public const string ParameterSearchTerm = "search_term";
 | 
					 | 
				
			||||||
			public const string ParameterShipping = "shipping";
 | 
					 | 
				
			||||||
			public const string ParameterShippingTier = "shipping_tier";
 | 
					 | 
				
			||||||
			public const string ParameterSignUpMethod = "sign_up_method";
 | 
					 | 
				
			||||||
			public const string ParameterSource = "source";
 | 
					 | 
				
			||||||
			public const string ParameterStartDate = "start_date";
 | 
					 | 
				
			||||||
			public const string ParameterSuccess = "success";
 | 
					 | 
				
			||||||
			public const string ParameterTax = "tax";
 | 
					 | 
				
			||||||
			public const string ParameterTerm = "term";
 | 
					 | 
				
			||||||
			public const string ParameterTransactionId = "transaction_id";
 | 
					 | 
				
			||||||
			public const string ParameterTravelClass = "travel_class";
 | 
					 | 
				
			||||||
			public const string ParameterValue = "value";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
	        #endregion
 | 
					 | 
				
			||||||
	        
 | 
					 | 
				
			||||||
	        #region Guru BI Events & Parameters
 | 
					 | 
				
			||||||
	        
 | 
					 | 
				
			||||||
            public const string TAG = "Analytics";
 | 
					 | 
				
			||||||
			// 美元符号
 | 
					 | 
				
			||||||
			public const string USD = "USD"; 
 | 
					 | 
				
			||||||
			// 广告平台
 | 
					 | 
				
			||||||
			public const string AdMAX = "MAX"; 
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			//IAP打点事件
 | 
					 | 
				
			||||||
			public const string EventIAPFirst = "first_iap";
 | 
					 | 
				
			||||||
			public const string EventIAPImp = "iap_imp";
 | 
					 | 
				
			||||||
			public const string EventIAPClose = "iap_close";
 | 
					 | 
				
			||||||
			public const string EventIAPClick = "iap_clk";
 | 
					 | 
				
			||||||
			public const string EventIAPReturnTrue = "iap_ret_true";
 | 
					 | 
				
			||||||
			public const string EventIAPReturnFalse = "iap_ret_false";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			//横幅广告打点事件
 | 
					 | 
				
			||||||
			public const string EventBadsLoad = "bads_load";
 | 
					 | 
				
			||||||
			public const string EventBadsLoaded = "bads_loaded";
 | 
					 | 
				
			||||||
			public const string EventBadsFailed = "bads_failed";
 | 
					 | 
				
			||||||
			public const string EventBadsClick = "bads_clk";
 | 
					 | 
				
			||||||
			public const string EventBadsImp = "bads_imp";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			//插屏广告打点事件
 | 
					 | 
				
			||||||
			public const string EventIadsLoad = "iads_load";
 | 
					 | 
				
			||||||
			public const string EventIadsLoaded = "iads_loaded";
 | 
					 | 
				
			||||||
			public const string EventIadsFailed = "iads_failed";
 | 
					 | 
				
			||||||
			public const string EventIadsImp = "iads_imp";
 | 
					 | 
				
			||||||
			public const string EventIadsClick = "iads_clk";
 | 
					 | 
				
			||||||
			public const string EventIadsClose = "iads_close";
 | 
					 | 
				
			||||||
	    
 | 
					 | 
				
			||||||
			//激励视频广告打点事件
 | 
					 | 
				
			||||||
			public const string EventRadsLoad = "rads_load";
 | 
					 | 
				
			||||||
			public const string EventRadsLoaded = "rads_loaded";
 | 
					 | 
				
			||||||
			public const string EventRadsFailed = "rads_failed";
 | 
					 | 
				
			||||||
			public const string EventRadsImp = "rads_imp";
 | 
					 | 
				
			||||||
			public const string EventRadsRewarded = "rads_rewarded";
 | 
					 | 
				
			||||||
			public const string EventRadsClick = "rads_clk";
 | 
					 | 
				
			||||||
			public const string EventRadsClose = "rads_close";
 | 
					 | 
				
			||||||
			public const string EventFirstRadsRewarded = "first_rads_rewarded";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			//广告收益打点事件
 | 
					 | 
				
			||||||
			public const string EventTchAdRev001Impression = "tch_ad_rev_roas_001";
 | 
					 | 
				
			||||||
			public const string EventTchAdRev02Impression = "tch_ad_rev_roas_02";
 | 
					 | 
				
			||||||
			public const string EventTchAdRevAbnormal = "tch_ad_rev_value_abnormal";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			//内购成功事件上报
 | 
					 | 
				
			||||||
			public const string EventIAPPurchase = "iap_purchase";
 | 
					 | 
				
			||||||
			public const string EventSubPurchase = "sub_purchase";
 | 
					 | 
				
			||||||
			public const string IAPStoreCategory = "Store";
 | 
					 | 
				
			||||||
			public const string IAPTypeProduct = "product";
 | 
					 | 
				
			||||||
			public const string IAPTypeSubscription = "subscription";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			//打点参数名
 | 
					 | 
				
			||||||
			public const string ParameterResult = "result";
 | 
					 | 
				
			||||||
			public const string ParameterStep = "step";
 | 
					 | 
				
			||||||
			public const string ParameterDuration = "duration";
 | 
					 | 
				
			||||||
			public const string ParameterErrorCode = "error_code";
 | 
					 | 
				
			||||||
			public const string ParameterProductId = "product_id";
 | 
					 | 
				
			||||||
			public const string ParameterPlatform = "platform";
 | 
					 | 
				
			||||||
			public const string ParameterStartType = "start_type"; // 游戏启动类型
 | 
					 | 
				
			||||||
			public const string ParameterReplay =  "replay"; // 游戏重玩
 | 
					 | 
				
			||||||
			public const string ParameterContinue = "continue"; // 游戏继续
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			// 评价参数
 | 
					 | 
				
			||||||
			public const string EventRateImp = "rate_imp"; // 评价弹窗展示
 | 
					 | 
				
			||||||
			public const string EventRateNow = "rate_now"; // 点击评分引导弹窗中的评分
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			//打点内部执行错误
 | 
					 | 
				
			||||||
			public static string ParameterEventError => "event_error";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			//ios ATT打点
 | 
					 | 
				
			||||||
			public const string ATTGuideShow = "att_guide_show";
 | 
					 | 
				
			||||||
			public const string ATTGuideOK = "att_guide_ok";
 | 
					 | 
				
			||||||
			public const string ATTWindowShow = "att_window_show";
 | 
					 | 
				
			||||||
			public const string ATTOptIn = "att_opt_in";
 | 
					 | 
				
			||||||
			public const string ATTOpOut = "att_opt_out";
 | 
					 | 
				
			||||||
			public const string ParameterATTStatus = "att_status";
 | 
					 | 
				
			||||||
			public const string EventAttResult = "att_result";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			// 用户属性
 | 
					 | 
				
			||||||
			public const string PropertyFirstOpenTime = "first_open_time"; 		//用户第一次first_open的时间
 | 
					 | 
				
			||||||
			public const string PropertyDeviceID = "device_id"; //用户的设备ID
 | 
					 | 
				
			||||||
			public const string PropertyUserID = "user_id";
 | 
					 | 
				
			||||||
			public const string PropertyLevel = "b_level"; //"每次完成通关上升一次,显示用户完成的最大关卡数。只针对主关卡和主玩法的局数做累加,初始值为0。"
 | 
					 | 
				
			||||||
			public const string PropertyPlay = "b_play"; //每完成一局或者游戏触发,
 | 
					 | 
				
			||||||
			public const string PropertyLastPlayedLevel = "last_played_level";
 | 
					 | 
				
			||||||
			public const string PropertyGrade = "grade"; //当游戏玩家角色升级时触发
 | 
					 | 
				
			||||||
			public const string PropertyIsIAPUser = "is_iap_user"; 		//付费成功后设置属性参数为true,如果没有发生付费可以不用设置该属性
 | 
					 | 
				
			||||||
			public const string PropertyIAPCoin = "iap_coin"; //付费所得的总金币数(iap获取累计值)\
 | 
					 | 
				
			||||||
			public const string PropertyNonIAPCoin = "noniap_coin"; //非付费iap获取累计值
 | 
					 | 
				
			||||||
			public const string PropertyCoin = "coin"; //当前金币数
 | 
					 | 
				
			||||||
			public const string PropertyExp = "exp"; // 经验值
 | 
					 | 
				
			||||||
			public const string PropertyHp = "hp"; // 生命值/体力
 | 
					 | 
				
			||||||
			public const string PropertyNetwork = "network"; // 网络状态
 | 
					 | 
				
			||||||
			public const string PropertyAndroidID = "android_id"; // Android 平台 AndroidID
 | 
					 | 
				
			||||||
			public const string PropertyIDFV = "idfv"; // iOS  平台 IDFV
 | 
					 | 
				
			||||||
			public const string PropertyPicture = "picture"; // 玩家在主线的mapid
 | 
					 | 
				
			||||||
			public const string PropertyNoAds = "no_ads"; // 玩家是否去广告
 | 
					 | 
				
			||||||
			public const string PropertyATTStatus = "att_status";  // ATT 状态
 | 
					 | 
				
			||||||
			public const string PropertyNotiPerm = "noti_perm";  // Notification Permission 状态
 | 
					 | 
				
			||||||
			public const string PropertyAdjustId = "adjust_id";  // AdjustId
 | 
					 | 
				
			||||||
			public const string PropertyGDPR = "gdpr"; // GDPR状态
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			// 经济相关
 | 
					 | 
				
			||||||
			public const string ParameterBalance = "balance"; // 用于余额
 | 
					 | 
				
			||||||
			public const string ParameterDefaultScene = "in_game"; // 货币消费默认场景
 | 
					 | 
				
			||||||
			public const string ParameterVirtualCurrencyName = "virtual_currency_name"; // 虚拟货币名称
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			public const string CurrencyNameProps = "props"; // props
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			public const string CurrencyCategoryReward = "reward"; // common, ads
 | 
					 | 
				
			||||||
			public const string CurrencyCategoryIAP = "iap_buy"; // In app purchase
 | 
					 | 
				
			||||||
			public const string CurrencyCategoryBonus = "bonus"; // ads+items, gift box, item group 
 | 
					 | 
				
			||||||
			public const string CurrencyCategoryIGC = "igc"; // In game currency
 | 
					 | 
				
			||||||
			public const string CurrencyCategoryIGB = "igb"; // In game barter
 | 
					 | 
				
			||||||
			public const string CurrencyCategoryProp = "prop"; // prop
 | 
					 | 
				
			||||||
			public const string CurrencyCategoryProps = "props"; // props
 | 
					 | 
				
			||||||
			public const string CurrencyCategoryBundle = "bundle"; // prop groups
 | 
					 | 
				
			||||||
			public const string CurrencyCategoryBoost = "boost"; // boost
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			// SDK 
 | 
					 | 
				
			||||||
			public const string EventSDKInfo = "sdk_info";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			//----------------- 关卡开始类型 ---------------------
 | 
					 | 
				
			||||||
			public const string EventLevelStartModePlay = "play";
 | 
					 | 
				
			||||||
			public const string EventLevelStartModeReplay = "replay";
 | 
					 | 
				
			||||||
			public const string EventLevelStartModeContinue= "continue";
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
			//----------------- 关卡结束类型 ---------------------
 | 
					 | 
				
			||||||
			public const string EventLevelEndSuccess = "success";
 | 
					 | 
				
			||||||
			public const string EventLevelEndFail = "fail";
 | 
					 | 
				
			||||||
			public const string EventLevelEndExit = "exit";
 | 
					 | 
				
			||||||
			public const string EventLevelEndTimeout = "timeout";
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			/// <summary>
 | 
					 | 
				
			||||||
			/// 主线关卡类型
 | 
					 | 
				
			||||||
			/// 只有传入此类型时才会进行 Blevel 的累加
 | 
					 | 
				
			||||||
			/// </summary>
 | 
					 | 
				
			||||||
			public const string LevelTypeMain = "main";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			#endregion
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 35b764b09fe74efe8e2ebead9716279a
 | 
					 | 
				
			||||||
timeCreated: 1709802191
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,60 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public partial class GuruSDK
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private const string K_CMD_NAME_DEBUGGER = "gurusdk.unity.dbg";
 | 
					 | 
				
			||||||
        private const string K_CMD_NAME_WATERMARK = "gurusdk.unity.wm";
 | 
					 | 
				
			||||||
        private const string K_CMD_NAME_CONSOLE = "gurusdk.unity.con";
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Android 测试入口
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 启动 Android 测试配置
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private void StartAndroidDebugCmds()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(AppBundleId))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                UnityEngine.Debug.LogError("--- App Bundle Id is empty, please set it first. ---");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            string val;
 | 
					 | 
				
			||||||
            string key;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            key = K_CMD_NAME_DEBUGGER;
 | 
					 | 
				
			||||||
            val = AndroidSystemPropertiesHelper.Get(key);
 | 
					 | 
				
			||||||
            if (val == AppBundleId)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // 显示应用调试状态栏
 | 
					 | 
				
			||||||
                Debugger.ShowAdStatus();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            key = K_CMD_NAME_WATERMARK;
 | 
					 | 
				
			||||||
            val = AndroidSystemPropertiesHelper.Get(key);
 | 
					 | 
				
			||||||
            if (val == AppBundleId)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // 显示应用水印
 | 
					 | 
				
			||||||
                // TODO
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            key = K_CMD_NAME_CONSOLE;
 | 
					 | 
				
			||||||
            val = AndroidSystemPropertiesHelper.Get(key);
 | 
					 | 
				
			||||||
            if (val == AppBundleId)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // 显示控制台
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private bool IsCmdAvailable(string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return value == "1" || value == "true";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 36f2d28a30ff46568d6adc0b728a3268
 | 
					 | 
				
			||||||
timeCreated: 1714354376
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,129 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public partial class GuruSDK
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private static readonly bool _useBaseOptions = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static GuruDebugger _debugger;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static GuruDebugger Debugger
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (_debugger == null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _debugger = GuruDebugger.Instance;
 | 
					 | 
				
			||||||
                    if (_useBaseOptions)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        InitDebuggerLayout();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return _debugger;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 显示广告状态
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static bool ShowAdStatus()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!IsServiceReady) return false;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            Debugger.ShowAdStatus();
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 显示 Debugger
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static bool ShowDebugger()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!IsServiceReady) return false;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            Debugger.ShowPage(); // 显示 Debugger 界面
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void InitDebuggerLayout()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var settings = GuruSettings.Instance;
 | 
					 | 
				
			||||||
            var v = GuruAppVersion.Load();
 | 
					 | 
				
			||||||
            var app_version = (v == null ? $"{Application.version} (unknown)" : $"{v.version}  ({v.code})");
 | 
					 | 
				
			||||||
            var uid = (string.IsNullOrEmpty(UID) ? "NULL" : UID);
 | 
					 | 
				
			||||||
            var device_id = (string.IsNullOrEmpty(DeviceId) ? "NULL" : DeviceId);
 | 
					 | 
				
			||||||
            var push_token = (string.IsNullOrEmpty(PushToken) ? "NULL" : PushToken);
 | 
					 | 
				
			||||||
            var auth_token = (string.IsNullOrEmpty(AuthToken) ? "NULL" : AuthToken);
 | 
					 | 
				
			||||||
            var fid = (string.IsNullOrEmpty(FirebaseId) ? "NULL" : FirebaseId);
 | 
					 | 
				
			||||||
            var adjust_id = (string.IsNullOrEmpty(AdjustId) ? "NULL" : AdjustId);
 | 
					 | 
				
			||||||
            var idfa = (string.IsNullOrEmpty(IDFA) ? "NULL" : IDFA);
 | 
					 | 
				
			||||||
            var gsid = (string.IsNullOrEmpty(GSADID) ? "NULL" : GSADID);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // ------------ Info Page --------------------
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Guru SDK", GuruSDK.Version);
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Unity Version", Application.unityVersion);
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Name", settings.ProductName);
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Bundle Id", settings.GameIdentifier);
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Version", app_version);
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Uid", uid).AddCopyButton();
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Device ID", device_id).AddCopyButton();
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Push Token", push_token).AddCopyButton();
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Auth Token", auth_token).AddCopyButton();
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Firebase Id", fid).AddCopyButton();
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Adjust Id", adjust_id).AddCopyButton();
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/IDFA", idfa).AddCopyButton();
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/GSADID", gsid).AddCopyButton();
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Debug Mode", GuruSDK.IsDebugMode? "true" : "false");
 | 
					 | 
				
			||||||
            Debugger.AddOption("Info/Screen size", $"{Screen.width} x {Screen.height}");
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // ------------ Ads Page --------------------
 | 
					 | 
				
			||||||
            Debugger.AddOption("Ads/Show Ads Debug Panel", "", ShowMaxDebugPanel);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var badsId = settings.ADSetting.GetBannerID();
 | 
					 | 
				
			||||||
            var iadsId = settings.ADSetting.GetInterstitialID();
 | 
					 | 
				
			||||||
            var radsId = settings.ADSetting.GetRewardedVideoID();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            Debugger.AddOption("Ads/Banner Id", badsId);
 | 
					 | 
				
			||||||
            Debugger.AddOption("Ads/Interstitial Id", iadsId);
 | 
					 | 
				
			||||||
            Debugger.AddOption("Ads/Rewarded Id", radsId);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            GuruDebugger.OnClosed -= OnDebuggerClosed;
 | 
					 | 
				
			||||||
            GuruDebugger.OnClosed += OnDebuggerClosed;
 | 
					 | 
				
			||||||
            Callbacks.SDK.InvokeOnDebuggerDisplayed(true);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static void OnDebuggerClosed()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            GuruDebugger.OnClosed -= OnDebuggerClosed;
 | 
					 | 
				
			||||||
            Callbacks.SDK.InvokeOnDebuggerDisplayed(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 显示 Debugger
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="debugger"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static bool ShowDebuggerWithData(out GuruDebugger debugger)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            debugger = null;
 | 
					 | 
				
			||||||
            bool res = ShowDebugger();
 | 
					 | 
				
			||||||
            if (res)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                debugger = GuruDebugger.Instance;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return res;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static GuruDebugger.OptionLayout AddOption(string uri, string content = "", Action clickHandler = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return Debugger.AddOption(uri, content, clickHandler);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 002605b9e408487bb69d27d07dda016c
 | 
					 | 
				
			||||||
timeCreated: 1711078536
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,335 +0,0 @@
 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using System.Linq;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public partial class GuruSDK
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public static bool IsIAPReady = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //---------- 支付失败原因 ----------
 | 
					 | 
				
			||||||
        public const string BuyFail_PurchasingUnavailable = "PurchasingUnavailable";
 | 
					 | 
				
			||||||
        public const string BuyFail_Pending = "ExistingPurchasePending";
 | 
					 | 
				
			||||||
        public const string BuyFail_ProductUnavailable = "ProductUnavailable";
 | 
					 | 
				
			||||||
        public const string BuyFail_SignatureInvalid = "SignatureInvalid";
 | 
					 | 
				
			||||||
        public const string BuyFail_UserCancelled = "UserCancelled";
 | 
					 | 
				
			||||||
        public const string BuyFail_PaymentDeclined = "PaymentDeclined";
 | 
					 | 
				
			||||||
        public const string BuyFail_DuplicateTransaction = "DuplicateTransaction";
 | 
					 | 
				
			||||||
        public const string BuyFail_Unknown = "Unknown";
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Start
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 初始化IAP 功能
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static void InitIAP(string uid, byte[] googleKey, byte[] appleRootCerts)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            GuruIAP.Instance.OnInitResult += OnIAPInitResult;
 | 
					 | 
				
			||||||
            GuruIAP.Instance.OnRestored += OnRestored;
 | 
					 | 
				
			||||||
            GuruIAP.Instance.OnBuyStart += OnBuyStart;
 | 
					 | 
				
			||||||
            GuruIAP.Instance.OnBuyEnd += OnBuyEnd;
 | 
					 | 
				
			||||||
            GuruIAP.Instance.OnBuyFailed += OnBuyFailed;
 | 
					 | 
				
			||||||
            GuruIAP.Instance.OnGetProductReceipt += OnGetReceipt;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            Callbacks.IAP.InvokeOnIAPInitStart(); // 初始化之前进行调用
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            GuruIAP.Instance.InitWithKeys(uid, googleKey, appleRootCerts, IsDebugMode);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 初始化结果
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="success"></param>
 | 
					 | 
				
			||||||
        private static void OnIAPInitResult(bool success)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            LogI($"IAP init result: {success}");
 | 
					 | 
				
			||||||
            IsIAPReady = success;
 | 
					 | 
				
			||||||
            Callbacks.IAP.InvokeOnIAPInitComplete(success);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static bool CheckIAPReady()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!IsIAPReady)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogE("IAP is not ready, call <GuruSDK.InitIAP> first.");
 | 
					 | 
				
			||||||
                return false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Data
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 获取商品信息
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productName"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static ProductInfo GetProductInfo(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance?.GetInfo(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 获取商品信息 (提供 ProductId)
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productId"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static ProductInfo GetProductInfoById(string productId)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance?.GetInfoById(productId);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        [Obsolete("Will be discarded in next version. Using GetProductInfoById(string productId) instead.")]
 | 
					 | 
				
			||||||
        public static ProductSetting GetProductSettingById(string productId)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var products = GuruSettings.Instance.Products;
 | 
					 | 
				
			||||||
            if (products != null && products.Length > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return products.FirstOrDefault(p => p.ProductId == productId);   
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        [Obsolete("Will be discarded in next version. Using GetProductInfo(string productName) instead.")]
 | 
					 | 
				
			||||||
        public static ProductSetting GetProductSetting(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var products = GuruSettings.Instance.Products;
 | 
					 | 
				
			||||||
            if (products != null && products.Length > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return products.FirstOrDefault(p => p.ProductName == productName);   
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 查询某个商品是否已经包含订单了
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productName"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static bool IsProductHasReceipt(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.IsProductHasReceipt(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static string GetProductLocalizedPriceString(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.GetLocalizedPriceString(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Purchase
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static Action<string, bool> InvokeOnPurchaseCallback;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 老接口, 将会被废弃
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productName"></param>
 | 
					 | 
				
			||||||
        /// <param name="purchaseCallback"></param>
 | 
					 | 
				
			||||||
        [Obsolete("Will be discarded in next version. Using Purchase(string productName, string category, Action<string, bool> purchaseCallback) instead.")]
 | 
					 | 
				
			||||||
        internal static void Purchase(string productName, Action<string, bool> purchaseCallback = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Purchase(productName, "", purchaseCallback);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 购买商品, 通过商品Name
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productName"></param>
 | 
					 | 
				
			||||||
        /// <param name="category"></param>
 | 
					 | 
				
			||||||
        /// <param name="purchaseCallback"></param>
 | 
					 | 
				
			||||||
        public static void Purchase(string productName, string category = "", Action<string, bool> purchaseCallback = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (CheckIAPReady())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                InvokeOnPurchaseCallback = purchaseCallback;
 | 
					 | 
				
			||||||
                GuruIAP.Instance.Buy(productName, category);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 购买商品, 通过商品ID
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productId"></param>
 | 
					 | 
				
			||||||
        /// <param name="category"></param>
 | 
					 | 
				
			||||||
        /// <param name="purchaseCallback"></param>
 | 
					 | 
				
			||||||
        public static bool PurchaseById(string productId, string category = "", Action<string, bool> purchaseCallback = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var productName = GetProductInfoById(productId)?.Name ?? "";
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (CheckIAPReady() && !string.IsNullOrEmpty(productName))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Purchase(productName, category, purchaseCallback);
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 支付回调
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productName"></param>
 | 
					 | 
				
			||||||
        /// <param name="success"></param>
 | 
					 | 
				
			||||||
        private static void OnBuyEnd(string productName, bool success)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            InvokeOnPurchaseCallback?.Invoke(productName, success);
 | 
					 | 
				
			||||||
            Callbacks.IAP.InvokeOnPurchaseEnd(productName, success);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 支付开始
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productName"></param>
 | 
					 | 
				
			||||||
        private static void OnBuyStart(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Callbacks.IAP.InvokeOnPurchaseStart(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 支付失败
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productName"></param>
 | 
					 | 
				
			||||||
        /// <param name="reason"></param>
 | 
					 | 
				
			||||||
        private static void OnBuyFailed(string productName, string reason)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Callbacks.IAP.InvokeOnPurchaseFailed(productName, reason);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Restore Purchase
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 恢复购买
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="restoreCallback"></param>
 | 
					 | 
				
			||||||
        public static void Restore(Action<bool, string> restoreCallback = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if( restoreCallback != null) Callbacks.IAP.OnIAPRestored += restoreCallback;
 | 
					 | 
				
			||||||
            if (CheckIAPReady())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                GuruIAP.Instance.Restore();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        private static void OnRestored(bool success, string msg)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Callbacks.IAP.InvokeOnIAPRestored(success, msg); // 更新回复购买回调
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Receipt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 获取订单收据
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productId"></param>
 | 
					 | 
				
			||||||
        /// <param name="receipt"></param>
 | 
					 | 
				
			||||||
        /// <param name="appleProductIsRestored"></param>
 | 
					 | 
				
			||||||
        private static void OnGetReceipt(string productId, string receipt, bool appleProductIsRestored = false)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var productName = GetProductInfoById(productId)?.Name ?? "";
 | 
					 | 
				
			||||||
            if(!string.IsNullOrEmpty(productName))
 | 
					 | 
				
			||||||
                Model.AddReceipt(receipt, productName, productId, appleProductIsRestored);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Subscriptions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 订阅是否被取消
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productName"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static bool IsSubscriptionCancelled(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.IsSubscriptionCancelled(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 订阅是否可用
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productName"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static bool IsSubscriptionAvailable(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.IsSubscriptionAvailable(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 订阅是否过期
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="productName"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static bool IsSubscriptionExpired(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.IsSubscriptionExpired(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static bool IsSubscriptionFreeTrail(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.IsSubscriptionFreeTrail(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static bool IsSubscriptionAutoRenewing(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.IsSubscriptionAutoRenewing(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static bool IsSubscriptionIntroductoryPricePeriod(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.IsSubscriptionIntroductoryPricePeriod(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public DateTime GetSubscriptionExpireDate(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.GetSubscriptionExpireDate(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public DateTime GetSubscriptionPurchaseDate(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.GetSubscriptionPurchaseDate(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public DateTime GetSubscriptionCancelDate(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.GetSubscriptionCancelDate(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public TimeSpan GetSubscriptionRemainingTime(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.GetSubscriptionRemainingTime(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public TimeSpan GetSubscriptionIntroductoryPricePeriod(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.GetSubscriptionIntroductoryPricePeriod(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public TimeSpan GetSubscriptionFreeTrialPeriod(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.GetSubscriptionFreeTrialPeriod(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public string GetSubscriptionInfoJsonString(string productName)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return GuruIAP.Instance.GetSubscriptionInfoJsonString(productName);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,108 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public partial class GuruSDK
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        // UID
 | 
					 | 
				
			||||||
        public static string UID
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if(Model != null && !string.IsNullOrEmpty(Model.UserId)) 
 | 
					 | 
				
			||||||
                    return Model.UserId;
 | 
					 | 
				
			||||||
                return IPMConfig.IPM_UID;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public static string UUID => IPMConfig.IPM_UUID ?? "";
 | 
					 | 
				
			||||||
        public static string DeviceId => IPMConfig.IPM_DEVICE_ID ?? "";  // TODO: change it to _model member later.
 | 
					 | 
				
			||||||
        public static string PushToken => IPMConfig.IPM_PUSH_TOKEN ?? ""; // TODO: change it to _model member later.
 | 
					 | 
				
			||||||
        public static string AuthToken => IPMConfig.IPM_TOKEN ?? ""; // TODO: change it to _model member later.
 | 
					 | 
				
			||||||
        public static string SupportEmail => GuruSettings.SupportEmail ?? "";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static string StoreUrl
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                string url = "";
 | 
					 | 
				
			||||||
#if UNITY_EDITOR
 | 
					 | 
				
			||||||
                url = "https://test@com.guru.ai";
 | 
					 | 
				
			||||||
#elif UNITY_ANDROID
 | 
					 | 
				
			||||||
                url = GuruSettings?.AndroidStoreUrl ?? "";
 | 
					 | 
				
			||||||
#elif UNITY_IOS
 | 
					 | 
				
			||||||
                url = GuruSettings?.IOSStoreUrl ?? "";
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
                return url;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static string PrivacyUrl => GuruSettings.PriacyUrl ?? "";
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static string TermsUrl => GuruSettings.TermsUrl ?? "";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static string AppVersion => GuruAppVersion.version;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static string AppVersionCode => GuruAppVersion.code;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static string AppVersionString => GuruAppVersion.ToString();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static bool IsNewUser => IPMConfig.IPM_NEWUSER;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static string FirebaseId => IPMConfig.FIREBASE_ID;
 | 
					 | 
				
			||||||
        public static string IDFA => IPMConfig.ADJUST_IDFA;
 | 
					 | 
				
			||||||
        public static string AdjustId => IPMConfig.ADJUST_ID;
 | 
					 | 
				
			||||||
        public static string GSADID => IPMConfig.ADJUST_ADID;
 | 
					 | 
				
			||||||
        public static string CdnHost => _appServicesConfig?.CdnHost() ?? "";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static GuruAppVersion _appVersion;
 | 
					 | 
				
			||||||
        private static GuruAppVersion GuruAppVersion
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if(_appVersion == null) _appVersion = GuruAppVersion.Load();
 | 
					 | 
				
			||||||
                return _appVersion;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string _appBundleId;
 | 
					 | 
				
			||||||
        public static string AppBundleId => _appBundleId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 设置购买去广告道具的标志位
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="value"></param>
 | 
					 | 
				
			||||||
        public static void SetBuyNoAds(bool value = true)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Model.IsNoAds = value;
 | 
					 | 
				
			||||||
            ADService.Instance.IsBuyNoAds = value;
 | 
					 | 
				
			||||||
            if (value)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Analytics.SetIsIapUser(true);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 所有成功的主线关卡数量 (b_level)
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static int BLevel
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get => GuruSDKModel.Instance.BLevel;
 | 
					 | 
				
			||||||
            set => GuruSDKModel.Instance.BLevel = value;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 成功关卡总计数量 (b_play)
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static int BPlay
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get => GuruSDKModel.Instance.BPlay;
 | 
					 | 
				
			||||||
            set => GuruSDKModel.Instance.BPlay = value;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: ab7d65b5c8774a12924f3fe72a6e4a38
 | 
					 | 
				
			||||||
timeCreated: 1710154459
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,97 +0,0 @@
 | 
				
			||||||
using System;
 | 
					 | 
				
			||||||
using UnityEngine;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public partial class GuruSDK
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        #region System
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 打开页面
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="url"></param>
 | 
					 | 
				
			||||||
        public static void OpenURL(string url)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            GuruWebview.OpenPage(url);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 打开隐私协议页面
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static void OpenPrivacyPage()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(PrivacyUrl))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogE("PrivacyUrl is null"); 
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            OpenURL(PrivacyUrl);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 打开服务条款页面
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static void OpenTermsPage()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(TermsUrl))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogE("TermsUrl is null"); 
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            OpenURL(TermsUrl);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Android System
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if UNITY_ANDROID
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 获取 AndroidSDK 的系统版本号
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static int GetAndroidSystemVersion()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // sdkInt 是 Android SDK 的整数版本号,例如 Android 10 对应 29。
 | 
					 | 
				
			||||||
                // release 是 Android 版本的字符串表示,例如 "10"。
 | 
					 | 
				
			||||||
                using (AndroidJavaClass jc = new AndroidJavaClass("android.os.Build$VERSION"))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    int sdkInt = jc.GetStatic<int>("SDK_INT");
 | 
					 | 
				
			||||||
                    Debug.LogWarning($"[SDK] --- Android SDK Version:{sdkInt}");
 | 
					 | 
				
			||||||
                    return sdkInt;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception ex)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.LogError(ex);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Clear Data Cache
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 清除数据缓存
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static void ClearData()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Model.ClearData();
 | 
					 | 
				
			||||||
            GuruIAP.Instance.ClearData();
 | 
					 | 
				
			||||||
            PlayerPrefs.DeleteAll();
 | 
					 | 
				
			||||||
            PlayerPrefs.Save();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 80d35b4c8f0f4d23a7228fddfb06dd03
 | 
					 | 
				
			||||||
timeCreated: 1713238972
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,29 +0,0 @@
 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public partial class GuruSDK
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private ThreadHandler _threadHandler;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void InitThreadHandler()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _threadHandler = new ThreadHandler();
 | 
					 | 
				
			||||||
            RegisterUpdater(_threadHandler);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void AddActionToMainThread(Action action)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _threadHandler?.AddAction(action);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static void RunOnMainThread(Action action)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
           Instance.AddActionToMainThread(action);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 8a0605c6b4b64b37a0036b34cbadb9ca
 | 
					 | 
				
			||||||
timeCreated: 1712622534
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,843 +0,0 @@
 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using System.Collections;
 | 
					 | 
				
			||||||
    using System.Collections.Generic;
 | 
					 | 
				
			||||||
    using Debug = UnityEngine.Debug;
 | 
					 | 
				
			||||||
    using Guru.Network;
 | 
					 | 
				
			||||||
    using System.Linq;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public partial class GuruSDK: MonoBehaviour
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        // SDK_VERSION
 | 
					 | 
				
			||||||
        public const string Version = "1.1.0"; 
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Const
 | 
					 | 
				
			||||||
        private const string Tag = "[Guru]";
 | 
					 | 
				
			||||||
        public const string ServicesConfigKey = "guru_services";
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static GuruSDK _instance;
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 单利引用
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static GuruSDK Instance
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if(null == _instance)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _instance = CreateInstance();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return _instance;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private GuruSDKInitConfig _initConfig;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static GuruSDKInitConfig InitConfig => Instance._initConfig;
 | 
					 | 
				
			||||||
        private static GuruSDKModel Model => GuruSDKModel.Instance;
 | 
					 | 
				
			||||||
        private static GuruServicesConfig _appServicesConfig;
 | 
					 | 
				
			||||||
        private static GuruSettings _guruSettings;
 | 
					 | 
				
			||||||
        private static GuruSettings GuruSettings
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (_guruSettings == null) _guruSettings = GuruSettings.Instance;
 | 
					 | 
				
			||||||
                return _guruSettings;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static DateTime _initTime;
 | 
					 | 
				
			||||||
        private static bool _isDebugEnabled = false;
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Debug Mode
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static bool IsDebugMode
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
#if UNITY_EDITOR || DEBUG
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
                return _isDebugEnabled;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 初始化成功标志位
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static bool IsInitialSuccess { get; private set; } = false;
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Firebase 就绪标志位
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static bool IsFirebaseReady { get; private set; } = false;
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 服务就绪标志位
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        public static bool IsServiceReady { get; private set; } = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private Firebase.Auth.FirebaseUser _firebaseUser;
 | 
					 | 
				
			||||||
        [Obsolete("获取 FirebaseUser 的属性接口即将废弃,请改用 <GuruSDK.Callbacks.SDK.OnFirebaseUserAuthResult += OnMyGetFirebaseUserCallback> 来异步获取该属性")]
 | 
					 | 
				
			||||||
        public static Firebase.Auth.FirebaseUser FirebaseUser => Instance?._firebaseUser ?? null; 
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region 初始化
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static GuruSDK CreateInstance()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var go = new GameObject(nameof(GuruSDK));
 | 
					 | 
				
			||||||
            DontDestroyOnLoad(go);
 | 
					 | 
				
			||||||
            _instance = go.AddComponent<GuruSDK>();
 | 
					 | 
				
			||||||
            return _instance;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // TODO : 下个版本需要将 整个 GuruSDK 做功能性的拆分
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static void Init(Action<bool> onComplete)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Init(GuruSDKInitConfig.Builder().Build(), onComplete);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        public static void Init(GuruSDKInitConfig config, Action<bool> onComplete)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _initTime = DateTime.UtcNow;
 | 
					 | 
				
			||||||
            // ----- First Open Time -----
 | 
					 | 
				
			||||||
            // SetFirstOpenTime(GetFirstOpenTime());  // FirstOpenTime 
 | 
					 | 
				
			||||||
            LogI($"#1 ---- Guru SDK [{Version}] ----\n{config}");
 | 
					 | 
				
			||||||
            Instance.StartWithConfig(config, onComplete);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 启动SDK
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="config"></param>
 | 
					 | 
				
			||||||
        /// <param name="onComplete"></param>
 | 
					 | 
				
			||||||
        private void StartWithConfig(GuruSDKInitConfig config, Action<bool> onComplete)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            IsInitialSuccess = false;
 | 
					 | 
				
			||||||
            _initConfig = config;
 | 
					 | 
				
			||||||
            _isDebugEnabled = config.DebugMode;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (config.EnableDebugLogEvent) Analytics.EnableDebugAnalytics = true; // 允许 Debug 模式下打点
 | 
					 | 
				
			||||||
            if (!config.AutoNotificationPermission) FirebaseUtil.SetAutoFetchFcmToken(false); // 不允许自动启动获取 FCM Token
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            InitUpdaters(); // Updaters
 | 
					 | 
				
			||||||
            InitThreadHandler(); // 初始化线程处理器
 | 
					 | 
				
			||||||
            InitServices(); // 初始化所有的服务
 | 
					 | 
				
			||||||
            InitNetworkMonitor(); // 网络状态
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            onComplete?.Invoke(true);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void InitServices()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            //---------- Start Analytics ------------
 | 
					 | 
				
			||||||
            LogI($"#1.1 ---- Init Analytics ----");
 | 
					 | 
				
			||||||
            // 初始化打点类
 | 
					 | 
				
			||||||
            Analytics.Init(); 
 | 
					 | 
				
			||||||
            // 从 Model 中注入打点属性初始值
 | 
					 | 
				
			||||||
            Analytics.SetFirstOpenTime(IPMConfig.FIRST_OPEN_TIME);
 | 
					 | 
				
			||||||
            Analytics.SetIsIapUser(Model.IsIapUser);
 | 
					 | 
				
			||||||
            // Analytics.SetBLevel(Model.BLevel);
 | 
					 | 
				
			||||||
            // Analytics.SetBPlay(Model.BPlay);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            //---- Start All tools ----
 | 
					 | 
				
			||||||
            LogI($"#2 --- InitFirebase ---");
 | 
					 | 
				
			||||||
            //---------- Start Firebase ------------
 | 
					 | 
				
			||||||
            StartFirebaseService();
 | 
					 | 
				
			||||||
            LogI($"#2.1 --- InitFacebook ---");
 | 
					 | 
				
			||||||
            //---------- Start Facebook ------------
 | 
					 | 
				
			||||||
            FBService.Instance.StartService(Analytics.OnFBInitComplete);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            IsInitialSuccess = true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 注入云控参数基础数据
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        private string LoadDefaultGuruServiceJson()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // 加载本地 Services 配置值
 | 
					 | 
				
			||||||
            var txtAsset = Resources.Load<TextAsset>(ServicesConfigKey);
 | 
					 | 
				
			||||||
            if (txtAsset != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return txtAsset.text;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return "";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 拉取云控参数完成
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="success"></param>
 | 
					 | 
				
			||||||
        private void OnFetchRemoteCallback(bool success)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            LogI($"#6 --- Remote fetch complete: {success} ---");
 | 
					 | 
				
			||||||
            ABTestManager.Init(); // 启动AB测试解析器
 | 
					 | 
				
			||||||
            Callbacks.Remote.InvokeOnRemoteFetchComplete(success);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void Update()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            UpdateAllUpdates(); // 驱动所有的更新器
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region App Remote Update
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Apply Cloud guru-service configs for sdk assets
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private void InitAllGuruServices()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // -------- Init Analytics ---------
 | 
					 | 
				
			||||||
            SetSDKEventPriority();
 | 
					 | 
				
			||||||
            // -------- Init Notification -----------
 | 
					 | 
				
			||||||
            InitNotiPermission();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            bool useKeywords = false;
 | 
					 | 
				
			||||||
            bool useIAP = _initConfig.IAPEnabled;
 | 
					 | 
				
			||||||
            bool appleReview = false;
 | 
					 | 
				
			||||||
            // bool enableAnaErrorLog = false;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            //----------- Set GuruServices ----------------
 | 
					 | 
				
			||||||
            var services = GetRemoteServicesConfig();
 | 
					 | 
				
			||||||
            if (services != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _appServicesConfig = services;
 | 
					 | 
				
			||||||
                useKeywords = _appServicesConfig.KeywordsEnabled();
 | 
					 | 
				
			||||||
                // 使用初始化配置中的 IAPEnable来联合限定, 如果本地写死关闭则不走云控开启
 | 
					 | 
				
			||||||
                useIAP = _initConfig.IAPEnabled && _appServicesConfig.IsIAPEnabled(); 
 | 
					 | 
				
			||||||
                // enableAnaErrorLog = _appServicesConfig.EnableAnaErrorLog();
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                Try(() =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    LogI($"#4.1 --- Start apply services ---");
 | 
					 | 
				
			||||||
                    //----------------------------------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // 自打点设置错误上报
 | 
					 | 
				
			||||||
                    // if(enableAnaErrorLog) GuruAnalytics.EnableErrorLog = true;
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                    // adjust 事件设置
 | 
					 | 
				
			||||||
                    if (null != _appServicesConfig.adjust_settings && null != GuruSettings)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        // 更新 Adjust Tokens
 | 
					 | 
				
			||||||
                        GuruSettings.UpdateAdjustTokens(
 | 
					 | 
				
			||||||
                            _appServicesConfig.adjust_settings.AndroidToken(),
 | 
					 | 
				
			||||||
                            _appServicesConfig.adjust_settings.iOSToken());
 | 
					 | 
				
			||||||
                        // 更新 Adjust Events
 | 
					 | 
				
			||||||
                        GuruSettings.UpdateAdjustEvents(_appServicesConfig.adjust_settings.events);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                    LogI($"#4.2 --- Start GuruSettings ---");
 | 
					 | 
				
			||||||
                    // GuruSettings 设置
 | 
					 | 
				
			||||||
                    if (null != _appServicesConfig.app_settings)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        if (_appServicesConfig.Tch02Value() > 0)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            Analytics.EnableTch02Event = true;
 | 
					 | 
				
			||||||
                            Analytics.SetTch02TargetValue(_appServicesConfig.Tch02Value());
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                     
 | 
					 | 
				
			||||||
                        // 设置获取设备 UUID 的方法
 | 
					 | 
				
			||||||
                        if (_appServicesConfig.UseUUID())
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            IPMConfig.UsingUUID = true; // 开始使用 UUID 作为 DeviceID 标识
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if UNITY_IOS 
 | 
					 | 
				
			||||||
                        // 苹果审核标志位
 | 
					 | 
				
			||||||
                        appleReview = _appServicesConfig.IsAppReview();
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                        if (null !=  GuruSettings)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            // 更新和升级 GuruSettings 对应的值
 | 
					 | 
				
			||||||
                            GuruSettings.UpdateAppSettings(
 | 
					 | 
				
			||||||
                                _appServicesConfig.app_settings.bundle_id,
 | 
					 | 
				
			||||||
                                _appServicesConfig.fb_settings?.fb_app_id ?? "",
 | 
					 | 
				
			||||||
                                _appServicesConfig.app_settings.support_email,
 | 
					 | 
				
			||||||
                                _appServicesConfig.app_settings.privacy_url,
 | 
					 | 
				
			||||||
                                _appServicesConfig.app_settings.terms_url,
 | 
					 | 
				
			||||||
                                _appServicesConfig.app_settings.android_store,
 | 
					 | 
				
			||||||
                                _appServicesConfig.app_settings.ios_store, 
 | 
					 | 
				
			||||||
                                _appServicesConfig.parameters?.using_uuid ?? false,
 | 
					 | 
				
			||||||
                                _appServicesConfig.parameters?.cdn_host ?? "");
 | 
					 | 
				
			||||||
                            
 | 
					 | 
				
			||||||
                            _appBundleId = _appServicesConfig.app_settings.bundle_id; // 配置预设的 BundleId
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    //---------------------------------
 | 
					 | 
				
			||||||
                }, ex =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Debug.LogError($"--- ERROR on apply services: {ex.Message}");
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
          
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            //----------- Set IAP ----------------
 | 
					 | 
				
			||||||
            if (useIAP)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // InitIAP(_initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
 | 
					 | 
				
			||||||
                Try(() =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    LogI($"#4.3 --- Start IAP ---");
 | 
					 | 
				
			||||||
                    if (_initConfig.GoogleKeys == null || _initConfig.AppleRootCerts == null)
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        LogEx("[IAP] GoogleKeys is null when using IAPService! Integration failed. App will Exit");
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                    InitIAP(UID, _initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
 | 
					 | 
				
			||||||
                }, ex =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Debug.LogError($"--- ERROR on useIAP: {ex.Message}");
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            //----------- Set Keywords ----------------
 | 
					 | 
				
			||||||
            if (useKeywords)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // KeywordsManager.Install(Model.IsIAPUser, Model.SuccessLevelId); // 启动Keyword管理器
 | 
					 | 
				
			||||||
                Try(() =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    LogI($"#4.4 --- Start Keywords ---");
 | 
					 | 
				
			||||||
                    KeywordsManager.Install(Model.IsIapUser, Model.BLevel); // 启动Keyword管理器
 | 
					 | 
				
			||||||
                }, ex =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Debug.LogError($"--- ERROR on Keywords: {ex.Message}");
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if UNITY_IOS
 | 
					 | 
				
			||||||
            if (appleReview)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程
 | 
					 | 
				
			||||||
                Try(() =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    LogI($"#4.5 ---  StartAppleReviewFlow ---");
 | 
					 | 
				
			||||||
                    StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程
 | 
					 | 
				
			||||||
                }, ex =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Debug.LogError($"--- ERROR on StartAppleReviewFlow: {ex.Message}");
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
            //----------- Set Consent ----------------
 | 
					 | 
				
			||||||
            if (!InitConfig.UseCustomConsent && !appleReview)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogI($"#4.6 --- Start Consent Flow ---");
 | 
					 | 
				
			||||||
                Try(StartConsentFlow, ex =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Debug.LogError($"--- ERROR on StartConsentFlow: {ex.Message}");
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
#if UNITY_ANDROID
 | 
					 | 
				
			||||||
            LogI($"#5.1 --- Android StartAndroidDebug Cmd lines---");
 | 
					 | 
				
			||||||
            // Android 命令行调试
 | 
					 | 
				
			||||||
            StartAndroidDebugCmds();           
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            IsServiceReady = true;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // 中台服务初始化结束
 | 
					 | 
				
			||||||
            Callbacks.SDK.InvokeOnGuruServiceReady();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Get the guru-service cloud config value;
 | 
					 | 
				
			||||||
        /// User can fetch the cloud guru-service config by using Custom Service Key
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        private GuruServicesConfig GetRemoteServicesConfig()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            string defaultJson = GetRemoteString(ServicesConfigKey);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            bool useCustomKey = false;
 | 
					 | 
				
			||||||
            string key = ServicesConfigKey;
 | 
					 | 
				
			||||||
            if (!string.IsNullOrEmpty(_initConfig.CustomServiceKey))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                key = _initConfig.CustomServiceKey;
 | 
					 | 
				
			||||||
                useCustomKey = true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            var json = GetRemoteString(key); // Cloud cached data
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(json) && useCustomKey && !string.IsNullOrEmpty(defaultJson))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // No remote data fetched from cloud, should use default values
 | 
					 | 
				
			||||||
                json = defaultJson;
 | 
					 | 
				
			||||||
                Debug.Log($"{Tag} --- No remote data found with: {key}  -> Using default key {ServicesConfigKey} and local data!!!");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!string.IsNullOrEmpty(json))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return JsonParser.ToObject<GuruServicesConfig>(json);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void Try(Action method, Action<Exception> onException = null, Action onFinal = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (method == null) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                method.Invoke();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception ex)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogEx(ex);
 | 
					 | 
				
			||||||
                // ignored
 | 
					 | 
				
			||||||
                onException?.Invoke(ex);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            finally
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                // Finally
 | 
					 | 
				
			||||||
                onFinal?.Invoke();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Apple 审核流程逻辑
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if UNITY_IOS
 | 
					 | 
				
			||||||
        private void StartAppleReviewFlow()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            CheckAttStatus();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #region Logging
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void LogI(object message)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Debug.Log($"{Tag} {message}");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void LogW(object message)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Debug.LogWarning($"{Tag} {message}");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void LogE(object message)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Debug.LogError($"{Tag} {message}");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void LogEx(string message)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            LogEx( new Exception($"{Tag} {message}"));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void LogEx(Exception e)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Debug.LogException(e);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 上报崩溃信息
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="message"></param>
 | 
					 | 
				
			||||||
        public static void Report(string message)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Analytics.LogCrashlytics(message, false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 上报异常
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="message"></param>
 | 
					 | 
				
			||||||
        public static void ReportException(string message)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Analytics.LogCrashlytics(message);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 上报异常 Exception
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="ex"></param>
 | 
					 | 
				
			||||||
        public static void ReportException(Exception ex)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Analytics.LogCrashlytics(ex);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region 生命周期
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 暂停时处理
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="paused"></param>
 | 
					 | 
				
			||||||
        private void OnAppPauseHandler(bool paused)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if(paused) Model.Save(true); // 强制保存数据
 | 
					 | 
				
			||||||
            Callbacks.App.InvokeOnAppPaused(paused);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private void OnApplicationPause(bool paused)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            OnAppPauseHandler(paused);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void OnApplicationFocus(bool hasFocus)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            OnAppPauseHandler(!hasFocus);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void OnApplicationQuit()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Model.Save(true);
 | 
					 | 
				
			||||||
            Callbacks.App.InvokeOnAppQuit();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region 延迟处理
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 启动协程
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="enumerator"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static Coroutine DoCoroutine(IEnumerator enumerator)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return Instance != null ? Instance.StartCoroutine(enumerator) : null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static void KillCoroutine(Coroutine coroutine)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if(coroutine != null)
 | 
					 | 
				
			||||||
                Instance.StopCoroutine(coroutine);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 延时执行
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="seconds"></param>
 | 
					 | 
				
			||||||
        /// <param name="callback"></param>
 | 
					 | 
				
			||||||
        public static Coroutine Delay(float seconds, Action callback)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return DoCoroutine(Instance.OnDelayCall(seconds, callback));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private IEnumerator OnDelayCall(float delay, Action callback)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (delay > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                yield return new WaitForSeconds(delay);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                yield return null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            callback?.Invoke();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region 帧更新 Updater
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private List<IUpdater> _updaterRunningList;
 | 
					 | 
				
			||||||
        private List<IUpdater> _updaterRemoveList;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void InitUpdaters()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _updaterRunningList = new List<IUpdater>(20);
 | 
					 | 
				
			||||||
            _updaterRemoveList = new List<IUpdater>(20);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void UpdateAllUpdates()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            int i = 0;
 | 
					 | 
				
			||||||
            // ---- Updater Trigger ----
 | 
					 | 
				
			||||||
            if (_updaterRunningList.Count > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                i = 0;
 | 
					 | 
				
			||||||
                while (i < _updaterRunningList.Count)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var updater = _updaterRunningList[i];
 | 
					 | 
				
			||||||
                    if (updater != null) 
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        if (updater.State == UpdaterState.Running)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            updater.OnUpdate();
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        else if(updater.State == UpdaterState.Kill)
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            _updaterRemoveList.Add(updater);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        _updaterRunningList.RemoveAt(i);
 | 
					 | 
				
			||||||
                        i--;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    i++;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (_updaterRemoveList.Count > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                i = 0;
 | 
					 | 
				
			||||||
                while (i < _updaterRemoveList.Count)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    RemoveUpdater(_updaterRemoveList[i]);
 | 
					 | 
				
			||||||
                    i++;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                _updaterRemoveList.Clear();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 注册帧更新器
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="updater"></param>
 | 
					 | 
				
			||||||
        public static void RegisterUpdater(IUpdater updater)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Instance.AddUpdater(updater);
 | 
					 | 
				
			||||||
            updater.Start();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void AddUpdater(IUpdater updater)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_updaterRunningList == null) _updaterRunningList = new List<IUpdater>(20);
 | 
					 | 
				
			||||||
            _updaterRunningList.Add(updater);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void RemoveUpdater(IUpdater updater)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (_updaterRunningList != null && updater != null)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                _updaterRunningList.Remove(updater);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region 中台推送管理
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static int _messageRetry = 0;
 | 
					 | 
				
			||||||
        public static void SetPushNotificationEnabled(bool enabled)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            DeviceInfoUploadRequest request = (DeviceInfoUploadRequest) new DeviceInfoUploadRequest()
 | 
					 | 
				
			||||||
                .SetRetryTimes(1)
 | 
					 | 
				
			||||||
                .SetSuccessCallBack(() =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _messageRetry = 0;
 | 
					 | 
				
			||||||
                    Debug.Log($"[SDK] --- Set Push Enabled: {enabled} success");
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                .SetFailCallBack(() =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    double retryDelay = Math.Pow(2, _messageRetry);
 | 
					 | 
				
			||||||
                    _messageRetry++;
 | 
					 | 
				
			||||||
                    CoroutineHelper.Instance.StartDelayed((float)retryDelay, ()=> SetPushNotificationEnabled(enabled));
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (request == null) return;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            request.SetPushEnabled(enabled);
 | 
					 | 
				
			||||||
            request.Send();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Deeplink
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 添加回调链接
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="deeplink"></param>
 | 
					 | 
				
			||||||
        private void OnDeeplinkCallback(string deeplink)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
           Callbacks.SDK.InvokeDeeplinkCallback(deeplink); // 尝试调用回调
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region 网络状态上报
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private NetworkStatusMonitor _networkStatusMonitor;
 | 
					 | 
				
			||||||
        private string _lastNetworkStatus;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private void InitNetworkMonitor()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _networkStatusMonitor = new NetworkStatusMonitor(Analytics.SetNetworkStatus, 
 | 
					 | 
				
			||||||
                lastStatus =>
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                LogEvent("guru_offline", new Dictionary<string, dynamic>()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    ["from"] = lastStatus
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 获取当前的网络状态
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        private string GetNetworkStatus() => _networkStatusMonitor.GetNetworkStatus();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region Firebase 服务
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 启动 Firebase 服务
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private void StartFirebaseService()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            FirebaseUtil.Init(OnFirebaseDepsCheckResult, 
 | 
					 | 
				
			||||||
                OnGetFirebaseId, 
 | 
					 | 
				
			||||||
                OnGetGuruUID, 
 | 
					 | 
				
			||||||
                OnFirebaseLoginResult); // 确保所有的逻辑提前被调用到
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void OnGetGuruUID(bool success)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (success)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Model.UserId = IPMConfig.IPM_UID;
 | 
					 | 
				
			||||||
                if (GuruIAP.Instance != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    GuruIAP.Instance.SetUID(UID);
 | 
					 | 
				
			||||||
                    GuruIAP.Instance.SetUUID(UUID);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                // 自打点设置用户 ID
 | 
					 | 
				
			||||||
                Analytics.SetUid(UID);
 | 
					 | 
				
			||||||
                // Crashlytics 设置 uid
 | 
					 | 
				
			||||||
                CrashlyticsAgent.SetUserId(UID);
 | 
					 | 
				
			||||||
                // 上报所有的事件
 | 
					 | 
				
			||||||
                Analytics.ShouldFlushGuruEvents();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            Callbacks.SDK.InvokeOnGuruUserAuthResult(success);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private void OnGetFirebaseId(string fid)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // 初始化 Adjust 服务
 | 
					 | 
				
			||||||
            InitAdjustService(fid, InitConfig.OnAdjustDeeplinkCallback);
 | 
					 | 
				
			||||||
            // 初始化自打点
 | 
					 | 
				
			||||||
            Analytics.InitGuruAnalyticService(fid);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            //---------- Event SDK Info ------------
 | 
					 | 
				
			||||||
            LogI($"#6.0 --- SDK is ready, report Info ---");
 | 
					 | 
				
			||||||
            LogSDKInfo((DateTime.UtcNow - _initTime).TotalSeconds);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // TODO: 需要之后用宏隔离应用和实现
 | 
					 | 
				
			||||||
        // Auth 登录认证
 | 
					 | 
				
			||||||
        private void OnFirebaseLoginResult(bool success, Firebase.Auth.FirebaseUser firebaseUser)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _firebaseUser = firebaseUser;
 | 
					 | 
				
			||||||
            Callbacks.SDK.InvokeOnFirebaseAuthResult(success, firebaseUser);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 开始各种组件初始化
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private void OnFirebaseDepsCheckResult(bool success)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            LogI($"#3 --- On FirebaseDeps: {success} ---");
 | 
					 | 
				
			||||||
            IsFirebaseReady = success;
 | 
					 | 
				
			||||||
            Callbacks.SDK.InvokeOnFirebaseReady(success);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Analytics.OnFirebaseInitCompleted();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            LogI($"#3.5 --- Call InitRemoteConfig ---");
 | 
					 | 
				
			||||||
            // 开始Remote Manager初始化 
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            var defaultGuruServiceJson = LoadDefaultGuruServiceJson();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var dict = _initConfig.DefaultRemoteData.ToDictionary(
 | 
					 | 
				
			||||||
                entry => entry.Key,
 | 
					 | 
				
			||||||
                entry => entry.Value);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (!string.IsNullOrEmpty(defaultGuruServiceJson))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                dict[ServicesConfigKey] = defaultGuruServiceJson;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            RemoteConfigManager.Init(dict);
 | 
					 | 
				
			||||||
            RemoteConfigManager.OnFetchCompleted += OnFetchRemoteCallback;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            LogI($"#4 --- Apply remote services config ---");
 | 
					 | 
				
			||||||
            // 根据缓存的云控配置来初始化参数
 | 
					 | 
				
			||||||
            InitAllGuruServices();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
        		
 | 
					 | 
				
			||||||
        #region Adjust服务
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// 启动 Adjust 服务
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        private static void InitAdjustService(string firebaseId, Action<string> onDeeplinkCallback = null)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            // 启动 AdjustService
 | 
					 | 
				
			||||||
            string appToken = GuruSettings.Instance.AdjustSetting?.GetAppToken() ?? "";
 | 
					 | 
				
			||||||
            string fbAppId = GuruSettings.Instance.IPMSetting.FacebookAppId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // if (!string.IsNullOrEmpty(IPMConfig.ADJUST_ID))
 | 
					 | 
				
			||||||
            //     Analytics.SetAdjustId(IPMConfig.ADJUST_ID); // 二次启动后,若有值则立即上报属性
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            AdjustService.Instance.Start(appToken, fbAppId, firebaseId, DeviceId,
 | 
					 | 
				
			||||||
                OnAdjustInitComplete, onDeeplinkCallback ,OnGetGoogleAdId );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Adjust 初始化结束
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="adjustId"></param>
 | 
					 | 
				
			||||||
        /// <param name="idfv"></param>
 | 
					 | 
				
			||||||
        /// <param name="idfa"></param>
 | 
					 | 
				
			||||||
        private static void OnAdjustInitComplete(string adjustId, string idfv, string idfa)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Debug.Log($"{Tag} --- OnAdjustInitComplete:  adjustId:{adjustId}  idfv:{idfv}  idfa:{idfa}");
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // 获取 ADID 
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(adjustId)) adjustId = "not_set";
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(idfv)) idfv = "not_set";
 | 
					 | 
				
			||||||
            if (string.IsNullOrEmpty(idfa)) idfa = "not_set";
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            IPMConfig.ADJUST_ID = adjustId;
 | 
					 | 
				
			||||||
            IPMConfig.ADJUST_IDFV = idfv;
 | 
					 | 
				
			||||||
            IPMConfig.ADJUST_IDFA = idfa;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            Analytics.SetAdjustId(adjustId);
 | 
					 | 
				
			||||||
            Analytics.SetIDFV(idfv);
 | 
					 | 
				
			||||||
            Analytics.SetIDFA(idfa);
 | 
					 | 
				
			||||||
            Analytics.OnAdjustInitComplete();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static void OnGetGoogleAdId(string googleAdId)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Debug.Log($"{Tag} --- OnGetGoogleAdId: {googleAdId}");
 | 
					 | 
				
			||||||
            // IPMConfig.ADJUST_GOOGLE_ADID = googleAdId;
 | 
					 | 
				
			||||||
            Analytics.SetGoogleAdId(googleAdId);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
       
 | 
					 | 
				
			||||||
        #endregion
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 45ff6fac409a4698ada70e0d8d5e8269
 | 
					 | 
				
			||||||
timeCreated: 1714353187
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,67 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Android 系统属性获取器
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public class AndroidSystemPropertiesHelper
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        private static string _appBundleId;
 | 
					 | 
				
			||||||
        public static string AppBundleId
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get => _appBundleId;
 | 
					 | 
				
			||||||
            set => _appBundleId = value;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Get the system property value by key
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="key"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static string Get(string key)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
#if UNITY_ANDROID
 | 
					 | 
				
			||||||
            return GetPropValue(key);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
            return "";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
#if UNITY_ANDROID
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static AndroidJavaClass _systemPropsCls;
 | 
					 | 
				
			||||||
        private const string SYSTEM_PROPS_CLASS = "android.os.SystemProperties";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private static string GetPropValue(string key)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (_systemPropsCls == null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    _systemPropsCls = new AndroidJavaClass(SYSTEM_PROPS_CLASS);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (_systemPropsCls != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    return _systemPropsCls.CallStatic<string>("get", key);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception e)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.LogException(e);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return "";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 41272ed6104b4d80be5322135a811186
 | 
					 | 
				
			||||||
timeCreated: 1714353340
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 75760985db114e44bfcd901ed50af96c
 | 
					 | 
				
			||||||
timeCreated: 1712580213
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,23 +0,0 @@
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    "name": "GuruSDK.Tests",
 | 
					 | 
				
			||||||
    "rootNamespace": "Guru.Tests",
 | 
					 | 
				
			||||||
    "references": [
 | 
					 | 
				
			||||||
        "UnityEngine.TestRunner",
 | 
					 | 
				
			||||||
        "UnityEditor.TestRunner",
 | 
					 | 
				
			||||||
        "Guru.Runtime",
 | 
					 | 
				
			||||||
        "GuruSDK"
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    "includePlatforms": [],
 | 
					 | 
				
			||||||
    "excludePlatforms": [],
 | 
					 | 
				
			||||||
    "allowUnsafeCode": false,
 | 
					 | 
				
			||||||
    "overrideReferences": true,
 | 
					 | 
				
			||||||
    "precompiledReferences": [
 | 
					 | 
				
			||||||
        "nunit.framework.dll"
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    "autoReferenced": false,
 | 
					 | 
				
			||||||
    "defineConstraints": [
 | 
					 | 
				
			||||||
        "UNITY_INCLUDE_TESTS"
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    "versionDefines": [],
 | 
					 | 
				
			||||||
    "noEngineReferences": false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,7 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 9eaf5abb3054d452ba2776169a31aa91
 | 
					 | 
				
			||||||
AssemblyDefinitionImporter:
 | 
					 | 
				
			||||||
  externalObjects: {}
 | 
					 | 
				
			||||||
  userData: 
 | 
					 | 
				
			||||||
  assetBundleName: 
 | 
					 | 
				
			||||||
  assetBundleVariant: 
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,51 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru.Tests
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using UnityEditor;
 | 
					 | 
				
			||||||
    using NUnit.Framework;
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public class Test_IAP
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Test]
 | 
					 | 
				
			||||||
        public void Test__AppleOrders()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var model = IAPModel.Load();
 | 
					 | 
				
			||||||
            int level = 1;
 | 
					 | 
				
			||||||
            int orderType = 0;
 | 
					 | 
				
			||||||
            for (int i = 0; i < 5; i++)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                model.AddAppleOrder(new AppleOrderData(orderType, 
 | 
					 | 
				
			||||||
                    $"i.iap.test.icon_{i}", 
 | 
					 | 
				
			||||||
                    $"receipt_{i}", 
 | 
					 | 
				
			||||||
                    $"order_id_{i}", 
 | 
					 | 
				
			||||||
                    DateTime.Now.ToString("g"), 
 | 
					 | 
				
			||||||
                    level, 
 | 
					 | 
				
			||||||
                    "RMB", 6.99d, "Store"));
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                level++;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (model.HasUnreportedAppleOrder)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                int i = 0;
 | 
					 | 
				
			||||||
                while (model.appleOrders.Count > 0 
 | 
					 | 
				
			||||||
                       && i < model.appleOrders.Count)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var o = model.appleOrders[i];
 | 
					 | 
				
			||||||
                    model.RemoveAppleOrder(o);
 | 
					 | 
				
			||||||
                    i++;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 7181a5508c604c439d3f4585ff49a5e0
 | 
					 | 
				
			||||||
timeCreated: 1712580254
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,49 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Guru.Tests
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    using NUnit.Framework;
 | 
					 | 
				
			||||||
    using System.Threading;
 | 
					 | 
				
			||||||
    using UnityEngine;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    public class Test_Threading
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private int TestCount
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            get => PlayerPrefs.GetInt(nameof(TestCount), 0);
 | 
					 | 
				
			||||||
            set => PlayerPrefs.SetInt(nameof(TestCount), value);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        [Test]
 | 
					 | 
				
			||||||
        public void Test_ThreadingCall()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            GuruSDK.Init(success =>
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                GuruSDK.Delay(0.1f, () =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    CallThreading();
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void CallThreading()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Debug.Log($"--------- CallThreading -------------");
 | 
					 | 
				
			||||||
            var t = new Thread(() =>
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                Debug.Log($"--------- Thread Start -------------");
 | 
					 | 
				
			||||||
                Thread.Sleep(2000);
 | 
					 | 
				
			||||||
                GuruSDK.RunOnMainThread(() =>
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    TestCount++;
 | 
					 | 
				
			||||||
                    Debug.Log($">>>>> CallThreading: {TestCount}");
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            t.Start();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
fileFormatVersion: 2
 | 
					 | 
				
			||||||
guid: 67d9cfd9949c48f3a8f6e6821b733ff1
 | 
					 | 
				
			||||||
timeCreated: 1712649738
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,11 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "name": "com.guru.unity.sdk",
 | 
					  "name": "com.guru.unity.sdk",
 | 
				
			||||||
  "displayName": "Guru SDK",
 | 
					  "displayName": "Guru SDK",
 | 
				
			||||||
  "version": "1.1.0",
 | 
					  "version": "1.0.0",
 | 
				
			||||||
  "description": "Guru SDK for unity project",
 | 
					  "description": "Guru SDK for unity project",
 | 
				
			||||||
  "unity": "2020.3",
 | 
					  "unity": "2020.3",
 | 
				
			||||||
  "author":{
 | 
					  "author":{
 | 
				
			||||||
    "name": "Guru Game"
 | 
					    "name": "Guru Fungames Studio"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "license": "MIT",
 | 
					  "license": "MIT",
 | 
				
			||||||
  "category": "Game,Tool,Development",
 | 
					  "category": "Game,Tool,Development",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue