com.amanotes.gdk 0.2.45 → 0.2.47

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 (48) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/Editor/AmaGDKEditor.cs +325 -120
  3. package/Editor/MatchSDKVersion.cs +3 -6
  4. package/Editor/Utils/AmaGDKEditor.ExtractVersion.cs +6 -4
  5. package/Extra/AmaGDKInstaller.unitypackage +0 -0
  6. package/Extra/AmaGDKProject.unitypackage +0 -0
  7. package/Packages/AmaGDKConfig.unitypackage +0 -0
  8. package/Packages/AmaGDKExample.unitypackage +0 -0
  9. package/Packages/AmaGDKTest.unitypackage +0 -0
  10. package/Packages/AppsFlyerAdapter.unitypackage +0 -0
  11. package/Packages/{FirebaseAnalyticsAdapter_v9.1.0.unitypackage.meta → AppsFlyerAdapter.unitypackage.meta} +1 -1
  12. package/Packages/FirebaseAnalyticsAdapter.unitypackage +0 -0
  13. package/Packages/{IronSourceAdapter_v7.5.2.unitypackage.meta → FirebaseAnalyticsAdapter.unitypackage.meta} +1 -1
  14. package/Packages/FirebaseRemoteConfigAdapter.unitypackage +0 -0
  15. package/Packages/{FirebaseRemoteConfigAdapter_v9.1.0.unitypackage.meta → FirebaseRemoteConfigAdapter.unitypackage.meta} +1 -1
  16. package/Packages/IronSourceAdapter.unitypackage +0 -0
  17. package/Packages/{AppsFlyerAdapter_v6.5.4.unitypackage.meta → IronSourceAdapter.unitypackage.meta} +1 -1
  18. package/Packages/RevenueCatAdapter.unitypackage +0 -0
  19. package/Packages/RevenueCatAdapter.unitypackage.meta +7 -0
  20. package/Runtime/AmaGDK.Adapters.cs +55 -77
  21. package/Runtime/AmaGDK.Ads.cs +155 -163
  22. package/Runtime/AmaGDK.Analytics.cs +245 -125
  23. package/Runtime/AmaGDK.Config.cs +2 -1
  24. package/Runtime/AmaGDK.IAP.cs +47 -48
  25. package/Runtime/AmaGDK.RemoteConfig.cs +38 -35
  26. package/Runtime/AmaGDK.UserProfile.cs +27 -19
  27. package/Runtime/AmaGDK.cs +158 -111
  28. package/Runtime/Core.meta +8 -0
  29. package/{Editor/Utils/AmaGDKEditor.GUI.cs → Runtime/Internal/AmaGDK.Internal.AmaGUI.cs} +110 -85
  30. package/{Editor/Utils/AmaGDKEditor.GUI.cs.meta → Runtime/Internal/AmaGDK.Internal.AmaGUI.cs.meta} +1 -1
  31. package/Runtime/{AmaGDK.Internal.SemVer.cs → Internal/AmaGDK.Internal.SemVer.cs} +21 -17
  32. package/Runtime/{AmaGDK.Internal.cs → Internal/AmaGDK.Internal.cs} +17 -6
  33. package/Runtime/{AmaGDK.Utils.cs → Internal/AmaGDK.Utils.cs} +91 -17
  34. package/Runtime/Internal.meta +8 -0
  35. package/package.json +1 -1
  36. package/Packages/AppsFlyerAdapter_v6.5.4.unitypackage +0 -0
  37. package/Packages/FirebaseAnalyticsAdapter_v9.1.0.unitypackage +0 -0
  38. package/Packages/FirebaseRemoteConfigAdapter_v9.1.0.unitypackage +0 -0
  39. package/Packages/IronSourceAdapter_v7.5.2.unitypackage +0 -0
  40. package/Packages/IronsourceAdapter_v7.2.6.unitypackage +0 -0
  41. package/Packages/IronsourceAdapter_v7.2.6.unitypackage.meta +0 -7
  42. package/Packages/RevenueCatAdapter_v6.0.0.unitypackage +0 -0
  43. package/Packages/RevenueCatAdapter_v6.0.0.unitypackage.meta +0 -7
  44. /package/Runtime/{AmaGDK.Internal.SemVer.cs.meta → Internal/AmaGDK.Internal.SemVer.cs.meta} +0 -0
  45. /package/Runtime/{AmaGDK.Internal.cs.meta → Internal/AmaGDK.Internal.cs.meta} +0 -0
  46. /package/Runtime/{AmaGDK.Utils.cs.meta → Internal/AmaGDK.Utils.cs.meta} +0 -0
  47. /package/Runtime/{AmaGDK.WebUtils.cs → Internal/AmaGDK.WebUtils.cs} +0 -0
  48. /package/Runtime/{AmaGDK.WebUtils.cs.meta → Internal/AmaGDK.WebUtils.cs.meta} +0 -0
