com.amanotes.gdk 0.2.63 → 0.2.65-1

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 (54) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/Editor/AmaGDKEditor.cs +23 -7
  3. package/Editor/HideInNormalInspectorDrawer.cs +22 -0
  4. package/Editor/HideInNormalInspectorDrawer.cs.meta +3 -0
  5. package/Extra/AmaGDKInstaller.unitypackage +0 -0
  6. package/Extra/CheckDiskSpace.unitypackage +0 -0
  7. package/Extra/ForceUpdate.unitypackage +0 -0
  8. package/Extra/GoogleCMP.unitypackage +0 -0
  9. package/Extra/GoogleCMP.unitypackage.meta +7 -0
  10. package/Extra/PostProcessor.unitypackage +0 -0
  11. package/Extra/SDKVersionTracking.unitypackage +0 -0
  12. package/Packages/AmaGDKConfig.unitypackage +0 -0
  13. package/Packages/AmaGDKExample.unitypackage +0 -0
  14. package/Packages/AmaGDKTest.unitypackage +0 -0
  15. package/Packages/AppsFlyerAdapter.PurchaseConnector.unitypackage +0 -0
  16. package/Packages/AppsFlyerAdapter.PurchaseConnector.unitypackage.meta +7 -0
  17. package/Packages/AppsFlyerAdapter.unitypackage +0 -0
  18. package/Packages/FirebaseAnalyticsAdapter.unitypackage +0 -0
  19. package/Packages/FirebaseRemoteConfigAdapter.unitypackage +0 -0
  20. package/Packages/IronSourceAdapter.unitypackage +0 -0
  21. package/Packages/RevenueCatAdapter.unitypackage +0 -0
  22. package/Packages/SqliteAnalyticsAdapter.unitypackage +0 -0
  23. package/Runtime/AmaGDK.Adapters.cs +14 -3
  24. package/Runtime/AmaGDK.Ads.cs +29 -2
  25. package/Runtime/AmaGDK.Analytics.cs +9 -20
  26. package/Runtime/AmaGDK.Mono.cs +111 -0
  27. package/Runtime/AmaGDK.Mono.cs.meta +3 -0
  28. package/Runtime/AmaGDK.RemoteConfig.cs +3 -0
  29. package/Runtime/AmaGDK.Singleton.cs +27 -0
  30. package/Runtime/AmaGDK.Singleton.cs.meta +3 -0
  31. package/Runtime/AmaGDK.UserProfile.cs +119 -40
  32. package/Runtime/AmaGDK.cs +38 -59
  33. package/Runtime/Core/GDKRoutine.cs +61 -0
  34. package/Runtime/Core/GDKRoutine.cs.meta +3 -0
  35. package/Runtime/Core/GDKUnityCallback.cs +141 -0
  36. package/Runtime/Core/GDKUnityCallback.cs.meta +3 -0
  37. package/Runtime/Core.meta +8 -0
  38. package/Runtime/Internal/AmaGDK.Internal.AmaGUI.cs +19 -2
  39. package/Runtime/Internal/AmaGDK.Internal.cs +4 -8
  40. package/Runtime/Internal/AmaGDK.Utils.cs +3 -56
  41. package/Runtime/Internal/HideInNormalInspectorAttribute.cs +9 -0
  42. package/Runtime/Internal/HideInNormalInspectorAttribute.cs.meta +3 -0
  43. package/Runtime/Klavar/Attributes/AccumulatedCountAttribute.cs +0 -1
  44. package/Runtime/Klavar/Attributes/MinMaxAttribute.cs +86 -79
  45. package/Runtime/Klavar/Attributes/MinMaxLengthAttribute.cs +47 -26
  46. package/Runtime/Klavar/Attributes/NotNullAttribute.cs +1 -1
  47. package/Runtime/Klavar/Attributes/RegexPatternAttribute.cs +10 -7
  48. package/Runtime/Klavar/Attributes/ToStringAttribute.cs +1 -1
  49. package/Runtime/Klavar/KlavarContainer.cs +21 -5
  50. package/Runtime/Klavar/KlavarEvent.cs +4 -1
  51. package/Runtime/Utils/GDKUtils.cs +35 -0
  52. package/Runtime/Utils/GDKUtils.cs.meta +3 -0
  53. package/Runtime/Utils.meta +8 -0
  54. package/package.json +1 -1
