cordova-plugin-admob-nextgen 1.9.9 → 2.0.1-beta.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.
package/README.md CHANGED
@@ -115,7 +115,7 @@ This plugin is regularly updated to support the latest standards.
115
115
 
116
116
  | Component | Platform | Version | Release Notes |
117
117
  | :--- | :--- | :--- | :--- |
118
- | **GMA Next-Gen SDK** | Android | **1.2.1** | [View Notes](https://developers.google.com/admob/android/next-gen/rel-notes) |
118
+ | **GMA Next-Gen SDK** | Android | **1.3.0** | [View Notes](https://developers.google.com/admob/android/next-gen/rel-notes) |
119
119
  | **UMP SDK** | Android | **4.0.0** | [View Notes](https://developers.google.com/admob/android/privacy/release-notes) |
120
120
  | **Mobile Ads SDK** | iOS | **13.3.0** | [View Notes](https://developers.google.com/admob/ios/rel-notes) |
121
121
  | **UMP SDK** | iOS | **3.1.0** | [View Notes](https://developers.google.com/ad-manager/mobile-ads-sdk/ios/privacy/download) |
@@ -147,6 +147,7 @@ Add this to your `config.xml` to restore the plugin automatically.
147
147
 
148
148
  ### Supporting Mediation: **[⚡ admob-mediation-suite ](https://github.com/swaplab-engine/admob-mediation-suite)** (Optional)
149
149
  ### Supporting Native ads: **[⚡ cordova-plugin-admob-nextgen-native ](https://github.com/swaplab-engine/cordova-plugin-admob-nextgen-native)** (Optional)
150
+ ### New COPPA : **[⚡ cordova-plugin-play-age-signals ](https://github.com/swaplab-engine/cordova-plugin-play-age-signals)** (Optional)
150
151
 
151
152
  ---
152
153
 
@@ -163,13 +164,11 @@ Add this to your `config.xml` to restore the plugin automatically.
163
164
 
164
165
  **IMPORTANT:** Configure Global Settings and handle Privacy Consent (UMP) **BEFORE** initializing the SDK.
165
166
 
166
- ### Step 1: Global Configuration (Run First)
167
-
168
167
  // Optional: Mute ads globally
169
168
  admobNextGen.setAppVolume(0.5);
170
169
  admobNextGen.setAppMuted(true);
171
170
 
172
- ### Step 2: Request Consent (UMP) & Initialize
171
+ ### Step 1: Request Consent (UMP) & Initialize
173
172
 
174
173
  // 1. Request Consent Information
175
174
  admobNextGen.requestConsentInfo({
@@ -214,7 +213,7 @@ Add this to your `config.xml` to restore the plugin automatically.
214
213
  }
215
214
 
216
215
 
217
- // 4. Privacy options
216
+ // 4. Privacy options
218
217
  admobNextGen.showPrivacyOptionsForm(function() {
219
218
  startSdk();
220
219
  }, function(err) {
@@ -247,19 +246,87 @@ function checkAppTracking() {
247
246
  // admobNextGen.getTrackingAuthorizationStatus(success, error);
248
247
  ```
249
248
 
250
- ### Step 3: Check Consent Status (Smart Analysis)
251
- You can check if the user has granted permission for Personalized Ads without parsing complex IAB strings manually.
249
+ ### Step 2: Check Consent Status (Smart Analysis)
250
+ - Understanding Consent Flags (`getTCData`)
252
251
 
253
- admobNextGen.getTCData(function(data) {
254
- // --- SMART ANALYSIS (Easy) ---
255
- console.log("Personalized Allowed: " + data.isPersonalizedAllowed); // Boolean: true/false
256
- console.log("Status: " + data.statusMessage);
252
+ *(See [Publisher integration with the IAB Europe TCF](https://support.google.com/admanager/answer/9805023?hl=en#consent-policies))*
253
+ >
257
254
 
258
- // --- RAW DATA (Expert) ---
259
- console.log("TC String: " + data.tcString);
260
- console.log("GDPR Applies: " + data.gdprApplies); // 1=Yes, 0=No
261
- console.log("Purpose Consents: " + data.purposeConsents);
262
- });
255
+ When you call `admobNextGen.getTCData()`, the plugin returns a JSON object containing raw IAB TCF strings alongside pre-calculated boolean flags. To comply with AdMob's strict policies in GDPR regions, This plugin provide specific AdMob flags alongside the legacy flag.
256
+
257
+ | Flag | Description | GDPR Requirements Checked |
258
+ | :--- | :--- | :--- |
259
+ | `isAdMobPersonalizedAdsAllowed` | **(Recommended)** Returns `true` if the user has granted all necessary consents for AdMob to serve **Personalized Ads**. | Consent for Purposes 1, 3, 4 **AND** (Consent or Legitimate Interest) for Purposes 2, 7, 9, 10 **AND** Vendor 755 (Google). |
260
+ | `isAdMobNonPersonalizedAdsAllowed` | **(Recommended)** Returns `true` if the user has granted the minimum consent required for AdMob to serve **Non-Personalized Ads**. | Consent for Purpose 1 **AND** (Consent or Legitimate Interest) for Purposes 2, 7, 9, 10 **AND** Vendor 755 (Google). |
261
+ | `isPersonalizedAllowed` | **(Legacy)** Preserved for backward compatibility with older app versions. **Not recommended for strict AdMob policy compliance.** | Consent for Purpose 1 only. |
262
+
263
+ > **Note:** If the user is outside the GDPR region (e.g., USA, Indonesia), all of the ready-to-use flags above will return `true` by default.
264
+
265
+ ### A: Standard Usage (Ready-to-use Flags)
266
+ For most users, relying on the built-in boolean flags is the easiest way to ensure AdMob compliance without parsing complex strings.
267
+
268
+ ```javascript
269
+ // Request TC Data
270
+ admobNextGen.getTCData(function(tcData) {
271
+
272
+
273
+ console.log(tcData.adMobConsentStatus);
274
+
275
+ if (tcData.isAdMobPersonalizedAdsAllowed) {
276
+ console.log("Consent met: AdMob can serve Personalized Ads.");
277
+ // Proceed to load ads...
278
+ }
279
+ else if (tcData.isAdMobNonPersonalizedAdsAllowed) {
280
+ console.log("Consent met: AdMob will serve Non-Personalized Ads (Limited Ads).");
281
+ // Proceed to load ads...
282
+ }
283
+ else {
284
+ console.log("Consent insufficient: AdMob cannot serve any ads.");
285
+ // You might want to show the privacy options form again here
286
+ }
287
+
288
+ });
289
+ ```
290
+
291
+ #### B: Advanced Usage (Raw Data Control)
292
+
293
+ Advanced users who want full control over the consent logic can completely ignore the built-in boolean flags. The plugin now dynamically extracts and exposes **ALL standard IAB TCF v2 keys** directly from the device's local storage.
294
+
295
+ This means you have full access to every `IABTCF_` prefixed key (such as `IABTCF_PurposeConsents`, `IABTCF_VendorLegitimateInterests`, `IABTCF_SpecialFeaturesOptIns`, etc.), allowing you to manipulate and build custom enum conditions based on your own specific architecture requirements.
296
+
297
+ ```javascript
298
+ admobNextGen.getTCData(function(tcData) {
299
+
300
+ // Ignore the built-in flags and parse the raw data yourself.
301
+ // Note: IAB TCF strings are 1-indexed, but JavaScript strings are 0-indexed.
302
+ // Index 0 represents Purpose 1, Index 2 represents Purpose 3, etc.
303
+
304
+ // You can use the standard IABTCF_ keys extracted dynamically by the plugin
305
+ const purposes = tcData.IABTCF_PurposeConsents || "";
306
+ const lis = tcData.IABTCF_PurposeLegitimateInterests || "";
307
+ const vendors = tcData.IABTCF_VendorConsents || "";
308
+ const vendorLIs = tcData.IABTCF_VendorLegitimateInterests || "";
309
+
310
+ // const tcs = tcData.IABTCF_TCString;
311
+ // const region = tcData.IABTCF_gdprApplies;
312
+
313
+ // Custom logic example: Check if Purpose 1 and Purpose 3 are explicitly granted
314
+ const hasPurpose1 = purposes.charAt(0) === '1';
315
+ const hasPurpose3 = purposes.charAt(2) === '1';
316
+
317
+ // Check if Vendor 755 (Google) Consent OR Legitimate Interest is granted (Index 754)
318
+ const hasGoogleVendorConsent = vendors.charAt(754) === '1';
319
+ const hasGoogleVendorLI = vendorLIs.charAt(754) === '1';
320
+
321
+ if (hasPurpose1 && hasPurpose3 && (hasGoogleVendorConsent || hasGoogleVendorLI)) {
322
+ console.log("Custom advanced condition met!");
323
+ // Your custom ad loading logic here
324
+ } else {
325
+ console.log("Custom advanced condition failed.");
326
+ }
327
+
328
+ });
329
+ ```
263
330
 
264
331
  ---
265
332
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cordova-plugin-admob-nextgen",
3
- "version": "1.9.9",
3
+ "version": "2.0.1-beta.1",
4
4
  "description": "Google Mobile Ads Next Gen SDK for Cordova. High performance and modular architecture. ",
5
5
  "cordova": {
6
6
  "id": "cordova-plugin-admob-nextgen",
package/plugin.xml CHANGED
@@ -1,6 +1,6 @@
1
1
  <?xml version='1.0' encoding='utf-8'?>
2
2
  <plugin id="cordova-plugin-admob-nextgen"
3
- version="1.9.9"
3
+ version="2.0.1-beta.1"
4
4
  xmlns="http://apache.org/cordova/ns/plugins/1.0"
5
5
  xmlns:android="http://schemas.android.com/apk/res/android">
6
6
 
@@ -53,7 +53,7 @@
53
53
 
54
54
  </platform>
55
55
 
56
- <preference name="NEXT_GEN_SDK_VERSION" default="1.2.1" />
56
+ <preference name="NEXT_GEN_SDK_VERSION" default="1.3.0" />
57
57
  <preference name="UMP_VERSION" default="4.0.0" />
58
58
  <!-- Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713 -->
59
59
  <preference name="APP_ID_ANDROID" default="ca-app-pub-3940256099942544~3347511713" />
@@ -18,6 +18,7 @@ import org.json.JSONObject;
18
18
  import java.security.MessageDigest;
19
19
  import java.security.NoSuchAlgorithmException;
20
20
  import java.util.Locale;
21
+ import java.util.Map;
21
22
 
22
23
  import com.google.android.ump.ConsentDebugSettings;
23
24
  import com.google.android.ump.ConsentInformation;
@@ -47,6 +48,7 @@ public class ConsentExecutor {
47
48
  boolean resetConsent = false;
48
49
  boolean tagForUnderAgeOfConsent = false;
49
50
 
51
+ boolean tempShowFormIfRequired = true;
50
52
  if (options != null) {
51
53
  debugMode = options.optBoolean("debug", false);
52
54
  manualTestDeviceId = options.optString("testDeviceId", "");
@@ -55,6 +57,8 @@ public class ConsentExecutor {
55
57
  if (options.has("tagForUnderAgeOfConsent")) {
56
58
  tagForUnderAgeOfConsent = options.getBoolean("tagForUnderAgeOfConsent");
57
59
  }
60
+
61
+ tempShowFormIfRequired = options.optBoolean("showFormIfRequired", true);
58
62
  }
59
63
 
60
64
  if (resetConsent) {
@@ -62,6 +66,8 @@ public class ConsentExecutor {
62
66
 
63
67
  }
64
68
 
69
+ final boolean showFormIfRequired = tempShowFormIfRequired;
70
+
65
71
  ConsentRequestParameters.Builder paramsBuilder = new ConsentRequestParameters.Builder();
66
72
  paramsBuilder.setTagForUnderAgeOfConsent(tagForUnderAgeOfConsent);
67
73
 
@@ -94,21 +100,25 @@ public class ConsentExecutor {
94
100
 
95
101
  fireEvent("on.consent.info.update", null);
96
102
 
97
- UserMessagingPlatform.loadAndShowConsentFormIfRequired(
98
- activity,
99
- (FormError loadAndShowError) -> {
100
- if (loadAndShowError != null) {
101
-
102
- sendErrorEvent(loadAndShowError);
103
- callbackContext.error(loadAndShowError.getMessage());
104
- } else {
103
+ if (showFormIfRequired) {
104
+ UserMessagingPlatform.loadAndShowConsentFormIfRequired(
105
+ activity,
106
+ (FormError loadAndShowError) -> {
107
+ if (loadAndShowError != null) {
108
+
109
+ sendErrorEvent(loadAndShowError);
110
+ callbackContext.error(loadAndShowError.getMessage());
111
+ } else {
112
+ fireEvent("on.consent.form.dismissed", null);
113
+ sendConsentStatus(callbackContext);
114
+ }
115
+ }
116
+ );
117
+ } else {
105
118
 
106
- fireEvent("on.consent.form.dismissed", null);
119
+ sendConsentStatus(callbackContext);
120
+ }
107
121
 
108
- sendConsentStatus(callbackContext);
109
- }
110
- }
111
- );
112
122
  },
113
123
  (FormError requestConsentError) -> {
114
124
 
@@ -128,43 +138,86 @@ public class ConsentExecutor {
128
138
  SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(cordova.getActivity());
129
139
  JSONObject tcData = new JSONObject();
130
140
 
131
- String tcString = prefs.getString("IABTCF_TCString", "");
141
+ Map<String, ?> allEntries = prefs.getAll();
142
+ for (Map.Entry<String, ?> entry : allEntries.entrySet()) {
143
+ if (entry.getKey().startsWith("IABTCF_")) {
144
+ tcData.put(entry.getKey(), entry.getValue());
145
+ }
146
+ }
147
+
132
148
  String purposeConsents = prefs.getString("IABTCF_PurposeConsents", "");
149
+ String purposeLegitimateInterests = prefs.getString("IABTCF_PurposeLegitimateInterests", "");
133
150
  String vendorConsents = prefs.getString("IABTCF_VendorConsents", "");
134
151
  int gdprApplies = prefs.getInt("IABTCF_gdprApplies", 0);
135
152
 
136
- tcData.put("tcString", tcString);
137
153
  tcData.put("purposeConsents", purposeConsents);
154
+ tcData.put("purposeLegitimateInterests", purposeLegitimateInterests);
138
155
  tcData.put("vendorConsents", vendorConsents);
139
156
  tcData.put("gdprApplies", gdprApplies);
140
157
 
141
158
  boolean isPersonalizedAllowed = false;
142
159
  String statusMessage = "Unknown";
143
160
 
161
+ boolean isAdMobPersonalizedAdsAllowed = false;
162
+ boolean isAdMobNonPersonalizedAdsAllowed = false;
163
+ String adMobConsentStatus = "Unknown";
164
+
144
165
  if (gdprApplies == 0) {
145
166
 
146
167
  isPersonalizedAllowed = true;
147
168
  statusMessage = "Not GDPR region. Personalized Ads allowed by default.";
169
+
170
+ isAdMobPersonalizedAdsAllowed = true;
171
+ isAdMobNonPersonalizedAdsAllowed = true;
172
+ adMobConsentStatus = "Not GDPR region. AdMob ads allowed by default.";
148
173
  } else {
149
174
 
150
175
  if (purposeConsents != null && purposeConsents.length() > 0) {
151
176
  char p1 = purposeConsents.charAt(0);
152
177
  if (p1 == '1') {
153
178
  isPersonalizedAllowed = true;
154
- statusMessage = "Purpose 1 Granted. Personalized Ads allowed.";
179
+ statusMessage = "Purpose 1 Granted. Legacy check passed.";
155
180
  } else {
156
181
  isPersonalizedAllowed = false;
157
- statusMessage = "Purpose 1 Denied. Non-Personalized / Limited Ads only.";
182
+ statusMessage = "Purpose 1 Denied. Legacy check failed.";
158
183
  }
159
- } else {
184
+ }
185
+
186
+ boolean hasPurpose1 = checkConsent(purposeConsents, 1);
187
+ boolean hasPurpose3 = checkConsent(purposeConsents, 3);
188
+ boolean hasPurpose4 = checkConsent(purposeConsents, 4);
160
189
 
161
- isPersonalizedAllowed = false;
162
- statusMessage = "No consent data found (User hasn't answered yet).";
190
+ boolean hasRequiredLI_or_Consent =
191
+ hasConsentOrLI(purposeConsents, purposeLegitimateInterests, 2) &&
192
+ hasConsentOrLI(purposeConsents, purposeLegitimateInterests, 7) &&
193
+ hasConsentOrLI(purposeConsents, purposeLegitimateInterests, 9) &&
194
+ hasConsentOrLI(purposeConsents, purposeLegitimateInterests, 10);
195
+
196
+ boolean hasVendorGoogle = checkConsent(vendorConsents, 755);
197
+
198
+ if (hasPurpose1 && hasVendorGoogle && hasRequiredLI_or_Consent) {
199
+ isAdMobNonPersonalizedAdsAllowed = true;
200
+
201
+ if (hasPurpose3 && hasPurpose4) {
202
+ isAdMobPersonalizedAdsAllowed = true;
203
+ adMobConsentStatus = "Strict requirements met for Personalized Ads (Purposes 1,3,4 + LI 2,7,9,10 + Vendor 755).";
204
+ } else {
205
+ isAdMobPersonalizedAdsAllowed = false;
206
+ adMobConsentStatus = "Requirements met for Non-Personalized Ads only.";
207
+ }
208
+ } else {
209
+ isAdMobPersonalizedAdsAllowed = false;
210
+ isAdMobNonPersonalizedAdsAllowed = false;
211
+ adMobConsentStatus = "Insufficient strict consent (Missing P1, Vendor 755, or P2,7,9,10). Limited Ads only.";
163
212
  }
164
213
  }
165
214
 
166
- tcData.put("isPersonalizedAllowed", isPersonalizedAllowed);
167
- tcData.put("statusMessage", statusMessage);
215
+ tcData.put("isPersonalizedAllowed", isPersonalizedAllowed);
216
+ tcData.put("statusMessage", statusMessage);
217
+
218
+ tcData.put("isAdMobPersonalizedAdsAllowed", isAdMobPersonalizedAdsAllowed);
219
+ tcData.put("isAdMobNonPersonalizedAdsAllowed", isAdMobNonPersonalizedAdsAllowed);
220
+ tcData.put("adMobConsentStatus", adMobConsentStatus);
168
221
 
169
222
  callbackContext.success(tcData);
170
223
  } catch (Exception e) {
@@ -172,6 +225,19 @@ public class ConsentExecutor {
172
225
  }
173
226
  }
174
227
 
228
+ private boolean checkConsent(String consentString, int id) {
229
+ if (consentString == null || consentString.length() < id) {
230
+ return false;
231
+ }
232
+ return consentString.charAt(id - 1) == '1';
233
+ }
234
+
235
+ private boolean hasConsentOrLI(String consents, String lis, int id) {
236
+ boolean hasConsent = checkConsent(consents, id);
237
+ boolean hasLI = checkConsent(lis, id);
238
+ return hasConsent || hasLI;
239
+ }
240
+
175
241
  private String getDeviceId() {
176
242
  try {
177
243
  Context context = cordova.getActivity();
@@ -26,6 +26,8 @@
26
26
  BOOL resetConsent = options[@"reset"] ? [options[@"reset"] boolValue] : NO;
27
27
  BOOL tagForUnderAgeOfConsent = options[@"tagForUnderAgeOfConsent"] ? [options[@"tagForUnderAgeOfConsent"] boolValue] : NO;
28
28
 
29
+ BOOL showFormIfRequired = options[@"showFormIfRequired"] ? [options[@"showFormIfRequired"] boolValue] : YES;
30
+
29
31
  if (resetConsent) {
30
32
  [UMPConsentInformation.sharedInstance reset];
31
33
 
@@ -65,22 +67,24 @@
65
67
 
66
68
  [self.plugin fireEvent:@"document" event:@"on.consent.info.update" withData:nil];
67
69
 
68
- [UMPConsentForm loadAndPresentIfRequiredFromViewController:self.plugin.viewController completionHandler:^(NSError * _Nullable loadAndPresentError) {
69
-
70
- if (loadAndPresentError) {
71
-
72
- [self sendErrorEvent:loadAndPresentError];
73
- if (command) {
74
- CDVPluginResult* errRes = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:loadAndPresentError.localizedDescription];
75
- [self.plugin.commandDelegate sendPluginResult:errRes callbackId:command.callbackId];
70
+ if (showFormIfRequired) {
71
+ [UMPConsentForm loadAndPresentIfRequiredFromViewController:self.plugin.viewController completionHandler:^(NSError * _Nullable loadAndPresentError) {
72
+
73
+ if (loadAndPresentError) {
74
+ [self sendErrorEvent:loadAndPresentError];
75
+ if (command) {
76
+ CDVPluginResult* errRes = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:loadAndPresentError.localizedDescription];
77
+ [self.plugin.commandDelegate sendPluginResult:errRes callbackId:command.callbackId];
78
+ }
79
+ } else {
80
+ [self.plugin fireEvent:@"document" event:@"on.consent.form.dismissed" withData:nil];
81
+ [self sendConsentStatus:command];
76
82
  }
77
- } else {
78
-
79
- [self.plugin fireEvent:@"document" event:@"on.consent.form.dismissed" withData:nil];
83
+ }];
84
+ } else {
80
85
 
81
- [self sendConsentStatus:command];
82
- }
83
- }];
86
+ [self sendConsentStatus:command];
87
+ }
84
88
  }];
85
89
 
86
90
  } @catch (NSException *exception) {
@@ -98,14 +102,21 @@
98
102
  NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
99
103
  NSMutableDictionary *tcData = [NSMutableDictionary dictionary];
100
104
 
101
- NSString *tcString = [prefs stringForKey:@"IABTCF_TCString"] ?: @"";
105
+ NSDictionary *allDefaults = [prefs dictionaryRepresentation];
106
+ for (NSString *key in allDefaults) {
107
+ if ([key hasPrefix:@"IABTCF_"]) {
108
+ tcData[key] = allDefaults[key];
109
+ }
110
+ }
111
+
102
112
  NSString *purposeConsents = [prefs stringForKey:@"IABTCF_PurposeConsents"] ?: @"";
113
+ NSString *purposeLegitimateInterests = [prefs stringForKey:@"IABTCF_PurposeLegitimateInterests"] ?: @"";
103
114
  NSString *vendorConsents = [prefs stringForKey:@"IABTCF_VendorConsents"] ?: @"";
104
115
  NSString *additionalConsent = [prefs stringForKey:@"IABTCF_AddtlConsent"] ?: @"";
105
- NSInteger gdprApplies = [prefs integerForKey:@"IABTCF_gdprApplies"];
116
+ NSInteger gdprApplies = [prefs integerForKey:@"IABTCF_gdprApplies"];
106
117
 
107
- tcData[@"tcString"] = tcString;
108
118
  tcData[@"purposeConsents"] = purposeConsents;
119
+ tcData[@"purposeLegitimateInterests"] = purposeLegitimateInterests;
109
120
  tcData[@"vendorConsents"] = vendorConsents;
110
121
  tcData[@"additionalConsent"] = additionalConsent;
111
122
  tcData[@"gdprApplies"] = @(gdprApplies);
@@ -113,24 +124,55 @@
113
124
  BOOL isPersonalizedAllowed = NO;
114
125
  NSString *statusMessage = @"Unknown";
115
126
 
116
- if (gdprApplies == 0) {
127
+ BOOL isAdMobPersonalizedAdsAllowed = NO;
128
+ BOOL isAdMobNonPersonalizedAdsAllowed = NO;
129
+ NSString *adMobConsentStatus = @"Unknown";
117
130
 
131
+ if (gdprApplies == 0) {
118
132
  isPersonalizedAllowed = YES;
119
133
  statusMessage = @"Not GDPR region. Personalized Ads allowed by default.";
134
+
135
+ isAdMobPersonalizedAdsAllowed = YES;
136
+ isAdMobNonPersonalizedAdsAllowed = YES;
137
+ adMobConsentStatus = @"Not GDPR region. AdMob ads allowed by default.";
120
138
  } else {
121
139
 
122
140
  if (purposeConsents.length > 0) {
123
-
124
141
  if ([purposeConsents hasPrefix:@"1"]) {
125
142
  isPersonalizedAllowed = YES;
126
- statusMessage = @"Purpose 1 Granted. Personalized Ads allowed.";
143
+ statusMessage = @"Purpose 1 Granted. Legacy check passed.";
127
144
  } else {
128
145
  isPersonalizedAllowed = NO;
129
- statusMessage = @"Purpose 1 Denied. Non-Personalized / Limited Ads only.";
146
+ statusMessage = @"Purpose 1 Denied. Legacy check failed.";
147
+ }
148
+ }
149
+
150
+ BOOL hasPurpose1 = [self checkConsent:purposeConsents consentId:1];
151
+ BOOL hasPurpose3 = [self checkConsent:purposeConsents consentId:3];
152
+ BOOL hasPurpose4 = [self checkConsent:purposeConsents consentId:4];
153
+
154
+ BOOL hasRequiredLI_or_Consent =
155
+ [self hasConsentOrLI:purposeConsents lis:purposeLegitimateInterests consentId:2] &&
156
+ [self hasConsentOrLI:purposeConsents lis:purposeLegitimateInterests consentId:7] &&
157
+ [self hasConsentOrLI:purposeConsents lis:purposeLegitimateInterests consentId:9] &&
158
+ [self hasConsentOrLI:purposeConsents lis:purposeLegitimateInterests consentId:10];
159
+
160
+ BOOL hasVendorGoogle = [self checkConsent:vendorConsents consentId:755];
161
+
162
+ if (hasPurpose1 && hasVendorGoogle && hasRequiredLI_or_Consent) {
163
+ isAdMobNonPersonalizedAdsAllowed = YES;
164
+
165
+ if (hasPurpose3 && hasPurpose4) {
166
+ isAdMobPersonalizedAdsAllowed = YES;
167
+ adMobConsentStatus = @"Strict requirements met for Personalized Ads (Purposes 1,3,4 + LI 2,7,9,10 + Vendor 755).";
168
+ } else {
169
+ isAdMobPersonalizedAdsAllowed = NO;
170
+ adMobConsentStatus = @"Requirements met for Non-Personalized Ads only.";
130
171
  }
131
172
  } else {
132
- isPersonalizedAllowed = NO;
133
- statusMessage = @"No consent data found (User hasn't answered yet).";
173
+ isAdMobPersonalizedAdsAllowed = NO;
174
+ isAdMobNonPersonalizedAdsAllowed = NO;
175
+ adMobConsentStatus = @"Insufficient strict consent (Missing P1, Vendor 755, or P2,7,9,10). Limited Ads only.";
134
176
  }
135
177
  }
136
178
 
@@ -143,6 +185,10 @@
143
185
  tcData[@"statusMessage"] = statusMessage;
144
186
  tcData[@"isACVersion2"] = @(isACVersion2);
145
187
 
188
+ tcData[@"isAdMobPersonalizedAdsAllowed"] = @(isAdMobPersonalizedAdsAllowed);
189
+ tcData[@"isAdMobNonPersonalizedAdsAllowed"] = @(isAdMobNonPersonalizedAdsAllowed);
190
+ tcData[@"adMobConsentStatus"] = adMobConsentStatus;
191
+
146
192
  if (command) {
147
193
  CDVPluginResult* res = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:tcData];
148
194
  [self.plugin.commandDelegate sendPluginResult:res callbackId:command.callbackId];
@@ -156,6 +202,20 @@
156
202
  });
157
203
  }
158
204
 
205
+ - (BOOL)checkConsent:(NSString *)consentString consentId:(NSInteger)consentId {
206
+ if (!consentString || consentString.length < consentId) {
207
+ return NO;
208
+ }
209
+ unichar c = [consentString characterAtIndex:(consentId - 1)];
210
+ return c == '1';
211
+ }
212
+
213
+ - (BOOL)hasConsentOrLI:(NSString *)consents lis:(NSString *)lis consentId:(NSInteger)consentId {
214
+ BOOL hasConsent = [self checkConsent:consents consentId:consentId];
215
+ BOOL hasLI = [self checkConsent:lis consentId:consentId];
216
+ return hasConsent || hasLI;
217
+ }
218
+
159
219
  - (void)showPrivacyOptionsForm:(CDVInvokedUrlCommand *)command {
160
220
  dispatch_async(dispatch_get_main_queue(), ^{
161
221
  [UMPConsentForm presentPrivacyOptionsFormFromViewController:self.plugin.viewController completionHandler:^(NSError * _Nullable formError) {
@@ -15,10 +15,12 @@ var AdMobNextGen = {
15
15
  },
16
16
 
17
17
  setRequestConfiguration: function (config, success, error) {
18
+ config = config || {};
18
19
  exec(success, error, 'AdMobNextGen', 'setRequestConfiguration', [config]);
19
20
  },
20
21
 
21
22
  requestConsentInfo: function (options, success, error) {
23
+ options = options || {};
22
24
  exec(success, error, 'AdMobNextGen', 'requestConsentInfo', [options]);
23
25
  },
24
26