@@ -1,29 +1,15 @@
1
1
  using System;
2
2
  using System.Collections.Generic;
3
3
  using System.Text;
4
+ using System.Text.RegularExpressions;
4
5
  using Amanotes.Core.Internal;
5
6
  using UnityEngine;
7
+ using UnityEngine.Profiling;
6
8
  using static Amanotes.Core.AmaGDK.Analytics;
7
9
  using static Amanotes.Core.Internal.Logging;
8
10
 
9
11
  namespace Amanotes.Core
10
12
  {
11
- [Serializable]
12
- public class AnalyticsAdapterConfig
13
- {
14
- public bool SendEventByDefault;
15
-
16
- public List<string> NeverSend;
17
- public List<string> AlwaysSend;
18
-
19
- public bool IsForbidden(string eventName)
20
- {
21
- return SendEventByDefault
22
- ? NeverSend.Contains(eventName)
23
- : !AlwaysSend.Contains(eventName);
24
- }
25
- }
26
-
27
13
  partial class AmaGDK
28
14
  {
29
15
  //PUBLIC APIS
@@ -85,6 +71,11 @@ namespace Amanotes.Core
85
71
  public static IEventParamsBuilder LogEvent(string eventName)
86
72
  {
87
73
  NormalizeEventName(ref eventName, true);
74
+ if (localData.GetEventDetail(eventName) == null && localData.eventDetails.Count >= 500)
75
+ {
76
+ LogWarning($" - You have reported 500 types of events. New event <{eventName}> will be ignored.");
77
+ return null;
78
+ }
88
79
  var result = new EventParams(eventName);
89
80
  eventQueue.Enqueue(result);
90
81
  return result;
@@ -146,41 +137,45 @@ namespace Amanotes.Core
146
137
 
147
138
  private static IntegrityFlag MergeData(List<string> funnelEvents, Dictionary<string, int> eventCountMap, bool sumEventCount)
148
139
  {
140
+ Profiler.BeginSample("AmaGDK.Analytics.MergeData()");
149
141
  var flag = IntegrityFlag.Ok;
150
142
 
151
- foreach (string e in funnelEvents)
152
- {
153
- if (localData.funnelSignatures.Contains(e)) continue;
154
-
155
- flag |= IntegrityFlag.NewFunnel;
156
- localData.AddFunnelSignature(e);
157
- }
158
-
159
- foreach (KeyValuePair<string, int> e in eventCountMap)
160
- {
161
- EventDetail eventDetail = localData.GetEventDetail(e.Key);
162
- if (eventDetail == null)
143
+ if (funnelEvents != null)
144
+ foreach (string e in funnelEvents)
163
145
  {
164
- localData.eventDetails.Add(new EventDetail
165
- {
166
- eventName = e.Key,
167
- totalCount = e.Value
168
- });
169
- flag |= IntegrityFlag.NewEvent;
170
- continue;
171
- }
146
+ if (localData.funnelSignatures.Contains(e)) continue;
172
147
 
173
- if (eventDetail.totalCount != e.Value)
174
- {
175
- flag |= IntegrityFlag.DifferentCount;
148
+ flag |= IntegrityFlag.NewFunnel;
149
+ localData.AddFunnelSignature(e);
176
150
  }
177
151
 
178
- if (sumEventCount)
152
+ if (eventCountMap != null)
153
+ foreach (KeyValuePair<string, int> e in eventCountMap)
179
154
  {
180
- eventDetail.totalCount += e.Value;
181
- }
182
- }
155
+ EventDetail eventDetail = localData.GetEventDetail(e.Key);
156
+ if (eventDetail == null)
157
+ {
158
+ localData.eventDetails.Add(new EventDetail
159
+ {
160
+ eventName = e.Key,
161
+ totalCount = e.Value
162
+ });
163
+ flag |= IntegrityFlag.NewEvent;
164
+ continue;
165
+ }
166
+
167
+ if (eventDetail.totalCount != e.Value)
168
+ {
169
+ flag |= IntegrityFlag.DifferentCount;
170
+ }
183
171
 
172
+ if (sumEventCount)
173
+ {
174
+ eventDetail.totalCount += e.Value;
175
+ }
176
+ }
177
+
178
+ Profiler.EndSample();
184
179
  return flag;
185
180
  }
186
181
 
@@ -197,7 +192,7 @@ namespace Amanotes.Core
197
192
  }
198
193
 
199
194
  //Integrity check
200
- if (!Config.common.checkMigrationIntegrity) return;
195
+ if (!Config.analytics.checkMigrationIntegrity) return;
201
196
 
202
197
  // False: DO NOT UPDATE EXISTED COUNT VALUE BECAUSE WE DON'T HAVE
203
198
  // ENOUGH INFORMATION TO DECIDE WHICH ONE IS MORE ACCURATE
@@ -211,11 +206,9 @@ namespace Amanotes.Core
211
206
  {
212
207
  foreach (string adapterID in adapterIDs)
213
208
  {
214
- AdapterContext adapter = listAdapters.Find(a => a.adapterId == adapterID);
209
+ AnalyticsAdapter adapter = listAdapters.Find(a => a.adapterId == adapterID);
215
210
  if (adapter == null) continue;
216
-
217
- var config = adapter.GetAdapterConfig<AnalyticsAdapterConfig>();
218
- config.AlwaysSend = eventNames;
211
+ adapter.AlwaysSend = eventNames;
219
212
  }
220
213
  }
221
214
 
@@ -223,11 +216,9 @@ namespace Amanotes.Core
223
216
  {
224
217
  foreach (string adapterID in adapterIDs)
225
218
  {
226
- AdapterContext adapter = listAdapters.Find(a => a.adapterId == adapterID);
219
+ AnalyticsAdapter adapter = listAdapters.Find(a => a.adapterId == adapterID);
227
220
  if (adapter == null) continue;
228
-
229
- var config = adapter.GetAdapterConfig<AnalyticsAdapterConfig>();
230
- config.NeverSend = eventNames;
221
+ adapter.NeverSend = eventNames;
231
222
  }
232
223
  }
233
224
  }