@@ -72,83 +72,152 @@ namespace Amanotes.Core
72
72
  _userId = value;
73
73
  Logging.Log($"GDK UserID Updated: {_userId}");
74
74
  dispatcher.Dispatch(Event.GDK_USER_ID_UPDATED, _userId);
75
- Save();
75
+ _dirty = true;
76
+ if (isReady) SendMapUserId();
76
77
  }
77
78
  }
78
79
 
79
80
  public string GAId
80
81
  {
81
82
  get => _gaid;
82
- set => _gaid = ValidateId(value, ALPHANUMERIC_HYPHEN_PATTERN);
83
+ set
84
+ {
85
+ if (!ValidateId(value, ALPHANUMERIC_HYPHEN_PATTERN))
86
+ {
87
+ Logging.LogError($"[UserProfile] Wrong format for GAId. Value: {value}");
88
+ return;
89
+ }
90
+ _gaid = value;
91
+ UpdateUserId();
92
+ }
83
93
  }
84
94
 
85
95
  public string IDFA
86
96
  {
87
97
  get => _idfa;
88
- set => _idfa = ValidateId(value, ALPHANUMERIC_HYPHEN_PATTERN);
98
+ set
99
+ {
100
+ if (!ValidateId(value, ALPHANUMERIC_HYPHEN_PATTERN))
101
+ {
102
+ Logging.LogError($"[UserProfile] Wrong format for IDFA. Value: {value}");
103
+ return;
104
+ }
105
+ _idfa = value;
106
+ UpdateUserId();
107
+ }
89
108
  }
90
-
109
+
91
110
  public string IDFV
92
111
  {
93
112
  get => _idfv;
94
- set => _idfv = ValidateId(value, ALPHANUMERIC_HYPHEN_PATTERN);
113
+ set
114
+ {
115
+ if (!ValidateId(value, ALPHANUMERIC_HYPHEN_PATTERN))
116
+ {
117
+ Logging.LogError($"[UserProfile] Wrong format for IDFV. Value: {value}");
118
+ return;
119
+ }
120
+ _idfv = value;
121
+ UpdateUserId();
122
+ }
95
123
  }
96
-
124
+
97
125
  public string AndroidId
98
126
  {
99
127
  get => _androidId;
100
- set => _androidId = ValidateId(value, ALPHANUMERIC_PATTERN);
128
+ set
129
+ {
130
+ if (!ValidateId(value, ALPHANUMERIC_PATTERN))
131
+ {
132
+ Logging.LogError($"[UserProfile] Wrong format for AndroidId. Value: {value}");
133
+ return;
134
+ }
135
+ _androidId = value;
136
+ UpdateUserId();
137
+ }
101
138
  }
102
-
139
+
103
140
  public string PseudoId
104
141
  {
105
142
  get => _pseudoId;
106
- set => _pseudoId = ValidateId(value, ALPHANUMERIC_PATTERN);
143
+ set
144
+ {
145
+ if (!ValidateId(value, ALPHANUMERIC_PATTERN))
146
+ {
147
+ Logging.LogError($"[UserProfile] Wrong format for PseudoId. Value: {value}");
148
+ return;
149
+ }
150
+ _pseudoId = value;
151
+ UpdateUserId();
152
+ }
107
153
  }
108
154
 
109
- #if UNITY_ANDROID && !UNITY_EDITOR
110
155
  public string AmaDeviceId
111
156
  {
112
157
  get => _amaDeviceId;
113
- set => _amaDeviceId = ValidateId(value, ALPHANUMERIC_PATTERN);
114
- }
115
- #else
116
- public string AmaDeviceId
117
- {
118
- get => _amaDeviceId;
119
- set => _amaDeviceId = ValidateId(value, ALPHANUMERIC_HYPHEN_PATTERN);
158
+ set
159
+ {
160
+ var pattern = ALPHANUMERIC_HYPHEN_PATTERN;
161
+ #if UNITY_ANDROID && !UNITY_EDITOR
162
+ pattern = ALPHANUMERIC_PATTERN;
163
+ #endif
164
+ if (!ValidateId(value, pattern))
165
+ {
166
+ Logging.LogError($"[UserProfile] Wrong format for AmaDeviceId. Value: {value}");
167
+ return;
168
+ }
169
+ _amaDeviceId = value;
170
+ UpdateUserId();
171
+ }
120
172
  }
