com.amanotes.gdk 0.2.68 → 0.2.70-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 (55) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/Editor/AmaGDKEditor.cs +78 -23
  3. package/Editor/AmaGDKExtra.cs +14 -13
  4. package/Editor/Extra/GDKAutoUpdateAdapter.cs +14 -1
  5. package/Editor/Extra/GDKCIBuildCommand.cs +2 -16
  6. package/Editor/Extra/GDKSymbolDefine.cs +372 -0
  7. package/Editor/{AmaGDKManager.cs.meta → Extra/GDKSymbolDefine.cs.meta} +1 -1
  8. package/Editor/Utils/AmaGDKEditor.ExtractVersion.cs +41 -3
  9. package/Extra/AmaGDKInstaller.unitypackage +0 -0
  10. package/Extra/CheckDiskSpace.unitypackage +0 -0
  11. package/Extra/Consent.unitypackage +0 -0
  12. package/Extra/{GoogleCMP.unitypackage.meta → Consent.unitypackage.meta} +1 -1
  13. package/Extra/ForceUpdate.unitypackage +0 -0
  14. package/Extra/LegacyGDKUpdateHelper.unitypackage +0 -0
  15. package/Extra/PostProcessor.unitypackage +0 -0
  16. package/Packages/AmaGDKConfig.unitypackage +0 -0
  17. package/Packages/AmaGDKExample.unitypackage +0 -0
  18. package/Packages/AmaGDKTest.unitypackage +0 -0
  19. package/Packages/AppsFlyerAdapter.PurchaseConnector.unitypackage +0 -0
  20. package/Packages/AppsFlyerAdapter.unitypackage +0 -0
  21. package/Packages/FirebaseAnalyticsAdapter.unitypackage +0 -0
  22. package/Packages/FirebaseRemoteConfigAdapter.unitypackage +0 -0
  23. package/Packages/IronSourceAdapter.unitypackage +0 -0
  24. package/Packages/MaxAdNetworkAdapter.unitypackage +0 -0
  25. package/{Editor/Resources.meta → Packages/MaxAdNetworkAdapter.unitypackage.meta} +1 -2
  26. package/Packages/RevenueCatAdapter.unitypackage +0 -0
  27. package/Packages/SqliteAnalyticsAdapter.unitypackage +0 -0
  28. package/Runtime/Ad/AdExtension.cs +2 -0
  29. package/Runtime/Ad/AdLogic.cs +83 -70
  30. package/Runtime/Ad/AdShowContext.cs +1 -0
  31. package/Runtime/Ad/AmaGDK.Ads.cs +17 -3
  32. package/Runtime/AmaGDK.Adapters.cs +12 -5
  33. package/Runtime/AmaGDK.Analytics.cs +45 -38
  34. package/Runtime/AmaGDK.Consent.cs +259 -0
  35. package/Runtime/AmaGDK.Consent.cs.meta +11 -0
  36. package/Runtime/AmaGDK.Device.cs +4 -4
  37. package/Runtime/AmaGDK.RemoteConfig.cs +3 -1
  38. package/Runtime/AmaGDK.cs +28 -19
  39. package/Runtime/AnalyticQualityAsset.cs +16 -9
  40. package/Runtime/AudioToolkit/AmaGDK.Audio.Latency.cs +1 -3
  41. package/Runtime/AudioToolkit/AmaGDK.Audio.cs +41 -25
  42. package/Runtime/AudioToolkit/Plugins/AudioDeviceDetector.cs +13 -12
  43. package/Runtime/Core/GDKPool.cs +83 -17
  44. package/Runtime/Fps/AmaFPS.cs +0 -1
  45. package/Runtime/Fps/AmaFPSVisualizer.cs +7 -7
  46. package/Runtime/Internal/AmaGDK.Internal.AmaGUI.cs +1 -1
  47. package/Runtime/Internal/AmaGDK.Internal.cs +2 -5
  48. package/Runtime/Internal/AmaGDK.Utils.cs +32 -81
  49. package/Runtime/Internal/GDKGeoLocationcs.cs +3 -1
  50. package/Runtime/Internal/GDKServerTime.cs +13 -10
  51. package/package.json +1 -1
  52. package/Editor/AmaGDKManager.cs +0 -235
  53. package/Editor/Resources/amasdk-modules.json +0 -31
  54. package/Editor/Resources/amasdk-modules.json.meta +0 -7
  55. package/Extra/GoogleCMP.unitypackage +0 -0