@@ -244,33 +235,78 @@ namespace Amanotes.Core
244
235
  }
245
236
 
246
237
  internal static readonly Dictionary<string, EventConfig> sendAtCount = new Dictionary<string, EventConfig>();
247
- internal static Predicate<EventParams> logEventHook = null;
238
+ internal static Predicate<EventParams> logEventHook;
248
239
  internal static readonly Queue<EventParams> eventQueue = new Queue<EventParams>();
249
240
  internal static readonly Queue<Action> actionQueue = new Queue<Action>();
250
- internal static readonly List<AdapterContext> listAdapters = new List<AdapterContext>();
241
+ internal static readonly List<AnalyticsAdapter> listAdapters = new List<AnalyticsAdapter>();
251
242
  internal static readonly AnalyticsData localData = new AnalyticsData().LoadJsonFromFile();
252
243
  internal static readonly SessionStat sessionStat = new SessionStat();
253
- internal static AdapterContext primaryAdapter;
254
-
255
- internal static void Init()
244
+ internal static AnalyticsAdapter primaryAdapter;
245
+ internal static readonly HashSet<string> blackListPrefixes = new HashSet<string>() {
246
+ "firebase_", "google_", "ga_"
247
+ };
248
+ internal static readonly HashSet<string> blackListEvents = new HashSet<string>()
249
+ {
250
+ "ad_activeview",
251
+ "ad_click",
252
+ "ad_exposure",
253
+ "ad_query",
254
+ "ad_reward",
255
+ "adunit_exposure",
256
+ "app_background",
257
+ "app_clear_data",
258
+ "app_exception",
259
+ "app_remove",
260
+ "app_store_refund",
261
+ "app_store_subscription_cancel",
262
+ "app_store_subscription_convert",
263
+ "app_store_subscription_renew",
264
+ "app_update",
265
+ "app_upgrade",
266
+ "dynamic_link_app_open",
267
+ "dynamic_link_app_update",
268
+ "dynamic_link_first_open",
269
+ "error",
270
+ "first_open",
271
+ "first_visit",
272
+ "in_app_purchase",
273
+ "notification_dismiss",
274
+ "notification_foreground",
275
+ "notification_open",
276
+ "notification_receive",
277
+ "os_update",
278
+ "session_start",
279
+ "session_start_with_rollout",
280
+ "user_engagement"
281
+ };
282
+
283
+ internal static void InitModule()
256
284
  {
257
285
  listAdapters.Clear();
258
- listAdapters.AddRange(Adapter.FindAll<IAnalyticAdapter>());
286
+ listAdapters.AddRange(Adapter2.GetAllAdapter<AnalyticsAdapter>());
259
287
  if (listAdapters.Count != 0)
260
288
  primaryAdapter = listAdapters.Find(a => a.adapterId == AdapterID.FIREBASE_ANALYTICS) ?? listAdapters[0];
289
+ }
261
290
 