121
- #endif
173
+
122
174
 
123
175
  public string AppsFlyerId
124
176
  {
125
177
  get => _appsFlyerId;
126
- set => _appsFlyerId = ValidateId(value, NUMERIC_HYPHEN_PATTERN);
178
+ set
179
+ {
180
+ if (!Application.isEditor && !ValidateId(value, NUMERIC_HYPHEN_PATTERN))
181
+ {
182
+ Logging.LogError($"[UserProfile] Wrong format for AppsFlyerId. Value: {value}");
183
+ return;
184
+ }
185
+ _appsFlyerId = value;
186
+ UpdateUserId();
187
+ }
127
188
  }
128
-
189
+
129
190
  public string InstallationId
130
191
  {
131
192
  get => _installationId;
132
- set => _installationId = ValidateId(value, ALPHANUMERIC_PATTERN);
193
+ set
194
+ {
195
+ if (!ValidateId(value, ALPHANUMERIC_PATTERN))
196
+ {
197
+ Logging.LogError($"[UserProfile] Wrong format for InstallationId. Value: {value}");
198
+ return;
199
+ }
200
+ _installationId = value;
201
+ UpdateUserId();
202
+ }
133
203
  }
134
204
 
135
- public int Session
136
- {
137
- get => _session;
138
- }
205
+ public int Session => _session;
139
206
 
140
207
  public string Country
141
208
  {
142
209
  get => _country;
143
210
  set
144
211
  {
145
- _country = ValidateId(value, COUNTRY_CODE_PATTERN);
146
- SaveIfDirty();
212
+ if (!ValidateId(value, COUNTRY_CODE_PATTERN)) return;
213
+ _country = value;
214
+ _dirty = true;
147
215
  }
148
216
  }
149
217
 
150
- public void UpdateUserId()
218
+ private void UpdateUserId()
151
219
  {
220
+ _dirty = true;
152
221
  #if UNITY_ANDROID
153
222
  if (!string.IsNullOrWhiteSpace(_gaid))
154
223
  {
@@ -241,14 +310,14 @@ namespace Amanotes.Core
241
310
  {
242
311
  User.LoadJsonFromFile();
243
312
 
244
- #if UNITY_ANDROID
313
+ #if UNITY_ANDROID && !UNITY_EDITOR
245
314
  AndroidId = SystemInfo.deviceUniqueIdentifier;
246
315
  #elif UNITY_IOS
247
316
  IDFV = SystemInfo.deviceUniqueIdentifier;
248
317
  #endif
249
318
  GetAmaDeviceId();
250
319
  UpdateUserId();
251
- _session = _session + 1;
320
+ _session += 1;
252
321
  _dirty = true;
253
322
  }
254
323
 
@@ -257,8 +326,20 @@ namespace Amanotes.Core
257
326
  // Always get the latest advertising id
258
327
  GetAdvertisingId();
259
328
  Logging.Log($"[UserProfile] user_id: {_userId}");
260
- onFrameUpdate -= SaveIfDirty;
261
- onFrameUpdate += SaveIfDirty;
329
+ GDKUtils.OnFrameUpdate(SaveIfDirty);
330
+
331
+ var adsId = Application.platform == RuntimePlatform.Android ? GAId : IDFA;
332
+ var delay = string.IsNullOrEmpty(AppsFlyerId)
333
+ || string.IsNullOrEmpty(adsId)
334
+ || string.IsNullOrEmpty(PseudoId);
335
+
336
+ GDKUtils.DelayCall(delay ? 1 : 0, SendMapUserId);
337
+ }
338
+
339
+ private void SendMapUserId()
340
+ {
341
+ Analytics.LogEvent("map_user_id", AmaGDK.User.CollectAllUserIds())
342
+ .OnlyTo(AmaGDK.AdapterID.FIREBASE_ANALYTICS);
262
343
  }
263
344
 
264
345
  private void SetAdvertisingID(string id)
@@ -280,8 +361,7 @@ namespace Amanotes.Core
280
361
  {
281
362
  GDKUtils.DelayCall(0, () =>
282
363
  {
283
- SetAdvertisingID(advertisingId);
284
- Save();
364
+ SetAdvertisingID(advertisingId);
285
365
  });
286
366
  });
