com.amanotes.gdk 0.2.19 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,149 +1,158 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.21 - 2023-10-11]
4
+ - RevenueCat adapter v6.0.0
5
+ - Handle early Firebase call
6
+ - Fix null exception in non-subscription game
7
+ - Add log level
8
+
9
+ ## [0.2.20 - 2023-10-05]
10
+ - Update RevenueCat 5.3.0
11
+
3
12
  ## [0.2.19 - 2023-09-12]
4
- ### Add `ama_device_id` to all events
13
+ - Add `ama_device_id` to all events
5
14
 
6
15
  ## [0.2.18 - 2023-09-11]
7
- ### Add dev mode switch
8
- ### Auto get SDK version
9
- ### Add in-app purchase module, RevenueCat
10
- ### Support Unity 2019.4
11
- ### Remove AmaPassport
16
+ - Add dev mode switch
17
+ - Auto get SDK version
18
+ - Add in-app purchase module, RevenueCat
19
+ - Support Unity 2019.4
20
+ - Remove AmaPassport
12
21
 
13
22
  ## [0.2.14 - 2023-08-08]
14
- ### Support multiple embed remote config and load config by id
15
- ### Clear cached remote config on new app update
16
- ### Override remote config value in editor mode
17
- ### New feature: Analytics Quality
23
+ - Support multiple embed remote config and load config by id
24
+ - Clear cached remote config on new app update
25
+ - Override remote config value in editor mode
26
+ - New feature: Analytics Quality
18
27
 
19
28
  ## [0.2.13 - 2023-07-20]