291
+ internal static void StartModule()
292
+ {
293
+ // All adapters should be ready now!
294
+
295
+ // TODO: Refactor Resolve Early Calls
262
296
  onFrameUpdate -= ProcessEventQueue;
263
297
  onFrameUpdate += ProcessEventQueue;
264
298
 
265
299
  onFrameUpdate -= ProcessActionQueue;
266
300
  onFrameUpdate += ProcessActionQueue;
267
301
  }
268
-
302
+
269
303
  internal static void ProcessEventQueue()
270
304
  {
271
305
  if (eventQueue.Count <= 0) return;
272
- SDKConfig common = Config.common;
273
- bool showAnalyticsLog = common.showAnalyticsLog;
306
+
307
+ Profiler.BeginSample("AmaGDK.Analytics.ProcessEventQueue()");
308
+ AnalyticsConfig config = Config.analytics;
309
+ bool showAnalyticsLog = config.showAnalyticsLog;
274
310
  sessionStat.ClearLastFrameEventInfo();
275
311
 
276
312
  while (eventQueue.Count > 0)
@@ -279,27 +315,25 @@ namespace Amanotes.Core
279
315
  if (!ShouldSendEvent(eventData)) continue;
280
316
 
281
317
  var (countInSession, totalCount) = localData.IncreaseCounter(eventData);
282
- if (eventData.accumulated)
318
+ if (eventData.accumulated || eventData.parameters.ContainsKey("accumulated_count"))
283
319
  {
284
- eventData.AddParam("accumulated_count", eventData.withinSession ? countInSession : totalCount);
320
+ UpdateAccumulatedCount(eventData, countInSession, totalCount);
285
321
  }
286
322
 
287
323
  eventData.AddParam("ama_device_id", User.AmaDeviceId);
288
-
324
+
289
325
  string eventNameToSend = GetEventNameToSend(eventData.eventName, countInSession, totalCount);
290
- if (common.normalizeEventName) NormalizeEventName(ref eventNameToSend);
326
+ if (config.normalizeEventName) NormalizeEventName(ref eventNameToSend);
291
327
 
292
328
  if (showAnalyticsLog) Debug.Log($"AmaGDK | [Analytics] Event <{eventNameToSend}> (in session: {countInSession}, total: {totalCount})\n{JsonUtils.DictionaryToJson(eventData.parameters, true)}");
293
329
 
294
- common.quality.CheckEventQuality(eventData.eventName, eventData.parameters);
330
+ config.quality.CheckEventQuality(eventData.eventName, eventData.parameters);
295
331
 
296
332
  var inUseAdapterIDs = new List<string>();
297
- foreach (AdapterContext m in listAdapters)
333
+ foreach (AnalyticsAdapter m in listAdapters)
298
334
  {
299
335
  if (IsAdapterForbidden(m, eventData)) continue;
300
-
301
- var api = m.GetAdapterApi<IAnalyticAdapter>();
302
- api.LogEvent(eventNameToSend, eventData.parameters);
336
+ m.LogEvent(eventNameToSend, eventData.parameters);
303
337
  inUseAdapterIDs.Add(m.adapterId);
304
338
  }
305
339
  sessionStat.Add(eventNameToSend, eventData, inUseAdapterIDs);
@@ -307,24 +341,45 @@ namespace Amanotes.Core
307
341
 
308
342
  localData.SaveIfDirty();
309
343
  sessionStat.Save();
344
+ Profiler.EndSample();
310
345
  }
