react-native-google-mobile-ads 4.2.0 → 5.1.0
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/RNGoogleMobileAds.podspec +16 -9
- package/__tests__/consent.test.ts +17 -9
- package/android/build.gradle +4 -3
- package/android/src/main/java/io/invertase/googlemobileads/ReactNativeGoogleMobileAdsConsentModule.java +111 -168
- package/android/src/main/java/io/invertase/googlemobileads/ReactNativeGoogleMobileAdsModule.java +1 -1
- package/docs/common-reasons-for-ads-not-showing.mdx +26 -24
- package/docs/displaying-ads-hook.mdx +99 -0
- package/docs/displaying-ads.mdx +3 -4
- package/docs/european-user-consent.mdx +67 -156
- package/docs/index.mdx +16 -16
- package/docs/migrating-to-v5.mdx +63 -0
- package/docs.json +4 -2
- package/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.h +0 -1
- package/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.m +83 -124
- package/ios_config.sh +8 -0
- package/lib/commonjs/AdsConsent.js +58 -98
- package/lib/commonjs/AdsConsent.js.map +1 -1
- package/lib/commonjs/AdsConsentPurposes.js +47 -0
- package/lib/commonjs/AdsConsentPurposes.js.map +1 -0
- package/lib/commonjs/AdsConsentSpecialFeatures.js +39 -0
- package/lib/commonjs/AdsConsentSpecialFeatures.js.map +1 -0
- package/lib/commonjs/AdsConsentStatus.js +4 -3
- package/lib/commonjs/AdsConsentStatus.js.map +1 -1
- package/lib/commonjs/hooks/useAppOpenAd.js +51 -0
- package/lib/commonjs/hooks/useAppOpenAd.js.map +1 -0
- package/lib/commonjs/hooks/useFullScreenAd.js +120 -0
- package/lib/commonjs/hooks/useFullScreenAd.js.map +1 -0
- package/lib/commonjs/hooks/useInterstitialAd.js +51 -0
- package/lib/commonjs/hooks/useInterstitialAd.js.map +1 -0
- package/lib/commonjs/hooks/useRewardedAd.js +51 -0
- package/lib/commonjs/hooks/useRewardedAd.js.map +1 -0
- package/lib/commonjs/index.js +40 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types/AdStates.js +6 -0
- package/lib/commonjs/types/AdStates.js.map +1 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/module/AdsConsent.js +56 -98
- package/lib/module/AdsConsent.js.map +1 -1
- package/lib/module/AdsConsentPurposes.js +39 -0
- package/lib/module/AdsConsentPurposes.js.map +1 -0
- package/lib/module/AdsConsentSpecialFeatures.js +31 -0
- package/lib/module/AdsConsentSpecialFeatures.js.map +1 -0
- package/lib/module/AdsConsentStatus.js +4 -3
- package/lib/module/AdsConsentStatus.js.map +1 -1
- package/lib/module/hooks/useAppOpenAd.js +38 -0
- package/lib/module/hooks/useAppOpenAd.js.map +1 -0
- package/lib/module/hooks/useFullScreenAd.js +109 -0
- package/lib/module/hooks/useFullScreenAd.js.map +1 -0
- package/lib/module/hooks/useInterstitialAd.js +38 -0
- package/lib/module/hooks/useInterstitialAd.js.map +1 -0
- package/lib/module/hooks/useRewardedAd.js +38 -0
- package/lib/module/hooks/useRewardedAd.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/types/AdStates.js +2 -0
- package/lib/module/types/AdStates.js.map +1 -0
- package/lib/module/version.js +1 -1
- package/lib/module/version.js.map +1 -1
- package/lib/typescript/AdsConsentPurposes.d.ts +148 -0
- package/lib/typescript/AdsConsentSpecialFeatures.d.ts +22 -0
- package/lib/typescript/AdsConsentStatus.d.ts +10 -6
- package/lib/typescript/hooks/useAppOpenAd.d.ts +9 -0
- package/lib/typescript/hooks/useFullScreenAd.d.ts +5 -0
- package/lib/typescript/hooks/useInterstitialAd.d.ts +9 -0
- package/lib/typescript/hooks/useRewardedAd.d.ts +9 -0
- package/lib/typescript/index.d.ts +6 -1
- package/lib/typescript/types/AdStates.d.ts +85 -0
- package/lib/typescript/types/AdsConsent.interface.d.ts +229 -180
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +10 -4
- package/src/AdsConsent.ts +91 -135
- package/src/AdsConsentPurposes.ts +182 -0
- package/src/AdsConsentSpecialFeatures.ts +48 -0
- package/src/AdsConsentStatus.ts +11 -6
- package/src/hooks/useAppOpenAd.ts +46 -0
- package/src/hooks/useFullScreenAd.ts +104 -0
- package/src/hooks/useInterstitialAd.ts +46 -0
- package/src/hooks/useRewardedAd.ts +46 -0
- package/src/index.ts +5 -0
- package/src/types/AdStates.ts +87 -0
- package/src/types/AdsConsent.interface.ts +230 -187
- package/src/version.ts +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
require 'json'
|
|
2
2
|
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
google_mobile_ads_sdk_version = package['sdkVersions']['ios']['googleMobileAds']
|
|
5
|
+
google_ump_sdk_version = package['sdkVersions']['ios']['googleUmp']
|
|
5
6
|
|
|
6
7
|
Pod::Spec.new do |s|
|
|
7
8
|
s.name = "RNGoogleMobileAds"
|
|
@@ -18,24 +19,30 @@ Pod::Spec.new do |s|
|
|
|
18
19
|
s.social_media_url = 'http://twitter.com/invertaseio'
|
|
19
20
|
s.ios.deployment_target = "10.0"
|
|
20
21
|
s.source_files = 'ios/**/*.{h,m}'
|
|
22
|
+
s.frameworks = "AppTrackingTransparency"
|
|
21
23
|
|
|
22
24
|
# React Native dependencies
|
|
23
25
|
s.dependency 'React-Core'
|
|
24
26
|
|
|
25
27
|
# Other dependencies
|
|
26
|
-
|
|
28
|
+
if defined?($RNGoogleUmpSDKVersion)
|
|
29
|
+
Pod::UI.puts "#{s.name}: Using user specified Google UMP SDK version '#{$RNGoogleUmpSDKVersion}'"
|
|
30
|
+
google_ump_sdk_version = $RNGoogleUmpSDKVersion
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
s.dependency 'GoogleUserMessagingPlatform', google_ump_sdk_version
|
|
27
34
|
|
|
28
|
-
if defined?($
|
|
29
|
-
Pod::UI.puts "#{s.name}: Using user specified Google Mobile-Ads SDK version '#{$
|
|
30
|
-
|
|
35
|
+
if defined?($RNGoogleMobileAdsSDKVersion)
|
|
36
|
+
Pod::UI.puts "#{s.name}: Using user specified Google Mobile-Ads SDK version '#{$RNGoogleMobileAdsSDKVersion}'"
|
|
37
|
+
google_mobile_ads_sdk_version = $RNGoogleMobileAdsSDKVersion
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
# AdMob dependencies
|
|
34
|
-
s.dependency 'Google-Mobile-Ads-SDK',
|
|
41
|
+
s.dependency 'Google-Mobile-Ads-SDK', google_mobile_ads_sdk_version
|
|
35
42
|
|
|
36
|
-
if defined?($
|
|
37
|
-
Pod::UI.puts "#{s.name}: Using overridden static_framework value of '#{$
|
|
38
|
-
s.static_framework = $
|
|
43
|
+
if defined?($RNGoogleMobileAdsAsStaticFramework)
|
|
44
|
+
Pod::UI.puts "#{s.name}: Using overridden static_framework value of '#{$RNGoogleMobileAdsAsStaticFramework}'"
|
|
45
|
+
s.static_framework = $RNGoogleMobileAdsAsStaticFramework
|
|
39
46
|
else
|
|
40
47
|
s.static_framework = false
|
|
41
48
|
end
|
|
@@ -2,23 +2,31 @@ import { AdsConsent } from '../src';
|
|
|
2
2
|
|
|
3
3
|
describe('Google Mobile Ads AdsConsent', function () {
|
|
4
4
|
describe('requestInfoUpdate', function () {
|
|
5
|
-
it('throws if
|
|
5
|
+
it('throws if options are not an object', function () {
|
|
6
6
|
// @ts-ignore
|
|
7
|
-
expect(() => AdsConsent.requestInfoUpdate('
|
|
8
|
-
"AdsConsent.requestInfoUpdate(*) '
|
|
7
|
+
expect(() => AdsConsent.requestInfoUpdate('123')).toThrowError(
|
|
8
|
+
"AdsConsent.requestInfoUpdate(*) 'options' expected an object value.",
|
|
9
9
|
);
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
it('throws if
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
it('throws if options.debugGeography is not a valid value.', function () {
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
expect(() => AdsConsent.requestInfoUpdate({ debugGeography: -1 })).toThrowError(
|
|
15
|
+
"AdsConsent.requestInfoUpdate(*) 'options.debugGeography' expected one of AdsConsentDebugGeography.DISABLED, AdsConsentDebugGeography.EEA or AdsConsentDebugGeography.NOT_EEA.",
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('throws if options.tagForUnderAgeOfConsent is not a boolean.', function () {
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
expect(() => AdsConsent.requestInfoUpdate({ tagForUnderAgeOfConsent: '123' })).toThrowError(
|
|
22
|
+
"AdsConsent.requestInfoUpdate(*) 'options.tagForUnderAgeOfConsent' expected a boolean value.",
|
|
15
23
|
);
|
|
16
24
|
});
|
|
17
25
|
|
|
18
|
-
it('throws if
|
|
26
|
+
it('throws if options.testDeviceIdentifiers is not an array', function () {
|
|
19
27
|
// @ts-ignore
|
|
20
|
-
expect(() => AdsConsent.requestInfoUpdate(
|
|
21
|
-
"AdsConsent.requestInfoUpdate(*) '
|
|
28
|
+
expect(() => AdsConsent.requestInfoUpdate({ testDeviceIdentifiers: '123' })).toThrowError(
|
|
29
|
+
"AdsConsent.requestInfoUpdate(*) 'options.testDeviceIdentifiers' expected an array of string values.",
|
|
22
30
|
);
|
|
23
31
|
});
|
|
24
32
|
});
|
package/android/build.gradle
CHANGED
|
@@ -22,6 +22,7 @@ plugins {
|
|
|
22
22
|
|
|
23
23
|
def packageJson = PackageJson.getForProject(project)
|
|
24
24
|
def googleMobileAdsVersion = packageJson['sdkVersions']['android']['googleMobileAds']
|
|
25
|
+
def googleUmpVersion = packageJson['sdkVersions']['android']['googleUmp']
|
|
25
26
|
def jsonMinSdk = packageJson['sdkVersions']['android']['minSdk']
|
|
26
27
|
def jsonTargetSdk = packageJson['sdkVersions']['android']['targetSdk']
|
|
27
28
|
def jsonCompileSdk = packageJson['sdkVersions']['android']['compileSdk']
|
|
@@ -44,7 +45,7 @@ project.ext {
|
|
|
44
45
|
],
|
|
45
46
|
|
|
46
47
|
ads : [
|
|
47
|
-
consent:
|
|
48
|
+
consent: googleUmpVersion,
|
|
48
49
|
],
|
|
49
50
|
],
|
|
50
51
|
])
|
|
@@ -88,8 +89,8 @@ repositories {
|
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
dependencies {
|
|
91
|
-
implementation("com.google.android.gms:play-services-ads
|
|
92
|
-
|
|
92
|
+
implementation("com.google.android.gms:play-services-ads:${googleMobileAdsVersion}") { force = true; }
|
|
93
|
+
api "com.google.android.ump:user-messaging-platform:${googleUmpVersion}"
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
ReactNative.shared.applyPackageVersion()
|
|
@@ -16,211 +16,154 @@ package io.invertase.googlemobileads;
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
import android.content.SharedPreferences;
|
|
20
|
+
import android.preference.PreferenceManager;
|
|
20
21
|
import com.facebook.react.bridge.Arguments;
|
|
21
22
|
import com.facebook.react.bridge.Promise;
|
|
22
23
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
23
24
|
import com.facebook.react.bridge.ReactMethod;
|
|
24
25
|
import com.facebook.react.bridge.ReadableArray;
|
|
25
26
|
import com.facebook.react.bridge.ReadableMap;
|
|
26
|
-
import com.facebook.react.bridge.WritableArray;
|
|
27
27
|
import com.facebook.react.bridge.WritableMap;
|
|
28
|
-
import com.google.
|
|
29
|
-
import com.google.
|
|
30
|
-
import com.google.
|
|
31
|
-
import com.google.
|
|
32
|
-
import com.google.ads.consent.ConsentInformation;
|
|
33
|
-
import com.google.ads.consent.ConsentStatus;
|
|
34
|
-
import com.google.ads.consent.DebugGeography;
|
|
28
|
+
import com.google.android.ump.ConsentDebugSettings;
|
|
29
|
+
import com.google.android.ump.ConsentInformation;
|
|
30
|
+
import com.google.android.ump.ConsentRequestParameters;
|
|
31
|
+
import com.google.android.ump.UserMessagingPlatform;
|
|
35
32
|
import io.invertase.googlemobileads.common.ReactNativeModule;
|
|
36
|
-
import
|
|
37
|
-
import java.net.URL;
|
|
38
|
-
import java.util.List;
|
|
33
|
+
import javax.annotation.Nonnull;
|
|
39
34
|
|
|
40
35
|
public class ReactNativeGoogleMobileAdsConsentModule extends ReactNativeModule {
|
|
36
|
+
|
|
41
37
|
private static final String TAG = "RNGoogleMobileAdsConsentModule";
|
|
42
38
|
private ConsentInformation consentInformation;
|
|
43
|
-
private ConsentForm consentForm;
|
|
44
39
|
|
|
45
40
|
public ReactNativeGoogleMobileAdsConsentModule(ReactApplicationContext reactContext) {
|
|
46
41
|
super(reactContext, TAG);
|
|
47
|
-
consentInformation =
|
|
42
|
+
consentInformation = UserMessagingPlatform.getConsentInformation(reactContext);
|
|
48
43
|
}
|
|
49
44
|
|
|
50
|
-
private
|
|
45
|
+
private String getConsentStatusString(int consentStatus) {
|
|
51
46
|
switch (consentStatus) {
|
|
52
|
-
case
|
|
53
|
-
return
|
|
54
|
-
case
|
|
55
|
-
return
|
|
56
|
-
case
|
|
47
|
+
case ConsentInformation.ConsentStatus.REQUIRED:
|
|
48
|
+
return "REQUIRED";
|
|
49
|
+
case ConsentInformation.ConsentStatus.NOT_REQUIRED:
|
|
50
|
+
return "NOT_REQUIRED";
|
|
51
|
+
case ConsentInformation.ConsentStatus.OBTAINED:
|
|
52
|
+
return "OBTAINED";
|
|
53
|
+
case ConsentInformation.ConsentStatus.UNKNOWN:
|
|
57
54
|
default:
|
|
58
|
-
return
|
|
55
|
+
return "UNKNOWN";
|
|
59
56
|
}
|
|
60
57
|
}
|
|
61
58
|
|
|
62
59
|
@ReactMethod
|
|
63
|
-
public void requestInfoUpdate(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
public void requestInfoUpdate(@Nonnull final ReadableMap options, final Promise promise) {
|
|
61
|
+
try {
|
|
62
|
+
ConsentRequestParameters.Builder paramsBuilder = new ConsentRequestParameters.Builder();
|
|
63
|
+
ConsentDebugSettings.Builder debugSettingsBuilder =
|
|
64
|
+
new ConsentDebugSettings.Builder(getApplicationContext());
|
|
65
|
+
|
|
66
|
+
if (options.hasKey("testDeviceIdentifiers")) {
|
|
67
|
+
ReadableArray devices = options.getArray("testDeviceIdentifiers");
|
|
68
|
+
|
|
69
|
+
for (int i = 0; i < devices.size(); i++) {
|
|
70
|
+
debugSettingsBuilder.addTestDeviceHashedId(devices.getString(i));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (options.hasKey("debugGeography")) {
|
|
75
|
+
debugSettingsBuilder.setDebugGeography(options.getInt("debugGeography"));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
paramsBuilder.setConsentDebugSettings(debugSettingsBuilder.build());
|
|
79
|
+
|
|
80
|
+
if (options.hasKey("tagForUnderAgeOfConsent")) {
|
|
81
|
+
paramsBuilder.setTagForUnderAgeOfConsent(options.getBoolean("tagForUnderAgeOfConsent"));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
ConsentRequestParameters consentRequestParameters = paramsBuilder.build();
|
|
85
|
+
|
|
86
|
+
if (getCurrentActivity() == null) {
|
|
87
|
+
rejectPromiseWithCodeAndMessage(
|
|
88
|
+
promise,
|
|
89
|
+
"null-activity",
|
|
90
|
+
"Attempted to request a consent info update but the current Activity was null.");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
consentInformation.requestConsentInfoUpdate(
|
|
95
|
+
getCurrentActivity(),
|
|
96
|
+
consentRequestParameters,
|
|
97
|
+
() -> {
|
|
72
98
|
WritableMap requestInfoMap = Arguments.createMap();
|
|
73
|
-
requestInfoMap.
|
|
99
|
+
requestInfoMap.putString(
|
|
100
|
+
"status", getConsentStatusString(consentInformation.getConsentStatus()));
|
|
74
101
|
requestInfoMap.putBoolean(
|
|
75
|
-
"
|
|
76
|
-
consentInformation.isRequestLocationInEeaOrUnknown());
|
|
102
|
+
"isConsentFormAvailable", consentInformation.isConsentFormAvailable());
|
|
77
103
|
promise.resolve(requestInfoMap);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
@ReactMethod
|
|
88
|
-
public void showForm(ReadableMap options, Promise promise) {
|
|
89
|
-
if (getCurrentActivity() == null) {
|
|
90
|
-
rejectPromiseWithCodeAndMessage(
|
|
91
|
-
promise,
|
|
92
|
-
"null-activity",
|
|
93
|
-
"Consent form attempted to show but the current Activity was null.");
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
getCurrentActivity()
|
|
97
|
-
.runOnUiThread(
|
|
98
|
-
() -> {
|
|
99
|
-
URL privacyUrl = null;
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
privacyUrl = new URL(options.getString("privacyPolicy"));
|
|
103
|
-
} catch (MalformedURLException e) {
|
|
104
|
-
// Validated in JS land
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
ConsentFormListener listener =
|
|
108
|
-
new ConsentFormListener() {
|
|
109
|
-
@Override
|
|
110
|
-
public void onConsentFormLoaded() {
|
|
111
|
-
try {
|
|
112
|
-
consentForm.show();
|
|
113
|
-
} catch (Exception e) {
|
|
114
|
-
rejectPromiseWithCodeAndMessage(
|
|
115
|
-
promise, "consent-form-error", e.toString());
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
@Override
|
|
120
|
-
public void onConsentFormClosed(
|
|
121
|
-
ConsentStatus consentStatus, Boolean userPrefersAdFree) {
|
|
122
|
-
WritableMap consentFormMap = Arguments.createMap();
|
|
123
|
-
consentFormMap.putInt("status", getConsentStatusInt(consentStatus));
|
|
124
|
-
consentFormMap.putBoolean("userPrefersAdFree", userPrefersAdFree);
|
|
125
|
-
promise.resolve(consentFormMap);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
@Override
|
|
129
|
-
public void onConsentFormError(String reason) {
|
|
130
|
-
rejectPromiseWithCodeAndMessage(promise, "consent-form-error", reason);
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
ConsentForm.Builder builder =
|
|
135
|
-
new ConsentForm.Builder(getCurrentActivity(), privacyUrl).withListener(listener);
|
|
136
|
-
|
|
137
|
-
if (options.hasKey("withPersonalizedAds")
|
|
138
|
-
&& options.getBoolean("withPersonalizedAds")) {
|
|
139
|
-
builder = builder.withPersonalizedAdsOption();
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (options.hasKey("withNonPersonalizedAds")
|
|
143
|
-
&& options.getBoolean("withNonPersonalizedAds")) {
|
|
144
|
-
builder = builder.withNonPersonalizedAdsOption();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (options.hasKey("withAdFree") && options.getBoolean("withAdFree")) {
|
|
148
|
-
builder = builder.withAdFreeOption();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
consentForm = builder.build();
|
|
152
|
-
consentForm.load();
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
@ReactMethod
|
|
157
|
-
public void getStatus(Promise promise) {
|
|
158
|
-
ConsentStatus status = consentInformation.getConsentStatus();
|
|
159
|
-
promise.resolve(getConsentStatusInt(status));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
@ReactMethod
|
|
163
|
-
public void setStatus(int status, Promise promise) {
|
|
164
|
-
ConsentStatus consentStatus = ConsentStatus.UNKNOWN;
|
|
165
|
-
|
|
166
|
-
switch (status) {
|
|
167
|
-
case 0:
|
|
168
|
-
consentStatus = ConsentStatus.UNKNOWN;
|
|
169
|
-
break;
|
|
170
|
-
case 1:
|
|
171
|
-
consentStatus = ConsentStatus.NON_PERSONALIZED;
|
|
172
|
-
break;
|
|
173
|
-
case 2:
|
|
174
|
-
consentStatus = ConsentStatus.PERSONALIZED;
|
|
175
|
-
break;
|
|
104
|
+
},
|
|
105
|
+
formError ->
|
|
106
|
+
rejectPromiseWithCodeAndMessage(
|
|
107
|
+
promise, "consent-update-failed", formError.getMessage()));
|
|
108
|
+
} catch (Exception e) {
|
|
109
|
+
rejectPromiseWithCodeAndMessage(promise, "consent-update-failed", e.toString());
|
|
176
110
|
}
|
|
177
|
-
|
|
178
|
-
consentInformation.setConsentStatus(consentStatus);
|
|
179
|
-
promise.resolve(null);
|
|
180
111
|
}
|
|
181
112
|
|
|
182
113
|
@ReactMethod
|
|
183
|
-
public void
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
114
|
+
public void showForm(final Promise promise) {
|
|
115
|
+
try {
|
|
116
|
+
if (getCurrentActivity() == null) {
|
|
117
|
+
rejectPromiseWithCodeAndMessage(
|
|
118
|
+
promise,
|
|
119
|
+
"null-activity",
|
|
120
|
+
"Consent form attempted to show but the current Activity was null.");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
getCurrentActivity()
|
|
124
|
+
.runOnUiThread(
|
|
125
|
+
() ->
|
|
126
|
+
UserMessagingPlatform.loadConsentForm(
|
|
127
|
+
getReactApplicationContext(),
|
|
128
|
+
consentForm ->
|
|
129
|
+
consentForm.show(
|
|
130
|
+
getCurrentActivity(),
|
|
131
|
+
formError -> {
|
|
132
|
+
if (formError != null) {
|
|
133
|
+
rejectPromiseWithCodeAndMessage(
|
|
134
|
+
promise, "consent-form-error", formError.getMessage());
|
|
135
|
+
} else {
|
|
136
|
+
WritableMap consentFormMap = Arguments.createMap();
|
|
137
|
+
consentFormMap.putString(
|
|
138
|
+
"status",
|
|
139
|
+
getConsentStatusString(
|
|
140
|
+
consentInformation.getConsentStatus()));
|
|
141
|
+
promise.resolve(consentFormMap);
|
|
142
|
+
}
|
|
143
|
+
}),
|
|
144
|
+
formError ->
|
|
145
|
+
rejectPromiseWithCodeAndMessage(
|
|
146
|
+
promise, "consent-form-error", formError.getMessage())));
|
|
147
|
+
} catch (Exception e) {
|
|
148
|
+
rejectPromiseWithCodeAndMessage(promise, "consent-form-error", e.toString());
|
|
194
149
|
}
|
|
195
|
-
|
|
196
|
-
promise.resolve(formattedAdProviders);
|
|
197
150
|
}
|
|
198
151
|
|
|
199
152
|
@ReactMethod
|
|
200
|
-
public void
|
|
201
|
-
consentInformation.
|
|
202
|
-
promise.resolve(null);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
@ReactMethod
|
|
206
|
-
public void setDebugGeography(int geography, Promise promise) {
|
|
207
|
-
if (geography == 0) {
|
|
208
|
-
consentInformation.setDebugGeography(DebugGeography.DEBUG_GEOGRAPHY_DISABLED);
|
|
209
|
-
} else if (geography == 1) {
|
|
210
|
-
consentInformation.setDebugGeography(DebugGeography.DEBUG_GEOGRAPHY_EEA);
|
|
211
|
-
} else if (geography == 2) {
|
|
212
|
-
consentInformation.setDebugGeography(DebugGeography.DEBUG_GEOGRAPHY_NOT_EEA);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
promise.resolve(null);
|
|
153
|
+
public void reset() {
|
|
154
|
+
consentInformation.reset();
|
|
216
155
|
}
|
|
217
156
|
|
|
218
157
|
@ReactMethod
|
|
219
|
-
public void
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
158
|
+
public void getTCString(Promise promise) {
|
|
159
|
+
try {
|
|
160
|
+
SharedPreferences prefs =
|
|
161
|
+
PreferenceManager.getDefaultSharedPreferences(getReactApplicationContext());
|
|
162
|
+
// https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details
|
|
163
|
+
String tcString = prefs.getString("IABTCF_TCString", null);
|
|
164
|
+
promise.resolve(tcString);
|
|
165
|
+
} catch (Exception e) {
|
|
166
|
+
rejectPromiseWithCodeAndMessage(promise, "consent-string-error", e.toString());
|
|
223
167
|
}
|
|
224
|
-
promise.resolve(null);
|
|
225
168
|
}
|
|
226
169
|
}
|
package/android/src/main/java/io/invertase/googlemobileads/ReactNativeGoogleMobileAdsModule.java
CHANGED
|
@@ -117,7 +117,7 @@ public class ReactNativeGoogleMobileAdsModule extends ReactNativeModule {
|
|
|
117
117
|
@ReactMethod
|
|
118
118
|
public void initialize(Promise promise) {
|
|
119
119
|
MobileAds.initialize(
|
|
120
|
-
|
|
120
|
+
getApplicationContext(),
|
|
121
121
|
new OnInitializationCompleteListener() {
|
|
122
122
|
@Override
|
|
123
123
|
public void onInitializationComplete(InitializationStatus initializationStatus) {
|
|
@@ -13,6 +13,7 @@ You'll be notified of approval or rejection via email after review.
|
|
|
13
13
|
|
|
14
14
|
New apps and ad units take some time to activate.
|
|
15
15
|
Here are common reasons you may not see live impressions immediately:
|
|
16
|
+
|
|
16
17
|
- It usually takes at least an hour after you create an app or ad unit
|
|
17
18
|
- Sometimes it can take a few days for ads to appear in new apps or ad units
|
|
18
19
|
- New iOS apps will not show Google ads until they’re listed in the Apple App Store
|
|
@@ -35,6 +36,7 @@ Test devices can either be added in the AdMob UI or programmatically using the G
|
|
|
35
36
|
Follow the steps below to add your device as a test device.
|
|
36
37
|
|
|
37
38
|
#### Add your test device in the AdMob UI
|
|
39
|
+
|
|
38
40
|
For a simple, non-programmatic way to add a test device and test new or existing app builds, use the AdMob UI.
|
|
39
41
|
[Learn how](https://support.google.com/admob/answer/9691433).
|
|
40
42
|
|
|
@@ -48,39 +50,39 @@ If you want to test ads in your app as you're developing, follow the steps below
|
|
|
48
50
|
|
|
49
51
|
3. Check the console for a message that looks like this:
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
```
|
|
54
|
+
GADMobileAds.sharedInstance.requestConfiguration.testDeviceIdentifiers =
|
|
55
|
+
@[ @"2077ef9a63d2b398840261c8221a0c9b" ]; // Sample device ID
|
|
56
|
+
```
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
Copy your test device ID to your clipboard.
|
|
57
59
|
|
|
58
60
|
4. Check the logcat output for a message that looks like the one below, which shows you your device ID and how to add it as a test device:
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
```
|
|
63
|
+
I/Ads: Use RequestConfiguration.Builder.setTestDeviceIds(Arrays.asList("33BE2250B43518CCDA7DE426D04EE231"))
|
|
64
|
+
to get test ads on this device."
|
|
65
|
+
```
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
Copy your test device ID to your clipboard.
|
|
66
68
|
|
|
67
69
|
5. Modify your code to set the test device ID through testDeviceIdentifiers
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
```js
|
|
72
|
+
import mobileAds from 'react-native-google-mobile-ads';
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
mobileAds()
|
|
75
|
+
.setRequestConfiguration({
|
|
76
|
+
// An array of test device IDs to add to the allow list.
|
|
77
|
+
testDeviceIdentifiers: ['2077ef9a63d2b398840261c8221a0c9b', 'EMULATOR'],
|
|
78
|
+
})
|
|
79
|
+
.then(() => {
|
|
80
|
+
// Request config successfully set!
|
|
81
|
+
});
|
|
82
|
+
```
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
Ads with this Test Ad label are safe to click. Requests, impressions, and clicks on test ads will not show up in your account's reports.
|
|
84
|
+
6. Re-run your app. If the ad is a Google ad, you'll see a Test Ad label centered at the top of the ad.
|
|
85
|
+
Ads with this Test Ad label are safe to click. Requests, impressions, and clicks on test ads will not show up in your account's reports.
|
|
84
86
|
|
|
85
87
|
## Emulator vs real device
|
|
86
88
|
|
|
@@ -89,4 +91,4 @@ In some cases ads won't show up on an emulator but will show up while testing on
|
|
|
89
91
|
## Extra links
|
|
90
92
|
|
|
91
93
|
- Mobile Ads SDK [iOS](https://developers.google.com/admob/ios/quick-start), [Android](https://developers.google.com/admob/android/quick-start)
|
|
92
|
-
- [Common reasons for ads not showing](https://support.google.com/admob/answer/9469204)
|
|
94
|
+
- [Common reasons for ads not showing](https://support.google.com/admob/answer/9469204)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Hooks
|
|
2
|
+
|
|
3
|
+
The AdMob package provides hooks to help you to display ads in a functional component with tiny code. The supported ad formats are full-screen ads: App open, Interstitial & Rewarded.
|
|
4
|
+
|
|
5
|
+
## Load an ad
|
|
6
|
+
|
|
7
|
+
You can create a new ad by adding a corresponding ad type's hook to your component.
|
|
8
|
+
|
|
9
|
+
The first argument of the hook is the "Ad Unit ID".
|
|
10
|
+
For testing, we can use a Test ID, however for production the ID from the
|
|
11
|
+
Google AdMob dashboard under "Ad units" should be used:
|
|
12
|
+
|
|
13
|
+
```tsx {4-8}
|
|
14
|
+
import { useInterstitialAd, TestIds } from 'react-native-google-mobile-ads';
|
|
15
|
+
|
|
16
|
+
export default function App() {
|
|
17
|
+
const interstitialAd = useInterstitialAd(TestIds.Interstitial, {
|
|
18
|
+
requestNonPersonalizedAdsOnly: true,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return <View>{/* ... */}</View>;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
> The `adUnitid` parameter can also be used to manage creation and destruction of an ad instance.
|
|
26
|
+
> If `adUnitid` is set or changed, new ad instance will be created and previous ad instance will be destroyed if exists.
|
|
27
|
+
> If `adUnitid` is set to `null`, no ad instance will be created and previous ad instance will be destroyed if exists.
|
|
28
|
+
|
|
29
|
+
The second argument is an additional optional request options object to be sent whilst loading an advert, such as keywords & location.
|
|
30
|
+
Setting additional request options helps AdMob choose better tailored ads from the network. View the [`RequestOptions`](/reference/admob/requestoptions)
|
|
31
|
+
documentation to view the full range of options available.
|
|
32
|
+
|
|
33
|
+
## Show the ad
|
|
34
|
+
|
|
35
|
+
The hook returns several states and functions to control ad.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useInterstitialAd, TestIds } from 'react-native-google-mobile-ads';
|
|
39
|
+
|
|
40
|
+
export default function App({ navigation }) {
|
|
41
|
+
const { isLoaded, isClosed, load, show } = useInterstitialAd(TestIds.Interstitial, {
|
|
42
|
+
requestNonPersonalizedAdsOnly: true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
// Start loading the interstitial straight away
|
|
47
|
+
load();
|
|
48
|
+
}, [load]);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (isClosed) {
|
|
52
|
+
// Action after the ad is closed
|
|
53
|
+
navigation.navigate('NextScreen');
|
|
54
|
+
}
|
|
55
|
+
}, [isClosed, navigation]);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<View>
|
|
59
|
+
<Button
|
|
60
|
+
title="Navigate to next screen"
|
|
61
|
+
onPress={() => {
|
|
62
|
+
if (isLoaded) {
|
|
63
|
+
show();
|
|
64
|
+
} else {
|
|
65
|
+
// No advert ready to show yet
|
|
66
|
+
navigation.navigate('NextScreen');
|
|
67
|
+
}
|
|
68
|
+
}}
|
|
69
|
+
/>
|
|
70
|
+
</View>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The code above immediately starts to load a new advert from the network (via `load()`).
|
|
76
|
+
When user presses button, it checks if the ad is loaded via `isLoaded` value,
|
|
77
|
+
then the `show` function is called and the advert is shown over-the-top of your application.
|
|
78
|
+
Otherwise, if the ad is not loaded, the `navigation.navigate` method is called to navigate to the next screen without showing the ad.
|
|
79
|
+
After the ad is closed, the `isClosed` value is set to `true` and the `navigation.navigate` method is called to navigate to the next screen.
|
|
80
|
+
|
|
81
|
+
If needed, you can reuse the existing hook to load more adverts and show them when required. The states are initialized when the `load` function is called.
|
|
82
|
+
|
|
83
|
+
Return values of the hook are:
|
|
84
|
+
|
|
85
|
+
| Name | Type | Description |
|
|
86
|
+
| :------------- | :--------------------------------- | :----------------------------------------------------------------------------------------------------------------- |
|
|
87
|
+
| isLoaded | boolean | Whether the ad is loaded and ready to to be shown to the user. |
|
|
88
|
+
| isOpened | boolean | Whether the ad is opened. The value is remained `true` even after the ad is closed unless **new ad is requested**. |
|
|
89
|
+
| isClosed | boolean | Whether your ad is dismissed. |
|
|
90
|
+
| isShowing | boolean | Whether your ad is showing. The value is equal with `isOpened && !isClosed`. |
|
|
91
|
+
| error | Error \| undefined | `Error` object throwed during ad load. |
|
|
92
|
+
| reward | [RewardedAdReward](#) \| undefined | Loaded reward item of the Rewarded Ad. Available only in RewardedAd. |
|
|
93
|
+
| isEarnedReward | boolean | Whether the user earned the reward by Rewarded Ad. |
|
|
94
|
+
| load | Function | Start loading the advert with the provided RequestOptions. |
|
|
95
|
+
| show | Function | Show the loaded advert to the user. |
|
|
96
|
+
|
|
97
|
+
> Note that `isOpened` value remains `true` even after the ad is closed.
|
|
98
|
+
> The value changes to `false` when ad is initialized via calling `load()`.
|
|
99
|
+
> To determine whether the ad is currently showing, use `isShowing` value.
|
package/docs/displaying-ads.mdx
CHANGED
|
@@ -27,11 +27,10 @@ const appOpenAd = AppOpenAd.createForAdRequest(adUnitId, {
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
// Preload an app open ad
|
|
30
|
-
appOpenAd.load()
|
|
30
|
+
appOpenAd.load();
|
|
31
31
|
|
|
32
32
|
// Show the app open ad when user brings the app to the foreground.
|
|
33
|
-
appOpenAd.show()
|
|
34
|
-
|
|
33
|
+
appOpenAd.show();
|
|
35
34
|
```
|
|
36
35
|
|
|
37
36
|
### Consider ad expiration
|
|
@@ -159,7 +158,7 @@ The purpose of a rewarded ad is to reward users with _something_ after completin
|
|
|
159
158
|
as watching a video or submitting an option via an interactive form. If the user completes the action, you can reward them
|
|
160
159
|
with something (e.g. in-game currency).
|
|
161
160
|
|
|
162
|
-
To create a new
|
|
161
|
+
To create a new rewarded ad, call the `createForAdRequest` method from the `RewardedAd` class. The first argument
|
|
163
162
|
of the method is the "Ad Unit ID". For testing, we can use a Test ID, however for production the ID from the
|
|
164
163
|
Google AdMob dashboard under "Ad units" should be used:
|
|
165
164
|
|