20
- ### Fix error Remote Config JSON caused by special chars (", ', \)
29
+ - Fix error Remote Config JSON caused by special chars (", ', \)
21
30
 
22
31
  ## [0.2.12 - 2023-07-07]
23
- ### Restructure GDK - symlink GDK source to unity projects
24
- ### Unity 2019 compatible - no null coalescing
25
- ### Remove redundant AmaPassport config field
26
- ### Auto request AmaId
32
+ - Restructure GDK - symlink GDK source to unity projects
33
+ - Unity 2019 compatible - no null coalescing
34
+ - Remove redundant AmaPassport config field
35
+ - Auto request AmaId
27
36
 
28
37
  ## [0.2.11 - 2023-06-29]
29
- ### SetLogCondition with EventParam
30
- ### Rename Migrated -> HasMigrated
31
- ### Fix StopShowAd
38
+ - SetLogCondition with EventParam
39
+ - Rename Migrated -> HasMigrated
40
+ - Fix StopShowAd
32
41
 
33
42
  ## [0.2.10 - 2023-06-23]
34
- ### Extra package: Project Panel
35
- ### Remote config: Allow user to enable / disable automatic default valuye updating (in play mode)
36
- ### Fix ad routine
37
- ### Expose analytics migration flag
43
+ - Extra package: Project Panel
44
+ - Remote config: Allow user to enable / disable automatic default valuye updating (in play mode)
45
+ - Fix ad routine
46
+ - Expose analytics migration flag
38
47
 
39
48
  ## [0.2.9 - 2023-06-15]
40
- ### Show total init time
41
- ### Show adapter version + status at init time
42
- ### Remove AmaGDK version in debug log
43
- ### Remove "Params" in analytics debug log
44
- ### Check "event_name" early
45
- ### Fix null exception in AmaGDKInstaller
46
- ### Add csc.rsp to enable AMAGDK_DEV
47
- ### Update Ads module
49
+ - Show total init time
50
+ - Show adapter version + status at init time
51
+ - Remove AmaGDK version in debug log
52
+ - Remove "Params" in analytics debug log
53
+ - Check "event_name" early
54
+ - Fix null exception in AmaGDKInstaller
55
+ - Add csc.rsp to enable AMAGDK_DEV
56
+ - Update Ads module
48
57
 
49
58
  ## [0.2.8 - 2023-06-12]
50
- ### Mute Appsflyer's init log on Editor
51
- ### Clear analytics log in adapters
52
- ### Check null event params before calling AddParam()
53
- ### Export AmaPassport info
59
+ - Mute Appsflyer's init log on Editor
60
+ - Clear analytics log in adapters
61
+ - Check null event params before calling AddParam()
62
+ - Export AmaPassport info
54
63
 
55
64
  ## [0.2.7 - 2023-06-09]
56
- ### Fix log single adapter
65
+ - Fix log single adapter
57
66
 
58
67
  ## [0.2.6 - 2023-06-08]
59
- ### Update AmaPassport
60
- ### Fix JSON parse in RemoteConfig
61
- ### Ads adds interstitial callbacks
62
- ### Clean & export AmaGDK Installer
68
+ - Update AmaPassport
69
+ - Fix JSON parse in RemoteConfig
70
+ - Ads adds interstitial callbacks
71
+ - Clean & export AmaGDK Installer
63
72
 
64
73
  ## [0.2.5 - 2023-06-07]
65
- ### Remove ATT dependency package
74
+ - Remove ATT dependency package
66
75
 
67
76
  ## [0.2.4 - 2023-06-07]
68
- ### Hotfix Ads module
69
- ### Update Analytics unit tests
77
+ - Hotfix Ads module
78
+ - Update Analytics unit tests
70
79
 
71
80
  ## [0.2.3 - 2023-06-06]
72
- ### Refactor Ads module, support ironSource
73
- ### Refactor AmaPassport
74
- ### Refactor AmaGDK structure
75
- ### Add callback when RemoteConfig module completes fetching
81
+ - Refactor Ads module, support ironSource
82
+ - Refactor AmaPassport
83
+ - Refactor AmaGDK structure
84
+ - Add callback when RemoteConfig module completes fetching
76
85
 
77
86
  ## [0.1.20 - 2023-04-20]
78
- ### Refactor Adapter architure to support reading adapter config from Core
79
- ### Add session stat for Analytics module
80
- ### Unit test for Json parser and Analytics module
87
+ - Refactor Adapter architure to support reading adapter config from Core
88
+ - Add session stat for Analytics module
89
+ - Unit test for Json parser and Analytics module
81
90
 
82
91
  ## [0.1.19 - 2023-04-11]
83
- ### Move AmaGDKTest out of AmaGDKCore
92
+ - Move AmaGDKTest out of AmaGDKCore
84
93
 
85
94
  ## [0.1.18 - 2023-03-27]
86
- ### Fix AmaGDK's autoInit saving issue
95
+ - Fix AmaGDK's autoInit saving issue
87
96
 
88
97
  ## [0.1.17 - 2023-03-13]
89
- ### Release AmaGDK v0.1.17
90
- ### Rename Analytics APIs
98
+ - Release AmaGDK v0.1.17
99
+ - Rename Analytics APIs
91
100
 
92
101
  ## [0.1.16 - 2023-03-10]
93
- ### Release AmaGDK v0.1.16
94
- ### SemVer supports 4 parts
95
- ### Analytics supports EventConfig
102
+ - Release AmaGDK v0.1.16
103
+ - SemVer supports 4 parts
104
+ - Analytics supports EventConfig
96
105
 
97
106
  ## [0.1.15 - 2023-03-06]
98
- ### Release AmaGDK v0.1.15
99
- ### Fix null exception in VersionExtractor
100
- ### Warn unhandle platform in Appsflyer initialization
101
- ### Allow dev to SetPermittedEvents and SetForbiddenEvents to the adapter config
107
+ - Release AmaGDK v0.1.15
108
+ - Fix null exception in VersionExtractor
109
+ - Warn unhandle platform in Appsflyer initialization
110
+ - Allow dev to SetPermittedEvents and SetForbiddenEvents to the adapter config
102
111
 
103
112
  ## [0.1.14 - 2023-03-02]
104
- ### Release AmaGDK v0.1.14
113
+ - Release AmaGDK v0.1.14
105
114
 
106
115
  ## [0.1.13 - 2023-03-01]
107
- ### Release AmaGDK v0.1.13
108
- ### Fix Semver parser
116
+ - Release AmaGDK v0.1.13
117
+ - Fix Semver parser
109
118
 
110
119
  ## [0.1.12 - 2023-03-01]
111
- ### Release AmaGDK v0.1.12
112
- ### Prioritize analytics instant config over the base config
113
- ### Public common config
120
+ - Release AmaGDK v0.1.12
121
+ - Prioritize analytics instant config over the base config
122
+ - Public common config
114
123
 
115
124
  ## [0.1.11 - 2023-02-28]
116
- ### Release AmaGDK v0.1.11
117
- ### Auto start and log Appsflyer open session
118
- ### Add analytics adapter base config
119
- ### Analytics: count by session
120
- ### Export example scene
121
- ### Add AmaGDKManager
122
- ### Refactor Adapter to support for init order configuration
125
+ - Release AmaGDK v0.1.11
126
+ - Auto start and log Appsflyer open session
127
+ - Add analytics adapter base config
128
+ - Analytics: count by session
129
+ - Export example scene
130
+ - Add AmaGDKManager
131
+ - Refactor Adapter to support for init order configuration
123
132
 
124
133
  ## [0.1.10 - 2023-02-17]
125
- ### Release AmaGDK v0.1.10
134
+ - Release AmaGDK v0.1.10
126
135
 
127
136
  ## [0.1.9 - 2023-02-15]
128
- ### Release AmaGDK v0.1.9
129
- ### Change Documentation URL
130
- ### Support API LogEvent(eventName, parameters)
137
+ - Release AmaGDK v0.1.9
138
+ - Change Documentation URL
139
+ - Support API LogEvent(eventName, parameters)
131
140
 
132
141
  ## [0.1.8 - 2023-02-15]
133
- ### Release AmaGDK v0.1.8
134
- ### Add AdapterID
142
+ - Release AmaGDK v0.1.8
143
+ - Add AdapterID
135
144
 
136
145
  ## [0.1.5 - 2023-02-13]
137
- ### Release AmaSDK v0.1.5
138
- ### Validate EventParams
146
+ - Release AmaSDK v0.1.5
147
+ - Validate EventParams
139
148
 
140
149
  ## [0.1.4 - 2023-02-13]
141
- ### Release AmaSDK v0.1.4
150
+ - Release AmaSDK v0.1.4
142
151
 
143
152
  ## [0.1.3 - 2023-02-10]
144
- ### Release AmaSDK v0.1.3
153
+ - Release AmaSDK v0.1.3
145
154
 
146
155
  ## [0.1.2] - 2023-02-08
147
- ### Release AmaGDK 0.1.2
148
- ### FirebaseAnalytics adapter compatible with FirebaseAnalytics SDK v9
149
- ### AppsFlyer adapter compatible with AppsFlyer SDK v6
156
+ - Release AmaGDK 0.1.2
157
+ - FirebaseAnalytics adapter compatible with FirebaseAnalytics SDK v9
158
+ - AppsFlyer adapter compatible with AppsFlyer SDK v6
@@ -1,5 +1,5 @@
1
1
  fileFormatVersion: 2
2
- guid: 1ea28b311cdc648ef9cd322abd1797b5
2
+ guid: 293341d9acee543a8b692edae57f0aa8
3
3
  MonoImporter:
4
4
  externalObjects: {}
5
5
  serializedVersion: 2
@@ -1,5 +1,5 @@
1
1
  fileFormatVersion: 2
2
- guid: 2e155e09a97324557b3803886b0dcfd7
2
+ guid: 55c96ab69ed9b471ebc383f02b0e4538
3
3
  TextScriptImporter:
4
4
  externalObjects: {}
5
5
  userData:
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  fileFormatVersion: 2
2
- guid: ec14a271afdcc4c72b0d1305fbd9db8c
2
+ guid: 8143c4447d1894317ac84680e221eec3
3
3
  DefaultImporter:
4
4
  externalObjects: {}
5
5
  userData:
package/README.md CHANGED
@@ -1,12 +1,57 @@
1
1
  # Amanotes GDK
2
2
 
3
- ### Before you begin
3
+ ## AmaGDK Dev Project Setup
4
4
 
5
- AmaGDK is a free library from Amanotes provides classes, interfaces and adapter packages for Unity game development.
5
+ ### Clone Project From GitLab
6
6
 
7
- # Amanotes GDK for your game
7
+ 1. Clone project from GitLab url: https://gitlab.amanotes.net/sdk/AmaSDK_v5
8
+ 2. Open terminal windows and cd to your project
9
+ 3. Run file create-links.sh (Link GDK folder children to GDKDev)
8
10
 
9
- # Getting started with AmaGDK
11
+ ![image](https://drive.google.com/uc?id=1mlilsAOxjRRqWbQPI-tSUDJamZiY01rm)
12
+
13
+ ### How To Open Project
14
+
15
+ * Open project GDKDev
16
+
17
+ ![image](https://drive.google.com/uc?id=1N2kM3N-LU22pgY_3Uhyq-YZdCmDTPxJa)
18
+
19
+ ### Main folder
20
+
21
+ * AmaGDK: adapters logic, example, installer, configs
22
+
23
+ * AmaGDKCore: core logic, packages
24
+
25
+ * AmaGDKDev: tools for dev as build tool, release tool
26
+
27
+ * AmaGDKTest: scenes test, unit test
28
+
29
+ ![image](https://drive.google.com/uc?id=1iYskLfKqFG95Ezsf_hUotnlHnTaAMTfW)
30
+
31
+ ### Switch To Dev Mode
32
+
33
+ 1. Add GDK_HOME variable to .zshrc file in Mac OS
34
+
35
+ * Step 1: Open terminal, input sudo nano `~/.zshrc`
36
+
37
+ * Step 2: Enter and input password, add new line export `GDK_HOME=/AmaGDKCoreFolderPath`
38
+
39
+ * Step 3: Press Ctr+X buttons -> Press Y button -> Press `Enter` button to save
40
+
41
+ ![image](https://drive.google.com/uc?id=11hUbHwTpkLuEhe3hjQEkaiTuJ0fNCR7X)
42
+
43
+ 2. Select AmaGDK prefab in scene → select `Change Dev Mode` in context menu
44
+
45
+ ![image](https://drive.google.com/uc?id=1tmbtcK9FmZf21HHpQYyICf5dN9FKUeKN)
46
+
47
+ ### Code Convention
48
+ [Convention](https://amanotesjsc.atlassian.net/wiki/spaces/AP/pages/3409413152)
49
+
50
+ ### Links Reference
51
+ [Reference](https://amanotesjsc.atlassian.net/wiki/spaces/AP/pages/3334930473)
52
+
53
+
54
+ ## Getting started with AmaGDK
10
55
 
11
56
  ### Add AmaGDK prefab to your bootstrap scene (the first scene in your game build settings)
12
57
  There are two ways to do:
@@ -35,10 +80,10 @@ public class YourGameSceneController
35
80
  }
36
81
  ```
37
82
 
38
- # Versioning
83
+ ### Versioning
39
84
 
40
85
  We use [SemVer](http://semver.org/) for versioning.
41
86
 
42
- # License
87
+ ### License
43
88
 
44
89
  This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
@@ -136,30 +136,12 @@ namespace Amanotes.Core
136
136
 
137
137
  public static void SetUserId(string userId)
138
138
  {
139
- if (listAdapters.Count == 0)
140
- {
141
- LogWarningOnce("ListAnalyticsAdapter is empty! Maybe you need to wait for Analytics to init");
142
- return;
143
- }
144
-
145
- foreach (AdapterContext s in listAdapters)
146
- {
147
- s.GetAdapterApi<IAnalyticAdapter>().SetUserId(userId);
148
- }
139
+ actionQueue.Enqueue(() => InternalSetUserId(userId));
149
140
  }
150
141
 
151
142
  public static void SetUserProperty(string key, string value)
152
143
  {
153
- if (listAdapters.Count == 0)
154
- {
155
- LogWarningOnce("ListAnalyticsAdapter is empty! Maybe you need to wait for Analytics to init");
156
- return;
157
- }
158
-
159
- foreach (AdapterContext s in listAdapters)
160
- {
161
- s.GetAdapterApi<IAnalyticAdapter>().SetUserProperty(key, value);
162
- }
144
+ actionQueue.Enqueue(() => InternalSetUserProperty(key, value));
163
145
  }
164
146
 
165
147
  private static IntegrityFlag MergeData(List<string> funnelEvents, Dictionary<string, int> eventCountMap, bool sumEventCount)
@@ -285,6 +267,7 @@ namespace Amanotes.Core
285
267
  internal static readonly Dictionary<string, EventConfig> sendAtCount = new Dictionary<string, EventConfig>();
286
268
  internal static Predicate<EventParams> logEventHook = null;
287
269
  internal static readonly Queue<EventParams> eventQueue = new Queue<EventParams>();
270
+ internal static readonly Queue<Action> actionQueue = new Queue<Action>();
288
271
  internal static readonly List<AdapterContext> listAdapters = new List<AdapterContext>();
289
272
  internal static readonly AnalyticsData localData = new AnalyticsData().LoadJsonFromFile();
290
273
  internal static readonly SessionStat sessionStat = new SessionStat();
@@ -296,6 +279,9 @@ namespace Amanotes.Core
296
279
 
297
280
  onFrameUpdate -= ProcessEventQueue;
298
281
  onFrameUpdate += ProcessEventQueue;
282
+
283
+ onFrameUpdate -= ProcessActionQueue;
284
+ onFrameUpdate += ProcessActionQueue;
299
285
  }
300
286
 
301
287
  internal static void ProcessEventQueue()
@@ -389,7 +375,7 @@ namespace Amanotes.Core
389
375
  sb.AppendLine($" - Malformed eventName (should not contains <space> or <dash>: <{eventName}>");
390
376
  }
391
377
  }
392
-
378
+
393
379
  if (!char.IsLetter(eventName[0])) sb.AppendLine($" - EventName must start with a letter: <{eventName}> ");
394
380
  if (eventName.Length > 40) sb.AppendLine($" - EventName is too long ({eventName.Length} > 40): <{eventName}> ");
395
381
  if (sb.Length > 0 && dryRun) LogWarningOnce(sb.ToString(), $"NormalizeEventName<{eventName}>");
@@ -418,6 +404,33 @@ namespace Amanotes.Core
418
404
 
419
405
  return true;
420
406
  }
407
+
408
+
409
+ internal static void ProcessActionQueue()
410
+ {
411
+ if (actionQueue.Count <= 0) return;
412
+ while (actionQueue.Count > 0)
413
+ {
414
+ var action = actionQueue.Dequeue();
415
+ action.Invoke();
416
+ }
417
+ }
418
+
419
+ internal static void InternalSetUserId(string userId)
420
+ {
421
+ foreach (AdapterContext s in listAdapters)
422
+ {
423
+ s.GetAdapterApi<IAnalyticAdapter>().SetUserId(userId);
424
+ }
425
+ }
426
+
427
+ internal static void InternalSetUserProperty(string key, string value)
428
+ {
429
+ foreach (AdapterContext s in listAdapters)
430
+ {
431
+ s.GetAdapterApi<IAnalyticAdapter>().SetUserProperty(key, value);
432
+ }
433
+ }
421
434
  }
422
435
 
423
436
  //PERSISTENT DATA
@@ -83,10 +83,19 @@ namespace Amanotes.Core.Internal
83
83
  #endif
84
84
  }
85
85
 
86
+ [Flags]
87
+ public enum LogLevel
88
+ {
89
+ None = 0,
90
+ Error = 1,
91
+ Warning = 2,
92
+ Verbose = 4
93
+ }
94
+
86
95
  [Serializable]
87
96
  public partial class SDKConfig
88
97
  {
89
- public bool verboseLog;
98
+ public LogLevel logLevel = LogLevel.Warning | LogLevel.Error;
90
99
  }
91
100
 
92
101
  [Serializable]
@@ -1,6 +1,7 @@
1
1
  using System;
2
2
  using System.Collections;
3
3
  using System.Collections.Generic;
4
+ using System.Globalization;
4
5
  using UnityEngine;
5
6
  using Amanotes.Core.Internal;
6
7
 
@@ -14,15 +15,21 @@ namespace Amanotes.Core
14
15
  public partial class IAP //Public
15
16
  {
16
17
  public static bool IsVIP => !string.IsNullOrEmpty(localData.activeSubscriptionId);
17
-
18
18
  public static string ActiveSubscriptionId => localData.activeSubscriptionId;
19
-
19
+ public static string SubscriptionPeriodType => localData.subscriptionPeriodType;
20
+ public static DateTime? SubscriptionLastPurchaseDate => localData.SubscriptionLastPurchaseDate;
21
+ public static DateTime? SubscriptionExpiredDate => localData.SubscriptionExpiredDate;
22
+ public static DateTime? UnsubscribedDate => localData.UnsubscribedDate;
20
23
  public static Dictionary<string, ProductInfo> AllProducts => dicProducts;
21
-
22
- public static ISetupBuilder GetProducts()
24
+ private const string DATE_FORMAT = "yyyy/MM/yy HH:mm:ss";
25
+
26
+ public static ISetupBuilder GetProducts(bool appendExistingList = false)
23
27
  {
24
- IAPExtension.isCleanUpConfig = false;
25
- dicProducts.Clear();
28
+ if (!appendExistingList)
29
+ {
30
+ IAPExtension.isCleanUpConfig = false;
31
+ dicProducts.Clear();
32
+ }
26
33
  var result = new SetupContext();
27
34
  ExecuteInNextFrame(result.GetProducts);
28
35
  return result;
@@ -31,7 +38,13 @@ namespace Amanotes.Core
31
38
  public static IPurchaseBuilder Purchase(string productId)
32
39
  {
33
40
  var result = new PurchaseContext();
34
- ExecuteInNextFrame(() => result.Purchase(productId));
41
+ if (!dicProducts.ContainsKey(productId))
42
+ {
43
+ LogWarning($"[IAP] Product {productId} is not found");
44
+ result.onFail?.Invoke(new PurchaseError($"Product {productId} is not found"));
45
+ return result;
46
+ }
47
+ ExecuteInNextFrame(() => result.Purchase(dicProducts[productId]));
35
48
  return result;
36
49
  }
37
50
 
@@ -46,7 +59,7 @@ namespace Amanotes.Core
46
59
  {
47
60
  if (!dicProducts.ContainsKey(productId))
48
61
  {
49
- LogWarning($"[IAP] Not found product with id <{productId}>. Make sure you have called Setup() first.");
62
+ LogWarning($"[IAP] Not found product with id <{productId}>. Make sure you have called GetProducts() first.");
50
63
  return null;
51
64
  }
52
65
 
@@ -73,6 +86,13 @@ namespace Amanotes.Core
73
86
  {
74
87
  return Adapter?.GetPurchaseHistory();
75
88
  }
89
+
90
+ public static DateTime? GetLastPurchaseDate(string productId)
91
+ {
92
+ var product = GetProduct(productId);
93
+ if (product == null) return null;
94
+ return Adapter?.GetLastPurchaseDate(product.productAndPlanId);
95
+ }
76
96
  }
77
97
 
78
98
  public partial class IAP //Internal
@@ -134,12 +154,68 @@ namespace Amanotes.Core
134
154
  internal class IAPData : IJsonFile
135
155
  {
136
156
  public string activeSubscriptionId;
157
+ public string subscriptionPeriodType;
158
+ public string subscriptionLastPurchaseDate;
159
+ public string subscriptionExpiredDate;
160
+ public string unsubscribedDate;
137
161
  public List<string> purchasedProducts;
162
+
163
+ private DateTime? _subscriptionLastPurchaseDate;
164
+ public DateTime? SubscriptionLastPurchaseDate
165
+ {
166
+ get
167
+ {
168
+ if (_subscriptionLastPurchaseDate != null) return _subscriptionLastPurchaseDate;
169
+ if (DateTime.TryParseExact(subscriptionLastPurchaseDate, DATE_FORMAT,
170
+ CultureInfo.InvariantCulture, DateTimeStyles.None, out var pd))
171
+ {
172
+ _subscriptionLastPurchaseDate = pd;
173
+ }
174
+ return _subscriptionLastPurchaseDate;
175
+ }
176
+ }
177
+
178
+ private DateTime? _subscriptionExpiredDate;
179
+ public DateTime? SubscriptionExpiredDate
180
+ {
181
+ get
182
+ {
183
+ if (_subscriptionExpiredDate != null) return _subscriptionExpiredDate;
184
+ if (DateTime.TryParseExact(subscriptionExpiredDate, DATE_FORMAT,
185
+ CultureInfo.InvariantCulture, DateTimeStyles.None, out var pd))
186
+ {
187
+ _subscriptionExpiredDate = pd;
188
+ }
189
+ return _subscriptionExpiredDate;
190
+ }
191
+ }
192
+
193
+ private DateTime? _unsubscribedDate;
194
+ public DateTime? UnsubscribedDate
195
+ {
196
+ get
197
+ {
198
+ if (_unsubscribedDate != null) return _unsubscribedDate;
199
+ if (DateTime.TryParseExact(unsubscribedDate, DATE_FORMAT,
200
+ CultureInfo.InvariantCulture, DateTimeStyles.None, out var pd))
201
+ {
202
+ _unsubscribedDate = pd;
203
+ }
204
+ return _unsubscribedDate;
205
+ }
206
+ }
138
207
 
139
208
  public void Sync()
140
209
  {
141
210
  activeSubscriptionId = Adapter?.GetActiveSubscriptionId();
211
+ subscriptionPeriodType = Adapter?.GetSubscriptionPeriodType();
142
212
  purchasedProducts = Adapter?.GetPurchasedProducts();
213
+ _subscriptionLastPurchaseDate = Adapter?.GetSubscriptionLastPurchaseDate();
214
+ subscriptionLastPurchaseDate = _subscriptionLastPurchaseDate?.ToString(DATE_FORMAT);
215
+ _subscriptionExpiredDate = Adapter?.GetSubscriptionExpiredDate();
216
+ subscriptionExpiredDate = _subscriptionExpiredDate?.ToString(DATE_FORMAT);
217
+ _unsubscribedDate = Adapter?.GetUnsubscribedDate();
218
+ unsubscribedDate = _unsubscribedDate?.ToString(DATE_FORMAT);
143
219
  this.SaveJsonToFile();
144
220
  }
145
221
  }
@@ -158,6 +234,7 @@ namespace Amanotes.Core
158
234
  {
159
235
  public string id;
160
236
  public ProductType type;
237
+ [HideInInspector] public string productAndPlanId;
161
238
  [HideInInspector] public string title;
162
239
  [HideInInspector] public string description;
163
240
  [HideInInspector] public float price;
@@ -186,8 +263,9 @@ namespace Amanotes.Core
186
263
  {
187
264
  }
188
265
 
189
- public PurchaseError(string errorMessage)
266
+ public PurchaseError(string errorMessage, string productId = null)
190
267
  {
268
+ this.productId = productId;
191
269
  error = errorMessage;
192
270
  }
193
271
  }
@@ -241,9 +319,11 @@ namespace Amanotes.Core
241
319
  {
242
320
  if (sc == null) return null;
243
321
  ClearOldProductConfig();
322
+ var ls = AdapterConfig?.products;
323
+ if (ls == null) return null;
244
324
  foreach (var pId in subs)
245
325
  {
246
- AdapterConfig?.products.Add(new ProductInfo { id = pId, type = ProductType.Subscription });
326
+ ls.Add(new ProductInfo { id = pId, type = ProductType.Subscription });
247
327
  }
248
328
  return sc;
249
329
  }
@@ -252,9 +332,11 @@ namespace Amanotes.Core
252
332
  {
253
333
  if (sc == null) return null;
254
334
  ClearOldProductConfig();
335
+ var ls = AdapterConfig?.products;
336
+ if (ls == null) return null;
255
337
  foreach (var pId in cons)
256
338
  {
257
- AdapterConfig?.products.Add(new ProductInfo { id = pId, type = ProductType.Consumable });
339
+ ls.Add(new ProductInfo { id = pId, type = ProductType.Consumable });
258
340
  }
259
341
  return sc;
260
342
  }
@@ -263,9 +345,11 @@ namespace Amanotes.Core
263
345
  {
264
346
  if (sc == null) return null;
265
347
  ClearOldProductConfig();
348
+ var ls = AdapterConfig?.products;
349
+ if (ls == null) return null;
266
350
  foreach (var pId in nons)
267
351
  {
268
- AdapterConfig?.products.Add(new ProductInfo { id = pId, type = ProductType.NonConsumable });
352
+ ls.Add(new ProductInfo { id = pId, type = ProductType.NonConsumable });
269
353
  }
270
354
  return sc;
271
355
  }
@@ -339,18 +423,29 @@ namespace Amanotes.Core.Internal
339
423
 
340
424
  public void GetProducts()
341
425
  {
342
- if (AdapterConfig?.products.Count == 0)
426
+ var ls = AdapterConfig?.products;
427
+ if (ls == null || ls.Count == 0)
343
428
  {
344
429
  LogWarning("There is no IAP product");
345
430
  return;
346
- }
431
+ }
432
+
433
+ #if UNITY_EDITOR
434
+ foreach (var p in ls)
435
+ {
436
+ if (dicProducts.ContainsKey(p.id)) dicProducts[p.id] = p;
437
+ else dicProducts.Add(p.id, p);
438
+ }
439
+ onSuccess?.Invoke(dicProducts);
440
+ return;
441
+ #else
347
442
 
348
443
  getProdQueue.Clear();
349
444
  var subs = new List<string>();
350
445
  var cons = new List<string>();
351
446
  var nons = new List<string>();
352
447
 
353
- foreach (var p in AdapterConfig.products)
448
+ foreach (var p in ls)
354
449
  {
355
450
  switch (p.type)
356
451
  {
@@ -373,6 +468,7 @@ namespace Amanotes.Core.Internal
373
468
  getProdQueue.Enqueue((cons, ProductType.Consumable));
374
469
  getProdQueue.Enqueue((nons, ProductType.NonConsumable));
375
470
  GetNextProductsInQueue();
471
+ #endif
376
472
  }
377
473
 
378
474
  void GetNextProductsInQueue()
@@ -393,9 +489,10 @@ namespace Amanotes.Core.Internal
393
489
  {
394
490
  AmaGDK.IAP.Adapter?.GetProducts(lsProduct.ToArray(), type, (pList) =>
395
491
  {
396
- foreach (var pI in pList)
492
+ foreach (var p in pList)
397
493
  {
398
- dicProducts.Add(pI.id, pI);
494
+ if (dicProducts.ContainsKey(p.id)) dicProducts[p.id] = p;
495
+ else dicProducts.Add(p.id, p);
399
496
  }
400
497
  ExecuteInNextFrame(GetNextProductsInQueue);
401
498
  }, error => onFail?.Invoke(error));
@@ -416,20 +513,14 @@ namespace Amanotes.Core.Internal
416
513
  public Action<PurchaseError> onFail;
417
514
  public string discountId;
418
515
 
419
- public void Purchase(string productId)
516
+ public void Purchase(ProductInfo productInfo)
420
517
  {
421
- if (!dicProducts.ContainsKey(productId))
422
- {
423
- LogWarning($"[IAP] Product {productId} is not found");
424
- return;
425
- }
426
-
427
518
  #if UNITY_EDITOR
428
519
  if (AdapterConfig.allPurchaseSuccessInEditor)
429
520
  {
430
521
  DoOnSuccess(new PurchaseSuccess
431
522
  {
432
- productIds = new[] { productId },
523
+ productIds = new[] { productInfo.id },
433
524
  isSandbox = true
434
525
  });
435
526
  return;
@@ -438,11 +529,11 @@ namespace Amanotes.Core.Internal
438
529
 
439
530
  if (!string.IsNullOrWhiteSpace(discountId))
440
531
  {
441
- AmaGDK.IAP.Adapter?.PurchaseDiscount(productId, dicProducts[productId].type, discountId, DoOnSuccess, DoOnFail);
532
+ AmaGDK.IAP.Adapter?.PurchaseDiscount(productInfo, discountId, DoOnSuccess, DoOnFail);
442
533
  return;
443
534
  }
444
535
 
445
- AmaGDK.IAP.Adapter?.Purchase(productId, dicProducts[productId].type, DoOnSuccess, DoOnFail);
536
+ AmaGDK.IAP.Adapter?.Purchase(productInfo, DoOnSuccess, DoOnFail);
446
537
  }
447
538
 
448
539
  public void RestorePurchase()
@@ -465,12 +556,17 @@ namespace Amanotes.Core.Internal
465
556
  public interface IIAPAdapter
466
557
  {
467
558
  void GetProducts(string[] productIds, ProductType productType, Action<ProductInfo[]> onSuccess = null, Action<string> onFail = null);
468
- void Purchase(string productId, ProductType productType, Action<PurchaseSuccess> onSuccess = null, Action<PurchaseError> onFail = null);
469
- void PurchaseDiscount(string productId, ProductType productType, string discountId, Action<PurchaseSuccess> onSuccess = null, Action<PurchaseError> onFail = null);
559
+ void Purchase(ProductInfo productInfo, Action<PurchaseSuccess> onSuccess = null, Action<PurchaseError> onFail = null);
560
+ void PurchaseDiscount(ProductInfo productInfo, string discountId, Action<PurchaseSuccess> onSuccess = null, Action<PurchaseError> onFail = null);
470
561
  void RestorePurchase(Action<PurchaseSuccess> onSuccess = null, Action<PurchaseError> onFail = null);
471
562
  List<string> GetActiveSubscriptions();
472
563
  List<string> GetPurchasedProducts();
473
564
  string GetActiveSubscriptionId();
565
+ string GetSubscriptionPeriodType();
566
+ DateTime? GetSubscriptionLastPurchaseDate();
567
+ DateTime? GetSubscriptionExpiredDate();
568
+ DateTime? GetUnsubscribedDate();
474
569
  HashSet<PurchaseRecord> GetPurchaseHistory();
570
+ DateTime? GetLastPurchaseDate(string productAndPlanId);
475
571
  }
476
- }
572
+ }
@@ -68,6 +68,7 @@ namespace Amanotes.Core.Internal
68
68
 
69
69
  public static void LogWarningOnce(string message, string key = null)
70
70
  {
71
+ if ((Config.common.logLevel & LogLevel.Warning) == 0) return;
71
72
  if (string.IsNullOrEmpty(key)) key = message;
72
73
  if (logWarningDict.Contains(key)) return;
73
74
  logWarningDict.Add(key);
@@ -75,15 +76,15 @@ namespace Amanotes.Core.Internal
75
76
  }
76
77
  public static void LogWarning(string message)
77
78
  {
78
- Debug.LogWarning($"AmaGDK {message}");
79
+ if ((Config.common.logLevel & LogLevel.Warning) != 0) Debug.LogWarning($"AmaGDK {message}");
79
80
  }
80
81
  public static void LogError(string message)
81
82
  {
82
- Debug.LogError($"AmaGDK {message}");
83
+ if ((Config.common.logLevel & LogLevel.Error) != 0) Debug.LogError($"AmaGDK {message}");
83
84
  }
84
85
  public static void Log(string message)
85
86
  {
86
- if (AmaGDK.Config.common.verboseLog) Debug.Log($"AmaGDK {message}");
87
+ if ((Config.common.logLevel & LogLevel.Verbose) != 0) Debug.Log($"AmaGDK {message}");
87
88
  }
88
89
  }
89
90
 
@@ -91,10 +92,10 @@ namespace Amanotes.Core.Internal
91
92
  {
92
93
  public static void DestroyAndReset()
93
94
  {
94
- if (AmaGDK._instance != null)
95
+ if (_instance != null)
95
96
  {
96
- UnityObject.Destroy(AmaGDK._instance);
97
- AmaGDK._instance = null;
97
+ UnityObject.Destroy(_instance);
98
+ _instance = null;
98
99
  }
99
100
 
100
101
  AmaGDK[] result = Resources.FindObjectsOfTypeAll<AmaGDK>();
@@ -113,8 +114,8 @@ namespace Amanotes.Core.Internal
113
114
  UnityObject.Destroy(result[i].gameObject);
114
115
  }
115
116
 
116
- AmaGDK._status = Status.None;
117
- AmaGDK._allowInit = false;
117
+ _status = Status.None;
118
+ _allowInit = false;
118
119
 
119
120
  // TODO: Destroy adapters? - Don't do this. An adapter registers just 1 time. Clear adapters cause Analytics test fail.
120
121
  //Adapter.listAdapters.Clear();
@@ -1,11 +1,12 @@
1
1
  using System;
2
+ using System.Collections;
2
3
  using Amanotes.Core.Internal;
3
4
  using UnityEngine;
4
5
  namespace Amanotes.Core
5
6
  {
6
7
  public partial class AmaGDK //User profile
7
8
  {
8
- public static readonly UserProfile User = new UserProfile().Load();
9
+ public static readonly UserProfile User = new UserProfile();
9
10
 
10
11
  [Serializable]
11
12
  public partial class UserProfile : IJsonFile
@@ -18,14 +19,6 @@ namespace Amanotes.Core
18
19
  GDKFileUtils.Save(FILE_NAME, JsonUtility.ToJson(this));
19
20
  }
20
21
 
21
- internal UserProfile Load()
22
- {
23
- string json = GDKFileUtils.Load(FILE_NAME);
24
- if (string.IsNullOrEmpty(json)) return this;
25
- JsonUtility.FromJsonOverwrite(json, this);
26
- return this;
27
- }
28
-
29
22
  internal void SaveIfDirty()
30
23
  {
31
24
  if (!_dirty) return;
@@ -57,10 +50,26 @@ namespace Amanotes.Core
57
50
  private string appsFlyerId;
58
51
 
59
52
  private const string AMA_DEVICE_ID_KEY = "ama_device_id";
53
+ private const float TIME_OUT = 5f;
54
+ private const float WAIT_TIME = 0.5f;
55
+ private WaitForSeconds waitForDuration = new WaitForSeconds(WAIT_TIME);
60
56
 
61
- internal void InitUserProfile()
57
+ internal IEnumerator Init()
62
58
  {
63
- if (!IsAmaDeviceIdExist()) GetAdvertisingId();
59
+ User.LoadJsonFromFile();
60
+ if (IsAmaDeviceIdExist()) yield break;
61
+
62
+ GetAdvertisingId();
63
+
64
+ float time = 0;
65
+ while (!User.IsAmaDeviceIdExist())
66
+ {
67
+ yield return waitForDuration;
68
+ time += WAIT_TIME;
69
+ if (time <= TIME_OUT) continue;
70
+ Logging.LogWarning($"User init time out: {TIME_OUT}");
71
+ break;
72
+ }
64
73
  }
65
74
 
66
75
  private bool IsAmaDeviceIdExist()
@@ -118,7 +127,7 @@ namespace Amanotes.Core
118
127
  break;
119
128
 
120
129
  default:
121
- Logging.LogWarning($"Not support ama_device_id on: {Application.platform}");
130
+ amaDeviceId = SystemInfo.deviceUniqueIdentifier;
122
131
  break;
123
132
  }
124
133
  Logging.Log($"Generate AmaDeviceId completed: {amaDeviceId}");
package/Runtime/AmaGDK.cs CHANGED
@@ -14,7 +14,7 @@ namespace Amanotes.Core
14
14
  {
15
15
  public partial class AmaGDK : MonoBehaviour
16
16
  {
17
- public const string VERSION = "0.2.19";
17
+ public const string VERSION = "0.2.21";
18
18
 
19
19
  internal static AmaGDK _instance;
20
20
  internal static Status _status = Status.None;
@@ -61,8 +61,10 @@ namespace Amanotes.Core
61
61
  throw new Exception("[AmaGDK] " + CONFIG_NOT_FOUND);
62
62
  }
63
63
 
64
+ yield return User.Init();
65
+
64
66
  float time = 0;
65
- float timeout = 3;
67
+ float timeout = 10;
66
68
 
67
69
  var counter = 0;
68
70
  const int INIT_TOLERANCE = 10 * 60;
@@ -106,7 +108,6 @@ namespace Amanotes.Core
106
108
  }
107
109
 
108
110
  // Init modules (Adapter managers)
109
- User.InitUserProfile();
110
111
  Analytics.Init();
111
112
  Ads.Init();
112
113
  IAP.Init();
@@ -1,5 +1,5 @@
1
1
  fileFormatVersion: 2
2
- guid: 703161d03cf9f4dc9a95125c44430ebe
2
+ guid: abf1efb2d0f644148901211ecbce462f
3
3
  PluginImporter:
4
4
  externalObjects: {}
5
5
  serializedVersion: 2
@@ -1,5 +1,5 @@
1
1
  fileFormatVersion: 2
2
- guid: e9aa069386a2746d2affa79fe86ce7a6
2
+ guid: 0d9bff6463df741a099d04c5694bc689
3
3
  folderAsset: yes
4
4
  DefaultImporter:
5
5
  externalObjects: {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.amanotes.gdk",
3
- "version": "0.2.19",
3
+ "version": "0.2.21",
4
4
  "displayName": "AmaGDK",
5
5
  "description": "Amanotes Game Development Kit",
6
6
  "unity": "2019.4",