com.amanotes.gdk 0.2.12 → 0.2.14
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/CHANGELOG.md +9 -0
- package/Editor/AmaGDKEditor.cs +21 -4
- package/Editor/AssemblyInfo.cs +5 -0
- package/Editor/AssemblyInfo.cs.meta +11 -0
- package/Editor/EmbedRemoteConfigAssetInspector.cs +191 -0
- package/Editor/EmbedRemoteConfigAssetInspector.cs.meta +11 -0
- package/Editor/Utils/AmaGDKEditor.Utils.cs +49 -0
- package/Extra/AmaGDKInstaller.unitypackage +0 -0
- package/Extra/AmaGDKProject.unitypackage +0 -0
- package/Packages/AdjustAdapter_v4.33.0.unitypackage +0 -0
- package/Packages/AmaGDKConfig.unitypackage +0 -0
- package/Packages/AmaGDKExample.unitypackage +0 -0
- package/Packages/AmaGDKTest.unitypackage +0 -0
- package/Packages/AmaPassport.ATTSupport.unitypackage +0 -0
- package/Packages/AmaPassportAdapter_v1.0.0.unitypackage +0 -0
- package/Packages/AppsFlyerAdapter_v6.5.4.unitypackage +0 -0
- package/Packages/FirebaseAnalyticsAdapter_v9.1.0.unitypackage +0 -0
- package/Packages/FirebaseRemoteConfigAdapter_v9.1.0.unitypackage +0 -0
- package/Packages/IronsourceAdapter_v7.2.6.unitypackage +0 -0
- package/Runtime/AmaGDK.Ads.cs +46 -14
- package/Runtime/AmaGDK.Analytics.cs +23 -4
- package/Runtime/AmaGDK.Internal.cs +0 -9
- package/Runtime/AmaGDK.RemoteConfig.cs +77 -47
- package/Runtime/AmaGDK.UserProfile.cs +2 -2
- package/Runtime/AmaGDK.Utils.cs +34 -6
- package/Runtime/AmaGDK.cs +50 -2
- package/Runtime/AnalyticQualityAsset.cs +218 -0
- package/Runtime/AnalyticQualityAsset.cs.meta +11 -0
- package/Runtime/AssemblyInfo.cs +2 -1
- package/Runtime/EmbedRemoteConfigAsset.cs +59 -0
- package/Runtime/EmbedRemoteConfigAsset.cs.meta +11 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.14 - 2023-08-08]
|
|
4
|
+
### Support multiple embed remote config and load config by id
|
|
5
|
+
### Clear cached remote config on new app update
|
|
6
|
+
### Override remote config value in editor mode
|
|
7
|
+
### New feature: Analytics Quality
|
|
8
|
+
|
|
9
|
+
## [0.2.13 - 2023-07-20]
|
|
10
|
+
### Fix error Remote Config JSON caused by special chars (", ', \)
|
|
11
|
+
|
|
3
12
|
## [0.2.12 - 2023-07-07]
|
|
4
13
|
### Restructure GDK - symlink GDK source to unity projects
|
|
5
14
|
### Unity 2019 compatible - no null coalescing
|
package/Editor/AmaGDKEditor.cs
CHANGED
|
@@ -3,6 +3,7 @@ using System.Text;
|
|
|
3
3
|
using Amanotes.Core;
|
|
4
4
|
using Amanotes.Core.Internal;
|
|
5
5
|
using System.Collections.Generic;
|
|
6
|
+
using System.IO;
|
|
6
7
|
using UnityEditor;
|
|
7
8
|
using UnityEngine;
|
|
8
9
|
using static Amanotes.Core.Internal.Logging;
|
|
@@ -23,6 +24,8 @@ namespace Amanotes.Editor
|
|
|
23
24
|
private static bool showConfigDetail;
|
|
24
25
|
private static bool showAdapterInstaller;
|
|
25
26
|
private static bool showExampleDetail;
|
|
27
|
+
private bool adapterScanned;
|
|
28
|
+
private readonly List<string> lstAdapter = new List<string>();
|
|
26
29
|
|
|
27
30
|
public static Color btnColor;
|
|
28
31
|
public static float btnLerp;
|
|
@@ -119,14 +122,28 @@ namespace Amanotes.Editor
|
|
|
119
122
|
EditorGUILayout.ObjectField(configAsset, configAsset.GetType(), false);
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
private void UpdateListAdapter()
|
|
126
|
+
{
|
|
127
|
+
if(adapterScanned)
|
|
128
|
+
return;
|
|
129
|
+
|
|
130
|
+
adapterScanned = true;
|
|
131
|
+
|
|
132
|
+
var adapterPath = Directory.GetFiles(Res.PACKAGE_PATH, "*Adapter*.unitypackage", SearchOption.AllDirectories);
|
|
133
|
+
|
|
134
|
+
for (int i = 0; i < adapterPath.Length; i++)
|
|
135
|
+
{
|
|
136
|
+
lstAdapter.Add(Path.GetFileName(adapterPath[i]));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
122
139
|
private void DrawGUI_AdapterList()
|
|
123
140
|
{
|
|
124
|
-
|
|
141
|
+
UpdateListAdapter();
|
|
142
|
+
foreach (var adapter in lstAdapter)
|
|
125
143
|
{
|
|
126
|
-
|
|
127
|
-
if (GUILayout.Button(ObjectNames.NicifyVariableName(adapterName)))
|
|
144
|
+
if (GUILayout.Button(ObjectNames.NicifyVariableName(adapter)))
|
|
128
145
|
{
|
|
129
|
-
var package = $"{Res.PACKAGE_PATH}{
|
|
146
|
+
var package = $"{Res.PACKAGE_PATH}{adapter}";
|
|
130
147
|
AssetDatabase.ImportPackage(package, false);
|
|
131
148
|
}
|
|
132
149
|
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Text;
|
|
3
|
+
using Amanotes.Core.Internal;
|
|
4
|
+
using UnityEditor;
|
|
5
|
+
using UnityEngine;
|
|
6
|
+
|
|
7
|
+
using static Amanotes.Core.Internal.Logging;
|
|
8
|
+
|
|
9
|
+
namespace Amanotes.Editor
|
|
10
|
+
{
|
|
11
|
+
[CustomEditor(typeof(EmbedRemoteConfigAsset))]
|
|
12
|
+
internal class EmbedRemoteConfigAssetInspector : UnityEditor.Editor
|
|
13
|
+
{
|
|
14
|
+
private EmbedRemoteConfigAsset remoteConfigAsset;
|
|
15
|
+
private SerializedProperty overrideInEditorProperty;
|
|
16
|
+
private SerializedProperty overrideConfigProperty;
|
|
17
|
+
private SerializedProperty configsProperty;
|
|
18
|
+
private bool[] foldouts;
|
|
19
|
+
|
|
20
|
+
private void OnEnable()
|
|
21
|
+
{
|
|
22
|
+
remoteConfigAsset = (EmbedRemoteConfigAsset)target;
|
|
23
|
+
overrideInEditorProperty = serializedObject.FindProperty("overrideInEditor");
|
|
24
|
+
overrideConfigProperty = serializedObject.FindProperty("overrideConfig");
|
|
25
|
+
configsProperty = serializedObject.FindProperty("configs");
|
|
26
|
+
foldouts = new bool[configsProperty.arraySize];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public override void OnInspectorGUI()
|
|
30
|
+
{
|
|
31
|
+
EditorGUILayout.BeginHorizontal();
|
|
32
|
+
EditorGUILayout.LabelField("Active Id:");
|
|
33
|
+
EditorGUILayout.LabelField(remoteConfigAsset.activeId);
|
|
34
|
+
EditorGUILayout.EndHorizontal();
|
|
35
|
+
|
|
36
|
+
serializedObject.Update();
|
|
37
|
+
|
|
38
|
+
DrawPropertiesExcluding(serializedObject, "m_Script", "activeId", "configs", "overrideConfig");
|
|
39
|
+
|
|
40
|
+
if (overrideInEditorProperty.boolValue)
|
|
41
|
+
{
|
|
42
|
+
EditorGUILayout.PropertyField(overrideConfigProperty);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (GUILayout.Button("Add a config set"))
|
|
46
|
+
{
|
|
47
|
+
configsProperty.arraySize++;
|
|
48
|
+
int lastIndex = configsProperty.arraySize - 1;
|
|
49
|
+
var config= configsProperty.GetArrayElementAtIndex(lastIndex);
|
|
50
|
+
config.FindPropertyRelative("id").stringValue = $"config{lastIndex}";
|
|
51
|
+
foldouts = AddElementToArray(foldouts, false);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (int i = 0; i < configsProperty.arraySize; i++)
|
|
55
|
+
{
|
|
56
|
+
SerializedProperty configProperty = configsProperty.GetArrayElementAtIndex(i);
|
|
57
|
+
SerializedProperty idProperty = configProperty.FindPropertyRelative("id");
|
|
58
|
+
SerializedProperty fetchUrlProperty = configProperty.FindPropertyRelative("fetchUrl");
|
|
59
|
+
SerializedProperty itemsProperty = configProperty.FindPropertyRelative("items");
|
|
60
|
+
|
|
61
|
+
EditorGUILayout.BeginVertical(GUI.skin.box);
|
|
62
|
+
foldouts[i] = EditorGUILayout.Foldout(foldouts[i], idProperty.stringValue);
|
|
63
|
+
if (foldouts[i])
|
|
64
|
+
{
|
|
65
|
+
EditorGUI.indentLevel++;
|
|
66
|
+
EditorGUILayout.BeginHorizontal();
|
|
67
|
+
if (GUILayout.Button("Set active", GUILayout.MinWidth(100)))
|
|
68
|
+
{
|
|
69
|
+
remoteConfigAsset.SetActiveConfig(idProperty.stringValue);
|
|
70
|
+
}
|
|
71
|
+
if (GUILayout.Button("Fetch", GUILayout.MinWidth(80)))
|
|
72
|
+
{
|
|
73
|
+
var url = fetchUrlProperty.stringValue;
|
|
74
|
+
var currentConfigIndex = i;
|
|
75
|
+
if (string.IsNullOrWhiteSpace(url))
|
|
76
|
+
{
|
|
77
|
+
ShowError("Please fill the fetch URL first");
|
|
78
|
+
}
|
|
79
|
+
else
|
|
80
|
+
{
|
|
81
|
+
WebRequest.Get(url, onProgress: FetchProgress, onSuccess: s => FillEmbedConfig(currentConfigIndex, s), onError:ShowError);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (GUILayout.Button("Remove", GUILayout.MinWidth(80)))
|
|
85
|
+
{
|
|
86
|
+
if (remoteConfigAsset.ActiveConfig?.id == idProperty.stringValue)
|
|
87
|
+
{
|
|
88
|
+
remoteConfigAsset.SetActiveConfig(string.Empty);
|
|
89
|
+
}
|
|
90
|
+
configsProperty.DeleteArrayElementAtIndex(i);
|
|
91
|
+
foldouts = RemoveElementAtIndex(foldouts, i);
|
|
92
|
+
}
|
|
93
|
+
if (GUILayout.Button("Copy", GUILayout.MinWidth(80)))
|
|
94
|
+
{
|
|
95
|
+
GUIUtility.systemCopyBuffer = KeyValueToText(itemsProperty);
|
|
96
|
+
}
|
|
97
|
+
if (GUILayout.Button("Paste", GUILayout.MinWidth(80)))
|
|
98
|
+
{
|
|
99
|
+
FillEmbedConfig(i, GUIUtility.systemCopyBuffer);
|
|
100
|
+
}
|
|
101
|
+
EditorGUILayout.EndHorizontal();
|
|
102
|
+
EditorGUILayout.PropertyField(idProperty);
|
|
103
|
+
EditorGUILayout.PropertyField(fetchUrlProperty);
|
|
104
|
+
EditorGUILayout.PropertyField(itemsProperty);
|
|
105
|
+
|
|
106
|
+
EditorGUI.indentLevel--;
|
|
107
|
+
}
|
|
108
|
+
EditorGUILayout.EndVertical();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
serializedObject.ApplyModifiedProperties();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private T[] AddElementToArray<T>(T[] array, T element)
|
|
115
|
+
{
|
|
116
|
+
T[] newArray = new T[array.Length + 1];
|
|
117
|
+
array.CopyTo(newArray, 0);
|
|
118
|
+
newArray[array.Length] = element;
|
|
119
|
+
return newArray;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private T[] RemoveElementAtIndex<T>(T[] array, int index)
|
|
123
|
+
{
|
|
124
|
+
T[] newArray = new T[array.Length - 1];
|
|
125
|
+
for (int i = 0, j = 0; i < array.Length; i++)
|
|
126
|
+
{
|
|
127
|
+
if (i != index)
|
|
128
|
+
{
|
|
129
|
+
newArray[j] = array[i];
|
|
130
|
+
j++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return newArray;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private void FetchProgress(float progress)
|
|
137
|
+
{
|
|
138
|
+
EditorUtility.DisplayProgressBar("Fetching default remote config", "Please wait...", progress);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private void FillEmbedConfig(int configIndex, string rawText)
|
|
142
|
+
{
|
|
143
|
+
try
|
|
144
|
+
{
|
|
145
|
+
string[] lines = rawText.Split("\n");
|
|
146
|
+
KeyValue[] arrKeyValue = new KeyValue[lines.Length];
|
|
147
|
+
for (int i = 0; i < lines.Length; i++)
|
|
148
|
+
{
|
|
149
|
+
var line = lines[i];
|
|
150
|
+
var idx = line.IndexOf('\t');
|
|
151
|
+
if (idx == -1)
|
|
152
|
+
{
|
|
153
|
+
LogWarning($"[RemoteConfig] Cannot get (key, value) at line {i+1}. Content:\n<{line}>");
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
arrKeyValue[i] = new KeyValue
|
|
157
|
+
{
|
|
158
|
+
key = line.Substring(0, idx),
|
|
159
|
+
value = line.Substring(idx + 1)
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
remoteConfigAsset.configs[configIndex].items = arrKeyValue;
|
|
163
|
+
}
|
|
164
|
+
catch (Exception e)
|
|
165
|
+
{
|
|
166
|
+
LogWarning(e.Message);
|
|
167
|
+
}
|
|
168
|
+
finally
|
|
169
|
+
{
|
|
170
|
+
EditorUtility.ClearProgressBar();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private string KeyValueToText(SerializedProperty items)
|
|
175
|
+
{
|
|
176
|
+
StringBuilder sb = new StringBuilder();
|
|
177
|
+
for (int i = 0; i < items.arraySize; i++)
|
|
178
|
+
{
|
|
179
|
+
SerializedProperty c = items.GetArrayElementAtIndex(i);
|
|
180
|
+
sb.AppendLine($"{c.FindPropertyRelative("key").stringValue}\t{c.FindPropertyRelative("value").stringValue}");
|
|
181
|
+
}
|
|
182
|
+
return sb.ToString();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private void ShowError(string error)
|
|
186
|
+
{
|
|
187
|
+
EditorUtility.ClearProgressBar();
|
|
188
|
+
EditorUtility.DisplayDialog("Error", error, "OK");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
using System;
|
|
2
2
|
using System.Collections;
|
|
3
|
+
using System.Collections.Generic;
|
|
3
4
|
using UnityEditor;
|
|
4
5
|
using UnityEngine;
|
|
6
|
+
using UnityEngine.Networking;
|
|
7
|
+
|
|
5
8
|
namespace Amanotes.Editor
|
|
6
9
|
{
|
|
7
10
|
internal static class Utils
|
|
@@ -31,4 +34,50 @@ namespace Amanotes.Editor
|
|
|
31
34
|
EditorApplication.update += closureCallback;
|
|
32
35
|
}
|
|
33
36
|
}
|
|
37
|
+
|
|
38
|
+
internal static class WebRequest
|
|
39
|
+
{
|
|
40
|
+
internal static void Get(string url, Dictionary<string, string> headers = null, Action<float> onProgress = null, Action<string> onSuccess = null, Action<string> onError = null)
|
|
41
|
+
{
|
|
42
|
+
GetCoroutine(url, headers, onProgress, onSuccess, onError).StartEditorCoroutine();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private static IEnumerator GetCoroutine(string url, Dictionary<string, string> headers = null, Action<float> onProgress = null, Action<string> onSuccess = null, Action<string> onError = null)
|
|
46
|
+
{
|
|
47
|
+
using var request = UnityWebRequest.Get(url);
|
|
48
|
+
// Add headers to the request
|
|
49
|
+
if (headers != null)
|
|
50
|
+
{
|
|
51
|
+
foreach (var header in headers)
|
|
52
|
+
{
|
|
53
|
+
request.SetRequestHeader(header.Key, header.Value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Send the request
|
|
58
|
+
request.timeout = 60;
|
|
59
|
+
yield return request.SendWebRequest();
|
|
60
|
+
|
|
61
|
+
while (request.isDone == false)
|
|
62
|
+
{
|
|
63
|
+
onProgress?.Invoke(request.downloadProgress);
|
|
64
|
+
yield return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check for errors
|
|
68
|
+
#pragma warning disable CS0618
|
|
69
|
+
if (request.isNetworkError || request.isHttpError)
|
|
70
|
+
#pragma warning restore CS0618
|
|
71
|
+
{
|
|
72
|
+
Debug.LogError("Error: " + request.error);
|
|
73
|
+
onError?.Invoke(request.error);
|
|
74
|
+
}
|
|
75
|
+
else
|
|
76
|
+
{
|
|
77
|
+
// Process the response
|
|
78
|
+
string responseText = request.downloadHandler.text;
|
|
79
|
+
onSuccess?.Invoke(responseText);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
34
83
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/Runtime/AmaGDK.Ads.cs
CHANGED
|
@@ -85,7 +85,7 @@ namespace Amanotes.Core
|
|
|
85
85
|
public AdStat interstitial = new AdStat();
|
|
86
86
|
public AdStat reward = new AdStat();
|
|
87
87
|
|
|
88
|
-
internal int
|
|
88
|
+
internal int TimeSinceLastShow(AdType adType)
|
|
89
89
|
{
|
|
90
90
|
return (int)(Time.realtimeSinceStartup - (adType == AdType.Interstitial ? interstitial.lastSuccessTime : reward.lastSuccessTime));
|
|
91
91
|
}
|
|
@@ -138,11 +138,11 @@ namespace Amanotes.Core
|
|
|
138
138
|
_adapter?.rewardVideo.StartLoadAd();
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
public static bool
|
|
142
|
-
public static bool
|
|
143
|
-
public static bool
|
|
144
|
-
public static bool
|
|
145
|
-
|
|
141
|
+
public static bool HasInterstitial => _adapter.interstitial.hasAd;
|
|
142
|
+
public static bool HasRewardedVideo => _adapter.rewardVideo.hasAd;
|
|
143
|
+
public static bool IsBannerShowing => _isBannerShowing;
|
|
144
|
+
public static bool IsAdShowing => context != null;
|
|
145
|
+
|
|
146
146
|
private static bool ClearPrevContext()
|
|
147
147
|
{
|
|
148
148
|
if (context == null) return true;
|
|
@@ -176,6 +176,15 @@ namespace Amanotes.Core
|
|
|
176
176
|
return context;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
public static bool IsInterstitialShowing
|
|
180
|
+
{
|
|
181
|
+
get
|
|
182
|
+
{
|
|
183
|
+
if (context == null) return false;
|
|
184
|
+
return context.adType == AdType.Interstitial;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
179
188
|
public static IAdCallback ShowRewardedVideo(string placementName = null, Dictionary<string, object> userData = null)
|
|
180
189
|
{
|
|
181
190
|
if (!ClearPrevContext()) return null;
|
|
@@ -184,19 +193,36 @@ namespace Amanotes.Core
|
|
|
184
193
|
_adapter.rewardVideo.StartShowAd();
|
|
185
194
|
return context;
|
|
186
195
|
}
|
|
196
|
+
|
|
197
|
+
public static bool IsRewardedVideoShowing
|
|
198
|
+
{
|
|
199
|
+
get
|
|
200
|
+
{
|
|
201
|
+
if (context == null) return false;
|
|
202
|
+
return context.adType == AdType.VideoReward;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
187
205
|
|
|
188
|
-
private static bool
|
|
189
|
-
public static void ShowBanner()
|
|
206
|
+
private static bool _isBannerShowing;
|
|
207
|
+
public static void ShowBanner(BannerPosition? bannerPosition = null)
|
|
190
208
|
{
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
209
|
+
if (_isBannerShowing) return;
|
|
210
|
+
_isBannerShowing = true;
|
|
211
|
+
|
|
212
|
+
if (bannerPosition.HasValue)
|
|
213
|
+
{
|
|
214
|
+
_adapter.ShowBanner(bannerPosition.Value);
|
|
215
|
+
}
|
|
216
|
+
else
|
|
217
|
+
{
|
|
218
|
+
_adapter.ShowBanner(_adConfig.bannerPosition);
|
|
219
|
+
}
|
|
194
220
|
}
|
|
195
221
|
|
|
196
222
|
public static void HideBanner()
|
|
197
223
|
{
|
|
198
|
-
if (!
|
|
199
|
-
|
|
224
|
+
if (!_isBannerShowing) return;
|
|
225
|
+
_isBannerShowing = false;
|
|
200
226
|
_adapter.HideBanner();
|
|
201
227
|
}
|
|
202
228
|
|
|
@@ -210,6 +236,12 @@ namespace Amanotes.Core
|
|
|
210
236
|
else
|
|
211
237
|
_adapter.rewardVideo.StopShowAd();
|
|
212
238
|
}
|
|
239
|
+
|
|
240
|
+
public static int TimeSinceLastInterstitial => localData.TimeSinceLastShow(AdType.Interstitial);
|
|
241
|
+
|
|
242
|
+
public static int TimeSinceLastVideoReward => localData.TimeSinceLastShow(AdType.VideoReward);
|
|
243
|
+
|
|
244
|
+
public static int TimeSinceLastAd => Math.Min(TimeSinceLastInterstitial, TimeSinceLastVideoReward) ;
|
|
213
245
|
}
|
|
214
246
|
}
|
|
215
247
|
}
|
|
@@ -287,7 +319,7 @@ namespace Amanotes.Core.Internal
|
|
|
287
319
|
// Stats
|
|
288
320
|
public float showCallAt;
|
|
289
321
|
public float showFinishAt;
|
|
290
|
-
|
|
322
|
+
|
|
291
323
|
public ShowAdContext(AdType adType, string placementName, Dictionary<string, object> userData)
|
|
292
324
|
{
|
|
293
325
|
this.adType = adType;
|
|
@@ -301,7 +301,8 @@ namespace Amanotes.Core
|
|
|
301
301
|
internal static void ProcessEventQueue()
|
|
302
302
|
{
|
|
303
303
|
if (eventQueue.Count <= 0) return;
|
|
304
|
-
|
|
304
|
+
SDKConfig common = Config.common;
|
|
305
|
+
bool showAnalyticsLog = common.showAnalyticsLog;
|
|
305
306
|
sessionStat.ClearLastFrameEventInfo();
|
|
306
307
|
|
|
307
308
|
while (eventQueue.Count > 0)
|
|
@@ -317,9 +318,11 @@ namespace Amanotes.Core
|
|
|
317
318
|
}
|
|
318
319
|
|
|
319
320
|
string eventNameToSend = GetEventNameToSend(eventData.eventName, countInSession, totalCount);
|
|
320
|
-
if (
|
|
321
|
+
if (common.normalizeEventName) NormalizeEventName(ref eventNameToSend);
|
|
321
322
|
|
|
322
|
-
|
|
323
|
+
if (showAnalyticsLog) Debug.Log($"AmaGDK | Event <{eventNameToSend}> (in session: {countInSession}, total: {totalCount})\n{JsonUtils.DictionaryToJson(eventData.parameters, true)}");
|
|
324
|
+
|
|
325
|
+
common.quality.CheckEventQuality(eventData.eventName, eventData.parameters);
|
|
323
326
|
|
|
324
327
|
var inUseAdapterIDs = new List<string>();
|
|
325
328
|
foreach (AdapterContext m in listAdapters)
|
|
@@ -585,7 +588,7 @@ namespace Amanotes.Core
|
|
|
585
588
|
contents.AppendLine(stat.ToTsv());
|
|
586
589
|
}
|
|
587
590
|
|
|
588
|
-
|
|
591
|
+
GDKFileUtils.SaveAppend(fileName, contents.ToString());
|
|
589
592
|
}
|
|
590
593
|
}
|
|
591
594
|
}
|
|
@@ -731,12 +734,28 @@ namespace Amanotes.Core
|
|
|
731
734
|
|
|
732
735
|
namespace Amanotes.Core.Internal
|
|
733
736
|
{
|
|
737
|
+
[Serializable]
|
|
738
|
+
public class AnalyticQualityConfig
|
|
739
|
+
{
|
|
740
|
+
public bool enable = false;
|
|
741
|
+
public AnalyticQualityAsset qualityAsset;
|
|
742
|
+
|
|
743
|
+
public void CheckEventQuality(string eventName, Dictionary<string, object> parameters)
|
|
744
|
+
{
|
|
745
|
+
if (enable && qualityAsset != null)
|
|
746
|
+
{
|
|
747
|
+
qualityAsset.CheckEventQuality(eventName, parameters);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
734
752
|
public partial class SDKConfig
|
|
735
753
|
{
|
|
736
754
|
public bool showAnalyticsLog = true;
|
|
737
755
|
public bool normalizeEventName = true;
|
|
738
756
|
public bool checkMigrationIntegrity = true;
|
|
739
757
|
public bool enableEventStat;
|
|
758
|
+
public AnalyticQualityConfig quality;
|
|
740
759
|
}
|
|
741
760
|
|
|
742
761
|
public interface IAnalyticAdapter
|
|
@@ -40,15 +40,6 @@ namespace Amanotes.Core.Internal
|
|
|
40
40
|
public const string AMAGDK_PREFAB = BASE_PATH + "Runtime/AmaGDK.prefab";
|
|
41
41
|
public const string AMAGDK_CONFIG_PACKAGE = PACKAGE_PATH + "AmaGDKConfig.unitypackage";
|
|
42
42
|
|
|
43
|
-
public static readonly string[] AMAGDK_ADAPTERS =
|
|
44
|
-
{
|
|
45
|
-
"FirebaseAnalyticsAdapter_v9.1.0",
|
|
46
|
-
"AppsFlyerAdapter_v6.5.4",
|
|
47
|
-
"AdjustAdapter_v4.33.0",
|
|
48
|
-
"IronsourceAdapter_v7.2.6",
|
|
49
|
-
"FirebaseRemoteConfigAdapter_v9.1.0"
|
|
50
|
-
};
|
|
51
|
-
|
|
52
43
|
#if UNITY_EDITOR
|
|
53
44
|
|
|
54
45
|
[MenuItem("GameObject/AmaGDK/Add AmaGDK Prefab", false, 10)]
|
|
@@ -16,8 +16,7 @@ namespace Amanotes.Core
|
|
|
16
16
|
{
|
|
17
17
|
public enum RemoteConfigSource
|
|
18
18
|
{
|
|
19
|
-
|
|
20
|
-
DefaultFile,
|
|
19
|
+
EmbedConfig,
|
|
21
20
|
Cache,
|
|
22
21
|
Internet
|
|
23
22
|
}
|
|
@@ -26,7 +25,7 @@ namespace Amanotes.Core
|
|
|
26
25
|
public partial class RemoteConfig //Public
|
|
27
26
|
{
|
|
28
27
|
public static Action<bool> onFetchCompleted;
|
|
29
|
-
public static Dictionary<string, string> DictConfig
|
|
28
|
+
public static Dictionary<string, string> DictConfig => dictConfig;
|
|
30
29
|
public static RemoteConfigSource Source { private set; get; }
|
|
31
30
|
static bool? _cacheExisted = null;
|
|
32
31
|
public static bool HasCache
|
|
@@ -34,7 +33,7 @@ namespace Amanotes.Core
|
|
|
34
33
|
get
|
|
35
34
|
{
|
|
36
35
|
if (_cacheExisted == null)
|
|
37
|
-
_cacheExisted =
|
|
36
|
+
_cacheExisted = GDKFileUtils.Exist(fileName);
|
|
38
37
|
return _cacheExisted.GetValueOrDefault();
|
|
39
38
|
}
|
|
40
39
|
}
|
|
@@ -64,6 +63,15 @@ namespace Amanotes.Core
|
|
|
64
63
|
|
|
65
64
|
public static T Get<T>(string key, T defaultValue = default(T))
|
|
66
65
|
{
|
|
66
|
+
#if UNITY_EDITOR
|
|
67
|
+
//Override value in editor environment
|
|
68
|
+
if (adapterConfig.embedRemoteConfig.overrideInEditor)
|
|
69
|
+
{
|
|
70
|
+
foreach (var c in adapterConfig.embedRemoteConfig.overrideConfig)
|
|
71
|
+
if (c.key == key)
|
|
72
|
+
return (T)Convert.ChangeType(c.value, typeof(T));
|
|
73
|
+
}
|
|
74
|
+
#endif
|
|
67
75
|
if (dictConfig.ContainsKey(key))
|
|
68
76
|
return (T)Convert.ChangeType(dictConfig[key], typeof(T));
|
|
69
77
|
return defaultValue;
|
|
@@ -78,6 +86,13 @@ namespace Amanotes.Core
|
|
|
78
86
|
{
|
|
79
87
|
return JsonUtils.DictionaryToJson(dictConfig);
|
|
80
88
|
}
|
|
89
|
+
|
|
90
|
+
public static void SetActiveEmbedConfig(string id)
|
|
91
|
+
{
|
|
92
|
+
if (adapterConfig == null || adapterConfig.embedRemoteConfig == null)
|
|
93
|
+
LogWarning("[RemoteConfig] No embed remote config available");
|
|
94
|
+
adapterConfig.embedRemoteConfig.SetActiveConfig(id);
|
|
95
|
+
}
|
|
81
96
|
}
|
|
82
97
|
|
|
83
98
|
public partial class RemoteConfig //Internal
|
|
@@ -91,7 +106,7 @@ namespace Amanotes.Core
|
|
|
91
106
|
SaveCache,
|
|
92
107
|
|
|
93
108
|
LoadCache,
|
|
94
|
-
|
|
109
|
+
LoadEmbedConfig,
|
|
95
110
|
|
|
96
111
|
Success,
|
|
97
112
|
Fail
|
|
@@ -100,9 +115,15 @@ namespace Amanotes.Core
|
|
|
100
115
|
static Dictionary<string, string> dictConfig = new Dictionary<string, string>();
|
|
101
116
|
static string fileName = $"{nameof(RemoteConfig)}.kv";
|
|
102
117
|
static string base64Prefix = "base64:";
|
|
118
|
+
const string VERSION_PREFIX = "version:";
|
|
119
|
+
static int EmbedVersion =>
|
|
120
|
+
adapterConfig != null && adapterConfig.embedRemoteConfig != null
|
|
121
|
+
? adapterConfig.embedRemoteConfig.version
|
|
122
|
+
: 1;
|
|
103
123
|
|
|
104
124
|
static FetchState _state = FetchState.None;
|
|
105
|
-
internal static FetchState State
|
|
125
|
+
internal static FetchState State => _state;
|
|
126
|
+
|
|
106
127
|
static readonly Dictionary<FetchState, Action[]> fetchSM = new Dictionary<FetchState, Action[]>
|
|
107
128
|
{
|
|
108
129
|
{ FetchState.None, new Action[]{ ResolveDependency }},
|
|
@@ -110,8 +131,8 @@ namespace Amanotes.Core
|
|
|
110
131
|
{ FetchState.CheckInternet, new Action[]{ LoadCache, Fetch}},
|
|
111
132
|
{ FetchState.Fetch, new Action[]{ LoadCache, SaveCache}},
|
|
112
133
|
{ FetchState.SaveCache, new Action[]{ SetDone }},
|
|
113
|
-
{ FetchState.LoadCache, new Action[]{
|
|
114
|
-
{ FetchState.
|
|
134
|
+
{ FetchState.LoadCache, new Action[]{ LoadEmbedConfig, SetDone}},
|
|
135
|
+
{ FetchState.LoadEmbedConfig, new Action[]{ SetFail, SetDone}}
|
|
115
136
|
};
|
|
116
137
|
|
|
117
138
|
internal static IRemoteConfig adapter;
|
|
@@ -177,30 +198,17 @@ namespace Amanotes.Core
|
|
|
177
198
|
{
|
|
178
199
|
_state = FetchState.SaveCache;
|
|
179
200
|
StringBuilder sb = new StringBuilder();
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
bool needUpdateDefault = Application.isEditor && adapterConfig.autoUpdateDefaultValue;
|
|
183
|
-
List<KeyValue> list = needUpdateDefault ? new List<KeyValue>() : null;
|
|
184
201
|
|
|
202
|
+
//Add embedded remote config version to force load from embedded config after app update
|
|
203
|
+
sb.AppendLine($"{VERSION_PREFIX}{EmbedVersion}");
|
|
204
|
+
|
|
185
205
|
foreach (var c in dictConfig)
|
|
186
206
|
{
|
|
187
207
|
sb.AppendLine($"{c.Key}\t{Encode(c.Value)}");
|
|
188
|
-
if (!needUpdateDefault) continue;
|
|
189
|
-
list.Add(new KeyValue() { key = c.Key, value = c.Value });
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
#if UNITY_EDITOR
|
|
193
|
-
if (needUpdateDefault)
|
|
194
|
-
{
|
|
195
|
-
list.Sort((item1, item2) => string.Compare(item1.key, item2.key, StringComparison.Ordinal));
|
|
196
|
-
adapterConfig.defaultValue = list.ToArray();
|
|
197
|
-
EditorUtility.SetDirty(_config);
|
|
198
|
-
Log($"[RemoteConfig] Updated default remote config!");
|
|
199
208
|
}
|
|
200
|
-
#endif
|
|
201
209
|
|
|
202
|
-
string filePath =
|
|
203
|
-
|
|
210
|
+
string filePath = GDKFileUtils.GetPath(fileName);
|
|
211
|
+
GDKFileUtils.Save(filePath, sb.ToString());
|
|
204
212
|
Log($"[RemoteConfig] Saved remote config to {filePath}");
|
|
205
213
|
NextState();
|
|
206
214
|
}
|
|
@@ -218,17 +226,34 @@ namespace Amanotes.Core
|
|
|
218
226
|
try
|
|
219
227
|
{
|
|
220
228
|
dictConfig.Clear();
|
|
221
|
-
using (StreamReader sr = new StreamReader(
|
|
229
|
+
using (StreamReader sr = new StreamReader(GDKFileUtils.GetPath(fileName)))
|
|
222
230
|
{
|
|
231
|
+
//Check new app update
|
|
232
|
+
string firstLine = sr.ReadLine();
|
|
233
|
+
if (firstLine == null)
|
|
234
|
+
{
|
|
235
|
+
NextState(false);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (ShouldClearCache(firstLine))
|
|
239
|
+
{
|
|
240
|
+
GDKFileUtils.Delete(fileName);
|
|
241
|
+
string oldVersion = firstLine.StartsWith(VERSION_PREFIX) ? firstLine : "<none>";
|
|
242
|
+
Log($"[RemoteConfig] Cached remote config cleared due to embed version changed:\n{oldVersion} -> {EmbedVersion}");
|
|
243
|
+
NextState(false);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
//Read key-value
|
|
223
248
|
string line;
|
|
224
|
-
int
|
|
249
|
+
int lineNo = 1;
|
|
225
250
|
while ((line = sr.ReadLine()) != null)
|
|
226
251
|
{
|
|
227
|
-
|
|
252
|
+
lineNo++;
|
|
228
253
|
var idx = line.IndexOf('\t');
|
|
229
254
|
if (idx == -1)
|
|
230
255
|
{
|
|
231
|
-
LogWarning($"[RemoteConfig] Cannot get (key, value) at line {
|
|
256
|
+
LogWarning($"[RemoteConfig] Cannot get (key, value) at line {lineNo} of cache. Content:\n<{line}>");
|
|
232
257
|
continue;
|
|
233
258
|
}
|
|
234
259
|
string key = line.Substring(0, idx);
|
|
@@ -250,24 +275,30 @@ namespace Amanotes.Core
|
|
|
250
275
|
NextState(true);
|
|
251
276
|
}
|
|
252
277
|
|
|
253
|
-
static void
|
|
278
|
+
static void LoadEmbedConfig()
|
|
254
279
|
{
|
|
255
|
-
_state = FetchState.
|
|
280
|
+
_state = FetchState.LoadEmbedConfig;
|
|
281
|
+
|
|
282
|
+
if (adapterConfig == null || adapterConfig.embedRemoteConfig == null)
|
|
283
|
+
{
|
|
284
|
+
NextState(false);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
256
287
|
|
|
257
|
-
var
|
|
288
|
+
var config = adapterConfig.embedRemoteConfig.ActiveConfig;
|
|
258
289
|
|
|
259
|
-
if (
|
|
290
|
+
if (config == null || config.items.Length == 0)
|
|
260
291
|
{
|
|
261
292
|
NextState(false);
|
|
262
293
|
return;
|
|
263
294
|
}
|
|
264
295
|
|
|
265
|
-
foreach (var item in
|
|
296
|
+
foreach (var item in config.items)
|
|
266
297
|
{
|
|
267
298
|
dictConfig.Add(item.key, item.value);
|
|
268
299
|
}
|
|
269
|
-
Source = RemoteConfigSource.
|
|
270
|
-
Log("[RemoteConfig] Load from
|
|
300
|
+
Source = RemoteConfigSource.EmbedConfig;
|
|
301
|
+
Log("[RemoteConfig] Load from embed asset");
|
|
271
302
|
NextState(true);
|
|
272
303
|
}
|
|
273
304
|
|
|
@@ -301,6 +332,12 @@ namespace Amanotes.Core
|
|
|
301
332
|
s = s.Remove(0, base64Prefix.Length);
|
|
302
333
|
return Encoding.UTF8.GetString(Convert.FromBase64String(s));
|
|
303
334
|
}
|
|
335
|
+
|
|
336
|
+
static bool ShouldClearCache(string cacheVersion)
|
|
337
|
+
{
|
|
338
|
+
if (!cacheVersion.StartsWith(VERSION_PREFIX)) return true;
|
|
339
|
+
return cacheVersion != VERSION_PREFIX + EmbedVersion;
|
|
340
|
+
}
|
|
304
341
|
}
|
|
305
342
|
}
|
|
306
343
|
|
|
@@ -324,19 +361,12 @@ namespace Amanotes.Core.Internal
|
|
|
324
361
|
[Serializable]
|
|
325
362
|
public class RemoteConfigConfig
|
|
326
363
|
{
|
|
327
|
-
|
|
328
|
-
|
|
364
|
+
[SerializeField]
|
|
365
|
+
internal EmbedRemoteConfigAsset embedRemoteConfig;
|
|
329
366
|
public float firstFetchTimeOutInSeconds = 10;
|
|
330
367
|
public float defaultTimeOutInSeconds = 3;
|
|
331
368
|
}
|
|
332
|
-
|
|
333
|
-
[Serializable]
|
|
334
|
-
public class KeyValue
|
|
335
|
-
{
|
|
336
|
-
public string key;
|
|
337
|
-
public string value;
|
|
338
|
-
}
|
|
339
|
-
|
|
369
|
+
|
|
340
370
|
public interface IRemoteConfig
|
|
341
371
|
{
|
|
342
372
|
void ResolveDependencies(Action<bool> onComplete);
|
|
@@ -15,12 +15,12 @@ namespace Amanotes.Core
|
|
|
15
15
|
|
|
16
16
|
public void Save()
|
|
17
17
|
{
|
|
18
|
-
|
|
18
|
+
GDKFileUtils.Save(FILE_NAME, JsonUtility.ToJson(this));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
public UserProfile Load()
|
|
22
22
|
{
|
|
23
|
-
string json =
|
|
23
|
+
string json = GDKFileUtils.Load(FILE_NAME);
|
|
24
24
|
if (string.IsNullOrEmpty(json)) return this;
|
|
25
25
|
JsonUtility.FromJsonOverwrite(json, this);
|
|
26
26
|
return this;
|
package/Runtime/AmaGDK.Utils.cs
CHANGED
|
@@ -3,12 +3,13 @@ using System.Collections;
|
|
|
3
3
|
using System.Collections.Generic;
|
|
4
4
|
using System.IO;
|
|
5
5
|
using System.Text;
|
|
6
|
+
using System.Text.RegularExpressions;
|
|
6
7
|
using UnityEngine;
|
|
7
8
|
using static Amanotes.Core.Internal.Logging;
|
|
8
9
|
|
|
9
10
|
namespace Amanotes.Core.Internal
|
|
10
11
|
{
|
|
11
|
-
public class
|
|
12
|
+
public class GDKUtils
|
|
12
13
|
{
|
|
13
14
|
public static void DelayCall(float seconds, Action action)
|
|
14
15
|
{
|
|
@@ -22,7 +23,7 @@ namespace Amanotes.Core.Internal
|
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
internal class
|
|
26
|
+
internal class GDKFileUtils
|
|
26
27
|
{
|
|
27
28
|
|
|
28
29
|
private static string _basePath;
|
|
@@ -39,6 +40,7 @@ namespace Amanotes.Core.Internal
|
|
|
39
40
|
return _basePath;
|
|
40
41
|
}
|
|
41
42
|
}
|
|
43
|
+
|
|
42
44
|
internal static string GetPath(string fileName)
|
|
43
45
|
{
|
|
44
46
|
return Path.Combine(basePath, fileName);
|
|
@@ -161,6 +163,26 @@ namespace Amanotes.Core.Internal
|
|
|
161
163
|
LogWarningOnce(path + "\n" + ex);
|
|
162
164
|
}
|
|
163
165
|
}
|
|
166
|
+
|
|
167
|
+
internal static void ClearCacheData()
|
|
168
|
+
{
|
|
169
|
+
StringBuilder logBuilder = new StringBuilder();
|
|
170
|
+
logBuilder.AppendLine("[AmaGDK] All cached data & stats cleared");
|
|
171
|
+
|
|
172
|
+
if (!Directory.Exists(_basePath))
|
|
173
|
+
{
|
|
174
|
+
logBuilder.AppendLine("Cache path not exists: " + _basePath);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
string[] filePaths = Directory.GetFiles(_basePath);
|
|
179
|
+
foreach (string path in filePaths)
|
|
180
|
+
{
|
|
181
|
+
Delete(path);
|
|
182
|
+
logBuilder.AppendLine($"[deleted] - {path}");
|
|
183
|
+
}
|
|
184
|
+
Debug.Log(logBuilder);
|
|
185
|
+
}
|
|
164
186
|
}
|
|
165
187
|
|
|
166
188
|
public class ActionQueue
|
|
@@ -238,7 +260,7 @@ namespace Amanotes.Core.Internal
|
|
|
238
260
|
instance = result;
|
|
239
261
|
}
|
|
240
262
|
string fileName = instance.GetType().Name;
|
|
241
|
-
string json =
|
|
263
|
+
string json = GDKFileUtils.Load(fileName);
|
|
242
264
|
JsonUtility.FromJsonOverwrite(json, instance);
|
|
243
265
|
return instance;
|
|
244
266
|
}
|
|
@@ -247,7 +269,7 @@ namespace Amanotes.Core.Internal
|
|
|
247
269
|
{
|
|
248
270
|
string fileName = instance.GetType().Name;
|
|
249
271
|
string json = JsonUtility.ToJson(instance);
|
|
250
|
-
|
|
272
|
+
GDKFileUtils.Save(fileName, json);
|
|
251
273
|
}
|
|
252
274
|
}
|
|
253
275
|
|
|
@@ -342,8 +364,7 @@ namespace Amanotes.Core.Internal
|
|
|
342
364
|
return;
|
|
343
365
|
}
|
|
344
366
|
}
|
|
345
|
-
|
|
346
|
-
jsonSb.Append($"\"{value}\"");
|
|
367
|
+
jsonSb.Append($"\"{EscapeJsonString(s)}\"");
|
|
347
368
|
return;
|
|
348
369
|
}
|
|
349
370
|
|
|
@@ -374,6 +395,13 @@ namespace Amanotes.Core.Internal
|
|
|
374
395
|
jsonSb.Append($"{JsonUtility.ToJson(value, format)}");
|
|
375
396
|
}
|
|
376
397
|
|
|
398
|
+
private static string EscapeJsonString(string input)
|
|
399
|
+
{
|
|
400
|
+
//Match any occurrence of a backslash(\), single quotation('), or double quotation (")
|
|
401
|
+
string pattern = "[\\\'\"]";
|
|
402
|
+
return Regex.Replace(input, pattern, match => $"\\{match.Value}");
|
|
403
|
+
}
|
|
404
|
+
|
|
377
405
|
//public static T[] FromJsonToArray<T>(string json)
|
|
378
406
|
//{
|
|
379
407
|
// if (!json.StartsWith("{\"items\":"))
|
package/Runtime/AmaGDK.cs
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
using System;
|
|
2
2
|
using System.Collections;
|
|
3
|
+
using System.Diagnostics;
|
|
4
|
+
using System.IO;
|
|
3
5
|
using System.Text;
|
|
6
|
+
using System.Text.RegularExpressions;
|
|
4
7
|
using Amanotes.Core.Internal;
|
|
5
8
|
using UnityEngine;
|
|
6
9
|
using static Amanotes.Core.Internal.Logging;
|
|
7
10
|
using static Amanotes.Core.Internal.Messsage;
|
|
11
|
+
using Debug = System.Diagnostics.Debug;
|
|
8
12
|
|
|
9
13
|
namespace Amanotes.Core
|
|
10
14
|
{
|
|
11
15
|
public partial class AmaGDK : MonoBehaviour
|
|
12
16
|
{
|
|
13
|
-
public const string VERSION = "0.2.
|
|
17
|
+
public const string VERSION = "0.2.14";
|
|
14
18
|
|
|
15
19
|
internal static AmaGDK _instance;
|
|
16
20
|
internal static Status _status = Status.None;
|
|
@@ -109,7 +113,7 @@ namespace Amanotes.Core
|
|
|
109
113
|
_status = Status.Ready;
|
|
110
114
|
_onReadyCallback?.Invoke();
|
|
111
115
|
_onReadyCallback = null;
|
|
112
|
-
|
|
116
|
+
Log($"AmaGDK v{VERSION} | {READY} in {Time.realtimeSinceStartup - startTime}s\n\n{adapterInfo.ToString()}");
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
void RegisterUnityCallback(AdapterContext adapter)
|
|
@@ -231,4 +235,48 @@ namespace Amanotes.Core
|
|
|
231
235
|
applicationQuit?.Invoke();
|
|
232
236
|
}
|
|
233
237
|
}
|
|
238
|
+
|
|
239
|
+
public partial class AmaGDK // Switch dev mode
|
|
240
|
+
{
|
|
241
|
+
[ContextMenu("Change Dev Mode")]
|
|
242
|
+
public void ChangeDevMode()
|
|
243
|
+
{
|
|
244
|
+
string variableName = "GDK_HOME";
|
|
245
|
+
string output = RunShellCommand($"source ~/.zshrc && echo ${variableName}");
|
|
246
|
+
output = output.Trim();
|
|
247
|
+
|
|
248
|
+
if (string.IsNullOrEmpty(output))
|
|
249
|
+
{
|
|
250
|
+
LogWarning($"{variableName} environment variable is not set.");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
UpdateManifest(output);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private string RunShellCommand(string command)
|
|
258
|
+
{
|
|
259
|
+
Process process = new Process();
|
|
260
|
+
process.StartInfo.FileName = "/bin/zsh";
|
|
261
|
+
process.StartInfo.Arguments = $"-c \"{command}\"";
|
|
262
|
+
process.StartInfo.RedirectStandardOutput = true;
|
|
263
|
+
process.StartInfo.UseShellExecute = false;
|
|
264
|
+
process.StartInfo.CreateNoWindow = true;
|
|
265
|
+
process.Start();
|
|
266
|
+
|
|
267
|
+
string output = process.StandardOutput.ReadToEnd();
|
|
268
|
+
process.WaitForExit();
|
|
269
|
+
|
|
270
|
+
return output;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private void UpdateManifest(string strDesired)
|
|
274
|
+
{
|
|
275
|
+
const string manifestPath = "Packages/manifest.json";
|
|
276
|
+
string manifestJson = File.ReadAllText(manifestPath);
|
|
277
|
+
|
|
278
|
+
manifestJson = Regex.Replace(manifestJson, "\"com\\.amanotes\\.gdk\"\\s*:\\s*\"\\d+\\.\\d+\\.\\d+\"", $"\"com.amanotes.gdk\": \"{strDesired}\"");
|
|
279
|
+
File.WriteAllText(manifestPath, manifestJson);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
234
282
|
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections.Generic;
|
|
3
|
+
using System.Linq;
|
|
4
|
+
using System.Text;
|
|
5
|
+
using UnityEngine;
|
|
6
|
+
using static Amanotes.Core.Internal.Logging;
|
|
7
|
+
#if UNITY_EDITOR
|
|
8
|
+
using UnityEditor;
|
|
9
|
+
#endif
|
|
10
|
+
|
|
11
|
+
namespace Amanotes.Core
|
|
12
|
+
{
|
|
13
|
+
public enum ParamType
|
|
14
|
+
{
|
|
15
|
+
String,
|
|
16
|
+
Integer,
|
|
17
|
+
RealNumber
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
[Serializable]
|
|
21
|
+
public class EventParam
|
|
22
|
+
{
|
|
23
|
+
public string paramName;
|
|
24
|
+
public ParamType type;
|
|
25
|
+
public bool isOptional;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
[Serializable]
|
|
29
|
+
public class EventQuality
|
|
30
|
+
{
|
|
31
|
+
public string eventName;
|
|
32
|
+
public List<EventParam> lstParam;
|
|
33
|
+
|
|
34
|
+
private Dictionary<string, EventParam> dicParam = null;
|
|
35
|
+
|
|
36
|
+
internal Dictionary<string, EventParam> DicParam
|
|
37
|
+
{
|
|
38
|
+
get
|
|
39
|
+
{
|
|
40
|
+
if (dicParam == null) BuildCache();
|
|
41
|
+
return dicParam;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private void BuildCache()
|
|
46
|
+
{
|
|
47
|
+
dicParam = new Dictionary<string, EventParam>();
|
|
48
|
+
foreach (var param in lstParam)
|
|
49
|
+
{
|
|
50
|
+
if (dicParam.ContainsKey(param.paramName)) continue;
|
|
51
|
+
|
|
52
|
+
dicParam.Add(param.paramName, param);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private bool IsNumericType(Type type)
|
|
57
|
+
{
|
|
58
|
+
return type == typeof(int) ||
|
|
59
|
+
type == typeof(long) ||
|
|
60
|
+
type == typeof(short) ||
|
|
61
|
+
type == typeof(byte) ||
|
|
62
|
+
type == typeof(double) ||
|
|
63
|
+
type == typeof(float) ||
|
|
64
|
+
type == typeof(decimal) ||
|
|
65
|
+
type == typeof(uint) ||
|
|
66
|
+
type == typeof(ulong) ||
|
|
67
|
+
type == typeof(ushort) ||
|
|
68
|
+
type == typeof(sbyte);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private bool IsIntegerType(Type type)
|
|
72
|
+
{
|
|
73
|
+
return type == typeof(sbyte) ||
|
|
74
|
+
type == typeof(byte) ||
|
|
75
|
+
type == typeof(short) ||
|
|
76
|
+
type == typeof(ushort) ||
|
|
77
|
+
type == typeof(int) ||
|
|
78
|
+
type == typeof(uint) ||
|
|
79
|
+
type == typeof(long) ||
|
|
80
|
+
type == typeof(ulong);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
internal void ValidateParams(string eventName, Dictionary<string, object> eventParams)
|
|
84
|
+
{
|
|
85
|
+
StringBuilder logBuilder = new StringBuilder();
|
|
86
|
+
logBuilder.AppendLine($"Analytics quality violation found for event <{eventName}>");
|
|
87
|
+
bool isValid = true;
|
|
88
|
+
HashSet<string> checkedParameters = new HashSet<string>();
|
|
89
|
+
|
|
90
|
+
foreach (var kvp in DicParam)
|
|
91
|
+
{
|
|
92
|
+
if (!eventParams.ContainsKey(kvp.Key))
|
|
93
|
+
{
|
|
94
|
+
if (!kvp.Value.isOptional)
|
|
95
|
+
{
|
|
96
|
+
isValid = false;
|
|
97
|
+
logBuilder.AppendLine($"[-] {kvp.Key}");
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
object paramValue = eventParams[kvp.Key];
|
|
103
|
+
|
|
104
|
+
if (!ValidateParamType(paramValue, kvp.Value.type))
|
|
105
|
+
{
|
|
106
|
+
isValid = false;
|
|
107
|
+
logBuilder.AppendLine($"[X] {kvp.Key}. Input type {paramValue.GetType()}, Expected type: {kvp.Value.type.ToString()}.");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
checkedParameters.Add(kvp.Key);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
foreach (var parameter in eventParams)
|
|
114
|
+
{
|
|
115
|
+
if (checkedParameters.Contains(parameter.Key)) continue;
|
|
116
|
+
|
|
117
|
+
isValid = false;
|
|
118
|
+
logBuilder.AppendLine($"[+] {parameter.Key}");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!isValid) LogWarningOnce(logBuilder.ToString());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private bool ValidateParamType(object paramValue, ParamType paramType)
|
|
125
|
+
{
|
|
126
|
+
bool isValidType = false;
|
|
127
|
+
|
|
128
|
+
switch (paramType)
|
|
129
|
+
{
|
|
130
|
+
case ParamType.String:
|
|
131
|
+
isValidType = paramValue is string;
|
|
132
|
+
break;
|
|
133
|
+
case ParamType.Integer:
|
|
134
|
+
isValidType = IsIntegerType(paramValue.GetType());
|
|
135
|
+
break;
|
|
136
|
+
case ParamType.RealNumber:
|
|
137
|
+
isValidType = IsNumericType(paramValue.GetType());
|
|
138
|
+
break;
|
|
139
|
+
default:
|
|
140
|
+
LogWarningOnce($"Unsupported EventParam type: + {paramType}");
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return isValidType;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
[CreateAssetMenu(fileName = "AmaGDKAnalyticsQuality", menuName = "Ama GDK/Analytics Quality", order = 3)]
|
|
149
|
+
[Serializable]
|
|
150
|
+
public class AnalyticQualityAsset : ScriptableObject
|
|
151
|
+
{
|
|
152
|
+
[SerializeField] private List<EventQuality> lstEvent = new List<EventQuality>();
|
|
153
|
+
|
|
154
|
+
private Dictionary<string, EventQuality> dictEvent = null;
|
|
155
|
+
private void BuildCache()
|
|
156
|
+
{
|
|
157
|
+
dictEvent = new Dictionary<string, EventQuality>();
|
|
158
|
+
foreach (var evt in lstEvent)
|
|
159
|
+
{
|
|
160
|
+
if (dictEvent.ContainsKey(evt.eventName))
|
|
161
|
+
{
|
|
162
|
+
LogWarningOnce($"Duplicate event: {evt.eventName}");
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
dictEvent.Add(evt.eventName, evt);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private Dictionary<string, EventQuality> DictEvent
|
|
171
|
+
{
|
|
172
|
+
get
|
|
173
|
+
{
|
|
174
|
+
if (dictEvent == null) BuildCache();
|
|
175
|
+
return dictEvent;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
#if UNITY_EDITOR
|
|
179
|
+
[ContextMenu("Validate Unique EventNames")]
|
|
180
|
+
private void ValidateUniqueEventNames()
|
|
181
|
+
{
|
|
182
|
+
HashSet<string> eventNames = new HashSet<string>();
|
|
183
|
+
|
|
184
|
+
lstEvent = lstEvent.OrderBy(evt => evt.eventName).ToList();
|
|
185
|
+
|
|
186
|
+
foreach (EventQuality eventQuality in lstEvent)
|
|
187
|
+
{
|
|
188
|
+
if (string.IsNullOrWhiteSpace(eventQuality.eventName)) Debug.LogWarning("Event name cannot be empty or whitespace.");
|
|
189
|
+
|
|
190
|
+
if (!eventNames.Add(eventQuality.eventName)) Debug.LogWarning($"Duplicate event name found: {eventQuality.eventName}. Event names must be unique.");
|
|
191
|
+
|
|
192
|
+
HashSet<string> paramNames = new HashSet<string>();
|
|
193
|
+
|
|
194
|
+
eventQuality.lstParam = eventQuality.lstParam.OrderBy(param => param.paramName).ToList();
|
|
195
|
+
|
|
196
|
+
foreach (var param in eventQuality.lstParam)
|
|
197
|
+
{
|
|
198
|
+
if (string.IsNullOrWhiteSpace(param.paramName)) Debug.LogWarning("Event parameter name cannot be empty or whitespace.");
|
|
199
|
+
|
|
200
|
+
if (!paramNames.Add(param.paramName)) Debug.LogWarning($"Duplicate parameter name found in event {eventQuality.eventName}: {param.paramName}. Parameter names must be unique within an event.");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
EditorUtility.SetDirty(this);
|
|
204
|
+
}
|
|
205
|
+
#endif
|
|
206
|
+
|
|
207
|
+
public void CheckEventQuality(string eventName, Dictionary<string, object> parameters)
|
|
208
|
+
{
|
|
209
|
+
if (!DictEvent.ContainsKey(eventName))
|
|
210
|
+
{
|
|
211
|
+
LogWarningOnce($"Event not found in AnalyticQuality: {eventName}");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
DictEvent[eventName].ValidateParams(eventName, parameters);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
package/Runtime/AssemblyInfo.cs
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Linq;
|
|
3
|
+
using UnityEngine;
|
|
4
|
+
|
|
5
|
+
namespace Amanotes.Core.Internal
|
|
6
|
+
{
|
|
7
|
+
[CreateAssetMenu(fileName = "AmaGDKEmbedRemoteConfig", menuName = "Ama GDK/Embed Remote Config", order = 2)]
|
|
8
|
+
[Serializable]
|
|
9
|
+
internal class EmbedRemoteConfigAsset : ScriptableObject
|
|
10
|
+
{
|
|
11
|
+
public int version = 1;
|
|
12
|
+
[SerializeField]
|
|
13
|
+
internal string activeId;
|
|
14
|
+
public bool overrideInEditor;
|
|
15
|
+
public KeyValue[] overrideConfig;
|
|
16
|
+
[SerializeField]
|
|
17
|
+
internal EmbedConfig[] configs;
|
|
18
|
+
private EmbedConfig activeConfig;
|
|
19
|
+
internal EmbedConfig ActiveConfig
|
|
20
|
+
{
|
|
21
|
+
get
|
|
22
|
+
{
|
|
23
|
+
if (activeConfig != null) return activeConfig;
|
|
24
|
+
activeConfig = GetConfigById(activeId);
|
|
25
|
+
return activeConfig;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
internal EmbedConfig GetConfigById(string id)
|
|
30
|
+
{
|
|
31
|
+
var config = configs?.FirstOrDefault(a => a.id == id);
|
|
32
|
+
if (config == null)
|
|
33
|
+
Logging.LogWarningOnce($"Not found embedded config <{id}>");
|
|
34
|
+
return config;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
internal void SetActiveConfig(string id)
|
|
38
|
+
{
|
|
39
|
+
activeId = id;
|
|
40
|
+
activeConfig = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
[Serializable]
|
|
45
|
+
internal class EmbedConfig
|
|
46
|
+
{
|
|
47
|
+
public string id;
|
|
48
|
+
public string fetchUrl;
|
|
49
|
+
public KeyValue[] items;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
[Serializable]
|
|
53
|
+
internal class KeyValue
|
|
54
|
+
{
|
|
55
|
+
public string key;
|
|
56
|
+
public string value;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|