287
367
  }
@@ -292,7 +372,7 @@ namespace Amanotes.Core
292
372
  switch (Application.platform)
293
373
  {
294
374
  case RuntimePlatform.IPhonePlayer:
295
- string v = KeyChain.GetKeyChain(AMA_DEVICE_ID_KEY);
375
+ var v = KeyChain.GetKeyChain(AMA_DEVICE_ID_KEY);
296
376
  if (string.IsNullOrEmpty(v))
297
377
  {
298
378
  v = Guid.NewGuid().ToString();
@@ -302,7 +382,7 @@ namespace Amanotes.Core
302
382
  break;
303
383
 
304
384
  case RuntimePlatform.Android:
305
- string cachedAmaDeviceId = PlayerPrefs.GetString(AMA_DEVICE_ID_KEY);
385
+ var cachedAmaDeviceId = PlayerPrefs.GetString(AMA_DEVICE_ID_KEY);
306
386
  if (string.IsNullOrEmpty(cachedAmaDeviceId))
307
387
  {
308
388
  cachedAmaDeviceId = SystemInfo.deviceUniqueIdentifier;
@@ -318,11 +398,10 @@ namespace Amanotes.Core
318
398
  Save();
319
399
  }
320
400
 
321
- private string ValidateId (string id, string pattern)
401
+ private bool ValidateId (string id, string pattern)
322
402
  {
323
- if (string.IsNullOrWhiteSpace(id)) return null;
324
- if (!Regex.IsMatch(id, pattern)) return null;
325
- return id;
403
+ if (string.IsNullOrWhiteSpace(id)) return false;
404
+ return Regex.IsMatch(id, pattern);
326
405
  }
327
406
  }
328
407
  }
package/Runtime/AmaGDK.cs CHANGED
@@ -17,17 +17,16 @@ namespace Amanotes.Core
17
17
  {
18
18
  public partial class AmaGDK : MonoBehaviour
19
19
  {
20
- public const string VERSION = "0.2.63";
21
-
22
- internal static AmaGDK _instance;
23
- internal static bool _allowInit = false;
20
+ public const string VERSION = "0.2.66";
21
+
24
22
  internal static Status _status = Status.None;
25
23
  private static ConfigAsset _config = null;
26
24
  internal static readonly GDKGeoLocation GeoLocation = new GDKGeoLocation();
27
25
  internal static readonly GDKServerTime ServerTime = new GDKServerTime();
28
26
 
29
-
27
+ internal static bool _allowInit = false;
30
28
  public bool autoInit = true;
29
+
31
30
  internal static partial class Event
32
31
  {
33
32
  public const string GDK_HOOK_START = nameof(GDK_HOOK_START);
@@ -48,26 +47,6 @@ namespace Amanotes.Core
48
47
  return _config;
49
48
  }
50
49
  }
51
-
52
- private void Awake()
53
- {
54
- if (_instance != null)
55
- {
56
- if (_instance != this)
57
- {
58
- LogWarning(MULTIPLE_INSTANCE);
59
- Destroy(this);
60
- }
61
- LogWarning("Should never be here!");
62
- return;
63
- }
64
-
65
- _instance = this;
66
- DontDestroyOnLoad(this);
67
-
68
- _allowInit = _allowInit || autoInit;
69
- StartCoroutine(InitRoutine());
70
- }
71
50
 
72
51
  IEnumerator InitHook()
73
52
  {
@@ -174,21 +153,24 @@ namespace Amanotes.Core
174
153
 
175
154
  IEnumerator InitRoutine()
176
155
  {
156
+ _allowInit = _allowInit || autoInit;
157
+
177
158
  if (Config == null)
178
159
  {
179
160
  throw new Exception("[AmaGDK] " + CONFIG_NOT_FOUND);
180
161
  }
181
162
 
182
- float startTime = Time.realtimeSinceStartup;
163
+ var startTime = Time.realtimeSinceStartup;
183
164
  var sb = new StringBuilder();
184
165
 
185
166
  InitModules();
186
167
 
187
- float moduleTime = Time.realtimeSinceStartup - startTime;
168
+ var moduleTime = Time.realtimeSinceStartup - startTime;
188
169
  sb.Append($"- InitModules() took (~ {moduleTime:#0.00}s)\n");
189
170
 
190
171
  yield return InitHook(); //Google CMP takes long at first time (~ 1.7s)
191
- float hookTime = Time.realtimeSinceStartup - moduleTime - startTime;
172
+
173
+ var hookTime = Time.realtimeSinceStartup - moduleTime - startTime;
192
174
  sb.Append($"- InitHook() took (~ {hookTime:#0.00}s)\n\n");
193
175
 
194
176
  _status = Status.Initialize;
@@ -206,9 +188,12 @@ namespace Amanotes.Core
206
188
 
207
189
  _status = Status.Ready;
208
190
  dispatcher.Dispatch(Event.GDK_READY);
191
+
192
+ var initDuration = Time.realtimeSinceStartup - startTime;
193
+ LogEvent_GDKInit(Mathf.RoundToInt(initDuration));
209
194
 
210
195
  sb.Append($"\nMapping UserId: {User.UserId}\n");
211
- Debug.Log($"AmaGDK v{VERSION} | {READY} in {Time.realtimeSinceStartup - startTime:#0.00}s\n\n{sb}");
196
+ Debug.Log($"AmaGDK v{VERSION} | {READY} in {initDuration:#0.00}s\n\n{sb}");
212
197
  }
213
198
 
214
199
  private void ConfigGeoLocation()
@@ -230,6 +215,29 @@ namespace Amanotes.Core
230
215
  }
231
216
  }