@@ -0,0 +1,259 @@
1
+ using System;
2
+ using Amanotes.Core.Internal;
3
+ using UnityEngine;
4
+
5
+ namespace Amanotes.Core
6
+ {
7
+ public partial class AmaGDK //Consent
8
+ {
9
+ public static partial class Consent //Public
10
+ {
11
+ private static bool _onGoingRequestAllConsents;
12
+ public static void RequestAllConsents(Action onComplete)
13
+ {
14
+ OnAllConsentComplete(onComplete);
15
+
16
+ if (_onGoingRequestAllConsents)
17
+ {
18
+ Logging.LogWarning($"[Consent] RequestAllConsents is called multiple times.");
19
+ return;
20
+ }
21
+
22
+ _onGoingRequestAllConsents = true;
23
+ #if UNITY_IOS
24
+ ATT.Request(ConsiderCMP);
25
+ #else
26
+ CMP.Request(EndConsent);
27
+ #endif
28
+ }
29
+
30
+ public static void OnAllConsentComplete(Action onComplete)
31
+ {
32
+ if (allConsentChecked)
33
+ {
34
+ onComplete?.Invoke();
35
+ return;
36
+ }
37
+
38
+ _onConsentComplete -= onComplete;
39
+ _onConsentComplete += onComplete;
40
+ }
41
+ }
42
+
43
+ public static partial class Consent //ATT
44
+ {
45
+ public static class ATT
46
+ {
47
+ public static bool IsAllowed
48
+ {
49
+ get
50
+ {
51
+ if (checkedATT) return allowedATT;
52
+ Logging.LogWarning("[Consent] ATT has not completed yet");
53
+ return false;
54
+ }
55
+ }
56
+
57
+ public static void Request(Action<bool> onComplete)
58
+ {
59
+ if (checkedATT)
60
+ {
61
+ onComplete?.Invoke(allowedATT);
62
+ return;
63
+ }
64
+
65
+ ConsentHook.ATTRequest(isAllowed =>
66
+ {
67
+ data.att = isAllowed ? Status.Allowed : Status.NotAllowed;
68
+ data.SaveJsonToFile();
69
+ onComplete?.Invoke(allowedATT);
70
+ });
71
+ }
72
+ }
73
+
74
+ }
75
+
76
+ public static partial class Consent //CMP
77
+ {
78
+ public static class CMP
79
+ {
80
+ public static bool IsRequired
81
+ {
82
+ get
83
+ {
84
+ if (checkedCMP) return data.cmp >= Status.Allowed;
85
+
86
+ if (!ConsentHook.IsRequiredCMP.HasValue)
87
+ {
88
+ Logging.LogWarningOnce("[Consent] CMP is not available or not ready");
89
+ return false;
90
+ }
91
+
92
+ return ConsentHook.IsRequiredCMP.Value;
93
+ }
94
+ }
95
+
96
+ public static void Request(Action<bool> onComplete)
97
+ {
98
+ if (checkedCMP)
99
+ {
100
+ onComplete?.Invoke(checkedCMP);
101
+ return;
102
+ }
103
+
104
+ ConsentHook.TryToShowCMP(successful =>
105
+ {
106
+ if (successful)
107
+ {
108
+ data.cmp = !IsRequired ? Status.NotRequired : Status.Allowed;
109
+ data.SaveJsonToFile();
110
+ }
111
+ onComplete?.Invoke(checkedCMP);
112
+ });
113
+ }
114
+
115
+ public static void ForceShowPopUp(Action onComplete)
116
+ {
117
+ OnAllConsentComplete(onComplete);
118
+ ConsentHook.ForceShowCMP(EndConsent);
119
+ }
120
+ }
121
+ }
122
+
123
+ public static partial class Consent //Internal
124
+ {
125
+ private static readonly ConsentData data = new ConsentData().LoadJsonFromFile();
126
+ private static Action _onConsentComplete;
127
+
128
+ internal static bool checkedATT => data.att >= Status.Allowed;
129
+ internal static bool allowedATT => data.att == Status.Allowed;
130
+ internal static bool checkedCMP => data.cmp >= Status.NotRequired;
131
+ internal static bool allConsentChecked => checkedATT && checkedCMP;
132
+
133
+ internal enum Status
134
+ {
135
+ None,
136
+ NotRequired,
137
+ Allowed,
138
+ NotAllowed
139
+ }
140
+
141
+ internal static bool InitModule()
142
+ {
143
+ if (allConsentChecked) return true;
144
+ if (!_config.consent.autoGetConsent) return true;
145
+ RequestAllConsents(null);
146
+ return true;
147
+ }
148
+
149
+ private static void ConsiderCMP(bool needToShowCMP)
150
+ {
151
+ if (checkedCMP)
152
+ {
153
+ EndConsent(true);
154
+ return;
155
+ }
156
+
157
+ if (needToShowCMP)
158
+ {
159
+ ConsentHook.TryToShowCMP(EndConsent);
160
+ }
161
+ else
162
+ {
163
+ ConsentHook.SetNotRequiredCMP(EndConsent);
164
+ }
165
+ }
166
+
167
+ private static void EndConsent(bool isConsentSuccess)
168
+ {
169
+ var tmpAction = _onConsentComplete;
170
+ _onConsentComplete = null;
171
+ tmpAction?.Invoke();
172
+ Logging.Log("[Consent] End consent checking");
173
+ }
174
+ }
175
+ }
176
+
177
+ [Serializable]
178
+ public class ConsentData : IJsonFile
179
+ {
180
+ [SerializeField] internal AmaGDK.Consent.Status att;
181
+ [SerializeField] internal AmaGDK.Consent.Status cmp;
182
+ }
183
+
184
+ public static class ConsentHook
185
+ {
186
+ public static bool? IsRequiredCMP;
187
+
188
+ private static Action<Action<bool>> _tryToShowCMP;
189
+ public static Action<Action<bool>> TryToShowCMP
190
+ {
191
+ get
192
+ {
193
+ if (_tryToShowCMP != null) return _tryToShowCMP;
194
+
195
+ Logging.LogWarning("[Consent] ConsentHook.TryToShowCMP is null");
196
+ _tryToShowCMP = callback => callback(false);
197
+ return _tryToShowCMP;
198
+ }
199
+ set => _tryToShowCMP = value;
200
+ }
201
+
202
+ private static Action<Action<bool>> _forceShowCMP;
203
+ public static Action<Action<bool>> ForceShowCMP
204
+ {
205
+ get
206
+ {
207
+ if (_forceShowCMP != null) return _forceShowCMP;
208
+
209
+ Logging.LogWarning("[Consent] ConsentHook.ForceShowCMP is null");
210
+ _forceShowCMP = callback => callback(false);
211
+ return _forceShowCMP;
212
+ }
213
+ set => _forceShowCMP = value;
214
+ }
215
+
216
+ private static Action<Action<bool>> _setNotRequiredCMP;
217
+ public static Action<Action<bool>> SetNotRequiredCMP
218
+ {
219
+ get
220
+ {
221
+ if (_setNotRequiredCMP != null) return _setNotRequiredCMP;
222
+
223
+ Logging.LogWarning("[Consent] ConsentHook.SetNotRequiredCMP is null");
224
+ _setNotRequiredCMP = callback => callback(false);
225
+ return _setNotRequiredCMP;
226
+ }
227
+ set => _setNotRequiredCMP = value;
228
+ }
229
+
230
+ private static Action<Action<bool>> _attRequest;
231
+ public static Action<Action<bool>> ATTRequest
232
+ {
233
+ get
234
+ {
235
+ if (_attRequest != null) return _attRequest;
236
+
237
+ Logging.LogWarning("[Consent] ConsentHook.ATTRequest is null");
238
+ _attRequest = callback => callback(false);
239
+ return _attRequest;
240
+ }
241
+ set => _attRequest = value;
242
+ }
243
+ }
244
+ }
245
+
246
+ namespace Amanotes.Core.Internal
247
+ {
248
+ [Serializable]
249
+ public class ConsentConfig
250
+ {
251
+ [Tooltip("Auto pop-up ATT and CMP form")]
252
+ public bool autoGetConsent = true;
253
+ }
254
+
255
+ public partial class ConfigAsset
256
+ {
257
+ [SerializeField] public ConsentConfig consent;
258
+ }
259
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 9af6959e72d274cce9036cd73c4b6bbc
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -67,16 +67,16 @@ namespace Amanotes.Core.Internal
67
67
 
68
68
  if (AmaGDK.Config.common.logLevel == LogLevel.Verbose)
69
69
  {
70
- var stringBuilder = new StringBuilder();
70
+ var stringBuilder = GDKPool.GetStringBuilder();
71
71
  stringBuilder.Append("Device info:");
72
72
  stringBuilder.AppendLine("DeviceSpec: " + result);
73
73
  stringBuilder.AppendLine("systemMemorySize: " + SystemInfo.systemMemorySize);
74
74
  stringBuilder.AppendLine("processorCount: " + SystemInfo.processorCount);
75
75
  stringBuilder.AppendLine("processorFrequency: " + SystemInfo.processorFrequency);
76
- stringBuilder.AppendLine("graphicsMemorySize: " + SystemInfo.graphicsMemorySize);
77
-
78
- Logging.Log(stringBuilder.ToString());
76
+ stringBuilder.AppendLine("graphicsMemorySize: " + SystemInfo.graphicsMemorySize);
77
+ Logging.Log(GDKPool.ReturnString(stringBuilder));
79
78
  }
79
+
80
80
  return result;
81
81
  }
