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 +83 -16
- package/package.json +1 -1
- package/plugin.xml +2 -2
- package/src/android/ConsentExecutor.java +88 -22
- package/src/ios/ConsentExecutor.m +83 -23
- package/www/admob-nextgen.js +2 -0
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
251
|
-
|
|
249
|
+
### Step 2: Check Consent Status (Smart Analysis)
|
|
250
|
+
- Understanding Consent Flags (`getTCData`)
|
|
252
251
|
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
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.
|
|
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.
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
179
|
+
statusMessage = "Purpose 1 Granted. Legacy check passed.";
|
|
155
180
|
} else {
|
|
156
181
|
isPersonalizedAllowed = false;
|
|
157
|
-
statusMessage = "Purpose 1 Denied.
|
|
182
|
+
statusMessage = "Purpose 1 Denied. Legacy check failed.";
|
|
158
183
|
}
|
|
159
|
-
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
boolean hasPurpose1 = checkConsent(purposeConsents, 1);
|
|
187
|
+
boolean hasPurpose3 = checkConsent(purposeConsents, 3);
|
|
188
|
+
boolean hasPurpose4 = checkConsent(purposeConsents, 4);
|
|
160
189
|
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
}
|
|
78
|
-
|
|
79
|
-
[self.plugin fireEvent:@"document" event:@"on.consent.form.dismissed" withData:nil];
|
|
83
|
+
}];
|
|
84
|
+
} else {
|
|
80
85
|
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
143
|
+
statusMessage = @"Purpose 1 Granted. Legacy check passed.";
|
|
127
144
|
} else {
|
|
128
145
|
isPersonalizedAllowed = NO;
|
|
129
|
-
statusMessage = @"Purpose 1 Denied.
|
|
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
|
-
|
|
133
|
-
|
|
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) {
|
package/www/admob-nextgen.js
CHANGED
|
@@ -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
|
|