com.amanotes.gdk 0.2.65-1 → 0.2.65-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Editor/Extra/SDKVersionTracking.AirTable.cs +73 -0
- package/Editor/Extra/SDKVersionTracking.AirTable.cs.meta +3 -0
- package/Editor/Extra/SDKVersionTracking.cs +232 -0
- package/Editor/Extra/SDKVersionTracking.cs.meta +11 -0
- package/Extra/AmaGDKInstaller.unitypackage +0 -0
- package/Extra/CheckDiskSpace.unitypackage +0 -0
- package/Extra/ForceUpdate.unitypackage +0 -0
- package/Extra/GoogleCMP.unitypackage +0 -0
- package/Extra/PostProcessor.unitypackage +0 -0
- package/Extra/SDKVersionTracking.unitypackage +0 -0
- package/Packages/AmaGDKConfig.unitypackage +0 -0
- package/Packages/AmaGDKExample.unitypackage +0 -0
- package/Packages/AmaGDKTest.unitypackage +0 -0
- package/Packages/AppsFlyerAdapter.PurchaseConnector.unitypackage +0 -0
- package/Packages/AppsFlyerAdapter.unitypackage +0 -0
- package/Packages/FirebaseAnalyticsAdapter.unitypackage +0 -0
- package/Packages/FirebaseRemoteConfigAdapter.unitypackage +0 -0
- package/Packages/IronSourceAdapter.unitypackage +0 -0
- package/Packages/RevenueCatAdapter.unitypackage +0 -0
- package/Packages/SqliteAnalyticsAdapter.unitypackage +0 -0
- package/Runtime/AmaGDK.Analytics.cs +1 -0
- package/Runtime/AmaGDK.RemoteConfig.cs +3 -4
- package/Runtime/AmaGDK.cs +5 -1
- package/Runtime/Internal/AmaGDK.Internal.cs +1 -1
- package/Runtime/Internal/AmaGDK.Utils.cs +61 -21
- package/Runtime/Internal/ForceQuitMonitor.cs +111 -0
- package/Runtime/Internal/ForceQuitMonitor.cs.meta +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
using System.Collections.Generic;
|
|
2
|
+
using System.Net.Http;
|
|
3
|
+
using System.Text;
|
|
4
|
+
using System.Threading.Tasks;
|
|
5
|
+
using Amanotes.Core.Internal;
|
|
6
|
+
using UnityEditor;
|
|
7
|
+
using UnityEditor.Callbacks;
|
|
8
|
+
using UnityEngine;
|
|
9
|
+
|
|
10
|
+
namespace Amanotes.Editor
|
|
11
|
+
{
|
|
12
|
+
public partial class SDKVersionTracking
|
|
13
|
+
{
|
|
14
|
+
private const string AUTH_TOKEN = "pat1CmLr9XozLt6ZE";
|
|
15
|
+
private const string URL = "https://gdk.amanotes.io/gdkVersionTracking";
|
|
16
|
+
|
|
17
|
+
[PostProcessBuild(999)]
|
|
18
|
+
public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
|
|
19
|
+
{
|
|
20
|
+
var rawData = AllVersions;
|
|
21
|
+
rawData["UnityVersion"] = Application.unityVersion;
|
|
22
|
+
#if UNITY_ANDROID
|
|
23
|
+
rawData["Platform"] = "Android";
|
|
24
|
+
rawData["Permissions"] = GetAllAndroidPermissions();
|
|
25
|
+
rawData["APILevel"] = (int)PlayerSettings.Android.targetSdkVersion;
|
|
26
|
+
#elif UNITY_IOS
|
|
27
|
+
rawData["SKAdNetwork"] = GetSkAdNetworkCount($"{pathToBuiltProject}/Info.plist");
|
|
28
|
+
rawData["Platform"] = "iOS";
|
|
29
|
+
#endif
|
|
30
|
+
|
|
31
|
+
var buildInfo = new Dictionary<string, object>
|
|
32
|
+
{
|
|
33
|
+
{"GameName", Application.productName},
|
|
34
|
+
{"BuildVersion", PlayerSettings.bundleVersion},
|
|
35
|
+
{"BuildNumber", GetBuildNumber()},
|
|
36
|
+
{"BuildTime", GetBuildTime()},
|
|
37
|
+
{"AppId", PlayerSettings.applicationIdentifier },
|
|
38
|
+
{"RawData", rawData }
|
|
39
|
+
};
|
|
40
|
+
string json = JsonUtils.DictionaryToJson(buildInfo, false, false);
|
|
41
|
+
SendDataToAirTable(json);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private static async Task SendDataToAirTable(string payload)
|
|
45
|
+
{
|
|
46
|
+
Debug.Log("AmaGDK | [SDKVersionTracking]: SendDataToAirTable ");
|
|
47
|
+
try
|
|
48
|
+
{
|
|
49
|
+
var client = new HttpClient();
|
|
50
|
+
var request = new HttpRequestMessage(HttpMethod.Post, URL);
|
|
51
|
+
request.Headers.Add("Authorization", AUTH_TOKEN);
|
|
52
|
+
request.Content = new StringContent(payload, Encoding.UTF8, "application/json");
|
|
53
|
+
|
|
54
|
+
var response = await client.SendAsync(request);
|
|
55
|
+
if (response.IsSuccessStatusCode)
|
|
56
|
+
{
|
|
57
|
+
string responseData = await response.Content.ReadAsStringAsync();
|
|
58
|
+
Debug.Log("AmaGDK | [SDKVersionTracking]: done " + responseData);
|
|
59
|
+
}
|
|
60
|
+
else
|
|
61
|
+
{
|
|
62
|
+
Debug.LogError("AmaGDK | [SDKVersionTracking] Error: " + response.StatusCode);
|
|
63
|
+
string errorData = await response.Content.ReadAsStringAsync();
|
|
64
|
+
Debug.LogError("AmaGDK | [SDKVersionTracking] Error Response: " + errorData);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (HttpRequestException e)
|
|
68
|
+
{
|
|
69
|
+
Debug.LogError("AmaGDK | [SDKVersionTracking] Request Exception: " + e.Message);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using System.IO;
|
|
4
|
+
using System.Linq;
|
|
5
|
+
using System.Text;
|
|
6
|
+
using System.Xml;
|
|
7
|
+
using UnityEditor;
|
|
8
|
+
using UnityEditor.Build;
|
|
9
|
+
using UnityEditor.Build.Reporting;
|
|
10
|
+
using UnityEngine;
|
|
11
|
+
|
|
12
|
+
#if UNITY_IOS
|
|
13
|
+
using UnityEditor.iOS.Xcode;
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
namespace Amanotes.Editor
|
|
17
|
+
{
|
|
18
|
+
public partial class SDKVersionTracking
|
|
19
|
+
{
|
|
20
|
+
private static Func<Dictionary<string, object>> _getExtraSDKVersion;
|
|
21
|
+
public static void SetSDKVersionHook(Func<Dictionary<string, object>> hook)
|
|
22
|
+
{
|
|
23
|
+
_getExtraSDKVersion = hook;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
public partial class SDKVersionTracking : IPreprocessBuildWithReport
|
|
28
|
+
{
|
|
29
|
+
public static Dictionary<string, object> AllVersions
|
|
30
|
+
{
|
|
31
|
+
get
|
|
32
|
+
{
|
|
33
|
+
return _dicThirdPartyVersion
|
|
34
|
+
.Concat(_dicExtraVersion)
|
|
35
|
+
.Concat(_dicIronSourceAdapterVersion)
|
|
36
|
+
.ToDictionary(x => x.Key, x => x.Value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private const int MAX_VERSION_KEY_LENGTH = 30;
|
|
41
|
+
private static Dictionary<string, object> _dicThirdPartyVersion = new Dictionary<string, object>();
|
|
42
|
+
private static Dictionary<string, object> _dicExtraVersion = new Dictionary<string, object>();
|
|
43
|
+
private static Dictionary<string, object> _dicIronSourceAdapterVersion = new Dictionary<string, object>();
|
|
44
|
+
|
|
45
|
+
private static string GetBuildNumber()
|
|
46
|
+
{
|
|
47
|
+
#if UNITY_ANDROID
|
|
48
|
+
return PlayerSettings.Android.bundleVersionCode.ToString();
|
|
49
|
+
#elif UNITY_IOS
|
|
50
|
+
return PlayerSettings.iOS.buildNumber;
|
|
51
|
+
#else
|
|
52
|
+
return string.Empty;
|
|
53
|
+
#endif
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private static string GetBuildTime()
|
|
57
|
+
{
|
|
58
|
+
return DateTime.Now.ToString("dd/MM/yyyy HH:mm");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public static int GetSkAdNetworkCount(string infoPlistPath)
|
|
62
|
+
{
|
|
63
|
+
int count = 0;
|
|
64
|
+
#if UNITY_IOS
|
|
65
|
+
if (!File.Exists(infoPlistPath))
|
|
66
|
+
{
|
|
67
|
+
Debug.LogWarning($"AmaGDK | [SDKVersionTracking] InfoPlist file is not exist!\n{infoPlistPath}\n");
|
|
68
|
+
return count;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
PlistDocument plist = new PlistDocument();
|
|
72
|
+
plist.ReadFromString(File.ReadAllText(infoPlistPath));
|
|
73
|
+
PlistElementDict rootDict = plist.root;
|
|
74
|
+
if (!rootDict.values.ContainsKey("SKAdNetworkItems"))
|
|
75
|
+
return count;
|
|
76
|
+
|
|
77
|
+
count = rootDict.values["SKAdNetworkItems"].AsArray().values.Count;
|
|
78
|
+
return count;
|
|
79
|
+
#else
|
|
80
|
+
return count;
|
|
81
|
+
#endif
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public static List<string> GetAllAndroidPermissions()
|
|
85
|
+
{
|
|
86
|
+
#if UNITY_ANDROID
|
|
87
|
+
StringBuilder permissions = new StringBuilder();
|
|
88
|
+
string projectPath = Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length);
|
|
89
|
+
|
|
90
|
+
List<string> manifestPaths = new List<string>();
|
|
91
|
+
GetAllManifestPaths(projectPath, manifestPaths);
|
|
92
|
+
|
|
93
|
+
HashSet<string> uniquePermissions = new HashSet<string>();
|
|
94
|
+
|
|
95
|
+
HashSet<string> removePermission = new HashSet<string>();
|
|
96
|
+
|
|
97
|
+
foreach (var path in manifestPaths)
|
|
98
|
+
{
|
|
99
|
+
XmlDocument xmlDoc = GetXML(path);
|
|
100
|
+
var permissionNodes = xmlDoc.SelectNodes("manifest/uses-permission");
|
|
101
|
+
|
|
102
|
+
if (permissionNodes == null)
|
|
103
|
+
continue;
|
|
104
|
+
|
|
105
|
+
foreach (XmlNode node in permissionNodes)
|
|
106
|
+
{
|
|
107
|
+
if (node.Attributes == null)
|
|
108
|
+
continue;
|
|
109
|
+
if(node.Attributes["android:name"] == null)
|
|
110
|
+
continue;
|
|
111
|
+
|
|
112
|
+
string permissionName = node.Attributes["android:name"].Value;
|
|
113
|
+
|
|
114
|
+
XmlAttribute attribute = node.Attributes["tools:node"];
|
|
115
|
+
|
|
116
|
+
if (attribute != null && attribute.Value.Trim().Equals("remove")) removePermission.Add(permissionName);
|
|
117
|
+
|
|
118
|
+
uniquePermissions.Add(permissionName);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
uniquePermissions.ExceptWith(removePermission);
|
|
123
|
+
|
|
124
|
+
List<string> permissionList = uniquePermissions.ToList();
|
|
125
|
+
permissionList.Sort();
|
|
126
|
+
return permissionList;
|
|
127
|
+
#else
|
|
128
|
+
return new List<string>();
|
|
129
|
+
#endif
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private static void GetAllManifestPaths(string directory, List<string> manifestPaths)
|
|
133
|
+
{
|
|
134
|
+
string[] manifestFiles = Directory.GetFiles(directory, "AndroidManifest.xml", SearchOption.AllDirectories);
|
|
135
|
+
manifestPaths.AddRange(manifestFiles);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private static XmlDocument GetXML(string path)
|
|
139
|
+
{
|
|
140
|
+
XmlDocument xmlDoc = new XmlDocument();
|
|
141
|
+
try
|
|
142
|
+
{
|
|
143
|
+
if (File.Exists(path))
|
|
144
|
+
{
|
|
145
|
+
xmlDoc.LoadXml(File.ReadAllText(path));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (Exception exception)
|
|
149
|
+
{
|
|
150
|
+
Debug.LogError("Can't parse xml: " + path + "\n" + exception.Message);
|
|
151
|
+
}
|
|
152
|
+
return xmlDoc;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private static void AppendVersion(StringBuilder sb, string sectionName, Dictionary<string,object> dict)
|
|
156
|
+
{
|
|
157
|
+
if (dict == null || dict.Count == 0) return;
|
|
158
|
+
AppendVersion(sb, sectionName, dict
|
|
159
|
+
.Where(kvp=>kvp.Value != null)
|
|
160
|
+
.Select(kvp=> (kvp.Key, kvp.Value))
|
|
161
|
+
.OrderBy(kvp=>kvp.Key)
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private static void AppendVersion(StringBuilder sb, string sectionName, IEnumerable<(string, object)> list)
|
|
166
|
+
{
|
|
167
|
+
if (list == null) return;
|
|
168
|
+
sb.Append($"\n// {sectionName}\n");
|
|
169
|
+
sb.Append("//----------------------------------------------------\n");
|
|
170
|
+
|
|
171
|
+
foreach ((string key, object value) in list)
|
|
172
|
+
{
|
|
173
|
+
sb.Append($"{key, -MAX_VERSION_KEY_LENGTH}\t=\t{value.ToString().Replace("'", "")}\n");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public static List<(string, object)> GetBuildInfo()
|
|
178
|
+
{
|
|
179
|
+
return new List<(string, object)>()
|
|
180
|
+
{
|
|
181
|
+
("Version", Application.version),
|
|
182
|
+
("BuildNumber", GetBuildNumber()),
|
|
183
|
+
("BuildTime", GetBuildTime()),
|
|
184
|
+
("UnityVersion", Application.unityVersion)
|
|
185
|
+
#if UNITY_ANDROID
|
|
186
|
+
,("API Level", (int)PlayerSettings.Android.targetSdkVersion)
|
|
187
|
+
#endif
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private static void WriteToStreamingAssets()
|
|
192
|
+
{
|
|
193
|
+
try
|
|
194
|
+
{
|
|
195
|
+
StringBuilder sb = new StringBuilder();
|
|
196
|
+
sb.Append("// Automatically generated by AmaGDK after every build\n");
|
|
197
|
+
|
|
198
|
+
AppendVersion(sb, "BUILD INFO", GetBuildInfo());
|
|
199
|
+
AppendVersion(sb, "THIRD PARTY", _dicThirdPartyVersion);
|
|
200
|
+
AppendVersion(sb, "IS ADAPTER", _dicIronSourceAdapterVersion);
|
|
201
|
+
AppendVersion(sb, "EXTRA", _dicExtraVersion);
|
|
202
|
+
|
|
203
|
+
string folderPath = Application.streamingAssetsPath;
|
|
204
|
+
string filePath = Path.Combine(folderPath, "SDKVersion.txt");
|
|
205
|
+
|
|
206
|
+
if (!Directory.Exists(folderPath))
|
|
207
|
+
{
|
|
208
|
+
Directory.CreateDirectory(folderPath);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
File.WriteAllText(filePath, sb.ToString());
|
|
212
|
+
Debug.Log("AmaGDK | [SDKVersionTracking] The third-party version logs have been written to: " + filePath);
|
|
213
|
+
AssetDatabase.Refresh();
|
|
214
|
+
}
|
|
215
|
+
catch (Exception e)
|
|
216
|
+
{
|
|
217
|
+
Debug.LogError("AmaGDK | [SDKVersionTracking] Exception encountered while logging local third-party versions: " + e);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
public int callbackOrder { get; }
|
|
222
|
+
public void OnPreprocessBuild(BuildReport report)
|
|
223
|
+
{
|
|
224
|
+
_dicThirdPartyVersion = AmaGDKExtra.GetThirdPartyVersions();
|
|
225
|
+
_dicIronSourceAdapterVersion = AmaGDKExtra.GetIronSourceAdapterVersions();
|
|
226
|
+
_dicExtraVersion = _getExtraSDKVersion?.Invoke() ?? _dicExtraVersion;
|
|
227
|
+
|
|
228
|
+
WriteToStreamingAssets();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
}
|
|
232
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -39,9 +39,8 @@ namespace Amanotes.Core
|
|
|
39
39
|
{
|
|
40
40
|
get
|
|
41
41
|
{
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return _cacheExisted.GetValueOrDefault();
|
|
42
|
+
_cacheExisted ??= GDKFileUtils.Exist(fileName);
|
|
43
|
+
return _cacheExisted.Value;
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
|
|
@@ -195,7 +194,7 @@ namespace Amanotes.Core
|
|
|
195
194
|
}
|
|
196
195
|
|
|
197
196
|
_fetchState = FetchState.Fetch;
|
|
198
|
-
adapter.FetchConfig(HasCache, (isSuccess, result) =>
|
|
197
|
+
adapter.FetchConfig(!HasCache, (isSuccess, result) =>
|
|
199
198
|
{
|
|
200
199
|
if (!isSuccess)
|
|
201
200
|
{
|
package/Runtime/AmaGDK.cs
CHANGED
|
@@ -17,14 +17,16 @@ namespace Amanotes.Core
|
|
|
17
17
|
{
|
|
18
18
|
public partial class AmaGDK : MonoBehaviour
|
|
19
19
|
{
|
|
20
|
-
public const string VERSION = "0.2.
|
|
20
|
+
public const string VERSION = "0.2.65-2";
|
|
21
21
|
|
|
22
22
|
internal static Status _status = Status.None;
|
|
23
23
|
private static ConfigAsset _config = null;
|
|
24
24
|
internal static readonly GDKGeoLocation GeoLocation = new GDKGeoLocation();
|
|
25
25
|
internal static readonly GDKServerTime ServerTime = new GDKServerTime();
|
|
26
|
+
private static readonly ForceQuitMonitor ForceQuit = new ForceQuitMonitor();
|
|
26
27
|
|
|
27
28
|
internal static bool _allowInit = false;
|
|
29
|
+
|
|
28
30
|
public bool autoInit = true;
|
|
29
31
|
|
|
30
32
|
internal static partial class Event
|
|
@@ -186,6 +188,8 @@ namespace Amanotes.Core
|
|
|
186
188
|
ServerTime.UpdateTime();
|
|
187
189
|
}
|
|
188
190
|
|
|
191
|
+
ForceQuit.Init();
|
|
192
|
+
|
|
189
193
|
_status = Status.Ready;
|
|
190
194
|
dispatcher.Dispatch(Event.GDK_READY);
|
|
191
195
|
|
|
@@ -107,7 +107,7 @@ namespace Amanotes.Core.Internal
|
|
|
107
107
|
private static bool presetEnableEventStat;
|
|
108
108
|
private static LogLevel presetLogLevel = LogLevel.Warning;
|
|
109
109
|
|
|
110
|
-
public static void
|
|
110
|
+
public static void LoadPresetConfig()
|
|
111
111
|
{
|
|
112
112
|
presetLogLevel = Config.common.logLevel;
|
|
113
113
|
Config.common.logLevel = LogLevel.Warning;
|
|
@@ -3,6 +3,7 @@ using System.Collections;
|
|
|
3
3
|
using System.Collections.Concurrent;
|
|
4
4
|
using System.Collections.Generic;
|
|
5
5
|
using System.IO;
|
|
6
|
+
using System.Linq;
|
|
6
7
|
using System.Reflection;
|
|
7
8
|
using System.Text;
|
|
8
9
|
using System.Text.RegularExpressions;
|
|
@@ -398,11 +399,7 @@ namespace Amanotes.Core.Internal
|
|
|
398
399
|
{
|
|
399
400
|
public static string TupleToJson(params (string key, object value)[] parameters)
|
|
400
401
|
{
|
|
401
|
-
var tupleToDict =
|
|
402
|
-
foreach (var item in parameters)
|
|
403
|
-
{
|
|
404
|
-
tupleToDict.Add(item.key, item.value);
|
|
405
|
-
}
|
|
402
|
+
var tupleToDict = parameters.ToDictionary(item => item.key, item => item.value);
|
|
406
403
|
return DictionaryToJson(tupleToDict);
|
|
407
404
|
}
|
|
408
405
|
|
|
@@ -419,13 +416,13 @@ namespace Amanotes.Core.Internal
|
|
|
419
416
|
Profiler.BeginSample("AmaGDK.JsonUtils.DictionaryToJson()");
|
|
420
417
|
var jsonSb = new StringBuilder();
|
|
421
418
|
jsonSb.Append("{");
|
|
422
|
-
|
|
419
|
+
bool isFirstElement = true;
|
|
423
420
|
|
|
424
421
|
foreach (DictionaryEntry entry in inputDict)
|
|
425
422
|
{
|
|
426
423
|
if (entry.Key == null || string.IsNullOrEmpty(entry.Key.ToString())) continue;
|
|
427
|
-
|
|
428
|
-
|
|
424
|
+
string key = entry.Key.ToString();
|
|
425
|
+
object value = entry.Value;
|
|
429
426
|
|
|
430
427
|
if (isFirstElement)
|
|
431
428
|
isFirstElement = false;
|
|
@@ -437,7 +434,7 @@ namespace Amanotes.Core.Internal
|
|
|
437
434
|
}
|
|
438
435
|
|
|
439
436
|
jsonSb.AppendFormat("{0}{1}", format ? "\n" : "", "}");
|
|
440
|
-
|
|
437
|
+
string result = jsonSb.ToString();
|
|
441
438
|
Profiler.EndSample();
|
|
442
439
|
|
|
443
440
|
return result;
|
|
@@ -446,7 +443,7 @@ namespace Amanotes.Core.Internal
|
|
|
446
443
|
private static void GetJsonIListValue(ref StringBuilder jsonSb, string key, IList list, bool format)
|
|
447
444
|
{
|
|
448
445
|
jsonSb.Append("[");
|
|
449
|
-
|
|
446
|
+
bool isFirstElement = true;
|
|
450
447
|
|
|
451
448
|
foreach (object item in list)
|
|
452
449
|
{
|
|
@@ -473,7 +470,7 @@ namespace Amanotes.Core.Internal
|
|
|
473
470
|
|
|
474
471
|
if (value is string)
|
|
475
472
|
{
|
|
476
|
-
|
|
473
|
+
string s = value.ToString();
|
|
477
474
|
if (checkJsonInString)
|
|
478
475
|
{
|
|
479
476
|
if (s.StartsWith("{") && s.EndsWith("}"))
|
|
@@ -497,34 +494,77 @@ namespace Amanotes.Core.Internal
|
|
|
497
494
|
return;
|
|
498
495
|
}
|
|
499
496
|
|
|
500
|
-
if (value is bool)
|
|
497
|
+
if (value is bool b)
|
|
501
498
|
{
|
|
502
|
-
jsonSb.Append(
|
|
499
|
+
jsonSb.Append(b ? "true" : "false");
|
|
503
500
|
return;
|
|
504
501
|
}
|
|
505
502
|
|
|
506
|
-
if (value is IDictionary)
|
|
503
|
+
if (value is IDictionary dictionary)
|
|
507
504
|
{
|
|
508
|
-
jsonSb.Append($"{DictionaryToJson(
|
|
505
|
+
jsonSb.Append($"{DictionaryToJson(dictionary, format)}");
|
|
509
506
|
return;
|
|
510
507
|
}
|
|
511
508
|
|
|
512
|
-
if (value is IList
|
|
509
|
+
if (value is IList list)
|
|
513
510
|
{
|
|
514
|
-
GetJsonIListValue(ref jsonSb, key,
|
|
511
|
+
GetJsonIListValue(ref jsonSb, key, list, format);
|
|
515
512
|
return;
|
|
516
513
|
}
|
|
517
514
|
|
|
518
515
|
jsonSb.Append($"{JsonUtility.ToJson(value, format)}");
|
|
519
516
|
}
|
|
520
517
|
|
|
521
|
-
|
|
518
|
+
/// <summary>
|
|
519
|
+
/// Escapes a string for inclusion in a JSON document according to RFC 8259.
|
|
520
|
+
/// For more details, see <a href="https://www.rfc-editor.org/rfc/rfc8259.txt">RFC 8259</a>.
|
|
521
|
+
/// </summary>
|
|
522
|
+
/// <param name="input">The input string to be escaped.</param>
|
|
523
|
+
/// <returns>The escaped string.</returns>
|
|
524
|
+
public static string EscapeJsonString(string input)
|
|
522
525
|
{
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
+
var sb = new StringBuilder();
|
|
527
|
+
foreach (char c in input)
|
|
528
|
+
{
|
|
529
|
+
switch (c)
|
|
530
|
+
{
|
|
531
|
+
case '\"': sb.Append("\\\""); break;
|
|
532
|
+
case '\\': sb.Append("\\\\"); break;
|
|
533
|
+
case '\b': sb.Append("\\b"); break;
|
|
534
|
+
case '\f': sb.Append("\\f"); break;
|
|
535
|
+
case '\n': sb.Append("\\n"); break;
|
|
536
|
+
case '\r': sb.Append("\\r"); break;
|
|
537
|
+
case '\t': sb.Append("\\t"); break;
|
|
538
|
+
default:
|
|
539
|
+
if (c < 0x20)
|
|
540
|
+
{
|
|
541
|
+
// Encode as \uXXXX for control characters
|
|
542
|
+
sb.Append("\\u" + ((int)c).ToString("X4"));
|
|
543
|
+
}
|
|
544
|
+
else if (char.IsSurrogate(c))
|
|
545
|
+
{
|
|
546
|
+
// Handle surrogate pairs
|
|
547
|
+
if (char.IsHighSurrogate(c))
|
|
548
|
+
{
|
|
549
|
+
sb.Append("\\u" + ((int)c).ToString("X4"));
|
|
550
|
+
}
|
|
551
|
+
else if (char.IsLowSurrogate(c))
|
|
552
|
+
{
|
|
553
|
+
sb.Append("\\u" + ((int)c).ToString("X4"));
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
else
|
|
557
|
+
{
|
|
558
|
+
// Keep characters as is if they are in the UTF-8 range
|
|
559
|
+
sb.Append(c);
|
|
560
|
+
}
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return sb.ToString();
|
|
526
565
|
}
|
|
527
566
|
|
|
567
|
+
|
|
528
568
|
//public static T[] FromJsonToArray<T>(string json)
|
|
529
569
|
//{
|
|
530
570
|
// if (!json.StartsWith("{\"items\":"))
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using UnityEngine;
|
|
4
|
+
|
|
5
|
+
namespace Amanotes.Core.Internal
|
|
6
|
+
{
|
|
7
|
+
public class ForceQuitMonitor : IOnApplicationFocus, IOnApplicationPause
|
|
8
|
+
{
|
|
9
|
+
[Serializable]
|
|
10
|
+
internal class AdsEvent
|
|
11
|
+
{
|
|
12
|
+
[NonSerialized]
|
|
13
|
+
internal const string EVENT_NAME = "force_quit";
|
|
14
|
+
[SerializeField]
|
|
15
|
+
internal string reason;
|
|
16
|
+
[SerializeField]
|
|
17
|
+
internal string detail;
|
|
18
|
+
|
|
19
|
+
internal Dictionary<string, object> Parameters => new Dictionary<string, object>
|
|
20
|
+
{
|
|
21
|
+
{"reason", reason},
|
|
22
|
+
{"detail", detail}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
[Serializable]
|
|
27
|
+
private class ForceQuitData : IJsonFile
|
|
28
|
+
{
|
|
29
|
+
[SerializeField]
|
|
30
|
+
internal AdsEvent adsEvent;
|
|
31
|
+
[NonSerialized]
|
|
32
|
+
internal bool isDirty;
|
|
33
|
+
|
|
34
|
+
internal void SaveIfDirty(bool forceSave = false)
|
|
35
|
+
{
|
|
36
|
+
if (!isDirty && !forceSave) return;
|
|
37
|
+
this.SaveJsonToFile();
|
|
38
|
+
isDirty = false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private readonly ForceQuitData localData = new ForceQuitData();
|
|
43
|
+
|
|
44
|
+
private string GetStringValueOrDefault(Dictionary<string, object> dict, string key, string defaultValue = "")
|
|
45
|
+
{
|
|
46
|
+
return dict.TryGetValue(key, out object result)
|
|
47
|
+
? result.ToString().Trim().ToLower()
|
|
48
|
+
: defaultValue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public void OnLogEvent(string eventNameToSend, Dictionary<string, object> parameters)
|
|
52
|
+
{
|
|
53
|
+
switch (eventNameToSend)
|
|
54
|
+
{
|
|
55
|
+
case "videoads_show_called":
|
|
56
|
+
localData.adsEvent = new AdsEvent { reason = "watch_videoads" };
|
|
57
|
+
localData.isDirty = true;
|
|
58
|
+
break;
|
|
59
|
+
case "fullads_show_called":
|
|
60
|
+
localData.adsEvent = new AdsEvent { reason = "watch_fullads" };
|
|
61
|
+
localData.isDirty = true;
|
|
62
|
+
break;
|
|
63
|
+
case "ad_impression":
|
|
64
|
+
string adFormat = GetStringValueOrDefault(parameters, "ad_format");
|
|
65
|
+
if (adFormat == "rewarded_video" || adFormat == "interstitial") break;
|
|
66
|
+
|
|
67
|
+
if (localData.adsEvent != null)
|
|
68
|
+
{
|
|
69
|
+
var detailParams = new Dictionary<string, object>
|
|
70
|
+
{
|
|
71
|
+
["ad_source"] = GetStringValueOrDefault(parameters, "ad_source"),
|
|
72
|
+
["ad_unit_name"] = GetStringValueOrDefault(parameters, "ad_unit_name")
|
|
73
|
+
};
|
|
74
|
+
localData.adsEvent.detail = JsonUtils.DictionaryToJson(detailParams);
|
|
75
|
+
localData.SaveIfDirty(true);
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
case "videoads_show_notready":
|
|
79
|
+
case "videoads_finish":
|
|
80
|
+
case "fullads_show_notready":
|
|
81
|
+
case "fullads_finish":
|
|
82
|
+
localData.adsEvent = null;
|
|
83
|
+
localData.isDirty = true;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public void Init()
|
|
89
|
+
{
|
|
90
|
+
GDKUtils.AddUnityCallbacks(this);
|
|
91
|
+
OnAppOpen();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public void OnAppOpen()
|
|
95
|
+
{
|
|
96
|
+
localData.LoadJsonFromFile();
|
|
97
|
+
if (localData.adsEvent == null || string.IsNullOrWhiteSpace(localData.adsEvent.reason)) return;
|
|
98
|
+
AmaGDK.Analytics.LogEvent(AdsEvent.EVENT_NAME, localData.adsEvent.Parameters);
|
|
99
|
+
localData.adsEvent = null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public void OnApplicationFocus(bool hasFocus)
|
|
103
|
+
{
|
|
104
|
+
if (!hasFocus) localData.SaveIfDirty();
|
|
105
|
+
}
|
|
106
|
+
public void OnApplicationPause(bool pauseStatus)
|
|
107
|
+
{
|
|
108
|
+
if (pauseStatus) localData.SaveIfDirty();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|