311
346
 
312
- internal static bool IsAdapterForbidden(AdapterContext adapterContext, EventParams eventData)
347
+ internal static void UpdateAccumulatedCount(EventParams eventData, int countInSession, int totalCount)
313
348
  {
314
- if (adapterContext == null) return false;
315
- var config = adapterContext.GetAdapterConfig<AnalyticsAdapterConfig>();
349
+ var ACCUMULATED_COUNT = "accumulated_count";
350
+ var countByGDK = eventData.withinSession ? countInSession : totalCount;
316
351
 
352
+ if (!eventData.parameters.ContainsKey(ACCUMULATED_COUNT))
353
+ {
354
+ eventData.AddParam(ACCUMULATED_COUNT, countByGDK);
355
+ return;
356
+ }
357
+
358
+ int.TryParse(eventData.parameters[ACCUMULATED_COUNT].ToString(), out int oriAccCount);
359
+ if (oriAccCount == countByGDK)
360
+ {
361
+ eventData.AddParam(ACCUMULATED_COUNT, countByGDK);
362
+ }
363
+ else
364
+ {
365
+ LogWarning($"[Analytics] event <{eventData.eventName}> has difference in accumulated count. Original count: {oriAccCount}. AmaGDK count: {countByGDK}");
366
+ }
367
+ }
368
+
369
+ internal static bool IsAdapterForbidden(AnalyticsAdapter adapter, EventParams eventData)
370
+ {
371
+ if (adapter == null) return false;
317
372
  if (eventData.overrideConfig)
318
373
  {
319
374
  // IMPORTANT NOTE:
320
375
  //
321
376
  // Once set overrideConfig, we should skip the adapter config completely!!!
322
377
  //
323
- if (eventData.IsAdapterForbidden(adapterContext.adapterId)) return true;
378
+ if (eventData.IsAdapterForbidden(adapter.adapterId)) return true;
324
379
  }
325
380
  else // Do not remove else
326
381
  {
327
- if (config.IsForbidden(eventData.eventName)) return true;
382
+ if (adapter.IsForbidden(eventData.eventName)) return true;
328
383
  }
329
384
 
330
385
  return false;
@@ -338,36 +393,70 @@ namespace Amanotes.Core
338
393
  return $"{eventName}_{(config.withinSession ? countInSession : totalCount).ToString(config.numberFormat)}";
339
394
  }
340
395
 
396
+ //https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event
341
397
  internal static void NormalizeEventName(ref string eventName, bool dryRun = false)
342
398
  {
343
- StringBuilder sb = tmpWarningSB;
344
- sb.Clear();
345
-
346
- if (string.IsNullOrEmpty(eventName))
399
+ Profiler.BeginSample("AmaGDK.Analytics.NormalizeEventName()");
347
400
  {
348
- sb.AppendLine(" - EventName should not be empty");
349
- return;
401
+ StringBuilder sb = tmpWarningSB;
402
+ sb.Clear();
403
+ NormalizeKeyString(ref eventName, sb, dryRun);
404
+ if (blackListEvents.Contains(eventName))
405
+ sb.AppendLine($" - EventName <{eventName}> is reserved and should not be used");
406
+ if (sb.Length > 0 && dryRun) LogWarningOnce($"Warning for event name <{eventName}>:\n{sb}", $"NormalizeEventName<{eventName}>");
350
407
  }
408
+ Profiler.EndSample();
409
+ }
351
410
 
