@capawesome/capacitor-age-signals 0.3.0 โ†’ 0.3.2

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/README.md CHANGED
@@ -12,6 +12,20 @@ Capacitor plugin to use the [Play Age Signals API](https://developer.android.com
12
12
 
13
13
  The **Play Age Signals API** is returning "Not yet implemented" because its live functionality is scheduled to begin on January 1, 2026.
14
14
 
15
+ ## Features
16
+
17
+ We are proud to offer one of the most complete and feature-rich Capacitor plugins for age verification. Here are some of the key features:
18
+
19
+ - ๐Ÿ–ฅ๏ธ **Cross-platform**: Supports Android and iOS.
20
+ - ๐Ÿ” **Age Verification**: Request user age signals using Play Age Signals API (Android) and DeclaredAgeRange (iOS).
21
+ - ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ **Parental Controls**: Support for supervised accounts with parental approval status.
22
+ - ๐Ÿงช **Testing Support**: Built-in `FakeAgeSignalsManager` integration for testing different age verification scenarios (Android).
23
+ - ๐ŸŒ **Compliance Ready**: Built for US state age verification requirements (effective January 1, 2026).
24
+ - ๐Ÿ“ฆ **SPM**: Supports Swift Package Manager for iOS.
25
+ - ๐Ÿ” **Up-to-date**: Always supports the latest Capacitor version.
26
+
27
+ Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look!
28
+
15
29
  ## Compatibility
16
30
 
17
31
  | Plugin Version | Capacitor Version | Status |
@@ -36,6 +50,8 @@ If needed, you can define the following project variable in your app's `variable
36
50
 
37
51
  This can be useful if you encounter dependency conflicts with other plugins in your project.
38
52
 
53
+ **Note**: The `FakeAgeSignalsManager` testing API is included in the main `age-signals` library, so no additional dependency is required for testing.
54
+
39
55
  ### iOS
40
56
 
41
57
  #### Entitlements
@@ -64,6 +80,110 @@ const checkAgeSignals = async () => {
64
80
  console.log('Age Lower:', result.ageLower);
65
81
  console.log('Age Upper:', result.ageUpper);
66
82
  };
83
+
84
+ const checkEligibility = async () => {
85
+ const result = await AgeSignals.checkEligibility();
86
+ console.log('Is Eligible:', result.isEligible);
87
+ };
88
+ ```
89
+
90
+ ## Testing
91
+
92
+ The plugin includes support for the `FakeAgeSignalsManager` API on Android, which allows you to simulate different age signals scenarios in your tests without requiring live responses from Google Play.
93
+
94
+ ### Android Testing
95
+
96
+ **Important**: Due to a known issue in versions 0.0.1 and 0.0.2 of the Age Signals API, you may encounter a `java.lang.VerifyError` when calling the builder method of `AgeSignalsResult` in unit tests. As a workaround, run your tests as Android instrumented tests within the `androidTest` source set.
97
+
98
+ #### Example: Testing a Verified Adult User
99
+
100
+ ```typescript
101
+ import { AgeSignals, UserStatus } from '@capawesome/capacitor-age-signals';
102
+
103
+ // Enable the fake manager
104
+ await AgeSignals.setUseFakeManager({ useFake: true });
105
+
106
+ // Set up a verified adult user
107
+ await AgeSignals.setNextAgeSignalsResult({
108
+ userStatus: UserStatus.Verified,
109
+ });
110
+
111
+ // Check age signals - will return the fake result
112
+ const result = await AgeSignals.checkAgeSignals();
113
+ console.log(result.userStatus); // 'VERIFIED'
114
+ ```
115
+
116
+ #### Example: Testing a Supervised User (13-17 years old)
117
+
118
+ ```typescript
119
+ import { AgeSignals, UserStatus } from '@capawesome/capacitor-age-signals';
120
+
121
+ await AgeSignals.setUseFakeManager({ useFake: true });
122
+
123
+ await AgeSignals.setNextAgeSignalsResult({
124
+ userStatus: UserStatus.Supervised,
125
+ ageLower: 13,
126
+ ageUpper: 17,
127
+ installId: 'fake_install_id',
128
+ });
129
+
130
+ const result = await AgeSignals.checkAgeSignals();
131
+ console.log(result.userStatus); // 'SUPERVISED'
132
+ console.log(result.ageLower); // 13
133
+ console.log(result.ageUpper); // 17
134
+ console.log(result.installId); // 'fake_install_id'
135
+ ```
136
+
137
+ #### Example: Testing Parental Approval Scenarios
138
+
139
+ ```typescript
140
+ import { AgeSignals, UserStatus } from '@capawesome/capacitor-age-signals';
141
+
142
+ await AgeSignals.setUseFakeManager({ useFake: true });
143
+
144
+ // Test pending approval
145
+ await AgeSignals.setNextAgeSignalsResult({
146
+ userStatus: UserStatus.SupervisedApprovalPending,
147
+ ageLower: 13,
148
+ ageUpper: 17,
149
+ mostRecentApprovalDate: '2025-02-01',
150
+ installId: 'fake_install_id',
151
+ });
152
+
153
+ const result = await AgeSignals.checkAgeSignals();
154
+ console.log(result.userStatus); // 'SUPERVISED_APPROVAL_PENDING'
155
+ console.log(result.mostRecentApprovalDate); // '2025-02-01'
156
+ ```
157
+
158
+ #### Example: Testing Error Scenarios
159
+
160
+ ```typescript
161
+ import { AgeSignals, ErrorCode } from '@capawesome/capacitor-age-signals';
162
+
163
+ await AgeSignals.setUseFakeManager({ useFake: true });
164
+
165
+ // Simulate a network error
166
+ await AgeSignals.setNextAgeSignalsException({
167
+ errorCode: ErrorCode.NetworkError,
168
+ });
169
+
170
+ try {
171
+ await AgeSignals.checkAgeSignals();
172
+ } catch (error) {
173
+ console.log('Caught network error:', error);
174
+ }
175
+ ```
176
+
177
+ #### Disabling the Fake Manager
178
+
179
+ ```typescript
180
+ import { AgeSignals } from '@capawesome/capacitor-age-signals';
181
+
182
+ // Switch back to the production manager
183
+ await AgeSignals.setUseFakeManager({ useFake: false });
184
+
185
+ // This will now use the real Age Signals API
186
+ const result = await AgeSignals.checkAgeSignals();
67
187
  ```
68
188
 
69
189
  ## API
@@ -71,6 +191,10 @@ const checkAgeSignals = async () => {
71
191
  <docgen-index>
72
192
 
73
193
  * [`checkAgeSignals(...)`](#checkagesignals)
194
+ * [`checkEligibility()`](#checkeligibility)
195
+ * [`setUseFakeManager(...)`](#setusefakemanager)
196
+ * [`setNextAgeSignalsResult(...)`](#setnextagesignalsresult)
197
+ * [`setNextAgeSignalsException(...)`](#setnextagesignalsexception)
74
198
  * [Interfaces](#interfaces)
75
199
  * [Enums](#enums)
76
200
 
@@ -98,6 +222,80 @@ Request the user's age signals.
98
222
  --------------------
99
223
 
100
224
 
225
+ ### checkEligibility()
226
+
227
+ ```typescript
228
+ checkEligibility() => Promise<CheckEligibilityResult>
229
+ ```
230
+
231
+ Check if the user is eligible for age-gated features.
232
+
233
+ Only available on iOS.
234
+
235
+ **Returns:** <code>Promise&lt;<a href="#checkeligibilityresult">CheckEligibilityResult</a>&gt;</code>
236
+
237
+ **Since:** 0.3.1
238
+
239
+ --------------------
240
+
241
+
242
+ ### setUseFakeManager(...)
243
+
244
+ ```typescript
245
+ setUseFakeManager(options: SetUseFakeManagerOptions) => Promise<void>
246
+ ```
247
+
248
+ Enable or disable the fake age signals manager for testing.
249
+
250
+ Only available on Android.
251
+
252
+ | Param | Type |
253
+ | ------------- | ----------------------------------------------------------------------------- |
254
+ | **`options`** | <code><a href="#setusefakemanageroptions">SetUseFakeManagerOptions</a></code> |
255
+
256
+ **Since:** 0.3.1
257
+
258
+ --------------------
259
+
260
+
261
+ ### setNextAgeSignalsResult(...)
262
+
263
+ ```typescript
264
+ setNextAgeSignalsResult(options: SetNextAgeSignalsResultOptions) => Promise<void>
265
+ ```
266
+
267
+ Set the next age signals result to be returned by the fake manager.
268
+
269
+ Only available on Android.
270
+
271
+ | Param | Type |
272
+ | ------------- | ----------------------------------------------------------------------------------------- |
273
+ | **`options`** | <code><a href="#setnextagesignalsresultoptions">SetNextAgeSignalsResultOptions</a></code> |
274
+
275
+ **Since:** 0.3.1
276
+
277
+ --------------------
278
+
279
+
280
+ ### setNextAgeSignalsException(...)
281
+
282
+ ```typescript
283
+ setNextAgeSignalsException(options: SetNextAgeSignalsExceptionOptions) => Promise<void>
284
+ ```
285
+
286
+ Set the next exception to be thrown by the fake manager.
287
+
288
+ Only available on Android.
289
+
290
+ | Param | Type |
291
+ | ------------- | ----------------------------------------------------------------------------------------------- |
292
+ | **`options`** | <code><a href="#setnextagesignalsexceptionoptions">SetNextAgeSignalsExceptionOptions</a></code> |
293
+
294
+ **Since:** 0.3.1
295
+
296
+ --------------------
297
+
298
+
101
299
  ### Interfaces
102
300
 
103
301
 
@@ -119,6 +317,38 @@ Request the user's age signals.
119
317
  | **`ageGates`** | <code>number[]</code> | The age ranges that the user falls into. The provided array must contain at least 2 and at most 3 ages. Only available on iOS. | <code>[13, 15, 18]</code> | 0.0.2 |
120
318
 
121
319
 
320
+ #### CheckEligibilityResult
321
+
322
+ | Prop | Type | Description | Since |
323
+ | ---------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
324
+ | **`isEligible`** | <code>boolean</code> | Whether the user is eligible for age-gated features. Returns `true` if the user is in an applicable region that requires additional age-related obligations. Always returns `false` on macOS. | 0.3.1 |
325
+
326
+
327
+ #### SetUseFakeManagerOptions
328
+
329
+ | Prop | Type | Description | Default | Since |
330
+ | ------------- | -------------------- | -------------------------------------------------------- | ------------------ | ----- |
331
+ | **`useFake`** | <code>boolean</code> | Whether to use the fake age signals manager for testing. | <code>false</code> | 0.3.1 |
332
+
333
+
334
+ #### SetNextAgeSignalsResultOptions
335
+
336
+ | Prop | Type | Description | Since |
337
+ | ---------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
338
+ | **`userStatus`** | <code><a href="#userstatus">UserStatus</a></code> | The user's verification status. | 0.3.1 |
339
+ | **`ageLower`** | <code>number</code> | The (inclusive) lower bound of a supervised user's age range. Only available when `userStatus` is `SUPERVISED`, `SUPERVISED_APPROVAL_PENDING`, or `SUPERVISED_APPROVAL_DENIED`. | 0.3.1 |
340
+ | **`ageUpper`** | <code>number</code> | The (inclusive) upper bound of a supervised user's age range. Only available when `userStatus` is `SUPERVISED`, `SUPERVISED_APPROVAL_PENDING`, or `SUPERVISED_APPROVAL_DENIED` and the user's age is under 18. | 0.3.1 |
341
+ | **`mostRecentApprovalDate`** | <code>string</code> | The effective from date of the most recent significant change that was approved. When an app is installed, the date of the most recent significant change prior to install is used. Only available when `userStatus` is `SUPERVISED_APPROVAL_PENDING` or `SUPERVISED_APPROVAL_DENIED`. | 0.3.1 |
342
+ | **`installId`** | <code>string</code> | An ID assigned to supervised user installs by Google Play, used for the purposes of notifying you of revoked app approval. Only available when `userStatus` is `SUPERVISED`, `SUPERVISED_APPROVAL_PENDING`, or `SUPERVISED_APPROVAL_DENIED`. | 0.3.1 |
343
+
344
+
345
+ #### SetNextAgeSignalsExceptionOptions
346
+
347
+ | Prop | Type | Description | Since |
348
+ | --------------- | ----------------------------------------------- | ------------------------------------------------ | ----- |
349
+ | **`errorCode`** | <code><a href="#errorcode">ErrorCode</a></code> | The error code to be thrown by the fake manager. | 0.3.1 |
350
+
351
+
122
352
  ### Enums
123
353
 
124
354
 
@@ -133,6 +363,22 @@ Request the user's age signals.
133
363
  | **`Unknown`** | <code>'UNKNOWN'</code> | The user is not verified or supervised in applicable jurisdictions and regions. These users could be over or under 18. To obtain an age signal from Google Play, ask the user to visit the Play Store to resolve their status. | 0.0.1 |
134
364
  | **`Empty`** | <code>'EMPTY'</code> | All other users return this value. | 0.0.1 |
135
365
 
366
+
367
+ #### ErrorCode
368
+
369
+ | Members | Value | Description | Since |
370
+ | --------------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
371
+ | **`ApiNotAvailable`** | <code>'API_NOT_AVAILABLE'</code> | The Play Age Signals API is not available. The Play Store app version installed on the device might be old. | 0.0.1 |
372
+ | **`PlayStoreNotFound`** | <code>'PLAY_STORE_NOT_FOUND'</code> | No Play Store app is found on the device. | 0.0.1 |
373
+ | **`NetworkError`** | <code>'NETWORK_ERROR'</code> | No available network is found. | 0.0.1 |
374
+ | **`PlayServicesNotFound`** | <code>'PLAY_SERVICES_NOT_FOUND'</code> | Play Services is not available or its version is too old. | 0.0.1 |
375
+ | **`CannotBindToService`** | <code>'CANNOT_BIND_TO_SERVICE'</code> | Binding to the service in the Play Store has failed. This can be due to having an old Play Store version installed on the device or device memory is overloaded. | 0.0.1 |
376
+ | **`PlayStoreVersionOutdated`** | <code>'PLAY_STORE_VERSION_OUTDATED'</code> | The Play Store app needs to be updated. | 0.0.1 |
377
+ | **`PlayServicesVersionOutdated`** | <code>'PLAY_SERVICES_VERSION_OUTDATED'</code> | Play Services needs to be updated. | 0.0.1 |
378
+ | **`ClientTransientError`** | <code>'CLIENT_TRANSIENT_ERROR'</code> | There was a transient error in the client device. | 0.0.1 |
379
+ | **`AppNotOwned`** | <code>'APP_NOT_OWNED'</code> | The app was not installed by Google Play. | 0.0.1 |
380
+ | **`InternalError`** | <code>'INTERNAL_ERROR'</code> | Unknown internal error. | 0.0.1 |
381
+
136
382
  </docgen-api>
137
383
 
138
384
  ## Changelog
@@ -1,13 +1,19 @@
1
1
  package io.capawesome.capacitorjs.plugins.agesignals;
2
2
 
3
3
  import androidx.annotation.NonNull;
4
+ import androidx.annotation.Nullable;
4
5
  import com.google.android.gms.tasks.Task;
5
6
  import com.google.android.play.agesignals.AgeSignalsManager;
6
7
  import com.google.android.play.agesignals.AgeSignalsManagerFactory;
7
8
  import com.google.android.play.agesignals.AgeSignalsRequest;
8
9
  import com.google.android.play.agesignals.AgeSignalsResult;
10
+ import com.google.android.play.agesignals.testing.FakeAgeSignalsManager;
9
11
  import io.capawesome.capacitorjs.plugins.agesignals.classes.CustomExceptions;
12
+ import io.capawesome.capacitorjs.plugins.agesignals.classes.options.SetNextAgeSignalsExceptionOptions;
13
+ import io.capawesome.capacitorjs.plugins.agesignals.classes.options.SetNextAgeSignalsResultOptions;
14
+ import io.capawesome.capacitorjs.plugins.agesignals.classes.options.SetUseFakeManagerOptions;
10
15
  import io.capawesome.capacitorjs.plugins.agesignals.classes.results.CheckAgeSignalsResult;
16
+ import io.capawesome.capacitorjs.plugins.agesignals.interfaces.EmptyCallback;
11
17
  import io.capawesome.capacitorjs.plugins.agesignals.interfaces.NonEmptyResultCallback;
12
18
 
13
19
  public class AgeSignals {
@@ -15,12 +21,23 @@ public class AgeSignals {
15
21
  @NonNull
16
22
  private final AgeSignalsPlugin plugin;
17
23
 
24
+ private boolean useFakeManager = false;
25
+
26
+ @Nullable
27
+ private FakeAgeSignalsManager fakeManager = null;
28
+
18
29
  public AgeSignals(@NonNull AgeSignalsPlugin plugin) {
19
30
  this.plugin = plugin;
20
31
  }
21
32
 
22
33
  public void checkAgeSignals(@NonNull NonEmptyResultCallback<CheckAgeSignalsResult> callback) {
23
- AgeSignalsManager manager = AgeSignalsManagerFactory.create(plugin.getActivity());
34
+ AgeSignalsManager manager;
35
+ if (useFakeManager && fakeManager != null) {
36
+ manager = fakeManager;
37
+ } else {
38
+ manager = AgeSignalsManagerFactory.create(plugin.getActivity());
39
+ }
40
+
24
41
  AgeSignalsRequest request = AgeSignalsRequest.builder().build();
25
42
 
26
43
  Task<AgeSignalsResult> task = manager.checkAgeSignals(request);
@@ -38,6 +55,46 @@ public class AgeSignals {
38
55
  });
39
56
  }
40
57
 
58
+ public void setUseFakeManager(@NonNull SetUseFakeManagerOptions options, @NonNull EmptyCallback callback) {
59
+ try {
60
+ this.useFakeManager = options.getUseFake();
61
+ if (this.useFakeManager) {
62
+ this.fakeManager = new FakeAgeSignalsManager();
63
+ } else {
64
+ this.fakeManager = null;
65
+ }
66
+ callback.success();
67
+ } catch (Exception exception) {
68
+ callback.error(exception);
69
+ }
70
+ }
71
+
72
+ public void setNextAgeSignalsResult(@NonNull SetNextAgeSignalsResultOptions options, @NonNull EmptyCallback callback) {
73
+ try {
74
+ if (!useFakeManager || fakeManager == null) {
75
+ throw CustomExceptions.FAKE_MANAGER_NOT_ENABLED;
76
+ }
77
+ com.google.android.play.agesignals.AgeSignalsResult result = options.buildAgeSignalsResult();
78
+ fakeManager.setNextAgeSignalsResult(result);
79
+ callback.success();
80
+ } catch (Exception exception) {
81
+ callback.error(exception);
82
+ }
83
+ }
84
+
85
+ public void setNextAgeSignalsException(@NonNull SetNextAgeSignalsExceptionOptions options, @NonNull EmptyCallback callback) {
86
+ try {
87
+ if (!useFakeManager || fakeManager == null) {
88
+ throw CustomExceptions.FAKE_MANAGER_NOT_ENABLED;
89
+ }
90
+ com.google.android.play.agesignals.AgeSignalsException exception = options.buildAgeSignalsException();
91
+ fakeManager.setNextAgeSignalsException(exception);
92
+ callback.success();
93
+ } catch (Exception exception) {
94
+ callback.error(exception);
95
+ }
96
+ }
97
+
41
98
  @NonNull
42
99
  private Exception mapErrorCodeToException(@NonNull Exception exception) {
43
100
  if (!(exception instanceof com.google.android.gms.common.api.ApiException)) {
@@ -8,7 +8,11 @@ import com.getcapacitor.Plugin;
8
8
  import com.getcapacitor.PluginCall;
9
9
  import com.getcapacitor.PluginMethod;
10
10
  import com.getcapacitor.annotation.CapacitorPlugin;
11
+ import io.capawesome.capacitorjs.plugins.agesignals.classes.options.SetNextAgeSignalsExceptionOptions;
12
+ import io.capawesome.capacitorjs.plugins.agesignals.classes.options.SetNextAgeSignalsResultOptions;
13
+ import io.capawesome.capacitorjs.plugins.agesignals.classes.options.SetUseFakeManagerOptions;
11
14
  import io.capawesome.capacitorjs.plugins.agesignals.classes.results.CheckAgeSignalsResult;
15
+ import io.capawesome.capacitorjs.plugins.agesignals.interfaces.EmptyCallback;
12
16
  import io.capawesome.capacitorjs.plugins.agesignals.interfaces.NonEmptyResultCallback;
13
17
  import io.capawesome.capacitorjs.plugins.agesignals.interfaces.Result;
14
18
 
@@ -50,6 +54,80 @@ public class AgeSignalsPlugin extends Plugin {
50
54
  }
51
55
  }
52
56
 
57
+ @PluginMethod
58
+ public void checkEligibility(PluginCall call) {
59
+ rejectCallAsUnimplemented(call);
60
+ }
61
+
62
+ @PluginMethod
63
+ public void setUseFakeManager(PluginCall call) {
64
+ try {
65
+ SetUseFakeManagerOptions options = new SetUseFakeManagerOptions(call);
66
+ EmptyCallback callback = new EmptyCallback() {
67
+ @Override
68
+ public void success() {
69
+ resolveCall(call);
70
+ }
71
+
72
+ @Override
73
+ public void error(@NonNull Exception exception) {
74
+ rejectCall(call, exception);
75
+ }
76
+ };
77
+
78
+ assert implementation != null;
79
+ implementation.setUseFakeManager(options, callback);
80
+ } catch (Exception exception) {
81
+ rejectCall(call, exception);
82
+ }
83
+ }
84
+
85
+ @PluginMethod
86
+ public void setNextAgeSignalsResult(PluginCall call) {
87
+ try {
88
+ SetNextAgeSignalsResultOptions options = new SetNextAgeSignalsResultOptions(call);
89
+ EmptyCallback callback = new EmptyCallback() {
90
+ @Override
91
+ public void success() {
92
+ resolveCall(call);
93
+ }
94
+
95
+ @Override
96
+ public void error(@NonNull Exception exception) {
97
+ rejectCall(call, exception);
98
+ }
99
+ };
100
+
101
+ assert implementation != null;
102
+ implementation.setNextAgeSignalsResult(options, callback);
103
+ } catch (Exception exception) {
104
+ rejectCall(call, exception);
105
+ }
106
+ }
107
+
108
+ @PluginMethod
109
+ public void setNextAgeSignalsException(PluginCall call) {
110
+ try {
111
+ SetNextAgeSignalsExceptionOptions options = new SetNextAgeSignalsExceptionOptions(call);
112
+ EmptyCallback callback = new EmptyCallback() {
113
+ @Override
114
+ public void success() {
115
+ resolveCall(call);
116
+ }
117
+
118
+ @Override
119
+ public void error(@NonNull Exception exception) {
120
+ rejectCall(call, exception);
121
+ }
122
+ };
123
+
124
+ assert implementation != null;
125
+ implementation.setNextAgeSignalsException(options, callback);
126
+ } catch (Exception exception) {
127
+ rejectCall(call, exception);
128
+ }
129
+ }
130
+
53
131
  private void rejectCall(@NonNull PluginCall call, @NonNull Exception exception) {
54
132
  String message = exception.getMessage();
55
133
  if (message == null) {
@@ -59,6 +137,14 @@ public class AgeSignalsPlugin extends Plugin {
59
137
  call.reject(message);
60
138
  }
61
139
 
140
+ private void rejectCallAsUnimplemented(@NonNull PluginCall call) {
141
+ call.unimplemented("This method is not available on this platform.");
142
+ }
143
+
144
+ private void resolveCall(@NonNull PluginCall call) {
145
+ call.resolve();
146
+ }
147
+
62
148
  private void resolveCall(@NonNull PluginCall call, @Nullable Result result) {
63
149
  if (result == null) {
64
150
  call.resolve();
@@ -33,4 +33,6 @@ public class CustomExceptions {
33
33
  );
34
34
  public static final CustomException APP_NOT_OWNED = new CustomException("APP_NOT_OWNED", "The app was not installed by Google Play.");
35
35
  public static final CustomException INTERNAL_ERROR = new CustomException("INTERNAL_ERROR", "Unknown internal error.");
36
+ public static final CustomException USER_STATUS_MISSING = new CustomException(null, "userStatus must be provided.");
37
+ public static final CustomException FAKE_MANAGER_NOT_ENABLED = new CustomException(null, "Fake manager is not enabled.");
36
38
  }
@@ -0,0 +1,53 @@
1
+ package io.capawesome.capacitorjs.plugins.agesignals.classes.options;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import com.getcapacitor.PluginCall;
5
+ import com.google.android.play.agesignals.AgeSignalsException;
6
+ import com.google.android.play.agesignals.model.AgeSignalsErrorCode;
7
+
8
+ public class SetNextAgeSignalsExceptionOptions {
9
+
10
+ @NonNull
11
+ private final String errorCode;
12
+
13
+ public SetNextAgeSignalsExceptionOptions(@NonNull PluginCall call) throws Exception {
14
+ String errorCode = call.getString("errorCode");
15
+ if (errorCode == null) {
16
+ throw new Exception("errorCode must be provided.");
17
+ }
18
+ this.errorCode = errorCode;
19
+ }
20
+
21
+ @NonNull
22
+ public AgeSignalsException buildAgeSignalsException() throws Exception {
23
+ int errorCodeInt = mapErrorCodeToAgeSignalsErrorCode(this.errorCode);
24
+ return new AgeSignalsException(errorCodeInt);
25
+ }
26
+
27
+ private int mapErrorCodeToAgeSignalsErrorCode(@NonNull String errorCode) throws Exception {
28
+ switch (errorCode) {
29
+ case "API_NOT_AVAILABLE":
30
+ return AgeSignalsErrorCode.API_NOT_AVAILABLE;
31
+ case "PLAY_STORE_NOT_FOUND":
32
+ return AgeSignalsErrorCode.PLAY_STORE_NOT_FOUND;
33
+ case "NETWORK_ERROR":
34
+ return AgeSignalsErrorCode.NETWORK_ERROR;
35
+ case "PLAY_SERVICES_NOT_FOUND":
36
+ return AgeSignalsErrorCode.PLAY_SERVICES_NOT_FOUND;
37
+ case "CANNOT_BIND_TO_SERVICE":
38
+ return AgeSignalsErrorCode.CANNOT_BIND_TO_SERVICE;
39
+ case "PLAY_STORE_VERSION_OUTDATED":
40
+ return AgeSignalsErrorCode.PLAY_STORE_VERSION_OUTDATED;
41
+ case "PLAY_SERVICES_VERSION_OUTDATED":
42
+ return AgeSignalsErrorCode.PLAY_SERVICES_VERSION_OUTDATED;
43
+ case "CLIENT_TRANSIENT_ERROR":
44
+ return AgeSignalsErrorCode.CLIENT_TRANSIENT_ERROR;
45
+ case "APP_NOT_OWNED":
46
+ return AgeSignalsErrorCode.APP_NOT_OWNED;
47
+ case "INTERNAL_ERROR":
48
+ return AgeSignalsErrorCode.INTERNAL_ERROR;
49
+ default:
50
+ throw new Exception("Invalid errorCode: " + errorCode);
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,109 @@
1
+ package io.capawesome.capacitorjs.plugins.agesignals.classes.options;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import androidx.annotation.Nullable;
5
+ import com.getcapacitor.PluginCall;
6
+ import com.google.android.play.agesignals.AgeSignalsResult;
7
+ import com.google.android.play.agesignals.model.AgeSignalsVerificationStatus;
8
+ import io.capawesome.capacitorjs.plugins.agesignals.classes.CustomExceptions;
9
+ import io.capawesome.capacitorjs.plugins.agesignals.enums.UserStatus;
10
+ import java.text.SimpleDateFormat;
11
+ import java.util.Date;
12
+ import java.util.Locale;
13
+
14
+ public class SetNextAgeSignalsResultOptions {
15
+
16
+ @NonNull
17
+ private final UserStatus userStatus;
18
+
19
+ @Nullable
20
+ private final Integer ageLower;
21
+
22
+ @Nullable
23
+ private final Integer ageUpper;
24
+
25
+ @Nullable
26
+ private final String mostRecentApprovalDate;
27
+
28
+ @Nullable
29
+ private final String installId;
30
+
31
+ public SetNextAgeSignalsResultOptions(@NonNull PluginCall call) throws Exception {
32
+ this.userStatus = SetNextAgeSignalsResultOptions.getUserStatusFromCall(call);
33
+ this.ageLower = call.getInt("ageLower");
34
+ this.ageUpper = call.getInt("ageUpper");
35
+ this.mostRecentApprovalDate = call.getString("mostRecentApprovalDate");
36
+ this.installId = call.getString("installId");
37
+ }
38
+
39
+ @NonNull
40
+ public AgeSignalsResult buildAgeSignalsResult() throws Exception {
41
+ AgeSignalsResult.Builder builder = AgeSignalsResult.builder();
42
+
43
+ // Set user status as integer
44
+ Integer verificationStatus = mapUserStatusToVerificationStatus(this.userStatus);
45
+ builder.setUserStatus(verificationStatus);
46
+
47
+ // Set optional fields
48
+ if (this.ageLower != null) {
49
+ builder.setAgeLower(this.ageLower);
50
+ }
51
+ if (this.ageUpper != null) {
52
+ builder.setAgeUpper(this.ageUpper);
53
+ }
54
+ if (this.mostRecentApprovalDate != null) {
55
+ Date date = parseDateString(this.mostRecentApprovalDate);
56
+ builder.setMostRecentApprovalDate(date);
57
+ }
58
+ if (this.installId != null) {
59
+ builder.setInstallId(this.installId);
60
+ }
61
+
62
+ return builder.build();
63
+ }
64
+
65
+ @NonNull
66
+ private static UserStatus getUserStatusFromCall(@NonNull PluginCall call) throws Exception {
67
+ String userStatusString = call.getString("userStatus");
68
+ if (userStatusString == null) {
69
+ throw new Exception(CustomExceptions.USER_STATUS_MISSING.getMessage());
70
+ }
71
+ try {
72
+ return UserStatus.valueOf(userStatusString);
73
+ } catch (IllegalArgumentException exception) {
74
+ throw new Exception("Invalid userStatus: " + userStatusString);
75
+ }
76
+ }
77
+
78
+ @NonNull
79
+ private Integer mapUserStatusToVerificationStatus(@NonNull UserStatus userStatus) {
80
+ switch (userStatus) {
81
+ case VERIFIED:
82
+ return AgeSignalsVerificationStatus.VERIFIED;
83
+ case SUPERVISED:
84
+ return AgeSignalsVerificationStatus.SUPERVISED;
85
+ case SUPERVISED_APPROVAL_PENDING:
86
+ return AgeSignalsVerificationStatus.SUPERVISED_APPROVAL_PENDING;
87
+ case SUPERVISED_APPROVAL_DENIED:
88
+ return AgeSignalsVerificationStatus.SUPERVISED_APPROVAL_DENIED;
89
+ case UNKNOWN:
90
+ return AgeSignalsVerificationStatus.UNKNOWN;
91
+ default:
92
+ return AgeSignalsVerificationStatus.UNKNOWN;
93
+ }
94
+ }
95
+
96
+ @NonNull
97
+ private Date parseDateString(@NonNull String dateString) throws Exception {
98
+ try {
99
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
100
+ Date date = format.parse(dateString);
101
+ if (date == null) {
102
+ throw new Exception("Failed to parse date: " + dateString);
103
+ }
104
+ return date;
105
+ } catch (Exception exception) {
106
+ throw new Exception("Invalid date format. Expected yyyy-MM-dd, got: " + dateString);
107
+ }
108
+ }
109
+ }
@@ -0,0 +1,16 @@
1
+ package io.capawesome.capacitorjs.plugins.agesignals.classes.options;
2
+
3
+ import com.getcapacitor.PluginCall;
4
+
5
+ public class SetUseFakeManagerOptions {
6
+
7
+ private final boolean useFake;
8
+
9
+ public SetUseFakeManagerOptions(PluginCall call) {
10
+ this.useFake = call.getBoolean("useFake", false);
11
+ }
12
+
13
+ public boolean getUseFake() {
14
+ return useFake;
15
+ }
16
+ }