82
82
 
@@ -214,7 +214,7 @@ namespace Amanotes.Core
214
214
  Profiler.BeginSample("AmaGDK.RemoteConfig.SaveCache()");
215
215
 
216
216
  _fetchState = FetchState.SaveCache;
217
- StringBuilder sb = new StringBuilder();
217
+ StringBuilder sb = GDKPool.GetStringBuilder(8);
218
218
 
219
219
  //Add embedded remote config version to force load from embedded config after app update
220
220
  sb.AppendLine($"{VERSION_PREFIX}{EmbedVersion}");
@@ -226,6 +226,8 @@ namespace Amanotes.Core
226
226
 
227
227
  string filePath = GDKFileUtils.GetPath(fileName);
228
228
  GDKFileUtils.Save(filePath, sb.ToString());
229
+ GDKPool.Return(sb);
230
+
229
231
  Log($"[RemoteConfig] Saved remote config to {filePath}");
230
232
  Profiler.EndSample();
231
233
  NextState();
package/Runtime/AmaGDK.cs CHANGED
@@ -17,7 +17,7 @@ namespace Amanotes.Core
17
17
  {
18
18
  public partial class AmaGDK : MonoBehaviour
19
19
  {
20
- public const string VERSION = "0.2.68";
20
+ public const string VERSION = "0.2.70-1";
21
21
 
22
22
  internal static Status _status = Status.None;
23
23
  private static ConfigAsset _config = null;
@@ -100,15 +100,12 @@ namespace Amanotes.Core
100
100
  Profiler.BeginSample("AmaGDK.InitModules()");
101
101
  {
102
102
  User.InitModule();
103
+ Consent.InitModule();
103
104
  Analytics.InitModule();
104
105
  Ads.InitModule();
105
106
  IAP.InitModule();
106
107
  RemoteConfig.InitModule();
107
-
108
- if (Config.enableAudioModule)
109
- {
110
- Audio.InitModule();
111
- }
108
+ if (Config.enableAudioModule) Audio.InitModule();
112
109
  }
113
110
  Profiler.EndSample();
114
111
  }
@@ -128,8 +125,6 @@ namespace Amanotes.Core
128
125
 
129
126
  IEnumerator InitAdapters(StringBuilder sb)
130
127
  {
131
- float TIMEOUT = 1;
132
-
133
128
  // sort on priority
134
129
  List<Adapter2> listAdapters = Adapter2.adapterMap
135
130
  .Values.ToList();
@@ -137,21 +132,32 @@ namespace Amanotes.Core
137
132
 
138
133
  foreach (Adapter2 adapter in listAdapters)
139
134
  {
140
- // Log($"Initiating {adapter.adapterId} --> initOrder: {adapter.initOrder} | {adapter.status}");
141
- float startTime = Time.realtimeSinceStartup;
142
- Profiler.BeginSample($"AmaGDK.adapter.InitSDK-{adapter.adapterId}");
135
+ if (!adapter.enabled)
143
136
  {
144
- adapter.InitSDK();
137
+ Log($"Adapter <{adapter.adapterId} is disabled!");
138
+ continue;
145
139
  }
146
- Profiler.EndSample();
147
-
148
- yield return GDKUtils.Timeout(TIMEOUT,() => adapter.status != Status.Initialize);
149
140
 
150
- if (adapter.status == Status.Initialize)
141
+ // Log($"Initiating {adapter.adapterId} --> initOrder: {adapter.initOrder} | {adapter.status}");
142
+ var startTime = Time.realtimeSinceStartup;
143
+ Profiler.BeginSample($"AmaGDK.adapter.InitSDK-{adapter.adapterId}");
144
+ adapter.InitSDK();
145
+ Profiler.EndSample();
146
+
147
+ if (adapter.status != Status.Ready)
151
148
  {
152
- LogWarning($"{adapter.adapterId} init time out: {TIMEOUT}");
149
+ var beginTime = Time.realtimeSinceStartup;
150
+ yield return GDKUtils.Timeout(adapter.Timeout,() => adapter.status != Status.Initialize);
151
+ var endTime = Time.realtimeSinceStartup;
152
+
153
+ if (adapter.status == Status.Initialize)
154
+ {
155
+ LogWarning($"{adapter.adapterId} init time out: {adapter.Timeout:#0.00}");
156
+ } else
157
+ {
158
+ Log($"{adapter.adapterId} init took: {(endTime-beginTime):#0.00} s");
159
+ }
153
160
  }
154
-
155
161
  dispatcher.Dispatch(Event.ADAPTER_READY, adapter.adapterId);
156
162
  sb.AppendLine($"[{(adapter.IsReady ? " OK " : "FAIL")}] {adapter.adapterId} (~ {Time.realtimeSinceStartup - startTime:#0.00}s)");
157
163
 
@@ -170,7 +176,7 @@ namespace Amanotes.Core
170
176
  }
171
177
 
172
178
  var startTime = Time.realtimeSinceStartup;
173
- var sb = new StringBuilder();
179
+ var sb = GDKPool.GetStringBuilder();
174
180
 
175
181
  InitModules();
176
182
 
@@ -205,6 +211,7 @@ namespace Amanotes.Core
205
211
 
206
212
  sb.Append($"\nMapping UserId: {User.UserId}\n");
207
213
  Debug.Log($"AmaGDK v{VERSION} | {READY} in {initDuration:#0.00}s\n\n{sb}");
214
+ GDKPool.Return(sb);
208
215
  }
209
216
 
210
217
  private void ConfigGeoLocation()
@@ -346,7 +353,9 @@ namespace Amanotes.Core
346
353
  public const string FIREBASE_REMOTE_CONFIG = "FirebaseRemoteConfig";
347
354
  public const string APPSFLYER = "AppsFlyer";
348
355
  public const string IRONSOURCE = "IronSource";
356
+ public const string MAX = "Max";
349
357
  public const string REVENUECAT = "RevenueCat";
358
+ public const string SQLITE_ANALYTICS = "SqliteAnalytics";
350
359
  }
