Compare commits
226 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
58cc5a3b46 | |
|
|
1ef9e3ccec | |
|
|
88efffa89f | |
|
|
018fb2c35b | |
|
|
0870e71cf9 | |
|
|
15785b92a2 | |
|
|
a67ba09408 | |
|
|
6dbbd27eb7 | |
|
|
643bb23939 | |
|
|
b0670ccafc | |
|
|
7214c447a6 | |
|
|
49774c3281 | |
|
|
c001fe09ac | |
|
|
a063c4866d | |
|
|
3062467e84 | |
|
|
d4f5342e7f | |
|
|
f099a99c95 | |
|
|
6b95c2ab2f | |
|
|
06792d098f | |
|
|
af1ec1e04f | |
|
|
2494bd2748 | |
|
|
5c3be01eb3 | |
|
|
149bc438ac | |
|
|
e814324e98 | |
|
|
46cd1717fb | |
|
|
aec21f3789 | |
|
|
cc65e9173c | |
|
|
2bde58e27e | |
|
|
b3c541cb84 | |
|
|
ceeeeaf15f | |
|
|
257f9796a5 | |
|
|
fd01939a5f | |
|
|
48c5235335 | |
|
|
42c7302ea3 | |
|
|
4da06c8f96 | |
|
|
ecf615598c | |
|
|
0a2fca05ab | |
|
|
555638e31e | |
|
|
3555b70ea7 | |
|
|
314aaaa88b | |
|
|
4246251295 | |
|
|
1e59e5efa6 | |
|
|
4721e027b2 | |
|
|
b9291e7885 | |
|
|
3050b67a31 | |
|
|
9f0ba03957 | |
|
|
3de35b69e5 | |
|
|
25f4d7e588 | |
|
|
1841edcbf3 | |
|
|
957f60e797 | |
|
|
2fc6faf831 | |
|
|
4d86931605 | |
|
|
804618b12b | |
|
|
f85eb8d2bb | |
|
|
31991a780b | |
|
|
331a845f02 | |
|
|
d8967001bf | |
|
|
f33fc2c764 | |
|
|
9d168cdd46 | |
|
|
6e5ede3958 | |
|
|
2724683377 | |
|
|
d89de67e44 | |
|
|
3be05511e4 | |
|
|
6fc0c24823 | |
|
|
52b6af9fd0 | |
|
|
d574ec5757 | |
|
|
855cc61936 | |
|
|
bcaed876f7 | |
|
|
42966759b3 | |
|
|
4a39747d3d | |
|
|
d51759bbd1 | |
|
|
68e90a4ed9 | |
|
|
3d8bc1c642 | |
|
|
338c886ef6 | |
|
|
fa3ec5adef | |
|
|
cd7074f490 | |
|
|
9d907463ee | |
|
|
86742abe46 | |
|
|
5306922cca | |
|
|
67000cbc63 | |
|
|
8acd6faf94 | |
|
|
1b97f8ec03 | |
|
|
529e2ddeda | |
|
|
68161b82c3 | |
|
|
0d06131677 | |
|
|
051baed476 | |
|
|
c3403a335f | |
|
|
2d857fc6a6 | |
|
|
31cb08b6df | |
|
|
d70f9c3566 | |
|
|
8b5f42706e | |
|
|
0c4d6b31e9 | |
|
|
bc62aec38e | |
|
|
2ea2db79c8 | |
|
|
d6ff9736d9 | |
|
|
8932d6893f | |
|
|
f2949347df | |
|
|
c2be368962 | |
|
|
8d1afddcd5 | |
|
|
5b1097464d | |
|
|
3fdd02c7b6 | |
|
|
9d7fd12cf3 | |
|
|
093e309e52 | |
|
|
aad0fd6d76 | |
|
|
b5a64eac4e | |
|
|
c6d6a1f5d8 | |
|
|
fe36a57a81 | |
|
|
6779c6ef73 | |
|
|
049de5acd3 | |
|
|
bc002d1501 | |
|
|
5494e6c3f9 | |
|
|
7fe982f5bc | |
|
|
852853629e | |
|
|
23a2616b95 | |
|
|
777ac87b18 | |
|
|
e4f51978d1 | |
|
|
4db35e10fc | |
|
|
a6f0d8d884 | |
|
|
5d7e32a9f1 | |
|
|
8e005c9237 | |
|
|
20938c5678 | |
|
|
7883d1f0e2 | |
|
|
41fcd7746e | |
|
|
0e4a0a68cb | |
|
|
4cd2e5ff19 | |
|
|
cd3c53d674 | |
|
|
2cc9d66aa1 | |
|
|
ad47a9696b | |
|
|
e11b61fa88 | |
|
|
50b99d00a2 | |
|
|
f69245a8c4 | |
|
|
3097635271 | |
|
|
14340e4f8b | |
|
|
92a05cf9ee | |
|
|
5089d82d66 | |
|
|
85ef81ff5e | |
|
|
4743fa298f | |
|
|
54ef66a97d | |
|
|
cdf379be10 | |
|
|
af54ea3864 | |
|
|
eba0171628 | |
|
|
6ae05aad01 | |
|
|
75abc82500 | |
|
|
860d353b9a | |
|
|
baae2a6eb4 | |
|
|
ef3a11c382 | |
|
|
78f9e3b330 | |
|
|
7b6e8c7cbe | |
|
|
75909ccc1b | |
|
|
faa8692667 | |
|
|
93935f87c4 | |
|
|
311b53ea5f | |
|
|
fec486e8a6 | |
|
|
b2fa553fb7 | |
|
|
2217ab8e46 | |
|
|
d3cf9a1b63 | |
|
|
ac3f88422a | |
|
|
17ea49120a | |
|
|
7bbbf34b60 | |
|
|
0ecf957902 | |
|
|
9d08938d77 | |
|
|
794673c272 | |
|
|
6d17f595b2 | |
|
|
87ff0521d3 | |
|
|
b5b7c46a46 | |
|
|
1cc30c8a1f | |
|
|
fb492af633 | |
|
|
e647f0ce6b | |
|
|
407e242b8c | |
|
|
a692c4f592 | |
|
|
d70c6eff8e | |
|
|
abf252360c | |
|
|
7f9263045b | |
|
|
01008ca8c7 | |
|
|
a962f7bb8c | |
|
|
16a99087d9 | |
|
|
4fadb2b469 | |
|
|
04d1c6df21 | |
|
|
75745ceb11 | |
|
|
7f805c56de | |
|
|
d2232e303b | |
|
|
b7051d3267 | |
|
|
60e6ae0716 | |
|
|
1f71522fa6 | |
|
|
19be8b5d48 | |
|
|
e451155063 | |
|
|
53f543887e | |
|
|
b1bf57cbed | |
|
|
bb4073aa2f | |
|
|
3871841988 | |
|
|
d17a6e2eae | |
|
|
6275afac9b | |
|
|
69edbeb6d9 | |
|
|
064edbd180 | |
|
|
ffff79d012 | |
|
|
219ab130d1 | |
|
|
748962528f | |
|
|
c7e255c95d | |
|
|
c50c5d170e | |
|
|
98c025a285 | |
|
|
7044f87d3b | |
|
|
8e88cb88ec | |
|
|
3995bba61a | |
|
|
ab0a27d835 | |
|
|
e5ce48dc7f | |
|
|
068f39a85d | |
|
|
3673e3c594 | |
|
|
bc67eb6301 | |
|
|
017d7177f4 | |
|
|
784f9da3a0 | |
|
|
0472bc2d98 | |
|
|
2845fc28a2 | |
|
|
a009a220e4 | |
|
|
d26cc45962 | |
|
|
cde02eaf1a | |
|
|
c1f4903a0e | |
|
|
6292994bbd | |
|
|
57d359a706 | |
|
|
f99584b54a | |
|
|
aa920208b5 | |
|
|
af06ddade2 | |
|
|
636049d8c0 | |
|
|
5b3f2844a6 | |
|
|
0bcda4f915 | |
|
|
09cf293d2e | |
|
|
9936809321 |
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f9c48f29655a4b8bb90520de5a7c2fa4
|
||||
timeCreated: 1719552474
|
||||
|
|
@ -0,0 +1,863 @@
|
|||
#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
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8c7dc826ea0848f0a3bf05009fab6377
|
||||
timeCreated: 1706856053
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 88694fb6aaa64ea192e0bffb4cb5b744
|
||||
timeCreated: 1719552889
|
||||
|
|
@ -10,12 +10,12 @@ namespace Guru.Editor
|
|||
|
||||
public class EditorGuruServiceIO
|
||||
{
|
||||
private static readonly string SourceConfigFileName = "guru-service";
|
||||
private const string LocalServicesConfigPath = "Guru/Resources";
|
||||
private const string SourceConfigExtension = ".json";
|
||||
private const string LocalConfigExtension = ".txt";
|
||||
internal static readonly string SourceConfigFileName = "guru-service";
|
||||
internal const string LocalServicesConfigPath = "Guru/Resources";
|
||||
internal const string SourceConfigExtension = ".json";
|
||||
internal const string LocalConfigExtension = ".txt";
|
||||
|
||||
private static string DefaultFilePath =
|
||||
internal static string DefaultFilePath =
|
||||
Path.GetFullPath(Path.Combine(Application.dataPath, $"{SourceConfigFileName}{SourceConfigExtension}"));
|
||||
|
||||
internal static string SourceServiceFilePath = "";
|
||||
|
|
@ -29,7 +29,7 @@ namespace Guru.Editor
|
|||
var a = AssetDatabase.FindAssets($"*{SourceConfigFileName}* t:TextAsset", new []{"Assets"});
|
||||
if (a == null || a.Length == 0)
|
||||
{
|
||||
Debug.Log($"<color=orange>--- Can't find guru-services file</color>");
|
||||
UnityEngine.Debug.Log($"<color=orange>--- Can't find guru-services file</color>");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -37,7 +37,7 @@ namespace Guru.Editor
|
|||
var fp = Path.GetFullPath(p);
|
||||
if (File.Exists(fp)) SourceServiceFilePath = fp;
|
||||
var t = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
|
||||
// Debug.Log($"<color=#88ff00>--- find services file:{p} \n{t.text}</color>");
|
||||
// UnityEngine.Debug.Log($"<color=#88ff00>--- find services file:{p} \n{t.text}</color>");
|
||||
return JsonMapper.ToObject<GuruServicesConfig>(t.text);
|
||||
}
|
||||
return null;
|
||||
|
|
@ -47,7 +47,7 @@ namespace Guru.Editor
|
|||
/// 保存配置
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
internal static void SaveConfig(GuruServicesConfig config = null)
|
||||
internal static void SaveConfig(GuruServicesConfig config = null, string savePath = "")
|
||||
{
|
||||
if (config == null)
|
||||
{
|
||||
|
|
@ -59,12 +59,13 @@ namespace Guru.Editor
|
|||
PrettyPrint = true,
|
||||
};
|
||||
JsonMapper.ToJson(config, jw);
|
||||
|
||||
var json = jw.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(SourceServiceFilePath)) SourceServiceFilePath = DefaultFilePath;
|
||||
File.WriteAllText(SourceServiceFilePath, json);
|
||||
Debug.Log($"Save config to {SourceServiceFilePath}");
|
||||
if (string.IsNullOrEmpty(savePath)) savePath = SourceServiceFilePath;
|
||||
if (string.IsNullOrEmpty(savePath)) savePath = DefaultFilePath;
|
||||
|
||||
File.WriteAllText(savePath, json);
|
||||
UnityEngine.Debug.Log($"Save config to {savePath}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -79,6 +80,7 @@ namespace Guru.Editor
|
|||
cfg.ad_settings = new GuruAdSettings();
|
||||
cfg.adjust_settings = new GuruAdjustSettings();
|
||||
cfg.fb_settings = new GuruFbSettings();
|
||||
cfg.parameters = new GuruParameters();
|
||||
return cfg;
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +109,7 @@ namespace Guru.Editor
|
|||
if (null != config)
|
||||
{
|
||||
if (File.Exists(path)) File.Delete(path);
|
||||
Debug.Log($"<color=#88ff00> --- setup {GuruSDK.ServicesConfigKey} to local resources.</color>");
|
||||
UnityEngine.Debug.Log($"<color=#88ff00> --- setup {GuruSDK.ServicesConfigKey} to local resources.</color>");
|
||||
File.Copy(from, path);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 210888b84fe449e88319129ae097514d
|
||||
timeCreated: 1722245010
|
||||
|
|
@ -2,7 +2,6 @@ org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
|
|||
org.gradle.parallel=true
|
||||
org.gradle.daemon=true
|
||||
org.gradle.caching=true
|
||||
android.enableR8=**MINIFY_WITH_R_EIGHT**
|
||||
unityStreamingAssets=**STREAMING_ASSETS**
|
||||
# Android Resolver Properties Start
|
||||
android.useAndroidX=true
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ dependencies {
|
|||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion **APIVERSION**
|
||||
namespace "**NAMESPACE**"
|
||||
compileSdkVersion **TARGETSDKVERSION**
|
||||
buildToolsVersion '**BUILDTOOLS**'
|
||||
|
||||
compileOptions {
|
||||
|
|
@ -22,6 +23,7 @@ android {
|
|||
}
|
||||
versionCode **VERSIONCODE**
|
||||
versionName '**VERSIONNAME**'
|
||||
multiDexEnabled true
|
||||
}
|
||||
|
||||
aaptOptions {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
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**
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?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>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e92a383bcfa548e2bdf0eeb8b0264205
|
||||
timeCreated: 1630417512
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
-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.** { *; }
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a08608d11bf7483fa4c49df80ebab657
|
||||
timeCreated: 1722245439
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4f7213bbbc8741bd89b3bd17a7bea43e
|
||||
timeCreated: 1722240718
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ee3d47ab5d544373ae6a43fdd36d96fb
|
||||
timeCreated: 1711761343
|
||||
|
|
@ -0,0 +1,389 @@
|
|||
|
||||
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 89a1c7f77fcf4982adbbaf6dc61bd62d
|
||||
timeCreated: 1711505949
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
using System.Collections;
|
||||
using Unity.EditorCoroutines.Editor;
|
||||
|
||||
namespace Guru.Editor
|
||||
{
|
||||
|
|
@ -16,6 +14,35 @@ namespace Guru.Editor
|
|||
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 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);
|
||||
|
||||
public static bool IsManifestExist() => File.Exists(TargetFullPath);
|
||||
|
|
@ -25,80 +52,65 @@ namespace Guru.Editor
|
|||
if (!IsManifestExist())
|
||||
{
|
||||
CopyManifest();
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
FixAndroidManifest();
|
||||
}
|
||||
|
||||
|
||||
/// <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()
|
||||
{
|
||||
if (File.Exists(TargetFullPath)) return;
|
||||
|
||||
var path = GuruEditorHelper.GetFilePath($"{nameof(AndroidManifestMod)}.cs t:Script");
|
||||
var path = GuruEditorHelper.GetAssetPath(nameof(AndroidManifestMod), "Script", true);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
var files = Path.GetFullPath($"{path}/../Files");
|
||||
var from = $"{files}/AndroidManifest.txt";
|
||||
var from = Path.GetFullPath($"{path}/../../Files/AndroidManifest.txt");
|
||||
if (File.Exists(from))
|
||||
{
|
||||
File.Copy(from, TargetFullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#region Testing
|
||||
|
||||
[Test]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f4547abfcddf84bc6b61e884ebfb30e2
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Linq;
|
||||
|
||||
|
||||
namespace Guru.Editor
|
||||
{
|
||||
|
|
@ -6,29 +6,46 @@ namespace Guru.Editor
|
|||
using UnityEngine;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
public class AndroidProjectMod
|
||||
{
|
||||
private static readonly int TargetSDKVersion = 33;
|
||||
private const int TargetSDKVersion = 34;
|
||||
private const string K_ANDROID_PLUGINS_NAME = "Plugins/Android";
|
||||
|
||||
private static readonly string LauncherName = "launcherTemplate";
|
||||
private static string LauncherFullPath = Path.Combine(Application.dataPath, $"Plugins/Android/{LauncherName}.gradle");
|
||||
private const string LauncherName = "launcherTemplate";
|
||||
private static readonly string LauncherFullPath = Path.Combine(Application.dataPath, $"{K_ANDROID_PLUGINS_NAME}/{LauncherName}.gradle");
|
||||
|
||||
private static readonly string MainName = "mainTemplate";
|
||||
private static string MainFullPath = Path.Combine(Application.dataPath, $"Plugins/Android/{MainName}.gradle");
|
||||
private const string MainName = "mainTemplate";
|
||||
private static readonly string MainFullPath = Path.Combine(Application.dataPath, $"{K_ANDROID_PLUGINS_NAME}/{MainName}.gradle");
|
||||
|
||||
private static readonly string PropertiesName = "gradleTemplate";
|
||||
private static string PropertiesFullPath = Path.Combine(Application.dataPath, $"Plugins/Android/{PropertiesName}.properties");
|
||||
private const string BaseProjectName = "baseProjectTemplate";
|
||||
private static readonly string BaseProjectFullPath = Path.Combine(Application.dataPath, $"{K_ANDROID_PLUGINS_NAME}/{BaseProjectName}.gradle");
|
||||
|
||||
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()
|
||||
{
|
||||
FixLauncher();
|
||||
FixMain();
|
||||
FixProperties();
|
||||
CheckTargetSDKVersion();
|
||||
ApplyLauncher();
|
||||
ApplyBaseProjectTemplates();
|
||||
ApplyMainTemplates();
|
||||
ApplyGradleTemplate();
|
||||
ApplySettings();
|
||||
ApplyProguardUser();
|
||||
CheckTargetSDKVersion(); // 强制修复构建版本号
|
||||
}
|
||||
|
||||
|
||||
private static void FixLauncher()
|
||||
|
||||
private static void ApplyLauncher()
|
||||
{
|
||||
if (!File.Exists(LauncherFullPath))
|
||||
{
|
||||
|
|
@ -41,10 +58,9 @@ namespace Guru.Editor
|
|||
var ptn2 = "abortOnError false";
|
||||
var lines = File.ReadAllLines(LauncherFullPath);
|
||||
|
||||
string line = "";
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
line = lines[i];
|
||||
var line = lines[i];
|
||||
if (line.Contains(ptn1))
|
||||
{
|
||||
lines[i] = line.Replace(ptn1, "\n\n\tpackagingOptions {\n\t\texclude(\"META-INF/*.kotlin_module\")\n\t}\n\n");
|
||||
|
|
@ -62,7 +78,7 @@ namespace Guru.Editor
|
|||
File.WriteAllLines(LauncherFullPath, lines);
|
||||
|
||||
}
|
||||
private static void FixMain()
|
||||
private static void ApplyMainTemplates()
|
||||
{
|
||||
if (!File.Exists(MainFullPath))
|
||||
{
|
||||
|
|
@ -70,13 +86,100 @@ namespace Guru.Editor
|
|||
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))
|
||||
{
|
||||
Debug.Log($"[MOD] --- Copy file to: {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()
|
||||
|
|
@ -93,8 +196,8 @@ namespace Guru.Editor
|
|||
|
||||
private static string GetMoveFilePath(string fileName)
|
||||
{
|
||||
var path = GuruEditorHelper.GetFilePath($"{nameof(AndroidProjectMod)}.cs t:Script");
|
||||
var files = Path.GetFullPath($"{path}/../Files");
|
||||
var path = GuruEditorHelper.GetAssetPath(nameof(AndroidProjectMod), "Script", true);
|
||||
var files = Path.GetFullPath($"{path}/../../Files");
|
||||
return $"{files}/{fileName}";
|
||||
}
|
||||
private static void CopyFile(string fileName, string toPath)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,159 @@
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 39d65f42b9694641b0c3509262aca1d4
|
||||
timeCreated: 1711724404
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c06b1730aac144929ca73fba2e481508
|
||||
timeCreated: 1706058636
|
||||
|
|
@ -1,17 +1,46 @@
|
|||
namespace Guru.Editor
|
||||
{
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
public class GuruEditorHelper
|
||||
{
|
||||
public static string GetFilePath(string filter)
|
||||
public static string GetAssetPath(string filter, bool useFullPath = false)
|
||||
{
|
||||
var guids = AssetDatabase.FindAssets(filter);
|
||||
string path = "";
|
||||
string fullPath = "";
|
||||
if (guids != null && guids.Length > 0)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
return path;
|
||||
path = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
fullPath = path.Replace("Assets", Application.dataPath);
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
return useFullPath? fullPath : path;
|
||||
}
|
||||
}
|
||||
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}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7f9d1111a9d94187bf583ae71d9192f0
|
||||
timeCreated: 1711858346
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 58ea378c89d742008a18f019afd54a27
|
||||
timeCreated: 1711858357
|
||||
|
|
@ -6,7 +6,9 @@
|
|||
"GuruSDK",
|
||||
"Guru.LitJson",
|
||||
"Guru.Runtime",
|
||||
"MaxSdk.Scripts.IntegrationManager.Editor"
|
||||
"MaxSdk.Scripts.IntegrationManager.Editor",
|
||||
"Guru.Editor",
|
||||
"GuruAdjust.Editor"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1d20da1a87484f37bc53360cc2414030
|
||||
timeCreated: 1712622721
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
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,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6ab7dd209c3a494eab0dfbedce0700d8
|
||||
timeCreated: 1712622736
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dc05f1b1ff5947c19ce6af1db301398d
|
||||
timeCreated: 1712624042
|
||||
|
|
@ -1,12 +1,17 @@
|
|||
|
||||
namespace Guru
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// 启动参数配置
|
||||
/// </summary>
|
||||
public partial class GuruSDKInitConfig
|
||||
public class GuruSDKInitConfig
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// 使用自定义的ConsentFlow启动流程
|
||||
/// </summary>
|
||||
|
|
@ -20,78 +25,189 @@ namespace Guru
|
|||
/// </summary>
|
||||
public bool IAPEnabled = true;
|
||||
/// <summary>
|
||||
/// 自动申请推送授权信息
|
||||
/// </summary>
|
||||
public bool AutoNotificationPermission = true;
|
||||
/// <summary>
|
||||
/// 自动记录完成的关卡
|
||||
/// </summary>
|
||||
public bool AutoRecordFinishedLevels = true;
|
||||
[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 = new Dictionary<string, object>();
|
||||
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;
|
||||
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 debugMode = false,
|
||||
Dictionary<string, object> defaultRemoteData = null,
|
||||
byte[] googleKeys = null,
|
||||
byte[] appleRootCerts = null)
|
||||
{
|
||||
// 创建启动用参数
|
||||
GuruSDKInitConfig config = new GuruSDKInitConfig()
|
||||
{
|
||||
UseCustomConsent = useCustomConsent,
|
||||
AutoLoadWhenAdsReady = autoLoadAds,
|
||||
IAPEnabled = iapEnabled,
|
||||
AutoRecordFinishedLevels = autoRecordFinishedLevels,
|
||||
DebugMode = debugMode,
|
||||
GoogleKeys = googleKeys,
|
||||
AppleRootCerts = appleRootCerts,
|
||||
DefaultRemoteData = defaultRemoteData ?? new Dictionary<string, object>(),
|
||||
};
|
||||
#if UNITY_EDITOR
|
||||
config.DebugMode = true;
|
||||
#endif
|
||||
return config;
|
||||
}
|
||||
|
||||
public byte[] GoogleKeys; // 数据取自 GooglePlayTangle.Data();
|
||||
public byte[] AppleRootCerts; // 数据取自 AppleTangle.Data();
|
||||
|
||||
#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: {DebugMode}");
|
||||
sb.AppendLine($"------- Custom init Config -------");
|
||||
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,15 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
|
||||
namespace Guru
|
||||
{
|
||||
using System;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
[Serializable]
|
||||
public class GuruServicesConfig
|
||||
{
|
||||
public long version = 0;
|
||||
public GuruAppSettings app_settings;
|
||||
public GuruParameters parameters;
|
||||
public GuruAdjustSettings adjust_settings;
|
||||
public GuruFbSettings fb_settings;
|
||||
public GuruAdSettings ad_settings;
|
||||
|
|
@ -34,10 +35,40 @@ namespace Guru
|
|||
public bool IsMolocoIOSEnabled() => ad_settings != null &&
|
||||
ad_settings.moloco_ids_ios != null &&
|
||||
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
|
||||
&& products != null && products.Length > 0;
|
||||
public bool IsKeywordsEnabled() => app_settings != null && app_settings.enable_keywords;
|
||||
public bool UseCustomKeystore() => app_settings?.custom_keystore ?? false;
|
||||
|
||||
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]
|
||||
|
|
@ -51,24 +82,37 @@ namespace Guru
|
|||
public string terms_url;
|
||||
public string android_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_facebook = true;
|
||||
public bool enable_adjust = true;
|
||||
public bool enable_iap = false;
|
||||
public double tch020_val = 0;
|
||||
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]
|
||||
public class GuruAdjustSettings
|
||||
{
|
||||
public string[] app_token;
|
||||
public string[] events;
|
||||
|
||||
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 AndroidToken() => app_token != null && app_token.Length > 0 ? app_token[0] : "";
|
||||
public string iOSToken() => app_token != null && app_token.Length > 1 ? app_token[1] : "";
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
|
@ -91,8 +135,8 @@ namespace Guru
|
|||
public string[] pubmatic_ids_ios;
|
||||
public string[] moloco_ids_android;
|
||||
public string[] moloco_ids_ios;
|
||||
public string[] tradplus_ids_android;
|
||||
public string[] tradplus_ids_ios;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -7,14 +7,17 @@ namespace Guru
|
|||
/// 获取BLevel
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override int GetBLevel() => GuruSDKModel.Instance.SuccessLevelCount; // BLevel
|
||||
protected override int GetBLevel() => Model.BLevel; // BLevel
|
||||
|
||||
private GuruSDKModel Model => GuruSDKModel.Instance;
|
||||
|
||||
protected override void OnPurchaseOver(bool success, string productName)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
GuruSDKModel.Instance.PurchasedCount++; // 记录成功购买次数
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearData()
|
||||
{
|
||||
_model.ClearData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,27 +12,17 @@ namespace Guru
|
|||
get => _value;
|
||||
set
|
||||
{
|
||||
if (_value.Equals(value)) return;
|
||||
|
||||
_value = value;
|
||||
OnValueChanged?.Invoke(value);
|
||||
}
|
||||
}
|
||||
public event Action<T> OnValueChanged;
|
||||
|
||||
|
||||
public BindableProperty()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public BindableProperty(Action<T> onChanged)
|
||||
{
|
||||
OnValueChanged = onChanged;
|
||||
}
|
||||
|
||||
public BindableProperty(T initValue, Action<T> onChanged)
|
||||
public BindableProperty(T initValue)
|
||||
{
|
||||
_value = initValue;
|
||||
OnValueChanged = onChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,46 @@
|
|||
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace Guru
|
||||
{
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
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]
|
||||
internal class GuruSDKModel
|
||||
{
|
||||
private const float SaveInterval = 3;
|
||||
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;
|
||||
|
|
@ -20,95 +48,77 @@ namespace Guru
|
|||
{
|
||||
get
|
||||
{
|
||||
if (null == _instance) _instance = Load();
|
||||
if (null == _instance) _instance = new GuruSDKModel();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------- data ---------------
|
||||
public string uid = "";
|
||||
public int b_level = 0;
|
||||
public int b_play = 0;
|
||||
public int buy_count = 0;
|
||||
//-------------- data ---------------
|
||||
|
||||
private float _lastSavedTime = 0;
|
||||
public GuruSDKModel()
|
||||
{
|
||||
// 读取内存值
|
||||
GuruSDKSerializedModel model = LoadModel();
|
||||
_uid = model.uid;
|
||||
_noAds = model.no_ads;
|
||||
_bLevel = model.b_level;
|
||||
_bPlay = model.b_play;
|
||||
_purchased = model.purchased;
|
||||
}
|
||||
|
||||
public int SuccessLevelCount
|
||||
public int BLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_successLevel == null) InitProperties();
|
||||
return _successLevel.Value;
|
||||
}
|
||||
get => _bLevel;
|
||||
set
|
||||
{
|
||||
if(_successLevel == null) InitProperties();
|
||||
_successLevel.Value = value;
|
||||
if (value < _bLevel)
|
||||
{
|
||||
// b_level 必须比上一次的值大
|
||||
Debug.LogWarning($"[SDK] :: Set b_level [{value}] should not be less than original value [{_bLevel}]");
|
||||
return;
|
||||
}
|
||||
_bLevel = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public int TotalPlayedCount
|
||||
public int BPlay
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_totalPlayed == null) InitProperties();
|
||||
return _totalPlayed.Value;
|
||||
}
|
||||
get => _bPlay;
|
||||
set
|
||||
{
|
||||
if(_totalPlayed == null) InitProperties();
|
||||
_totalPlayed.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int PurchasedCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_purchasedCount == null) InitProperties();
|
||||
return _purchasedCount.Value;
|
||||
}
|
||||
set
|
||||
{
|
||||
if(_purchasedCount == null) InitProperties();
|
||||
_purchasedCount.Value = value;
|
||||
_bPlay = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public string UserId
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_uid == null) InitProperties();
|
||||
return _uid.Value;
|
||||
}
|
||||
get => _uid;
|
||||
set
|
||||
{
|
||||
if(_uid == null) InitProperties();
|
||||
_uid.Value = value;
|
||||
_uid = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsIAPUser => PurchasedCount > 0;
|
||||
|
||||
public bool IsIapUser => _purchased.Count > 0;
|
||||
|
||||
private BindableProperty<int> _successLevel;
|
||||
private BindableProperty<int> _totalPlayed;
|
||||
private BindableProperty<int> _purchasedCount;
|
||||
private BindableProperty<string> _uid;
|
||||
public bool IsNoAds
|
||||
{
|
||||
get => _noAds;
|
||||
set
|
||||
{
|
||||
_noAds = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public BindableProperty<int> PropBLevel => _successLevel;
|
||||
public BindableProperty<int> PropBPlay => _totalPlayed;
|
||||
public BindableProperty<int> PropBuyCount => _purchasedCount;
|
||||
public BindableProperty<string> Uid => _uid;
|
||||
|
||||
#region 初始化
|
||||
|
||||
|
||||
public static GuruSDKModel Load()
|
||||
|
||||
private GuruSDKSerializedModel LoadModel()
|
||||
{
|
||||
GuruSDKModel model = null;
|
||||
GuruSDKSerializedModel model = null;
|
||||
if (PlayerPrefs.HasKey(SaveKey))
|
||||
{
|
||||
var json = PlayerPrefs.GetString(SaveKey, "");
|
||||
|
|
@ -116,7 +126,7 @@ namespace Guru
|
|||
{
|
||||
try
|
||||
{
|
||||
model = JsonUtility.FromJson<GuruSDKModel>(json);
|
||||
model = JsonUtility.FromJson<GuruSDKSerializedModel>(json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -124,95 +134,108 @@ namespace Guru
|
|||
}
|
||||
}
|
||||
}
|
||||
if(model == null) model = new GuruSDKModel();
|
||||
model.InitProperties();
|
||||
if(model == null) model = new GuruSDKSerializedModel();
|
||||
return model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存至数据
|
||||
/// 保存至 PlayerPrefs 数据
|
||||
/// </summary>
|
||||
private void SaveToPlayerPrefs()
|
||||
private void SetToMemory()
|
||||
{
|
||||
var json = JsonUtility.ToJson(this);
|
||||
PlayerPrefs.SetString(SaveKey, json);
|
||||
}
|
||||
|
||||
public void InitProperties()
|
||||
{
|
||||
_successLevel = new BindableProperty<int>(b_level, OnLevelChanged);
|
||||
_totalPlayed = new BindableProperty<int>(b_play, OnPlayedChanged);
|
||||
_purchasedCount = new BindableProperty<int>(buy_count, OnPurchasedNumChanged);
|
||||
_uid = new BindableProperty<string>(uid, OnUidChanged);
|
||||
var model = new GuruSDKSerializedModel()
|
||||
{
|
||||
uid = _uid,
|
||||
b_level = _bLevel,
|
||||
b_play = _bPlay,
|
||||
no_ads = _noAds,
|
||||
purchased = _purchased,
|
||||
};
|
||||
|
||||
var json = JsonUtility.ToJson(model);
|
||||
if (!string.IsNullOrEmpty(json))
|
||||
{
|
||||
PlayerPrefs.SetString(SaveKey, json);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存数据
|
||||
/// </summary>
|
||||
/// <param name="force"></param>
|
||||
public void Save(bool force = false)
|
||||
/// <param name="forceSave"></param>
|
||||
public void Save(bool forceSave = false)
|
||||
{
|
||||
bool save = force || (Time.realtimeSinceStartup - _lastSavedTime>= SaveInterval);
|
||||
if (save)
|
||||
{
|
||||
_lastSavedTime = Time.realtimeSinceStartup;
|
||||
SaveToPlayerPrefs();
|
||||
}
|
||||
}
|
||||
SetToMemory(); // 每次保存都要设置到 PlayerPrefs 内
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 数据绑定变化
|
||||
private void OnLevelChanged(int value)
|
||||
{
|
||||
b_level = value;
|
||||
Save();
|
||||
}
|
||||
private void OnPlayedChanged(int value)
|
||||
{
|
||||
b_play = value;
|
||||
Save();
|
||||
}
|
||||
|
||||
private void OnPurchasedNumChanged(int value)
|
||||
{
|
||||
buy_count = value;
|
||||
Save();
|
||||
bool shouldWriteToDisk = forceSave || (DateTime.Now - _lastSavedTime)>= TimeSpan.FromSeconds(SaveInterval);
|
||||
if (!shouldWriteToDisk) return;
|
||||
_lastSavedTime = DateTime.Now; // 更新最后保存时间
|
||||
PlayerPrefs.Save(); // 写入到磁盘
|
||||
}
|
||||
|
||||
private void OnUidChanged(string value)
|
||||
{
|
||||
uid = value;
|
||||
Save();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 启动配置
|
||||
|
||||
|
||||
#region 订单记录
|
||||
|
||||
public bool HasPurchasedProduct(string receipt)
|
||||
{
|
||||
if(_purchased.Count == 0) return false;
|
||||
return _purchased.Exists(p => p.receipt == receipt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 Streaming 加载 AppServices 配置
|
||||
/// 添加已支付订单
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string LoadDefaltServicesConfigJson()
|
||||
/// <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)
|
||||
{
|
||||
try
|
||||
if (!HasPurchasedProduct(receipt))
|
||||
{
|
||||
var txt = Resources.Load<TextAsset>(GuruSDK.ServicesConfigKey);
|
||||
return txt?.text ?? "";
|
||||
_purchased.Add(new PurchasedProduct()
|
||||
{
|
||||
receipt = receipt,
|
||||
productName = productName,
|
||||
productId = productId,
|
||||
appleProductIsRestored = appleProductIsRestored
|
||||
});
|
||||
Save();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception(e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public string[] GetReceipts(string productName)
|
||||
{
|
||||
var receipts = new List<string>();
|
||||
receipts.AddRange(from purchasedProduct in _purchased where purchasedProduct.productName == productName select purchasedProduct.receipt);
|
||||
return receipts.ToArray();
|
||||
}
|
||||
|
||||
|
||||
public string[] GetReceiptsById(string productId)
|
||||
{
|
||||
var receipts = new List<string>();
|
||||
receipts.AddRange(from purchasedProduct in _purchased where purchasedProduct.productId == productId select purchasedProduct.receipt);
|
||||
return receipts.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 清除数据
|
||||
|
||||
public void ClearData()
|
||||
{
|
||||
PlayerPrefs.DeleteKey(SaveKey);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,39 +1,61 @@
|
|||
|
||||
|
||||
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()
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认的启动顺序是先启动Consent后, 根据用户回调的结果来启动广告
|
||||
Instance.StartConsentFlow();
|
||||
}
|
||||
|
||||
// 默认的启动顺序是先启动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>
|
||||
public static void StartAdsWithCustomConsent(bool userAllow = true)
|
||||
/// <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)
|
||||
{
|
||||
StartAdService();
|
||||
#if UNITY_IOS
|
||||
Instance.CheckAttStatus();
|
||||
#else
|
||||
StartAdService(spec);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -41,94 +63,367 @@ namespace Guru
|
|||
}
|
||||
}
|
||||
|
||||
/// <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($"StartConsentFlow");
|
||||
GuruConsent.StartConsent(OnConsentOver);
|
||||
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);
|
||||
}
|
||||
|
||||
private void OnConsentOver(int code)
|
||||
/// <summary>
|
||||
/// Guru Consent flow is Over
|
||||
/// </summary>
|
||||
/// <param name="code"></param>
|
||||
private void OnGuruConsentOver(int code)
|
||||
{
|
||||
Callbacks.ConsentFlow._onConsentResult?.Invoke(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:
|
||||
// 已获取授权, 或者地区不可用
|
||||
#if UNITY_IOS
|
||||
CheckATTStatus();
|
||||
#else
|
||||
StartAdService();
|
||||
#endif
|
||||
// 已获取授权, 或者地区不可用, ATT 尚未启动
|
||||
// TODO: 添加后继处理逻辑
|
||||
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()
|
||||
private void AdServiceHandler()
|
||||
{
|
||||
LogI($"StartAdService");
|
||||
ADService.Instance.StartService(OnAdsInitComplete,
|
||||
InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
|
||||
|
||||
//--------- Callbacks -----------
|
||||
ADService.OnBannerLoaded = OnBannerLoaded;
|
||||
ADService.OnInterstitialLoaded = OnInterstitialLoaded;
|
||||
ADService.OnInterstitialFailed = OnInterstitialFailed;
|
||||
ADService.OnRewardLoaded = OnRewardLoaded;
|
||||
ADService.OnRewardFailed = OnRewardFailed;
|
||||
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);
|
||||
}
|
||||
|
||||
private static void OnBannerLoaded()
|
||||
=> Callbacks.Ads._onBannerADLoaded?.Invoke();
|
||||
/// <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._onInterstitialADLoaded?.Invoke();
|
||||
=> Callbacks.Ads.InvokeOnInterstitialADLoaded();
|
||||
private static void OnInterstitialFailed()
|
||||
=> Callbacks.Ads._onInterstitialADFailed?.Invoke();
|
||||
=> 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._onRewardedADLoaded?.Invoke();
|
||||
=> Callbacks.Ads.InvokeOnRewardedADLoaded();
|
||||
private static void OnRewardFailed()
|
||||
=> Callbacks.Ads._onRewardADFailed?.Invoke();
|
||||
=> Callbacks.Ads.InvokeOnRewardADFailed();
|
||||
private static void OnRewardClosed()
|
||||
=> Callbacks.Ads.InvokeOnRewardADClosed();
|
||||
|
||||
private static void OnAdsInitComplete()
|
||||
{
|
||||
_initAdsCompleted = true;
|
||||
Callbacks.Ads._onAdsInitComplete?.Invoke();
|
||||
|
||||
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 (!_initAdsCompleted)
|
||||
if (!IsAdsReady)
|
||||
{
|
||||
LogE("Ads is not ready. Call <GuruSDk.StartAdService> first.");
|
||||
LogW("[SDK] Ads is not ready. Call <GuruSDk.StartAdService> first.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -140,15 +435,40 @@ namespace Guru
|
|||
/// <param name="placement"></param>
|
||||
public static void ShowBanner(string placement = "")
|
||||
{
|
||||
if (!CheckAdsReady()) return;
|
||||
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()) return;
|
||||
if (!CheckAdsReady())
|
||||
{
|
||||
_preBannerAction = -1;
|
||||
return;
|
||||
}
|
||||
ADService.Instance.HideBanner();
|
||||
}
|
||||
|
||||
|
|
@ -227,5 +547,10 @@ namespace Guru
|
|||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,164 +0,0 @@
|
|||
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 _onBannerADLoaded;
|
||||
public static event Action OnBannerADLoaded
|
||||
{
|
||||
add => _onBannerADLoaded += value;
|
||||
remove => _onBannerADLoaded -= 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,378 @@
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 35b764b09fe74efe8e2ebead9716279a
|
||||
timeCreated: 1709802191
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 36f2d28a30ff46568d6adc0b728a3268
|
||||
timeCreated: 1714354376
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 002605b9e408487bb69d27d07dda016c
|
||||
timeCreated: 1711078536
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
namespace Guru
|
||||
{
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
|
|
@ -18,23 +17,25 @@ namespace Guru
|
|||
public const string BuyFail_DuplicateTransaction = "DuplicateTransaction";
|
||||
public const string BuyFail_Unknown = "Unknown";
|
||||
|
||||
|
||||
#region Start
|
||||
|
||||
/// <summary>
|
||||
/// 初始化IAP 功能
|
||||
/// </summary>
|
||||
public static void InitIAP(byte[] googleKey, byte[] appleRootCerts)
|
||||
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;
|
||||
|
||||
GuruIAP.Instance.InitWithKeys(googleKey, appleRootCerts, IsDebugMode);
|
||||
Callbacks.IAP.InvokeOnIAPInitStart(); // 初始化之前进行调用
|
||||
|
||||
GuruIAP.Instance.InitWithKeys(uid, googleKey, appleRootCerts, IsDebugMode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化结果
|
||||
/// </summary>
|
||||
|
|
@ -42,9 +43,8 @@ namespace Guru
|
|||
private static void OnIAPInitResult(bool success)
|
||||
{
|
||||
LogI($"IAP init result: {success}");
|
||||
|
||||
IsIAPReady = success;
|
||||
Callbacks.IAP._onIAPInitComplete?.Invoke(success);
|
||||
Callbacks.IAP.InvokeOnIAPInitComplete(success);
|
||||
}
|
||||
|
||||
private static bool CheckIAPReady()
|
||||
|
|
@ -57,9 +57,33 @@ namespace Guru
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Purchase
|
||||
|
||||
#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;
|
||||
|
|
@ -70,6 +94,7 @@ namespace Guru
|
|||
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;
|
||||
|
|
@ -79,35 +104,69 @@ namespace Guru
|
|||
}
|
||||
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
|
||||
|
||||
private static Action<string, bool> _onPurchaseCallback;
|
||||
#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, Action<string, bool> purchaseCallback = null)
|
||||
public static void Purchase(string productName, string category = "", Action<string, bool> purchaseCallback = null)
|
||||
{
|
||||
if (CheckIAPReady())
|
||||
{
|
||||
_onPurchaseCallback = purchaseCallback;
|
||||
GuruIAP.Instance.Buy(productName);
|
||||
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, Action<string, bool> purchaseCallback = null)
|
||||
public static bool PurchaseById(string productId, string category = "", Action<string, bool> purchaseCallback = null)
|
||||
{
|
||||
var productName = GetProductSettingById(productId)?.ProductName ?? "";
|
||||
var productName = GetProductInfoById(productId)?.Name ?? "";
|
||||
|
||||
if (CheckIAPReady() && !string.IsNullOrEmpty(productName))
|
||||
{
|
||||
Purchase(productName, purchaseCallback);
|
||||
Purchase(productName, category, purchaseCallback);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -117,11 +176,12 @@ namespace Guru
|
|||
/// <summary>
|
||||
/// 支付回调
|
||||
/// </summary>
|
||||
/// <param name="productId"></param>
|
||||
/// <param name="productName"></param>
|
||||
/// <param name="success"></param>
|
||||
private static void OnBuyEnd(string productName, bool success)
|
||||
{
|
||||
_onPurchaseCallback?.Invoke(productName, success);
|
||||
InvokeOnPurchaseCallback?.Invoke(productName, success);
|
||||
Callbacks.IAP.InvokeOnPurchaseEnd(productName, success);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -130,7 +190,7 @@ namespace Guru
|
|||
/// <param name="productName"></param>
|
||||
private static void OnBuyStart(string productName)
|
||||
{
|
||||
Callbacks.IAP._onPurchaseStart?.Invoke(productName);
|
||||
Callbacks.IAP.InvokeOnPurchaseStart(productName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -140,32 +200,136 @@ namespace Guru
|
|||
/// <param name="reason"></param>
|
||||
private static void OnBuyFailed(string productName, string reason)
|
||||
{
|
||||
Callbacks.IAP._onPurchaseFailed?.Invoke(productName, reason);
|
||||
Callbacks.IAP.InvokeOnPurchaseFailed(productName, reason);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Restore Purchase
|
||||
|
||||
private static Action<bool> _onRestored;
|
||||
/// <summary>
|
||||
/// 恢复购买
|
||||
/// </summary>
|
||||
/// <param name="restoreCallback"></param>
|
||||
public static void Restore(Action<bool> restoreCallback)
|
||||
public static void Restore(Action<bool, string> restoreCallback = null)
|
||||
{
|
||||
if( restoreCallback != null) Callbacks.IAP.OnIAPRestored += restoreCallback;
|
||||
if (CheckIAPReady())
|
||||
{
|
||||
_onRestored = restoreCallback;
|
||||
GuruIAP.Instance.Restore();
|
||||
}
|
||||
}
|
||||
private static void OnRestored(bool success)
|
||||
private static void OnRestored(bool success, string msg)
|
||||
{
|
||||
_onRestored?.Invoke(success);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ab7d65b5c8774a12924f3fe72a6e4a38
|
||||
timeCreated: 1710154459
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Firebase.RemoteConfig;
|
||||
|
||||
namespace Guru
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Firebase.RemoteConfig;
|
||||
|
||||
public partial class GuruSDK
|
||||
{
|
||||
|
||||
|
|
@ -30,27 +30,45 @@ namespace Guru
|
|||
{
|
||||
return RemoteConfigManager.GetConfig<T>(key);
|
||||
}
|
||||
public static string GetRemoteString(string key) => RemoteConfigManager.GetString(key);
|
||||
public static int GetRemoteInt(string key) => RemoteConfigManager.GetInt(key);
|
||||
public static long GetRemoteLong(string key) => RemoteConfigManager.GetLong(key);
|
||||
public static double GetRemoteDouble(string key) => RemoteConfigManager.GetDouble(key);
|
||||
public static float GetRemoteFloat(string key) => RemoteConfigManager.GetFloat(key);
|
||||
public static bool GetRemoteBool(string key) => RemoteConfigManager.GetBool(key);
|
||||
public static string GetRemoteString(string key, string defaultValue = "") => RemoteConfigManager.GetString(key, defaultValue);
|
||||
public static int GetRemoteInt(string key, int defaultValue = 0) => RemoteConfigManager.GetInt(key, defaultValue);
|
||||
public static long GetRemoteLong(string key, long defaultValue = 0 ) => RemoteConfigManager.GetLong(key, defaultValue);
|
||||
public static double GetRemoteDouble(string key, double defaultValue = 0) => RemoteConfigManager.GetDouble(key, defaultValue);
|
||||
public static float GetRemoteFloat(string key, float defaultValue = 0) => RemoteConfigManager.GetFloat(key, defaultValue);
|
||||
public static bool GetRemoteBool(string key, bool defaultValue = false) => RemoteConfigManager.GetBool(key, defaultValue);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 注册监听某个 Key 的变化
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="onValueChanged"></param>
|
||||
public static void RegisterOnValueChanged(string key, Action<string,string> 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)
|
||||
{
|
||||
RemoteConfigManager.UnRegisterOnValueChanged(key, onValueChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有云控配置
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 80d35b4c8f0f4d23a7228fddfb06dd03
|
||||
timeCreated: 1713238972
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8a0605c6b4b64b37a0036b34cbadb9ca
|
||||
timeCreated: 1712622534
|
||||
|
|
@ -4,12 +4,17 @@ namespace Guru
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Debug = UnityEngine.Debug;
|
||||
using Guru.Network;
|
||||
using System.Linq;
|
||||
|
||||
public partial class GuruSDK: MonoBehaviour
|
||||
{
|
||||
public const string Version = "1.0.4";
|
||||
public const string Tag = "[Guru]";
|
||||
// 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;
|
||||
|
|
@ -30,16 +35,11 @@ namespace Guru
|
|||
}
|
||||
|
||||
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 GuruSDKInitConfig InitConfig => Instance._initConfig;
|
||||
private static GuruSDKModel Model => GuruSDKModel.Instance;
|
||||
private static GuruServicesConfig _appServicesConfig;
|
||||
|
||||
private static GuruSettings _guruSettings;
|
||||
|
||||
private static GuruSettings GuruSettings
|
||||
{
|
||||
get
|
||||
|
|
@ -48,8 +48,8 @@ namespace Guru
|
|||
return _guruSettings;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static DateTime _initTime;
|
||||
private static bool _isDebugEnabled = false;
|
||||
/// <summary>
|
||||
/// Debug Mode
|
||||
|
|
@ -69,7 +69,21 @@ namespace Guru
|
|||
/// 初始化成功标志位
|
||||
/// </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()
|
||||
|
|
@ -79,34 +93,23 @@ namespace Guru
|
|||
_instance = go.AddComponent<GuruSDK>();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
public static GuruSDKInitConfig BuildConfig(
|
||||
bool useCustomConsent = false,
|
||||
bool autoLoadAds = true,
|
||||
bool iapEnabled = true,
|
||||
bool autoRecordFinishedLevels = true,
|
||||
bool debugMode = false,
|
||||
Dictionary<string, object> defaultRemoteData = null,
|
||||
byte[] googleKeys = null,
|
||||
byte[] appleRootCerts = null)
|
||||
{
|
||||
var config = GuruSDKInitConfig.Build(useCustomConsent, autoLoadAds, iapEnabled,
|
||||
autoRecordFinishedLevels, debugMode, defaultRemoteData, googleKeys, appleRootCerts);
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
// TODO : 下个版本需要将 整个 GuruSDK 做功能性的拆分
|
||||
|
||||
public static void Init(Action<bool> onComplete)
|
||||
{
|
||||
Init(GuruSDKInitConfig.Build(), onComplete);
|
||||
Init(GuruSDKInitConfig.Builder().Build(), onComplete);
|
||||
}
|
||||
|
||||
public static void Init(GuruSDKInitConfig config, Action<bool> onComplete)
|
||||
{
|
||||
LogI($"---- Guru SDK init ----\n{config.ToString()}");
|
||||
_initTime = DateTime.UtcNow;
|
||||
// ----- First Open Time -----
|
||||
// SetFirstOpenTime(GetFirstOpenTime()); // FirstOpenTime
|
||||
LogI($"#1 ---- Guru SDK [{Version}] ----\n{config}");
|
||||
Instance.StartWithConfig(config, onComplete);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 启动SDK
|
||||
/// </summary>
|
||||
|
|
@ -114,68 +117,57 @@ namespace Guru
|
|||
/// <param name="onComplete"></param>
|
||||
private void StartWithConfig(GuruSDKInitConfig config, Action<bool> onComplete)
|
||||
{
|
||||
Model.PropBLevel.OnValueChanged += OnBLevelChanged;
|
||||
Model.PropBPlay.OnValueChanged += OnBPlayChanged;
|
||||
|
||||
IsInitialSuccess = false;
|
||||
_initConfig = config;
|
||||
_onCompleteCallback = onComplete;
|
||||
_isDebugEnabled = config.DebugMode;
|
||||
|
||||
//--- 之后的逻辑放在 Start 方法内 ---
|
||||
if (config.EnableDebugLogEvent) Analytics.EnableDebugAnalytics = true; // 允许 Debug 模式下打点
|
||||
if (!config.AutoNotificationPermission) FirebaseUtil.SetAutoFetchFcmToken(false); // 不允许自动启动获取 FCM Token
|
||||
|
||||
InitUpdaters(); // Updaters
|
||||
InitThreadHandler(); // 初始化线程处理器
|
||||
InitServices(); // 初始化所有的服务
|
||||
InitNetworkMonitor(); // 网络状态
|
||||
|
||||
onComplete?.Invoke(true);
|
||||
}
|
||||
|
||||
void Start()
|
||||
private void InitServices()
|
||||
{
|
||||
//---- Init All tools ----
|
||||
|
||||
//---------- 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 ------------
|
||||
FirebaseUtil.InitFirebase(OnFirebaseReady);
|
||||
// FirebaseUtil.OnFetchRemoteSuccess+= OnFetchRemoteCallback;
|
||||
StartFirebaseService();
|
||||
LogI($"#2.1 --- InitFacebook ---");
|
||||
//---------- Start Facebook ------------
|
||||
FBService.Instance.StartService();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开始各种组件初始化
|
||||
/// </summary>
|
||||
private void OnFirebaseReady()
|
||||
{
|
||||
FBService.Instance.StartService(Analytics.OnFBInitComplete);
|
||||
|
||||
IsInitialSuccess = true;
|
||||
|
||||
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)
|
||||
private string LoadDefaultGuruServiceJson()
|
||||
{
|
||||
if (dict == null) dict = new Dictionary<string, object>(3);
|
||||
|
||||
// 注入默认的 Services 配置值
|
||||
string json = Model.LoadDefaltServicesConfigJson();
|
||||
if (!string.IsNullOrEmpty(json)) dict[ServicesConfigKey] = json;
|
||||
|
||||
return dict;
|
||||
// 加载本地 Services 配置值
|
||||
var txtAsset = Resources.Load<TextAsset>(ServicesConfigKey);
|
||||
if (txtAsset != null)
|
||||
{
|
||||
return txtAsset.text;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -184,154 +176,303 @@ namespace Guru
|
|||
/// <param name="success"></param>
|
||||
private void OnFetchRemoteCallback(bool success)
|
||||
{
|
||||
LogI($"--- Remote fetch complete: {success} ---");
|
||||
LogI($"#6 --- Remote fetch complete: {success} ---");
|
||||
ABTestManager.Init(); // 启动AB测试解析器
|
||||
Callbacks.Remote._onRemoteFetchComplete?.Invoke(success);
|
||||
Callbacks.Remote.InvokeOnRemoteFetchComplete(success);
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdateAllUpdates(); // 驱动所有的更新器
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region App Remote Update
|
||||
|
||||
private void SetupServicesConfig()
|
||||
/// <summary>
|
||||
/// Apply Cloud guru-service configs for sdk assets
|
||||
/// </summary>
|
||||
private void InitAllGuruServices()
|
||||
{
|
||||
bool useKeywords = true;
|
||||
bool useIAP = true;
|
||||
// -------- 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.IsKeywordsEnabled();
|
||||
useKeywords = _appServicesConfig.KeywordsEnabled();
|
||||
// 使用初始化配置中的 IAPEnable来联合限定, 如果本地写死关闭则不走云控开启
|
||||
useIAP = _initConfig.IAPEnabled && _appServicesConfig.IsIAPEnabled();
|
||||
// enableAnaErrorLog = _appServicesConfig.EnableAnaErrorLog();
|
||||
|
||||
if (null != GuruSettings)
|
||||
Try(() =>
|
||||
{
|
||||
if(_appServicesConfig.adjust_settings != null)
|
||||
{
|
||||
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);
|
||||
GuruSettings.UpdateAdjustTokens(
|
||||
_appServicesConfig.adjust_settings.AndroidToken(),
|
||||
_appServicesConfig.adjust_settings.iOSToken());
|
||||
// 更新 Adjust Events
|
||||
GuruSettings.UpdateAdjustEvents(_appServicesConfig.adjust_settings.events);
|
||||
}
|
||||
}
|
||||
useIAP = _appServicesConfig.IsIAPEnabled();
|
||||
|
||||
if (_appServicesConfig.app_settings != null)
|
||||
{
|
||||
if (_appServicesConfig.app_settings.tch020_val > 0)
|
||||
|
||||
LogI($"#4.2 --- Start GuruSettings ---");
|
||||
// GuruSettings 设置
|
||||
if (null != _appServicesConfig.app_settings)
|
||||
{
|
||||
Analytics.EnableTch02Event = true;
|
||||
Analytics.SetTch02TargetValue(_appServicesConfig.app_settings.tch020_val);
|
||||
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");
|
||||
}
|
||||
|
||||
// 更新和升级 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);
|
||||
}
|
||||
InitIAP(UID, _initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
|
||||
}, ex =>
|
||||
{
|
||||
Debug.LogError($"--- ERROR on useIAP: {ex.Message}");
|
||||
});
|
||||
}
|
||||
|
||||
if(useIAP) {
|
||||
InitIAP(_initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
|
||||
//----------- 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(useKeywords) {
|
||||
KeywordsManager.Install(Model.IsIAPUser, Model.SuccessLevelCount); // 启动Keyword管理器
|
||||
}
|
||||
}
|
||||
|
||||
#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()
|
||||
{
|
||||
var json = GetRemoteString(ServicesConfigKey);
|
||||
|
||||
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;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 数据
|
||||
|
||||
private void OnBLevelChanged(int blevel)
|
||||
private void Try(Action method, Action<Exception> onException = null, Action onFinal = null)
|
||||
{
|
||||
SetUserBLevel(blevel);
|
||||
}
|
||||
if (method == null) return;
|
||||
|
||||
private void OnBPlayChanged(int bplay)
|
||||
{
|
||||
SetUserBPlay(bplay);
|
||||
}
|
||||
|
||||
public static string UID => _model?.UserId ?? "";
|
||||
|
||||
public static string SupportEmail => GuruSettings.SupportEmail ?? "";
|
||||
|
||||
public static string StoreUrl
|
||||
{
|
||||
get
|
||||
try
|
||||
{
|
||||
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;
|
||||
method.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogEx(ex);
|
||||
// ignored
|
||||
onException?.Invoke(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Finally
|
||||
onFinal?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Misc
|
||||
|
||||
/// <summary>
|
||||
/// 打开页面
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
public static void OpenURL(string url)
|
||||
{
|
||||
GuruWebview.OpenPage(url);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Logging
|
||||
#region Apple 审核流程逻辑
|
||||
|
||||
#if UNITY_IOS
|
||||
private void StartAppleReviewFlow()
|
||||
{
|
||||
CheckAttStatus();
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
internal static void LogI(object message)
|
||||
#region Logging
|
||||
|
||||
private static void LogI(object message)
|
||||
{
|
||||
Debug.Log($"{Tag} {message}");
|
||||
}
|
||||
|
||||
internal static void LogW(object message)
|
||||
|
||||
private static void LogW(object message)
|
||||
{
|
||||
Debug.LogWarning($"{Tag} {message}");
|
||||
}
|
||||
|
||||
internal static void LogE(object message)
|
||||
|
||||
private static void LogE(object message)
|
||||
{
|
||||
Debug.LogError($"{Tag} {message}");
|
||||
}
|
||||
|
||||
|
||||
internal static void LogException(string message)
|
||||
|
||||
private static void LogEx(string message)
|
||||
{
|
||||
LogException( new Exception($"{Tag} {message}"));
|
||||
LogEx( new Exception($"{Tag} {message}"));
|
||||
}
|
||||
|
||||
internal static void LogException(Exception e)
|
||||
|
||||
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 生命周期
|
||||
|
|
@ -343,7 +484,7 @@ namespace Guru
|
|||
private void OnAppPauseHandler(bool paused)
|
||||
{
|
||||
if(paused) Model.Save(true); // 强制保存数据
|
||||
Callbacks.App._onAppPaused?.Invoke(paused);
|
||||
Callbacks.App.InvokeOnAppPaused(paused);
|
||||
}
|
||||
|
||||
private void OnApplicationPause(bool paused)
|
||||
|
|
@ -359,7 +500,7 @@ namespace Guru
|
|||
private void OnApplicationQuit()
|
||||
{
|
||||
Model.Save(true);
|
||||
Callbacks.App._onAppQuit?.Invoke();
|
||||
Callbacks.App.InvokeOnAppQuit();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -387,9 +528,9 @@ namespace Guru
|
|||
/// </summary>
|
||||
/// <param name="seconds"></param>
|
||||
/// <param name="callback"></param>
|
||||
public static void Delay(float seconds, Action callback)
|
||||
public static Coroutine Delay(float seconds, Action callback)
|
||||
{
|
||||
DoCoroutine(Instance.OnDelayCall(seconds, callback));
|
||||
return DoCoroutine(Instance.OnDelayCall(seconds, callback));
|
||||
}
|
||||
|
||||
private IEnumerator OnDelayCall(float delay, Action callback)
|
||||
|
|
@ -406,6 +547,297 @@ namespace Guru
|
|||
}
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 45ff6fac409a4698ada70e0d8d5e8269
|
||||
timeCreated: 1714353187
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 41272ed6104b4d80be5322135a811186
|
||||
timeCreated: 1714353340
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 75760985db114e44bfcd901ed50af96c
|
||||
timeCreated: 1712580213
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"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
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9eaf5abb3054d452ba2776169a31aa91
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7181a5508c604c439d3f4585ff49a5e0
|
||||
timeCreated: 1712580254
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 67d9cfd9949c48f3a8f6e6821b733ff1
|
||||
timeCreated: 1712649738
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "com.guru.unity.sdk",
|
||||
"displayName": "Guru SDK",
|
||||
"version": "1.0.4",
|
||||
"version": "1.1.0",
|
||||
"description": "Guru SDK for unity project",
|
||||
"unity": "2020.3",
|
||||
"author":{
|
||||
"name": "Guru Games"
|
||||
"name": "Guru Game"
|
||||
},
|
||||
"license": "MIT",
|
||||
"category": "Game,Tool,Development",
|
||||
|
|
|
|||
Loading…
Reference in New Issue