com.amanotes.gdk 0.2.75 → 0.2.78

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/Editor/AmaGDKEditor.cs +24 -15
  3. package/Editor/Extra/SDKVersionTracking.cs +29 -0
  4. package/Editor/Utils/GDKPackageInstaller.cs +116 -0
  5. package/Editor/Utils/GDKPackageInstaller.cs.meta +11 -0
  6. package/Extra/AmaGDKInstaller.unitypackage +0 -0
  7. package/Extra/AutoEventQC.unitypackage +0 -0
  8. package/{Runtime/GDKAudio/Plugins/iOS.meta → Extra/AutoEventQC.unitypackage.meta} +1 -2
  9. package/Extra/CheckDiskSpace.unitypackage +0 -0
  10. package/Extra/Consent.unitypackage +0 -0
  11. package/Extra/ForceUpdate.unitypackage +0 -0
  12. package/Extra/LegacyGDKUpdateHelper.unitypackage +0 -0
  13. package/Extra/PostProcessor.unitypackage +0 -0
  14. package/Packages/AmaGDKConfig.unitypackage +0 -0
  15. package/Packages/AmaGDKExample.unitypackage +0 -0
  16. package/Packages/AmaGDKTest.unitypackage +0 -0
  17. package/Packages/AppsFlyerAdapter.AdRevenue.AF6.14AndOlder.unitypackage +0 -0
  18. package/Packages/AppsFlyerAdapter.PurchaseConnector.unitypackage +0 -0
  19. package/Packages/AppsFlyerAdapter.unitypackage +0 -0
  20. package/Packages/FirebaseAnalyticsAdapter.unitypackage +0 -0
  21. package/Packages/FirebaseRemoteConfigAdapter.unitypackage +0 -0
  22. package/Packages/IronSourceAdapter.AdQuality.unitypackage +0 -0
  23. package/{Runtime/GDKAudio/Plugins/macOS.meta → Packages/IronSourceAdapter.AdQuality.unitypackage.meta} +1 -2
  24. package/Packages/IronSourceAdapter.unitypackage +0 -0
  25. package/Packages/MaxAdNetworkAdapter.unitypackage +0 -0
  26. package/Packages/RevenueCatAdapter.unitypackage +0 -0
  27. package/Packages/SqliteAnalyticsAdapter.unitypackage +0 -0
  28. package/Runtime/Ad/AdLogic.cs +52 -55
  29. package/Runtime/Ad/AdShowContext.cs +1 -1
  30. package/Runtime/AmaGDK.Adapters.cs +2 -2
  31. package/Runtime/AmaGDK.Analytics.cs +18 -15
  32. package/Runtime/AmaGDK.Consent.cs +3 -8
  33. package/Runtime/AmaGDK.Mono.cs +1 -1
  34. package/Runtime/AmaGDK.RemoteConfig.cs +12 -2
  35. package/Runtime/AmaGDK.UserProfile.cs +31 -23
  36. package/Runtime/AmaGDK.cs +140 -41
  37. package/Runtime/Core/GDKDebug.cs +12 -5
  38. package/Runtime/Core/GDKPrefabPool.cs +571 -0
  39. package/Runtime/Core/GDKPrefabPool.cs.meta +11 -0
  40. package/Runtime/Core/GDKRoutine.cs +48 -2
  41. package/Runtime/Internal/AmaGDK.Internal.cs +1 -1
  42. package/Runtime/Internal/AmaGDK.Utils.cs +38 -0
  43. package/Runtime/UI/ScrollView/Editor/GDKScrollViewEditor.cs +50 -0
  44. package/Runtime/UI/ScrollView/Editor/GDKScrollViewEditor.cs.meta +11 -0
  45. package/Runtime/{GDKAudio/Resources.meta → UI/ScrollView/Editor.meta} +1 -1
  46. package/Runtime/UI/ScrollView/GDKScrollView.cs +702 -0
  47. package/Runtime/UI/ScrollView/GDKScrollView.cs.meta +12 -0
  48. package/Runtime/UI/ScrollView.meta +9 -0
  49. package/Runtime/UI/ScrollableText/GDKScrollableText.cs +68 -0
  50. package/Runtime/UI/ScrollableText/GDKScrollableText.cs.meta +3 -0
  51. package/Runtime/UI/ScrollableText/GDKScrollableText.prefab +806 -0
  52. package/Runtime/UI/ScrollableText/GDKScrollableText.prefab.meta +7 -0
  53. package/Runtime/UI/ScrollableText.meta +3 -0
  54. package/Runtime/{GDKAudio/Plugins.meta → UI.meta} +1 -1
  55. package/Runtime/Utils/GDKUtils.cs +18 -0
  56. package/package.json +1 -1
  57. package/Packages/AppsFlyerAdapter.AdRevenue.unitypackage +0 -0
  58. package/Runtime/GDKAudio/GDKAudio.cs +0 -483
  59. package/Runtime/GDKAudio/GDKAudio.cs.meta +0 -3
  60. package/Runtime/GDKAudio/Plugins/Android/atensor-release.aar +0 -0
  61. package/Runtime/GDKAudio/Plugins/Android/atensor-release.aar.meta +0 -32
  62. package/Runtime/GDKAudio/Plugins/Android.meta +0 -3
  63. package/Runtime/GDKAudio/Plugins/iOS/libatensor.a +0 -0
  64. package/Runtime/GDKAudio/Plugins/iOS/libatensor.a.meta +0 -68
  65. package/Runtime/GDKAudio/Plugins/iOS/libcrypto.a +0 -0
  66. package/Runtime/GDKAudio/Plugins/iOS/libcrypto.a.meta +0 -68
  67. package/Runtime/GDKAudio/Plugins/iOS/libssl.a +0 -0
  68. package/Runtime/GDKAudio/Plugins/iOS/libssl.a.meta +0 -68
  69. package/Runtime/GDKAudio/Plugins/macOS/libatensor.dylib +0 -0
  70. package/Runtime/GDKAudio/Plugins/macOS/libatensor.dylib.meta +0 -81
  71. package/Runtime/GDKAudio/Resources/GDKAudioMixer.mixer +0 -133
  72. package/Runtime/GDKAudio/Resources/GDKAudioMixer.mixer.meta +0 -8
  73. package/Runtime/GDKAudio.meta +0 -3
  74. /package/Packages/{AppsFlyerAdapter.AdRevenue.unitypackage.meta → AppsFlyerAdapter.AdRevenue.AF6.14AndOlder.unitypackage.meta} +0 -0