351
360
  }
352
361
 
@@ -1,3 +1,4 @@
1
+ using Amanotes.Core.Internal;
1
2
  using System;
2
3
  using System.Collections.Generic;
3
4
  using System.Linq;
@@ -82,14 +83,15 @@ namespace Amanotes.Core
82
83
 
83
84
  internal void ValidateParams(string eventName, Dictionary<string, object> eventParams)
84
85
  {
85
- StringBuilder logBuilder = new StringBuilder();
86
+ var logBuilder = GDKPool.GetStringBuilder();
87
+
86
88
  logBuilder.AppendLine($"Analytics quality violation found for event <{eventName}>");
87
- bool isValid = true;
88
- HashSet<string> checkedParameters = new HashSet<string>();
89
+ var isValid = true;
90
+ HashSet<string> checkedParameters = GDKPool.GetHashSet();
89
91
 
90
92
  foreach (var kvp in DicParam)
91
93
  {
92
- if (!eventParams.ContainsKey(kvp.Key))
94
+ if (!eventParams.TryGetValue(kvp.Key, out object paramValue))
93
95
  {
94
96
  if (!kvp.Value.isOptional)
95
97
  {
@@ -99,8 +101,6 @@ namespace Amanotes.Core
99
101
  continue;
100
102
  }
101
103
 
102
- object paramValue = eventParams[kvp.Key];
103
-
104
104
  if (!ValidateParamType(paramValue, kvp.Value.type))
105
105
  {
106
106
  isValid = false;
@@ -117,8 +117,11 @@ namespace Amanotes.Core
117
117
  isValid = false;
118
118
  logBuilder.AppendLine($"[+] {parameter.Key}");
119
119
  }
120
-
120
+
121
121
  if (!isValid) LogWarningOnce(logBuilder.ToString());
122
+
123
+ GDKPool.Return(logBuilder);
124
+ GDKPool.Return(checkedParameters);
122
125
  }
123
126
 
124
127
  private bool ValidateParamType(object paramValue, ParamType paramType)
@@ -179,7 +182,7 @@ namespace Amanotes.Core
179
182
  [ContextMenu("Validate Unique EventNames")]
180
183
  private void ValidateUniqueEventNames()
181
184
  {
182
- HashSet<string> eventNames = new HashSet<string>();
185
+ HashSet<string> eventNames = GDKPool.GetHashSet();
183
186
 
184
187
  lstEvent = lstEvent.OrderBy(evt => evt.eventName).ToList();
185
188
 
@@ -189,7 +192,7 @@ namespace Amanotes.Core
189
192
 
190
193
  if (!eventNames.Add(eventQuality.eventName)) Debug.LogWarning($"Duplicate event name found: {eventQuality.eventName}. Event names must be unique.");
191
194
 
192
- HashSet<string> paramNames = new HashSet<string>();
195
+ HashSet<string> paramNames = GDKPool.GetHashSet();
193
196
 
194
197
  eventQuality.lstParam = eventQuality.lstParam.OrderBy(param => param.paramName).ToList();
195
198
 
@@ -199,7 +202,11 @@ namespace Amanotes.Core
199
202
 
200
203
  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
204
  }
205
+
206
+ GDKPool.Return(paramNames);
202
207
  }
208
+
209
+ GDKPool.Return(eventNames);
203
210
  EditorUtility.SetDirty(this);
204
211
  }