232
217
  }
218
+
219
+ private static void LogEvent_GDKInit(int initDuration)
220
+ {
221
+ var _dicKey = new Dictionary<string, object>
222
+ {
223
+ ["gdk_version"] = VERSION,
224
+ ["session"] = User.Session,
225
+ ["gdk_init_duration"] = initDuration,
226
+ ["user_id"] = User.UserId
227
+ };
228
+
229
+ void AppendServiceKey(string adapterId, string paramName)
230
+ {
231
+ var serviceKey = Adapter2.GetAdapterById(adapterId)?.GetServiceKey();
232
+ if (string.IsNullOrEmpty(serviceKey)) return;
233
+ _dicKey[paramName] = serviceKey;
234
+ }
235
+
236
+ AppendServiceKey(AdapterID.APPSFLYER, "appsflyer_key");
237
+ AppendServiceKey(AdapterID.IRONSOURCE, "ironsource_key");
238
+ AppendServiceKey(AdapterID.REVENUECAT, "revenuecat_key");
239
+ Analytics.LogEvent("gdk_init", _dicKey);
240
+ }
233
241
  }
234
242
 
235
243
  public partial class AmaGDK // PUBLIC APIS
@@ -330,35 +338,6 @@ namespace Amanotes.Core
330
338
  public const string REVENUECAT = "RevenueCat";
331
339
  }
332
340
  }
333
-
334
- public partial class AmaGDK
335
- {
336
- internal static event Action onFrameUpdate;
337
- internal static event Action<bool> onApplicationPause;
338
- internal static event Action<bool> applicationFocus;
339
- internal static event Action applicationQuit;
340
-
341
- private void OnApplicationPause(bool pauseStatus)
342
- {
343
- onApplicationPause?.Invoke(pauseStatus);
344
- }
345
-
346
- private void OnApplicationFocus(bool hasFocus)
347
- {
348
- applicationFocus?.Invoke(hasFocus);
349
- }
350
-
351
- private void Update()
352
- {
353
- if (!isReady) return;
354
- onFrameUpdate?.Invoke();
355
- }
356
-
357
- private void OnApplicationQuit()
358
- {
359
- applicationQuit?.Invoke();
360
- }
361
- }
362
341
 
363
342
  public partial class AmaGDK // Switch dev mode