@@ -197,6 +197,7 @@ namespace Amanotes.Core
197
197
  var flag = IntegrityFlag.Ok;
198
198
 
199
199
  if (funnelEvents != null)
200
+ {
200
201
  foreach (string e in funnelEvents)
201
202
  {
202
203
  if (localData.funnelSignatures.Contains(e)) continue;
@@ -204,6 +205,7 @@ namespace Amanotes.Core
204
205
  flag |= IntegrityFlag.NewFunnel;
205
206
  localData.AddFunnelSignature(e);
206
207
  }
208
+ }
207
209
 
208
210
  if (eventCountMap != null)
209
211
  {
@@ -416,7 +418,11 @@ namespace Amanotes.Core
416
418
 
417
419
  while (eventQueue.TryDequeue(out var eventData))
418
420
  {
419
- if (eventData.isLoggedImmediately) continue;
421
+ if (eventData.isLoggedImmediately)
422
+ {
423
+ epPool.Return(eventData);
424
+ continue;
425
+ }
420
426
  if (Config.analytics.immediatelyLoggedEvents.Contains(eventData.eventName)){
421
427
  GDKUtils.ThrowExceptionIfEditor($"This event '{eventData.eventName}' must be sent immediately by AmaGDK.Analytics.LogEvent(eventName).LogImmediately()");
422
428
  }
@@ -477,7 +483,6 @@ namespace Amanotes.Core
477
483
  internal static void LogEventImmediately(EventParams eventParams)
478
484
  {
479
485
  LogEventActually(eventParams);
480
- epPool.Return(eventParams);
481
486
  sessionStat?.Save();
482
487
  localData.SaveIfDirty();
483
488
  }
@@ -485,25 +490,23 @@ namespace Amanotes.Core
485
490
  internal static void UpdateAccumulatedCount(EventParams eventData, int countInSession, ref int totalCount)
486
491
  {
487
492
  var ACCUMULATED_COUNT = "accumulated_count";
488
- var countByGDK = eventData.withinSession ? countInSession : totalCount;
489
-
490
- if (!eventData.parameters.ContainsKey(ACCUMULATED_COUNT))
493
+ int countByGDK = eventData.withinSession ? countInSession : totalCount;
494
+ if (!eventData.parameters.TryGetValue(ACCUMULATED_COUNT, out object countByGame))
491
495
  {
492
496
  eventData.AddParam(ACCUMULATED_COUNT, countByGDK);
493
497
  return;
494
498
  }
495
499
 
496
- int.TryParse(eventData.parameters[ACCUMULATED_COUNT].ToString(), out int oriAccCount);
497
- if (oriAccCount != countByGDK)
500
+ int.TryParse(countByGame.ToString(), out int oriAccCount);
501
+ if (oriAccCount == countByGDK) return;
502
+
503
+ // Log($"[Analytics] event <{eventData.eventName}>, game count: {oriAccCount}, AmaGDK count: {countByGDK}");
504
+ if (Config.analytics.migrateAccumulatedCount && !eventData.withinSession)
498
505
  {
499
- // Log($"[Analytics] event <{eventData.eventName}>, game count: {oriAccCount}, AmaGDK count: {countByGDK}");
500
- if (Config.analytics.migrateAccumulatedCount && !eventData.withinSession)
501
- {
502
- Log($"[Analytics] Update event count of event <{eventData.eventName}> from {countByGDK} to {oriAccCount}");
503
- eventData.detail.totalCount = oriAccCount;
504
- totalCount = oriAccCount;
505
- localData._dirty = true;
506
- }
506
+ Log($"[Analytics] Update event count of event <{eventData.eventName}> from {countByGDK} to {oriAccCount}");
507
+ eventData.detail.totalCount = oriAccCount;
508
+ totalCount = oriAccCount;
509
+ localData._dirty = true;
507
510
  }
508
511
  }
509
512
 
@@ -143,7 +143,7 @@ namespace Amanotes.Core
143
143
  internal static bool InitModule()
144
144
  {
145
145
  if (allConsentChecked) return true;
146
- if (!_config.consent.autoGetConsent) return true;
146
+ if (!_config.autoGetConsent) return true;
147
147
  RequestAllConsents(null);
148
148
  return true;
149
149
  }
@@ -247,15 +247,10 @@ namespace Amanotes.Core
247
247
 
248
248
  namespace Amanotes.Core.Internal
249
249
  {
250
- [Serializable]
251
- public class ConsentConfig
250
+ public partial class ConfigAsset
252
251
  {
252
+ [SerializeField]
253
253
  [Tooltip("Auto pop-up ATT and CMP form")]
254
254
  public bool autoGetConsent = true;
255
255
  }
256
-
257
- public partial class ConfigAsset
258
- {
259
- [SerializeField] public ConsentConfig consent;
260
- }
261
256
  }
@@ -76,7 +76,7 @@ namespace Amanotes.Core.Internal
76
76
 
77
77
  public static void DelayCall(float seconds, Action action)
78
78
  {
79
- DoOnMainThread(() => StartCoroutine(DelayCallRoutine(seconds, action)));
79
+ StartCoroutine(DelayCallRoutine(seconds, action));
80
80
  }
81
81
 
82
82
  public static IEnumerator Timeout(float timeoutInSecs, Func<bool> completeCheckFunc)
@@ -1,5 +1,6 @@
1
1
  using System;
2
2
  using System.Collections.Generic;
3
+ using System.Globalization;
3
4
  using System.IO;
4
5
  using System.Text;
5
6
  using UnityEngine;
@@ -76,11 +77,20 @@ namespace Amanotes.Core
76
77
  {
77
78
  foreach (var c in embed.overrideConfig)
78
79
  if (c.key == key)
79
- return (T)Convert.ChangeType(c.value, typeof(T));
80
+ return (T)Convert.ChangeType(c.value, typeof(T), CultureInfo.InvariantCulture);
80
81
  }
81
82
  #endif
82
83
  if (DictConfig.ContainsKey(key))
83
- return (T)Convert.ChangeType(DictConfig[key], typeof(T));
84
+ {
85
+ try
86
+ {
87
+ return (T)Convert.ChangeType(DictConfig[key], typeof(T), CultureInfo.InvariantCulture);
88
+ }
89
+ catch (Exception e)
90
+ {
91
+ LogError($"[RemoteConfig] Convert failed with key: '{key}', value: '{DictConfig[key]}'.\nError: {e}");
92
+ }
93
+ }
84
94
  return defaultValue;
85
95
  }
86
96
 
@@ -9,11 +9,6 @@ namespace Amanotes.Core
9
9
  {
10
10
  public partial class AmaGDK //User profile
11
11
  {
12
- partial class Event
13
- {
14
- public const string GDK_USER_ID_UPDATED = nameof(GDK_USER_ID_UPDATED);
15
- }
16
-
17
12
  private static readonly Lazy<UserProfile> _user = new Lazy<UserProfile>(() => new UserProfile());
18
13
  public static UserProfile User => _user.Value;
19
14
 
@@ -22,9 +17,13 @@ namespace Amanotes.Core
22
17
  /// The callback triggers when a better UserID (gaid, idfv, pseudoId,...) was found and replace the current UserId
23
18
  /// </summary>
24
19
  /// <param name="onUserIdChange"></param>
25
- public static void SetCallback_OnUserIdUpdated(Action<string> onUserIdChange)
20
+
21
+ private static Action<string> _onUserIdChange;
22
+ public static void SetCallback_OnUserIdUpdated(Action<string> callback)
26
23
  {
27
- dispatcher.AddListener(Event.GDK_USER_ID_UPDATED, onUserIdChange);
24
+ if (callback == null) return;
25
+ _onUserIdChange -= callback;
26
+ _onUserIdChange += callback;
28
27
  }
29
28
 
30
29
  [Serializable]
@@ -47,18 +46,18 @@ namespace Amanotes.Core
47
46
 
48
47
  public partial class UserProfile //PUBLIC
49
48
  {
49
+ private readonly Dictionary<string, object> allUserIds = new Dictionary<string, object>();
50
50
  public Dictionary<string, object> CollectAllUserIds()
51
51
  {
52
- var dic = new Dictionary<string, object>();
53
- if (!string.IsNullOrWhiteSpace(IDFA)) dic["idfa"] = IDFA;
54
- if (!string.IsNullOrWhiteSpace(IDFV)) dic["idfv"] = IDFV;
55
- if (!string.IsNullOrWhiteSpace(GAId)) dic["gaid"] = GAId;
56
- if (!string.IsNullOrWhiteSpace(AndroidId)) dic["android_id"] = AndroidId;
57
- if (!string.IsNullOrWhiteSpace(PseudoId)) dic["pseudo_id"] = PseudoId;
58
- if (!string.IsNullOrWhiteSpace(AmaDeviceId)) dic["ama_device_id"] = AmaDeviceId;
59
- if (!string.IsNullOrWhiteSpace(AppsFlyerId)) dic["appsflyer_id"] = AppsFlyerId;
60
- if (!string.IsNullOrWhiteSpace(InstallationId)) dic["installation_id"] = InstallationId;
61
- return dic;
52
+ if (!string.IsNullOrWhiteSpace(IDFA)) allUserIds["idfa"] = IDFA;
53
+ if (!string.IsNullOrWhiteSpace(IDFV)) allUserIds["idfv"] = IDFV;
54
+ if (!string.IsNullOrWhiteSpace(GAId)) allUserIds["gaid"] = GAId;
55
+ if (!string.IsNullOrWhiteSpace(AndroidId)) allUserIds["android_id"] = AndroidId;
56
+ if (!string.IsNullOrWhiteSpace(PseudoId)) allUserIds["pseudo_id"] = PseudoId;
57
+ if (!string.IsNullOrWhiteSpace(AmaDeviceId)) allUserIds["ama_device_id"] = AmaDeviceId;
58
+ if (!string.IsNullOrWhiteSpace(AppsFlyerId)) allUserIds["appsflyer_id"] = AppsFlyerId;
59
+ if (!string.IsNullOrWhiteSpace(InstallationId)) allUserIds["installation_id"] = InstallationId;
60
+ return allUserIds;
62
61
  }
63
62
 
64
63
  public string UserId
@@ -72,8 +71,10 @@ namespace Amanotes.Core
72
71
  {
73
72
  if (_userId == value) return;
74
73
  _userId = value;
74
+
75
75
  Log($"GDK UserID Updated: {_userId}");
76
- dispatcher.Dispatch(Event.GDK_USER_ID_UPDATED, _userId);
76
+ if (_onUserIdChange != null) _onUserIdChange(_userId);
77
+
77
78
  _dirty = true;
78
79
  if (isReady) SendMapUserId();
79
80
  }
@@ -159,11 +160,7 @@ namespace Amanotes.Core
159
160
  get => _amaDeviceId;
160
161
  set
161
162
  {
162
- var pattern = ALPHANUMERIC_HYPHEN_PATTERN;
163
- #if UNITY_ANDROID && !UNITY_EDITOR_OSX
164
- pattern = ALPHANUMERIC_PATTERN;
165
- #endif
166
- if (!ValidateId(value, pattern))
163
+ if (!ValidateId(value, ALPHANUMERIC_OPTIONAL_HYPHEN_PATTERN))
167
164
  {
168
165
  LogError($"[UserProfile] Wrong format for AmaDeviceId. Value: {value}");
169
166
  return;
@@ -301,6 +298,7 @@ namespace Amanotes.Core
301
298
  [SerializeField]
302
299
  private string _country;
303
300
 
301
+ private const string ALPHANUMERIC_OPTIONAL_HYPHEN_PATTERN = @"^(?=.*\d)(?=.*[a-zA-Z])[a-zA-Z\d-]+$";
304
302
  private const string ALPHANUMERIC_HYPHEN_PATTERN = @"^(?=.*\d)(?=.*[a-zA-Z])(?=.*-).+$";
305
303
  private const string ALPHANUMERIC_PATTERN = @"^(?=.*\d)(?=.*[a-zA-Z])[a-zA-Z\d]+$";
306
304
  private const string NUMERIC_HYPHEN_PATTERN = @"^(?=.*\d)(?=.*-)[\d-]+$";
@@ -349,9 +347,19 @@ namespace Amanotes.Core
349
347
  switch (Application.platform)
350
348
  {
351
349
  case RuntimePlatform.IPhonePlayer:
350
+ if (!ValidateId(id, ALPHANUMERIC_HYPHEN_PATTERN))
351
+ {
352
+ LogWarning($"[UserProfile] Wrong format for IDFA. Value: {id}");
353
+ return;
354
+ }
352
355
  IDFA = id;
353
356
  break;
354
357
  case RuntimePlatform.Android:
358
+ if (!ValidateId(id, ALPHANUMERIC_HYPHEN_PATTERN))
359
+ {
360
+ LogWarning($"[UserProfile] Wrong format for GAId. Value: {id}");
361
+ return;
362
+ }
355
363
  GAId = id;
356
364
  break;
357
365
  }
package/Runtime/AmaGDK.cs CHANGED
@@ -14,17 +14,20 @@ using Debug = UnityEngine.Debug;
14
14
  using Amanotes.Core.Internal;
15
15
  using static Amanotes.Core.GDKDebug;
16
16
  using static Amanotes.Core.Internal.Messsage;
17
+ using System.Threading.Tasks;
18
+
17
19
 
18
20
  #if UNITY_EDITOR
19
21
  using UnityEditor;
20
22
  using UnityEditor.PackageManager;
21
23
  #endif
22
24
 
25
+
23
26
  namespace Amanotes.Core
24
27
  {
25
28
  public partial class AmaGDK : MonoBehaviour
26
29
  {
27
- public const string VERSION = "0.2.75";
30
+ public const string VERSION = "0.2.78";
28
31
 
29
32
  internal static Status _status = Status.None;
30
33
  private static ConfigAsset _config = null;
@@ -44,33 +47,47 @@ namespace Amanotes.Core
44
47
 
45
48
  internal static partial class Event
46
49
  {
47
- public const string GDK_HOOK_START = nameof(GDK_HOOK_START);
48
- public const string GDK_HOOK_END = nameof(GDK_HOOK_END);
50
+ // public const string GDK_HOOK_START = nameof(GDK_HOOK_START);
51
+ // public const string GDK_HOOK_END = nameof(GDK_HOOK_END);
49
52
 
50
- public const string GDK_INIT = nameof(GDK_INIT);
51
- public const string GDK_READY = nameof(GDK_READY);
52
- public const string ADAPTER_READY = nameof(ADAPTER_READY);
53
+ // public const string GDK_INIT = nameof(GDK_INIT);
54
+ // public const string GDK_READY = nameof(GDK_READY);
55
+ // public const string ADAPTER_READY = nameof(ADAPTER_READY);
53
56
  }
54
57
 
58
+ /// <summary>
59
+ /// Gets the ConfigAsset for the AmaGDK.
60
+ /// If the config hasn't been loaded yet, it loads the AmaGDKConfig from Resources.
61
+ /// In the Unity Editor's Play mode, it creates an instance of the config to avoid modifying the original asset.
62
+ /// In all other cases, it uses the loaded config directly.
63
+ /// </summary>
55
64
  public static ConfigAsset Config
56
65
  {
57
66
  get
58
67
  {
59
68
  if (_config != null) return _config;
60
- _config = Resources.Load<ConfigAsset>("AmaGDKConfig");
61
- GDKDebug.logLevel = _config.common.logLevel;
69
+ var config = Resources.Load<ConfigAsset>("AmaGDKConfig");
70
+ #if UNITY_EDITOR
71
+ if (EditorApplication.isPlayingOrWillChangePlaymode)
72
+ {
73
+ _config = Instantiate(config);
74
+ return _config;
75
+ }
76
+ #endif
77
+ _config = config;
62
78
  return _config;
63
79
  }
64
80
  }
65
81
 
66
82
  IEnumerator InitHook()
67
83
  {
68
- dispatcher.Dispatch(Event.GDK_HOOK_START);
84
+ // dispatcher.Dispatch(Event.GDK_HOOK_START);
85
+ if (_onHookStart!= null) GDKUtils.SafeInvokeAndClear(ref _onHookStart);
69
86
  int nHooks = Hook.hooks.Count;
70
87
  if (nHooks == 0)
71
88
  {
72
89
  Log($"[Hook] No hook registered!");
73
- dispatcher.Dispatch(Event.GDK_HOOK_END);
90
+ if (_onHookEnd!= null) GDKUtils.SafeInvokeAndClear(ref _onHookEnd);
74
91
  yield break;
75
92
  }
76
93
 
@@ -85,7 +102,7 @@ namespace Amanotes.Core
85
102
  yield return new WaitUntil(() => hookCompleted);
86
103
  float elapsed = Time.realtimeSinceStartup - stTime;
87
104
  Log($"[Hook] {nHooks} hooks complete after {elapsed} secs");
88
- dispatcher.Dispatch(Event.GDK_HOOK_END);
105
+ if (_onHookEnd!= null) GDKUtils.SafeInvokeAndClear(ref _onHookEnd);
89
106
  }
90
107
 
91
108
  IEnumerator WaitForManualInit()
@@ -169,7 +186,8 @@ namespace Amanotes.Core
169
186
  Log($"{adapter.adapterId} init took: {(endTime-beginTime):#0.00} s");
170
187
  }
171
188
  }
172
- dispatcher.Dispatch(Event.ADAPTER_READY, adapter.adapterId);
189
+
190
+ GDKUtils.SafeInvoke(_onAdapterReady, adapter.adapterId);
173
191
  sb.AppendLine($"[{(adapter.IsReady ? " OK " : "FAIL")}] {adapter.adapterId} (~ {Time.realtimeSinceStartup - startTime:#0.00}s)");
174
192
 
175
193
  // Wait for postprocessing of each adapter
@@ -200,8 +218,9 @@ namespace Amanotes.Core
200
218
  sb.Append($"- InitHook() took (~ {hookTime:#0.00}s)\n\n");
201
219
 
202
220
  _status = Status.Initialize;
203
- dispatcher.Dispatch(Event.GDK_INIT);
204
- yield return WaitForManualInit();
221
+ GDKUtils.SafeInvokeAndClear(ref _onGDKInit);
222
+
223
+ if (!_allowInit) yield return WaitForManualInit();
205
224
  yield return InitAdapters(sb);
206
225
 
207
226
  StartModules();
@@ -215,14 +234,19 @@ namespace Amanotes.Core
215
234
  ForceQuit.Init();
216
235
 
217
236
  _status = Status.Ready;
218
- dispatcher.Dispatch(Event.GDK_READY);
237
+ GDKUtils.SafeInvokeAndClear(ref _onGDKReady);
238
+
239
+ float initDuration = Time.realtimeSinceStartup - startTime;
240
+
241
+ yield return LogEvent_GDKInitAsync(Mathf.RoundToInt(initDuration));
219
242
 
220
- var initDuration = Time.realtimeSinceStartup - startTime;
221
- LogEvent_GDKInit(Mathf.RoundToInt(initDuration));
222
243
 
223
244
  sb.Append($"\nMapping UserId: {User.UserId}\n");
224
245
  ForceLog($"v{VERSION} | {READY} in {initDuration:#0.00}s\n\n{sb}");
225
246
  GDKPool.Return(sb);
247
+
248
+ // also clear callbacks
249
+ _onAdapterReady = null;
226
250
  }
227
251
 
228
252
  private void ConfigGeoLocation()
@@ -245,14 +269,76 @@ namespace Amanotes.Core
245
269
  }
246
270
  }
247
271
 
248
- private static void LogEvent_GDKInit(int initDuration)
272
+ public static async Task<Dictionary<string, object>> ParseThirdPartyVersionsAsync()
273
+ {
274
+ Dictionary<string, object> thirdPartyVersions = new Dictionary<string, object>();
275
+ string fileContent = await GDKFileUtils.LoadFromStreamingAssets("SDKVersion.txt");
276
+ if (string.IsNullOrEmpty(fileContent))
277
+ {
278
+ LogWarning($"Failed to load SDKVersion.txt.");
279
+ }
280
+ else
281
+ {
282
+ ProcessFileContent(fileContent, thirdPartyVersions);
283
+ }
284
+ return thirdPartyVersions;
285
+ }
286
+
287
+ private static void ProcessFileContent(string fileContent, Dictionary<string, object> thirdPartyVersions)
249
288
  {
289
+ bool foundThirdPartySection = false;
290
+ using (StringReader sr = new StringReader(fileContent))
291
+ {
292
+ string line;
293
+ while ((line = sr.ReadLine()) != null)
294
+ {
295
+ if (line.Contains("// THIRD PARTY"))
296
+ {
297
+ foundThirdPartySection = true;
298
+ continue;
299
+ }
300
+
301
+ if (line.Contains("//-"))
302
+ continue;
303
+
304
+ if (foundThirdPartySection)
305
+ {
306
+ if (string.IsNullOrWhiteSpace(line) || line.Contains("//"))
307
+ break;
308
+
309
+ string[] parts = line.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries);
310
+ if (parts.Length == 2)
311
+ {
312
+ string key = parts[0].Trim(); // Extract the key (name of the third-party library)
313
+ string value = parts[1].Trim(); // Extract the version number
314
+ thirdPartyVersions[key] = value;
315
+ }
316
+ }
317
+ }
318
+ }
319
+ if (!foundThirdPartySection)
320
+ {
321
+ LogWarning("Failed to fetch third-party packages' version from SDKVersions.txt.");
322
+ }
323
+ }
324
+
325
+ private static async Task LogEvent_GDKInitAsync(int initDuration)
326
+ {
327
+ var BToMB = 1.0 / (1024 * 1024);
328
+ var reservedMemory = Math.Round(Profiler.GetTotalReservedMemoryLong() * BToMB, 2); // in MB
329
+ var allocatedMemory = Math.Round(Profiler.GetTotalAllocatedMemoryLong() * BToMB, 2); // in MB
330
+ var unusedReservedMemory = Math.Round(Profiler.GetTotalUnusedReservedMemoryLong() * BToMB, 2); // in MB
331
+
250
332
  var _dicKey = new Dictionary<string, object>
251
333
  {
252
334
  ["gdk_version"] = VERSION,
253
335
  ["session"] = User.Session,
254
336
  ["gdk_init_duration"] = initDuration,
255
- ["user_id"] = User.UserId
337
+ ["user_id"] = User.UserId,
338
+ ["device_spec"] = Device.GetDeviceSpec().ToString(),
339
+ ["reserved_memory"] = reservedMemory,
340
+ ["allocated_memory"] = allocatedMemory,
341
+ ["unused_memory"] = unusedReservedMemory
256
342
  };
257
343
 
258
344
  void AppendServiceKey(string adapterId, string paramName)
@@ -265,6 +351,10 @@ namespace Amanotes.Core
265
351
  AppendServiceKey(AdapterID.APPSFLYER, "appsflyer_key");
266
352
  AppendServiceKey(AdapterID.IRONSOURCE, "ironsource_key");
267
353
  AppendServiceKey(AdapterID.REVENUECAT, "revenuecat_key");
354
+
355
+ var thirdPartyVersions = await ParseThirdPartyVersionsAsync();
356
+ _dicKey = _dicKey.Concat(thirdPartyVersions).ToDictionary(x=>x.Key, x=>x.Value);
357
+
268
358
  Analytics.LogEvent("gdk_init", _dicKey);
269
359
  }
270
360
  }
@@ -276,8 +366,8 @@ namespace Amanotes.Core
276
366
 
277
367
  public static LogLevel logLevel
278
368
  {
279
- get => _config == null ? Config.common.logLevel : GDKDebug.logLevel;
280
- set => GDKDebug.logLevel = value;
369
+ get => Config.common.logLevel;
370
+ set => Config.common.logLevel = value;
281
371
  }
282
372
 
283
373
  public static void Init(Action onReady = null)
@@ -285,13 +375,14 @@ namespace Amanotes.Core
285
375
  if (isReady)
286
376
  {
287
377
  LogWarning(SDK_INIT_CALLED);
288
- onReady?.Invoke();
378
+ if (onReady != null) onReady();
289
379
  return;
290
380
  }
291
381
 
292
382
  if (onReady != null)
293
383
  {
294
- dispatcher.AddListener(Event.GDK_READY, onReady, true);
384
+ _onGDKReady -= onReady;
385
+ _onGDKReady += onReady;
295
386
  }
296
387
 
297
388
  if (_allowInit)
@@ -307,6 +398,12 @@ namespace Amanotes.Core
307
398
  _instance = sdk.AddComponent<AmaGDK>();
308
399
  }