205
212
  #endif
@@ -1,6 +1,4 @@
1
1
  using System;
2
- using System.Collections;
3
- using Amanotes.Core.Internal;
4
2
  using UnityEngine;
5
3
 
6
4
  namespace Amanotes.Core
@@ -75,7 +73,7 @@ namespace Amanotes.Core
75
73
  return DEFAULT_FOR_SPEAKER;
76
74
  }
77
75
 
78
- string deviceName = DeviceInfo.Name;
76
+ string deviceName = currentDevice.name;
79
77
  int savedLatency = PlayerPrefs.GetInt(PREF_AUDIO_LATENCY + deviceName, -1);
80
78
 
81
79
  if (savedLatency >= 0)
@@ -1,7 +1,5 @@
1
1
  using UnityEngine;
2
2
  using System;
3
- using System.Collections;
4
- using System.Text;
5
3
  using Amanotes.Core.Internal;
6
4
 
7
5
  namespace Amanotes.Core.Internal
@@ -9,7 +7,8 @@ namespace Amanotes.Core.Internal
9
7
  public partial class ConfigAsset
10
8
  {
11
9
  [SerializeField]
12
- public bool enableAudioModule;
10
+ [Tooltip("Enable to get accurate audio latency & be informed when audio device changed\n\nApi: AmaGDK.Audio.DeviceInfo")]
11
+ public bool enableAudioModule = true;
13
12
  }