352
- string normalizedEventName = eventName.Trim()
353
- .Replace(' ', '_')
354
- .Replace('-', '_');
355
-
356
- if (eventName != normalizedEventName)
411
+ internal static void NormalizeKeyString(ref string key, StringBuilder warningLog, bool dryRun = false)
412
+ {
413
+ Profiler.BeginSample("AmaGDK.Analytics.NormalizeEventName()");
357
414
  {
358
- if (!dryRun)
415
+ if (string.IsNullOrWhiteSpace(key))
359
416
  {
360
- eventName = normalizedEventName;
417
+ LogWarning($" - Name should not be empty");
418
+ Profiler.EndSample();
419
+ return;
361
420
  }
362
- else
421
+
422
+ string normalizedString = key.Trim()
423
+ .Replace(' ', '_')
424
+ .Replace('-', '_');
425
+
426
+ if (key != normalizedString)
427
+ {
428
+ if (!dryRun)
429
+ {
430
+ key = normalizedString;
431
+ } else
432
+ {
433
+ warningLog.AppendLine($" - Malformed name (should not contains <space> or <dash>): <{key}>");
434
+ }
435
+ }
436
+
437
+ var r = new Regex("^[a-zA-Z0-9_]*$");
438
+ if (!r.IsMatch(normalizedString))
439
+ {
440
+ warningLog.AppendLine($" - Name may only contain alphanumeric characters and underscores");
441
+ }
442
+
443
+ if (!char.IsLetter(key[0]))
363
444
  {
364
- sb.AppendLine($" - Malformed eventName (should not contains <space> or <dash>: <{eventName}>");
445
+ warningLog.AppendLine($" - Name must start with a letter: <{key}> ");
446
+ }
447
+
448
+ foreach (var prefix in blackListPrefixes)
449
+ {
450
+ if (!normalizedString.StartsWith(prefix)) continue;
451
+ warningLog.AppendLine($" - <{key}> starts with reserved prefix <{prefix}>");
452
+ }
453
+
454
+ if (key.Length > 40)
455
+ {
456
+ warningLog.AppendLine($" - Name is too long ({key.Length} > 40): <{key}> ");
365
457
  }
366
458
  }
367
-
368
- if (!char.IsLetter(eventName[0])) sb.AppendLine($" - EventName must start with a letter: <{eventName}> ");
369
- if (eventName.Length > 40) sb.AppendLine($" - EventName is too long ({eventName.Length} > 40): <{eventName}> ");
370
- if (sb.Length > 0 && dryRun) LogWarningOnce(sb.ToString(), $"NormalizeEventName<{eventName}>");
459
+ Profiler.EndSample();
371
460
  }
372
461
 
373
462
  internal static bool ShouldSendEvent(EventParams eventData)
@@ -387,7 +476,7 @@ namespace Amanotes.Core
387
476
  //Check the rule to log event
388
477
  if (logEventHook != null)
389
478
  {
390
- EventDetail eventDetail = localData.GetEventDetail(eventData.eventName, true);
479
+ // localData.GetEventDetail(eventData.eventName, true);
391
480
  return logEventHook(eventData);
392
481
  }
393
482
 
@@ -407,17 +496,17 @@ namespace Amanotes.Core
407
496
 
408
497
  internal static void InternalSetUserId(string userId)
409
498
  {
410
- foreach (AdapterContext s in listAdapters)
499
+ foreach (AnalyticsAdapter s in listAdapters)
411
500
  {
412
- s.GetAdapterApi<IAnalyticAdapter>().SetUserId(userId);
501
+ s.SetUserId(userId);
413
502
  }
414
503
  }
415
504
 
416
505
  internal static void InternalSetUserProperty(string key, string value)
417
506
  {
418
- foreach (AdapterContext s in listAdapters)
507
+ foreach (AnalyticsAdapter s in listAdapters)
419
508
  {
420
- s.GetAdapterApi<IAnalyticAdapter>().SetUserProperty(key, value);
509
+ s.SetUserProperty(key, value);
421
510
  }
422
511
  }
423
512
  }
@@ -486,7 +575,6 @@ namespace Amanotes.Core
486
575
  [Serializable]
487
576
  internal class AnalyticsData : IJsonFile