309
400
 
401
+ private static Action _onGDKInit;
402
+ private static Action _onGDKReady;
403
+ internal static Action _onHookStart;
404
+ internal static Action _onHookEnd;
405
+ private static Action<string> _onAdapterReady;
406
+
310
407
  /// <summary>
311
408
  /// Will be triggered right after all the cached data being loaded
312
409
  /// And before any Adapter has been called to Init
@@ -314,13 +411,15 @@ namespace Amanotes.Core
314
411
  /// <param name="onInit"></param>
315
412
  public static void SetCallback_OnGDKInit(Action onInit)
316
413
  {
414
+ if (onInit == null) return;
317
415
  if (isInitted)
318
416
  {
319
- onInit.Invoke();
417
+ onInit();
320
418
  return;
321
419
  }
322
-
323
- dispatcher.AddListener(Event.GDK_INIT, onInit, true);
420
+
421
+ _onGDKInit += onInit;
422
+ _onGDKInit -= onInit;
324
423
  }
325
424
 
326
425
  /// <summary>
@@ -329,30 +428,30 @@ namespace Amanotes.Core
329
428
  /// <param name="onReady"></param>
330
429
  public static void SetCallback_OnGDKReady(Action onReady)
331
430
  {
431
+ if (onReady == null) return;
432
+
332
433
  if (isReady)
333
434
  {
334
- onReady.Invoke();
435
+ onReady();
335
436
  return;
336
437
  }
337
438
 
338
- dispatcher.AddListener(Event.GDK_READY, onReady, true);
439
+ _onGDKReady -= onReady;
440
+ _onGDKReady += onReady;
339
441
  }