14
13
  }
15
14
 
@@ -23,21 +22,38 @@ namespace Amanotes.Core
23
22
  public const string GDK_AUDIO_INTERRUPT_BEGIN = nameof(GDK_AUDIO_INTERRUPT_BEGIN);
24
23
  public const string GDK_AUDIO_INTERRUPT_END = nameof(GDK_AUDIO_INTERRUPT_END);
25
24
  }
26
- public class AudioDeviceInfo
27
- {
28
- internal AudioDeviceType Type;
29
- internal string RawType;
30
- public string Name;
31
- public int LatencyMs;
32
- }
25
+
33
26
  // Public Usage
34
- public partial class Audio
27
+ public static partial class Audio
35
28
  {
36
- public static AudioDeviceInfo DeviceInfo;
29
+ public class AudioDeviceInfo
30
+ {
31
+ internal AudioDeviceType type;
32
+ internal string rawType;
33
+ public string name;
34
+ public int latencyMs;
35
+
36
+ public override string ToString()
37
+ {
38
+ if (string.IsNullOrEmpty(rawType) || string.IsNullOrEmpty(name)) return base.ToString();
39
+ return $"type: {type.ToString().ToLower()} rawType:{rawType} name:{name}";
40
+ }
41
+ }
42
+
43
+ private static AudioDeviceInfo _currentDevice;
44
+
45
+ public static AudioDeviceInfo currentDevice
46
+ {
47
+ get => _currentDevice ?? NO_DEVICE;
48
+ private set => _currentDevice = value;
49
+ }
37
50
 
38
51
  private static readonly AudioDeviceInfo NO_DEVICE = new AudioDeviceInfo()
39
52
  {
40
- LatencyMs = DEFAULT_FOR_SPEAKER
53
+ type = AudioDeviceType.Unknown,
54
+ rawType = string.Empty,
55
+ name = "NO_DEVICE",
56
+ latencyMs = DEFAULT_FOR_SPEAKER
41
57
  };
42
58
 
43
59
  /// <remarks>
@@ -87,8 +103,8 @@ namespace Amanotes.Core
87
103
  _audioDeviceDetector.RegisterEvents();
88
104
  RegisterCallbacks();
89
105
 
90
- DeviceInfo = DetectAudioInfo();
91
- DeviceInfo.LatencyMs = GetAudioDeviceLatency();
106
+ currentDevice = DetectAudioInfo();
107
+ currentDevice.latencyMs = GetAudioDeviceLatency();
92
108
  }