488
577
  {
489
-
490
578
  public List<string> funnelSignatures = new List<string>();
491
579
  public List<EventDetail> eventDetails = new List<EventDetail>();
492
580
  public MigrationLog migrationLog = new MigrationLog();
@@ -561,7 +649,6 @@ namespace Amanotes.Core
561
649
 
562
650
  internal class SessionStat
563
651
  {
564
-
565
652
  private const string TSV_HEADER_ROW = "EventName\tAdapters\tParameters";
566
653
  private readonly string fileName = nameof(SessionStat) + DateTime.Now.ToString("_yyMMdd_HHmmss") + ".tsv";
567
654
  private StringBuilder contents = new StringBuilder();
@@ -581,7 +668,7 @@ namespace Amanotes.Core
581
668
 
582
669
  internal void Save()
583
670
  {
584
- if (!Config.common.enableEventStat || lastFrameEventStats.Count == 0) return;
671
+ if (!Config.analytics.enableEventStat || lastFrameEventStats.Count == 0) return;
585
672
 
586
673
  contents.Clear();
587
674
  if (isFirstSave)
@@ -607,7 +694,7 @@ namespace Amanotes.Core
607
694
  if (ap == null) return null;
608
695
 
609
696
  var data = ap as EventParams;
610
- data.funnelSignature = string.IsNullOrEmpty(signature) ? data.eventName : signature;
697
+ data!.funnelSignature = string.IsNullOrEmpty(signature) ? data.eventName : signature;
611
698
  return ap;
612
699
  }
613
700
 
@@ -616,7 +703,7 @@ namespace Amanotes.Core
616
703
  if (ap == null) return null;
617
704
 
618
705
  var data = ap as EventParams;
619
- data.accumulated = true;
706
+ data!.accumulated = true;
620
707
  data.withinSession = withinSession;
621
708
  return ap;
622
709
  }
@@ -631,7 +718,7 @@ namespace Amanotes.Core
631
718
  }
632
719
 
633
720
  var eventData = ap as EventParams;
634
- if (eventData.overrideConfig)
721
+ if (eventData!.overrideConfig)
635
722
  {
636
723
  LogWarning("Except() can only be set once!");
637
724
  return ap;
@@ -655,7 +742,7 @@ namespace Amanotes.Core
655
742
  }
656
743
 
657
744
  var eventData = ap as EventParams;
658
- if (eventData.overrideConfig)
745
+ if (eventData!.overrideConfig)
659
746
  {
660
747
  LogWarning("OnlyTo() can only be set once!");
661
748
  return ap;
@@ -674,14 +761,22 @@ namespace Amanotes.Core
674
761
  if (ap == null) return null;
675
762
  if (!IsValidKeyValue(ap, key, value)) return ap;
676
763
 
677
- Dictionary<string, object> parameters = (ap as EventParams)?.parameters;
678
- if (parameters != null && parameters.ContainsKey(key))
764
+ var eventParams = ap as EventParams;
765
+ Dictionary<string, object> parameters = eventParams?.parameters;
766
+ if (parameters == null)
767
+ return ap;
768
+ if (parameters.ContainsKey(key))
679
769
  {
680
- LogWarning($"Duplicated key: {key}!");
770
+ LogWarning($"Duplicated key: {key}. Event name: {eventParams.eventName}!");
771
+ return ap;
772
+ }
773
+ if (parameters.Count >= 25)
774
+ {
775
+ LogWarning($"Exceeded key: {key}. Event name: {eventParams.eventName}!");
681
776
  return ap;
682
777
  }
683
778
 
684
- parameters?.Add(key, value);
779
+ parameters.Add(key, value);
685
780
  return ap;
686
781
  }
687
782
 
@@ -707,7 +802,7 @@ namespace Amanotes.Core
707
802
  if (ap == null) return null;
708
803
  if (parameters.Length == 0)
709
804
  {
710
- LogWarning($"Param tuple is null. Event name: {(ap as EventParams).eventName}!");
805
+ LogWarning($"Param tuple is null. Event name: {(ap as EventParams)?.eventName}!");
711
806
  return ap;
712
807
  }
713
808
 