340
442
 
341
- public static void SetAdapterReadyCallback(Action<string> onAdapterReady)
443
+ public static void SetCallback_AdapterReady(Action<string> onReady)
342
444
  {
343
- if (onAdapterReady == null)
344
- {
345
- LogWarning("Cannot add a null callback!");
346
- return;
347
- }
348
-
349
- if (AmaGDK.isReady)
350
- {
351
- LogWarning("All adapters has been init / ready!");
352
- return;
353
- }
445
+ if (onReady == null || isReady) return;
354
446
 
355
- dispatcher.AddListener(Event.ADAPTER_READY, onAdapterReady);
447
+ _onAdapterReady -= onReady;
448
+ _onAdapterReady += onReady;
449
+ }
450
+
451
+ [Obsolete("Use SetCallback_AdapterReady instead")]
452
+ public static void SetAdapterReadyCallback(Action<string> onReady)
453
+ {
454
+ SetCallback_AdapterReady(onReady);
356
455
  }
357
456
  }
358
457
 
@@ -36,14 +36,13 @@ namespace Amanotes.Core
36
36
 
37
37
  public class GDKDebug
38
38
  {
39
- public static LogLevel logLevel;
40
39
  public static Action<string> logNonfatalErrorHook = null;
41
40
  private static readonly HashSet<string> nonFatalDict = new HashSet<string>();
42
41
  public static readonly HashSet<string> logWarningDict = new HashSet<string>();
43
42
 
44
43
  public static void LogWarningOnce(string message, string key = null)
45
44
  {
46
- if (logLevel < LogLevel.Warning) return;
45
+ if (AmaGDK.logLevel < LogLevel.Warning) return;
47
46
  if (string.IsNullOrEmpty(key)) key = message;
48
47
  if (!logWarningDict.Add(key)) return;
49
48
 
@@ -54,7 +53,7 @@ namespace Amanotes.Core
54
53
  }
55
54
  public static void LogWarning(string message)
56
55
  {
57
- if (logLevel < LogLevel.Warning) return;
56
+ if (AmaGDK.logLevel < LogLevel.Warning) return;
58
57
  using (NoStackTrace.Warning)
59
58
  {
60
59
  Debug.LogWarning($"AmaGDK {message}");
@@ -62,13 +61,21 @@ namespace Amanotes.Core
62
61
  }
63
62
  public static void LogError(string message)
64
63
  {
65
- if (logLevel < LogLevel.Error) return;
64
+ if (AmaGDK.logLevel < LogLevel.Error) return;
66
65
  using (NoStackTrace.Error)
67
66
  {
68
67
  Debug.LogError($"AmaGDK {message}");
69
68
  }
70
69
 
71
70
  if (logNonfatalErrorHook == null) return;
71
+
72
+ if (GDKUtils.isOnMainThread) // Most of the time
73
+ {
74
+ if (!nonFatalDict.Add(message)) return;
75
+ logNonfatalErrorHook(message);
76
+ return;
77
+ }
78
+
72
79
  GDKUtils.DoOnMainThread(() =>
73
80
  {
74
81
  if (!nonFatalDict.Add(message)) return; // LogOnce
@@ -78,7 +85,7 @@ namespace Amanotes.Core
78
85
 
79
86
  public static void Log(string message)
80
87
  {
81
- if (logLevel < LogLevel.Verbose) return;
88
+ if (AmaGDK.logLevel < LogLevel.Verbose) return;
82
89
  using (NoStackTrace.Log)
83
90
  {
84
91
  Debug.Log($"AmaGDK {message}");