93
109
 
94
110
  private static AudioDeviceInfo DetectAudioInfo()
@@ -102,9 +118,9 @@ namespace Amanotes.Core
102
118
  var info = _audioDeviceDetector.GetInfo();
103
119
  return new AudioDeviceInfo
104
120
  {
105
- Type = info.AudioDeviceType,
106
- RawType = info.RawType,
107
- Name = info.Name
121
+ type = info.AudioDeviceType,
122
+ rawType = info.RawType,
123
+ name = info.Name
108
124
  };
109
125
  }
110
126
  }
@@ -125,9 +141,9 @@ namespace Amanotes.Core
125
141
  {
126
142
  GDKUtils.DelayCall(1f, () =>
127
143
  {
128
- DeviceInfo = DetectAudioInfo();
129
- DeviceInfo.LatencyMs = GetAudioDeviceLatency();
130
- dispatcher.Dispatch(Event.GDK_AUDIO_INFO_CHANGE, DeviceInfo);
144
+ currentDevice = DetectAudioInfo();
145
+ currentDevice.latencyMs = GetAudioDeviceLatency();
146
+ dispatcher.Dispatch(Event.GDK_AUDIO_INFO_CHANGE, currentDevice);
131
147
  });
132
148
  }
133
149
 
@@ -144,10 +160,10 @@ namespace Amanotes.Core
144
160
  {
145
161
  GDKUtils.ThrowIf(!Config.enableAudioModule, "Audio Module is disabled, Please enable it in the AmaGDKConfig");
146
162
 
147
- return DeviceInfo != null
148
- && (DeviceInfo.RawType == "AVAudioSessionPortBluetoothA2DP"
149
- || DeviceInfo.RawType == "TYPE_BLUETOOTH_A2DP"
150
- || DeviceInfo.RawType == "AVAudioSessionPortBluetoothHFP");
163
+ return currentDevice != null
164
+ && (currentDevice.rawType == "AVAudioSessionPortBluetoothA2DP"
165
+ || currentDevice.rawType == "TYPE_BLUETOOTH_A2DP"
166
+ || currentDevice.rawType == "AVAudioSessionPortBluetoothHFP");
151
167
  }