@@ -719,9 +814,16 @@ namespace Amanotes.Core
719
814
  return ap;
720
815
  }
721
816
 
817
+ //https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Param
722
818
  private static bool IsValidKeyValue(IEventParamsBuilder ap, string key, object value)
723
819
  {
724
820
  string eventName = (ap as EventParams)?.eventName;
821
+
822
+ StringBuilder sb = tmpWarningSB;
823
+ sb.Clear();
824
+ NormalizeKeyString(ref key, sb);
825
+ if (sb.Length > 0) LogWarningOnce($"Warning for event <{eventName}>, param <{key}>:\n{sb}", $"NormalizeParamKey<{eventName}>");
826
+
725
827
  if (string.IsNullOrWhiteSpace(key))
726
828
  {
727
829
  LogWarning($"Param key is empty. Event name: {eventName}!");
@@ -740,10 +842,20 @@ namespace Amanotes.Core
740
842
 
741
843
  namespace Amanotes.Core.Internal
742
844
  {
845
+ [Serializable] public class AnalyticsConfig
846
+ {
847
+ // [Header("----- MODULE CONFIG -----")]
848
+ public bool showAnalyticsLog = true;
849
+ public bool normalizeEventName = true;
850
+ public bool checkMigrationIntegrity = true;
851
+ public bool enableEventStat;
852
+ public AnalyticQualityConfig quality;
853
+ }
854
+
743
855
  [Serializable]
744
856
  public class AnalyticQualityConfig
745
857
  {
746
- public bool enable = false;
858
+ public bool enable;
747
859
  public AnalyticQualityAsset qualityAsset;
748
860
 
749
861
  public void CheckEventQuality(string eventName, Dictionary<string, object> parameters)
@@ -754,24 +866,32 @@ namespace Amanotes.Core.Internal
754
866
  }
755
867
  }
756
868
  }
757
-
758
- public partial class SDKConfig
869
+
870
+ public partial class ConfigAsset
759
871
  {
760
- public bool showAnalyticsLog = true;
761
- public bool normalizeEventName = true;
762
- public bool checkMigrationIntegrity = true;
763
- public bool enableEventStat;
764
- public AnalyticQualityConfig quality;
872
+ [SerializeField] public AnalyticsConfig analytics;
765
873
  }
766
-
767
- public interface IAnalyticAdapter
874
+
875
+ public abstract class AnalyticsAdapter : Adapter2
768
876
  {
769
- void LogEvent(string eventNameToSend, Dictionary<string, object> parameters);
877
+ public bool SendEventByDefault;
878
+
879
+ public List<string> NeverSend;
880
+ public List<string> AlwaysSend;
881
+
882
+ public bool IsForbidden(string eventName)
883
+ {
884
+ return SendEventByDefault
885
+ ? NeverSend.Contains(eventName)
886
+ : !AlwaysSend.Contains(eventName);
887
+ }
888
+
889
+ public abstract void LogEvent(string eventNameToSend, Dictionary<string, object> parameters);
770
890
 
771
- void SetUserId(string userId);
891
+ public abstract void SetUserId(string userId);
772
892
 
773
- void SetUserProperty(string key, string value);
893
+ public abstract void SetUserProperty(string key, string value);
774
894
 
775
- void GetAppInstanceID(Action<string> appId);
895
+ public abstract void GetAppInstanceID(Action<string> appId);
776
896
  }
777
897
  }
@@ -2,6 +2,7 @@ using System;
2
2
  using System.Collections.Generic;
3
3
  using System.Reflection;
4
4
  using UnityEngine;
5
+
5
6
  #if UNITY_EDITOR
6
7
  using UnityEditor;
7
8
  #endif
@@ -51,7 +52,7 @@ namespace Amanotes.Core.Internal
51
52
  initOrder.Clear();
52
53
 
53
54
  var counter = 0;
54
- foreach (Type adapter in GetAllAdapters<AdapterContext>())
55
+ foreach (Type adapter in GetAllAdapters<Adapter2>())
55
56
  {
56
57
  string id = adapter.Name.Replace("Adapter", string.Empty);
57
58
  initOrder.Add(new InitOrder(){ id = id, order = counter++ });