364
343
  {
@@ -472,7 +451,7 @@ namespace Amanotes.Core
472
451
  {
473
452
  if (completed)
474
453
  {
475
- LogWarning("Register happen too late: Hook completed!");
454
+ LogWarning("Add happen too late: Hook completed!");
476
455
  return;
477
456
  }
478
457
 
@@ -0,0 +1,61 @@
1
+ using System;
2
+ using System.Collections;
3
+ using UnityEngine;
4
+ namespace Amanotes.Core.Internal
5
+ {
6
+ [Flags]
7
+ internal enum RoutineStatus
8
+ {
9
+ None,
10
+ Started = 1,
11
+ Stopped = 2
12
+ }
13
+
14
+ public class GDKRoutine
15
+ {
16
+ private RoutineStatus status;
17
+ private readonly IEnumerator action;
18
+ private Coroutine routine;
19
+
20
+ public bool isAlive => (status & RoutineStatus.Stopped) == 0;
21
+
22
+ public GDKRoutine(IEnumerator action)
23
+ {
24
+ this.action = action;
25
+ status = RoutineStatus.None;
26
+ }
27
+
28
+ public void StartRoutine()
29
+ {
30
+ if (!isAlive)
31
+ {
32
+ // StopRoutine() called before start: do nothing
33
+ return;
34
+ }
35
+
36
+ status |= RoutineStatus.Started;
37
+ routine = AmaGDK._instance.StartCoroutine(action);
38
+ }
39
+
40
+ public void StopRoutine()
41
+ {
42
+ status |= RoutineStatus.Stopped;
43
+ if (routine != null) AmaGDK._instance.StopCoroutine(routine);
44
+ }
45
+ }
46
+
47
+ public static partial class GDKUtils
48
+ {
49
+ public static GDKRoutine StartCoroutine(IEnumerator action)
50
+ {
51
+ var routine = new GDKRoutine(action);
52
+ DoOnMainThread(routine.StartRoutine);
53
+ return routine;
54
+ }
55
+
56
+ public static void StopCoroutine(GDKRoutine gdkRoutine)
57
+ {
58
+ gdkRoutine.StopRoutine();
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 1a341fd3b1ed4dff93eaccd1b4d442a3
3
+ timeCreated: 1718160748
@@ -0,0 +1,141 @@
1
+ using System;
2
+
3
+ namespace Amanotes.Core.Internal
4
+ {
5
+ public interface IOnApplicationPause { void OnApplicationPause(bool pauseStatus); }
6
+ public interface IOnApplicationQuit { void OnApplicationQuit(); }
7
+ public interface IOnApplicationFocus { void OnApplicationFocus(bool hasFocus); }
8
+ public interface IOnFrameUpdate { void OnFrameUpdate(); }
9
+ }
10
+
11
+ namespace Amanotes.Core.Internal
12
+ {
13
+ public class GDKUnityCallback
14
+ {
15
+ public Action<bool> OnApplicationPause;
16
+ public Action<bool> OnApplicationFocus;
17
+ public Action OnApplicationQuit;
18
+ public Action OnFrameUpdate;
19
+ public Action DoOnceNextFrame;
20
+
21
+ // Lock object to ensure thread-safe operations
22
+ private readonly object _locker = new object();
23
+
24
+ // Add Listener
25
+ public void AddCallback(ref Action source, Action callback)
26
+ {
27
+ if (callback == null) return;
28
+ lock (_locker)
29
+ {
30
+ source -= callback;
31
+ source += callback;
32
+ }
33
+ }
34
+ public void AddCallback<T>(ref Action<T> source, Action<T> callback)
35
+ {
36
+ if (callback == null) return;
37
+ lock (_locker)
38
+ {
39
+ source -= callback;
40
+ source += callback;
41
+ }
42
+ }
43
+
44
+
45
+ // Remove Listener
46
+ public void RemoveCallback(ref Action source, Action callback)
47
+ {
48
+ if (callback == null) return;
49
+ lock (_locker)
50
+ {
51
+ source -= callback;
52
+ }
53
+ }
54
+ public void RemoveCallback<T>(ref Action<T> source, Action<T> callback)
55
+ {
56
+ if (callback == null) return;
57
+ lock (_locker)
58
+ {
59
+ source -= callback;
60
+ }
61
+ }
62
+
63
+
64
+ // Invoke
65
+ public void Invoke(ref Action source)
66
+ {
67
+ if (source == null) return;
68
+ Action actionsToInvoke;
69
+ lock (_locker)
70
+ {
71
+ bool remove = source == DoOnceNextFrame;
72
+ actionsToInvoke = source;
73
+ if (remove) source = null;
74
+ }
75
+
76
+ GDKUtils.SafeInvoke(actionsToInvoke);
77
+ }
78
+ public void Invoke<T>(ref Action<T> source, T data)
79
+ {
80
+ if (source == null) return;
81
+ Action<T> actionsToInvoke;
82
+ lock (_locker)
83
+ {
84
+ actionsToInvoke = source;
85
+ }
86
+
87
+ GDKUtils.SafeInvoke(actionsToInvoke, data);
88
+ }
89
+
90
+
91
+ // Observer
92
+ public void AddObserver<T>(T observer)
93
+ {
94
+ if (observer == null) return;
95
+
96
+ if (observer is IOnFrameUpdate u)
97
+ {
98
+ AddCallback(ref OnFrameUpdate, u.OnFrameUpdate);
99
+ }
100
+
101
+ if (observer is IOnApplicationPause p)
102
+ {
103
+ AddCallback(ref OnApplicationPause, p.OnApplicationPause);
104
+ }
105
+
106
+ if (observer is IOnApplicationFocus f)
107
+ {
108
+ AddCallback(ref OnApplicationFocus, f.OnApplicationFocus);
109
+ }
110
+
111
+ if (observer is IOnApplicationQuit q)
112
+ {
113
+ AddCallback(ref OnApplicationQuit, q.OnApplicationQuit);
114
+ }
115
+ }
116
+ public void RemoveObserver<T>(T observer)
117
+ {
118
+ if (observer == null) return;
119
+
120
+ if (observer is IOnFrameUpdate u)
121
+ {
122
+ RemoveCallback(ref OnFrameUpdate, u.OnFrameUpdate);
123
+ }
124
+
125
+ if (observer is IOnApplicationPause p)
126
+ {
127
+ RemoveCallback(ref OnApplicationPause, p.OnApplicationPause);
128
+ }
129
+
130
+ if (observer is IOnApplicationFocus f)
131
+ {
132
+ RemoveCallback(ref OnApplicationFocus, f.OnApplicationFocus);
133
+ }
134
+
135
+ if (observer is IOnApplicationQuit q)
136
+ {
137
+ RemoveCallback(ref OnApplicationQuit, q.OnApplicationQuit);
138
+ }
139
+ }
140
+ }
141
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 597f549a7af24670b911da2047054e5e
3
+ timeCreated: 1718085791
@@ -0,0 +1,8 @@
1
+ fileFormatVersion: 2
2
+ guid: 2b84f1cdb15ec4442b3a2f0b038c711c
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant:
@@ -149,10 +149,27 @@ namespace Amanotes.Core.Internal
149
149
  return true;
150
150
  }
151
151
 
152
- public static bool Foldout(string label, ref bool isOpen, Action drawFunc, Action titleFunc = null, GUIStyle style = null)
152
+ public static bool Foldout(string label, ref bool isOpen, Action drawFunc, Action titleFunc = null)
153
+ {
154
+ return Foldout(label, null, ref isOpen, drawFunc, titleFunc);
155
+ }
156
+
157
+ public static bool Foldout(string label, Texture icon, ref bool isOpen, Action drawFunc, Action titleFunc = null)
153
158
  {
154
159
  GUILayout.BeginHorizontal();
155
- bool newValue = EditorGUILayout.Foldout(isOpen, label, EditorStyles.foldoutHeader);
160
+ float labelWidth = EditorGUIUtility.labelWidth;
161
+
162
+ var content = new GUIContent(label, icon);
163
+ float w = EditorStyles.foldoutHeader.CalcSize(content).x;
164
+ EditorGUIUtility.labelWidth = w;
165
+
166
+ // var c = GUI.color;
167
+ // GUI.color = Color.red;
168
+ Rect rect = GUILayoutUtility.GetRect(w, w, 18f, 18f);
169
+ bool newValue = EditorGUI.Foldout(rect, isOpen, content, EditorStyles.foldoutHeader);
170
+ EditorGUIUtility.labelWidth = labelWidth;
171
+ // GUI.color = c;
172
+
156
173
  bool changed = newValue != isOpen;
157
174
  titleFunc?.Invoke();
158
175
  GUILayout.EndHorizontal();