152
168
 
153
169
  private static string GetCurrentRouteInputs()
@@ -86,15 +86,13 @@ namespace Amanotes.Core
86
86
 
87
87
  private void OnAudioDeviceAdded(string param)
88
88
  {
89
- if (string.IsNullOrEmpty(param))
90
- return;
89
+ if (string.IsNullOrEmpty(param)) return;
91
90
  onChanged?.Invoke(param);
92
91
  }
93
92
 
94
93
  private void OnAudioDeviceRemoved(string param)
95
94
  {
96
- if (string.IsNullOrEmpty(param))
97
- return;
95
+ if (string.IsNullOrEmpty(param)) return;
98
96
  onChanged?.Invoke(param);
99
97
  }
100
98
 
@@ -170,15 +168,18 @@ namespace Amanotes.Core
170
168
  }
171
169
 
172
170
  StringBuilder sb = null;
173
-
174
- for (int i = 0; i < str.Length; i++)
171
+ for (var i = 0; i < str.Length; i++)
175
172
  {
176
173
  char ch = str[i];
177
174
 
178
175
  if (char.IsSurrogate(ch))
179
176
  {
180
- sb ??= new StringBuilder(str, 0, i, str.Length);
181
-
177
+ if (sb == null)
178
+ {
179
+ sb = GDKPool.GetStringBuilder();
180
+ sb.Append(str, 0, i);
181
+ }
182
+
182
183
  sb.Append(replacementCharacter);
183
184
 
184
185
  // If there is a high+low surrogate, skip the low surrogate
@@ -187,13 +188,13 @@ namespace Amanotes.Core
187
188
  i++;
188
189
  }
189
190
  }
190
- else if (sb != null)
191
+ else
191
192
  {
192
- sb.Append(ch);
193
+ sb?.Append(ch);
193
194
  }
194
195
  }
195
-
196
- return sb == null ? str : sb.ToString();
196
+
197
+ return sb == null ? str : GDKPool.ReturnString(sb);
197
198
  }
198
199
  }
199
200