diff --git a/README.md b/README.md index 409fed20..13be0335 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,66 @@ return (

📃 Reference

+## AdManager +AdManager allows you to configure your ads globally when the app starts + +### setRequestConfiguration(config) +Configure your Ad Requests during App Startup. You need to pass a single object as an argument with atleast one of the following properties + +| Name | Type | Required | +| --------- | -------- | -------- | +| testDeviceIds | `Array` | no | +| maxAdContentRating | AdManager.MAX_AD_CONTENT_RATING | no | +| tagForChildDirectedTreatment | AdManager.TAG_FOR_CHILD_DIRECTED_TREATMENT | no | +| tagForUnderAgeConsent | AdManager.TAG_FOR_UNDER_AGE_CONSENT | no | + +```js + +const config = { + testDeviceIds:["YOUR_TEST_DEVICE_ID"], + maxAdContetRating:AdManager.MAX_AD_CONTENT_RATING.MA, + tagForChildDirectedTreatment:AdManager.TAG_FOR_CHILD_DIRECTED_TREATMENT.FALSE, + tagForUnderAgeConsent:AdManager.TAG_FOR_UNDER_AGE_CONSENT.FALSE +} + +AdManager.setRequestConfiguration(config); + +``` + +### isTestDevice() +Check if the current device is registered as a test device to show test ads. + +```js + AdManager.isTestDevice().then(result => console.log(result)) +``` +return: `boolean` + +### AdManager.MAX_AD_CONTENT_RATING + +| Name | Description | +| --------- | -------- | +| G | "General audiences." Content suitable for all audiences, including families and children. | +| MA | "Mature audiences." Content suitable only for mature audiences; includes topics such as alcohol, gambling, sexual content, and weapons. | +| PG | "Parental guidance." Content suitable for most audiences with parental guidance, including topics like non-realistic, cartoonish violence. | +| T | "Teen." Content suitable for teen and older audiences, including topics such as general health, social networks, scary imagery, and fight sports. | +| UNSPECIFIED | Set default value to ""| + +### AdManager.TAG_FOR_CHILD_DIRECTED_TREATMENT + +| Name | Description | +| --------- | -------- | +| TRUE | Enabled | +| FALSE | Disabled | + +### AdManager.TAG_FOR_UNDER_AGE_CONSENT + +| Name | Description | +| --------- | -------- | +| TRUE | Enabled | +| FALSE | Disabled | + +# + ## NativeAdView NativeAdView will wrap all your views related to the ad and provides a context through which all the Views get their respective information and load it automatically. It has the following properties to it. @@ -238,7 +298,7 @@ Set Ad Unit ID for Native Advanced Ads that you created on your AdMob account. | -------- | -------- | -------- | | `string` | Yes | All | -# +# #### `testDevices` @@ -248,7 +308,7 @@ Set testDevices during testing ads or during development. | --------------- | -------- | -------- | | `Array` | no | All | -# +# #### `enableTestMode` @@ -258,7 +318,7 @@ Setting this to true will load a placeholder ad (Not from Admob server) incase y | --------- | -------- | -------- | | `boolean` | no | All | -# +# #### `delayAdloading` @@ -269,7 +329,7 @@ Delay ad loading and rendering by the specified time in milliseconds. This is a | `number` | no | 0 ms | All | -# +# #### `refreshInterval` @@ -279,7 +339,7 @@ Time in ms after which a new ad should be requested from the server. | -------- | -------- | ------------------- | -------- | | `number` | no | 60000 ms (1 minute) | All | -# +# ### Events diff --git a/android/build.gradle b/android/build.gradle index c6593848..4ed492ed 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -22,7 +22,8 @@ android { dependencies { implementation 'com.facebook.react:react-native:+' - implementation 'com.google.android.gms:play-services-ads:+' + implementation 'com.google.android.gms:play-services-ads:19.2.0' implementation 'com.android.support:support-annotations:28.0.0' + implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'com.android.support.constraint:constraint-layout:1.1.3' } diff --git a/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNAdMobNativePackage.java b/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNAdMobNativePackage.java index 0c66e14d..0ec8c739 100755 --- a/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNAdMobNativePackage.java +++ b/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNAdMobNativePackage.java @@ -14,7 +14,9 @@ public class RNAdMobNativePackage implements ReactPackage { @Override public List createNativeModules(ReactApplicationContext reactContext) { - return Arrays.asList(); + return Arrays.asList( + new RNAdmobNativeAdsManager(reactContext) + ); } public List> createJSModules() { diff --git a/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNAdMobNativeViewManager.java b/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNAdMobNativeViewManager.java index 68240c96..37b01c58 100755 --- a/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNAdMobNativeViewManager.java +++ b/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNAdMobNativeViewManager.java @@ -46,6 +46,8 @@ public class RNAdMobNativeViewManager extends ViewGroupManager list = nativeArray.toArrayList(); + List testDeviceIds = Arrays.asList(list.toArray(new String[list.size()])); + configuration.setTestDeviceIds(testDeviceIds); + } + + MobileAds.setRequestConfiguration(configuration.build()); + MobileAds.initialize(reactContext); + + } + + + + @ReactMethod + public void isTestDevice(Promise promise) { + + AdRequest builder = new AdRequest.Builder().build(); + if (builder != null) { + promise.resolve(builder.isTestDevice(reactContext)); + } + } +} diff --git a/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNNativeAdWrapper.java b/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNNativeAdWrapper.java index 18d05ec6..f6562723 100644 --- a/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNNativeAdWrapper.java +++ b/android/src/main/java/com/ammarahmed/rnadmob/nativeads/RNNativeAdWrapper.java @@ -1,6 +1,7 @@ package com.ammarahmed.rnadmob.nativeads; import android.content.Context; +import android.os.Bundle; import android.os.Handler; import android.view.LayoutInflater; import android.view.View; @@ -13,6 +14,7 @@ import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.RCTEventEmitter; +import com.google.ads.mediation.admob.AdMobAdapter; import com.google.android.gms.ads.AdListener; import com.google.android.gms.ads.AdLoader; import com.google.android.gms.ads.AdRequest; @@ -36,6 +38,11 @@ public void run() { Context mContext; UnifiedNativeAdView nativeAdView; UnifiedNativeAd unifiedNativeAd; + + private int adChoicesPlacement = 1; + private boolean requestNonPersonalizedAdsOnly = false; + + AdListener adListener = new AdListener() { @Override public void onAdFailedToLoad(int i) { @@ -136,9 +143,7 @@ public void addMediaView(int id) { if (adMediaView != null) { nativeAdView.setMediaView(adMediaView); adMediaView.requestLayout(); - if (unifiedNativeAd != null && unifiedNativeAd.getMediaContent().hasVideoContent()) { - unifiedNativeAd.getMediaContent().getVideoController().play(); - } + } } catch (Exception e) { @@ -162,18 +167,51 @@ private void setNativeAdToJS(UnifiedNativeAd nativeAd) { if (nativeAd.getStarRating() != null) { args.putInt("rating", nativeAd.getStarRating().intValue()); } - args.putString("aspectRatio", String.valueOf(nativeAd.getMediaContent().getAspectRatio())); + + float aspectRatio = 1.0f; + + + if (nativeAd.getResponseInfo().getMediationAdapterClassName().equals("com.google.ads.mediation.admob.AdMobAdapter")) { + if (nativeAd.getMediaContent() != null) { + aspectRatio = nativeAd.getMediaContent().getAspectRatio(); + + if (aspectRatio > 0) { + args.putString("aspectRatio", String.valueOf(aspectRatio)); + } else { + args.putString("aspectRatio", String.valueOf(1.0f)); + } + + } + } else { + args.putString("aspectRatio", String.valueOf(1.0f)); + } + + WritableArray images = Arguments.createArray(); - for (int i = 0; i < nativeAd.getImages().size(); i++) { - WritableMap map = Arguments.createMap(); - map.putString("url", nativeAd.getImages().get(i).getUri().toString()); - map.putInt("width", nativeAd.getImages().get(i).getWidth()); - map.putInt("height", nativeAd.getImages().get(i).getHeight()); - images.pushMap(map); + if (nativeAd.getImages() != null && nativeAd.getImages().size() > 0) { + + for (int i = 0; i < nativeAd.getImages().size(); i++) { + WritableMap map = Arguments.createMap(); + map.putString("url", nativeAd.getImages().get(i).getUri().toString()); + map.putInt("width", nativeAd.getImages().get(i).getWidth()); + map.putInt("height", nativeAd.getImages().get(i).getHeight()); + images.pushMap(map); + } + } + + if (images != null) { + args.putArray("images", images); + } else { + args.putArray("images", null); + } + + if (nativeAd.getIcon() != null || nativeAd.getIcon().getUri() != null) { + args.putString("icon", nativeAd.getIcon().getUri().toString()); + } else { + args.putString("icon", "empty"); } - args.putArray("images", images); - args.putString("icon", nativeAd.getIcon().getUri().toString()); + sendEvent(RNAdMobNativeViewManager.EVENT_UNIFIED_NATIVE_AD_LOADED, args); } catch (Exception e) { @@ -212,29 +250,39 @@ private void sendEvent(String name, @Nullable WritableMap event) { private void loadAd() { - try { - AdLoader.Builder builder = new AdLoader.Builder(mContext, admobAdUnitId); - builder.forUnifiedNativeAd(onUnifiedNativeAdLoadedListener); - VideoOptions videoOptions = new VideoOptions.Builder() - .setStartMuted(true) - .build(); + try { + AdLoader.Builder builder = new AdLoader.Builder(mContext, admobAdUnitId); + builder.forUnifiedNativeAd(onUnifiedNativeAdLoadedListener); + + VideoOptions videoOptions = new VideoOptions.Builder() + .setStartMuted(true) + .build(); - NativeAdOptions adOptions = new NativeAdOptions.Builder() - .setVideoOptions(videoOptions) - .setAdChoicesPlacement(NativeAdOptions.ADCHOICES_TOP_RIGHT) - .build(); - builder.withNativeAdOptions(adOptions); + NativeAdOptions adOptions = new NativeAdOptions.Builder() + .setVideoOptions(videoOptions) + .setAdChoicesPlacement(adChoicesPlacement) + .build(); + builder.withNativeAdOptions(adOptions); - AdLoader adLoader = builder.withAdListener(adListener) - .build(); - adLoader.loadAd(new AdRequest.Builder().build()); + AdLoader adLoader = builder.withAdListener(adListener) + .build(); - } catch (Exception e) { - } + AdRequest adRequest; + if (requestNonPersonalizedAdsOnly) { + Bundle extras = new Bundle(); + extras.putString("npa", "1"); + adRequest = new AdRequest.Builder().addNetworkExtrasBundle(AdMobAdapter.class, extras).build(); + } else { + adRequest = new AdRequest.Builder().build(); + } + + adLoader.loadAd(adRequest); + } catch (Exception e) { + } } public void setLoadWithDelay(int delay) { @@ -268,6 +316,14 @@ public void setAdUnitId(String id) { loadAd(); } + public void setAdChoicesPlacement(int location) { + adChoicesPlacement = location; + } + + public void setRequestNonPersonalizedAdsOnly(boolean npa) { + requestNonPersonalizedAdsOnly = npa; + } + @Override public void requestLayout() { super.requestLayout(); diff --git a/example/App.js b/example/App.js index 15aa9374..65488543 100644 --- a/example/App.js +++ b/example/App.js @@ -8,6 +8,7 @@ import NativeAdView, { AdvertiserView, MediaView, } from 'react-native-admob-native-ads'; +import { AdManager } from 'react-native-admob-native-ads'; const NATIVE_AD_ID = Platform.OS === 'ios' @@ -26,6 +27,7 @@ const App = () => { }; const _onAdLoaded = () => { + console.log('Ad has loaded'); }; @@ -48,13 +50,12 @@ const App = () => { alignSelf: 'center', height: 900, }} - adUnitID={NATIVE_AD_VIDEO_ID} // REPLACE WITH NATIVE_AD_VIDEO_ID for video ads. + adUnitID={NATIVE_AD_ID} // REPLACE WITH NATIVE_AD_VIDEO_ID for video ads. > App); diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 66fd7c0b..4612613c 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -19,35 +19,37 @@ PODS: - DoubleConversion - glog - glog (0.3.5) - - Google-Mobile-Ads-SDK (7.56.0): + - Google-Mobile-Ads-SDK (7.61.0): - GoogleAppMeasurement (~> 6.0) - - GoogleAppMeasurement (6.3.1): + - GoogleAppMeasurement (6.6.1): - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/Network (~> 6.0) - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (= 0.3.9011) - - GoogleUtilities/AppDelegateSwizzler (6.5.2): + - nanopb (~> 1.30905.0) + - GoogleUtilities/AppDelegateSwizzler (6.6.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (6.5.2) - - GoogleUtilities/Logger (6.5.2): + - GoogleUtilities/Environment (6.6.0): + - PromisesObjC (~> 1.2) + - GoogleUtilities/Logger (6.6.0): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.5.2): + - GoogleUtilities/MethodSwizzler (6.6.0): - GoogleUtilities/Logger - - GoogleUtilities/Network (6.5.2): + - GoogleUtilities/Network (6.6.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.5.2)" - - GoogleUtilities/Reachability (6.5.2): + - "GoogleUtilities/NSData+zlib (6.6.0)" + - GoogleUtilities/Reachability (6.6.0): - GoogleUtilities/Logger - - nanopb (0.3.9011): - - nanopb/decode (= 0.3.9011) - - nanopb/encode (= 0.3.9011) - - nanopb/decode (0.3.9011) - - nanopb/encode (0.3.9011) + - nanopb (1.30905.0): + - nanopb/decode (= 1.30905.0) + - nanopb/encode (= 1.30905.0) + - nanopb/decode (1.30905.0) + - nanopb/encode (1.30905.0) + - PromisesObjC (1.2.9) - RCTRequired (0.61.5) - RCTTypeSafety (0.61.5): - FBLazyVector (= 0.61.5) @@ -211,7 +213,7 @@ PODS: - React-cxxreact (= 0.61.5) - React-jsi (= 0.61.5) - React-jsinspector (0.61.5) - - react-native-admob-native-ads (0.1.1): + - react-native-admob-native-ads (0.2.9): - React - React-RCTActionSheet (0.61.5): - React-Core/RCTActionSheetHeaders (= 0.61.5) @@ -292,6 +294,7 @@ SPEC REPOS: - GoogleAppMeasurement - GoogleUtilities - nanopb + - PromisesObjC EXTERNAL SOURCES: DoubleConversion: @@ -356,10 +359,11 @@ SPEC CHECKSUMS: FBReactNativeSpec: 118d0d177724c2d67f08a59136eb29ef5943ec75 Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 glog: 1f3da668190260b06b429bb211bfbee5cd790c28 - Google-Mobile-Ads-SDK: 65e335fadc97c5a91a9d4546214bfd3a2fb11047 - GoogleAppMeasurement: c29d405ff76e18551b5d158eaba6753fda8c7542 - GoogleUtilities: ad0f3b691c67909d03a3327cc205222ab8f42e0e - nanopb: 18003b5e52dab79db540fe93fe9579f399bd1ccd + Google-Mobile-Ads-SDK: 4c3b24b04293b4bb370b2cb392cdd3ee97c87752 + GoogleAppMeasurement: 2fd5c5a56c069db635c8e7b92d4809a9591d0a69 + GoogleUtilities: 39530bc0ad980530298e9c4af8549e991fd033b1 + nanopb: c43f40fadfe79e8b8db116583945847910cbabc9 + PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75 RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1 RCTTypeSafety: 9aa1b91d7f9310fc6eadc3cf95126ffe818af320 React: b6a59ef847b2b40bb6e0180a97d0ca716969ac78 @@ -369,7 +373,7 @@ SPEC CHECKSUMS: React-jsi: cb2cd74d7ccf4cffb071a46833613edc79cdf8f7 React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386 React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0 - react-native-admob-native-ads: d81f8f3232ef42aab90e7de4031fa7241d768b11 + react-native-admob-native-ads: 37849d7d88dd3348a713d441d012ab23e7ddcd3e React-RCTActionSheet: 600b4d10e3aea0913b5a92256d2719c0cdd26d76 React-RCTAnimation: 791a87558389c80908ed06cc5dfc5e7920dfa360 React-RCTBlob: d89293cc0236d9cb0933d85e430b0bbe81ad1d72 diff --git a/index.d.ts b/index.d.ts index f83a8b5e..9b3e4ba9 100644 --- a/index.d.ts +++ b/index.d.ts @@ -18,6 +18,8 @@ type Image = { } + + type NativeAd = { /** * Title of the native ad. @@ -69,6 +71,40 @@ type NativeAd = { video: boolean; }; +type options = { + adChoicesPlacement: { + TOP_LEFT:number, + TOP_RIGHT:number, + BOTTOM_RIGHT:number, + BOTTOM_LEFT: number + } +} + +type MAX_AD_CONTENT_RATING = { + G: string, + MA:string, + PG: string, + T: string, + UNSPECIFIED:string, +} +type TAG_FOR_CHILD_DIRECTED_TREATMENT = { + TRUE:number, + FALSE:number +} + +type TAG_FOR_UNDER_AGE_CONSENT = { + TRUE:number, + FALSE:number +} + +type config = { + maxAdContentRating:MAX_AD_CONTENT_RATING, + tagForChildDirectedTreatment:TAG_FOR_CHILD_DIRECTED_TREATMENT, + tagForUnderAgeConsent:TAG_FOR_UNDER_AGE_CONSENT, + testDeviceIds:Array +} + + type NativeAdViewProps = { /** * When you are designing your ad, placeholders @@ -106,7 +142,35 @@ type NativeAdViewProps = { delayAdLoading?: number; /** - * Set testdevices for the ad. + * Placement of AdChoicesView in any of the 4 corners of the ad + * + * import AdOptions then pass the value from there. AdOptions.adChoicesPlacement + */ + + adChoicesPlacement?: { + TOP_LEFT:number, + TOP_RIGHT:number, + BOTTOM_RIGHT:number, + BOTTOM_LEFT: number + } + + /** + * Under the Google EU User Consent Policy, you must make certain disclosures + * to your users in the European Economic Area (EEA) and obtain their consent + * to use cookies or other local storage, where legally required, and to use + * personal data (such as AdID) to serve ads. This policy reflects the requirements + * of the EU ePrivacy Directive and the General Data Protection Regulation (GDPR). + * + * You can use library such as: https://github.com/birgernass/react-native-ad-consent + * to obtain the consent or if you are using rn-firebase you can obtain the consent from + * there and then pass the consent to this library. If user has selected + * non-personalized-ads then pass `true` and non-personalized ads will be shown to the user. + * + */ + requestNonPersonalizedAdsOnly:boolean; + + /** + * Set testdevices for the ad. (DEPRECATED) */ testDevices?: Array; onAdOpened?: Function; @@ -130,7 +194,11 @@ type NestedTextProps = { allowFontScaling?: boolean; }; + + declare module "react-native-admob-native-ads" { + + /** * * Wrapper for the UnifiedNativeAdView from Google Ads SDK. All your views should be @@ -142,7 +210,97 @@ declare module "react-native-admob-native-ads" { props: NativeAdViewProps ): React.FunctionComponent; + + const MAX_AD_CONTENT_RATING:MAX_AD_CONTENT_RATING; + const TAG_FOR_CHILD_DIRECTED_TREATMENT:TAG_FOR_CHILD_DIRECTED_TREATMENT; + const TAG_FOR_UNDER_AGE_CONSENT:TAG_FOR_UNDER_AGE_CONSENT; + + /** + * AdManager can be used to configure your ads on App Startup such as setting test devices. + * + */ + + export const AdManager = { + + /** + * Configure your Ad Requests during App Startup. You need to pass a single object as an argument with atleast one of the following properties + + | Name | Type | Required | + | --------- | -------- | -------- | + | testDeviceIds | `Array` | no | + | maxAdContentRating | AdManager.MAX_AD_CONTENT_RATING | no | + | tagForChildDirectedTreatment | AdManager.TAG_FOR_CHILD_DIRECTED_TREATMENT | no | + | tagForUnderAgeConsent | AdManager.TAG_FOR_UNDER_AGE_CONSENT | no | + + Example: + + ```js + + const config = { + testDeviceIds:["YOUR_TEST_DEVICE_ID"], + maxAdContetRating:AdManager.MAX_AD_CONTENT_RATING.MA, + tagForChildDirectedTreatment:AdManager.TAG_FOR_CHILD_DIRECTED_TREATMENT.FALSE, + tagForUnderAgeConsent:AdManager.TAG_FOR_UNDER_AGE_CONSENT.FALSE + } + + AdManager.setRequestConfiguration(config); + + ``` + * + */ + + setRequestConfiguration: (config:config) => {}, + + /** + * Check if the current device is registered as a test device to show test ads. + +```js + AdManager.isTestDevice().then(result => console.log(result)) +``` +return: `boolean` + */ + isTestDevice:async () => {}, + + + /** + * | Name | Description | +| --------- | -------- | +| G | "General audiences." Content suitable for all audiences, including families and children. | +| MA | "Mature audiences." Content suitable only for mature audiences; includes topics such as alcohol, gambling, sexual content, and weapons. | +| PG | "Parental guidance." Content suitable for most audiences with parental guidance, including topics like non-realistic, cartoonish violence. | +| T | "Teen." Content suitable for teen and older audiences, including topics such as general health, social networks, scary imagery, and fight sports. | +| UNSPECIFIED | Set default value to ""| + */ + + MAX_AD_CONTENT_RATING:MAX_AD_CONTENT_RATING, + + + /** + * | Name | Description | +| --------- | -------- | +| TRUE | Enabled | +| FALSE | Disabled | + */ + + TAG_FOR_CHILD_DIRECTED_TREATMENT:TAG_FOR_CHILD_DIRECTED_TREATMENT, + + /** + * | Name | Description | +| --------- | -------- | +| TRUE | Enabled | +| FALSE | Disabled | + */ + + + TAG_FOR_UNDER_AGE_CONSENT:TAG_FOR_UNDER_AGE_CONSENT +} + + + export const AdOptions:options; + + + /** * Ad Badge shows the {ad} badge on top of the ad telling the user that this is an AD. * */ diff --git a/index.js b/index.js index 75cc59b6..f0eef755 100755 --- a/index.js +++ b/index.js @@ -10,6 +10,8 @@ import StarRatingView from './src/StarRatingView' import PriceView from "./src/PriceView"; import AdBadge from "./src/AdBadge"; import NativeAdView from './src'; +import {AdOptions} from "./src/utils" +import AdManager from "./src/AdManager" export default NativeAdView; export { @@ -24,6 +26,8 @@ export { StarRatingView, PriceView, AdBadge, + AdOptions, + AdManager } diff --git a/ios/RNAdmobNativeAdsManager.h b/ios/RNAdmobNativeAdsManager.h new file mode 100644 index 00000000..f1238ba7 --- /dev/null +++ b/ios/RNAdmobNativeAdsManager.h @@ -0,0 +1,8 @@ +// CalendarManager.h +#import + + +@interface RNAdmobNativeAdsManager : NSObject + +@end + diff --git a/ios/RNAdmobNativeAdsManager.m b/ios/RNAdmobNativeAdsManager.m new file mode 100644 index 00000000..8908ae83 --- /dev/null +++ b/ios/RNAdmobNativeAdsManager.m @@ -0,0 +1,77 @@ +#import "RNAdmobNativeAdsManager.h" + +@import GoogleMobileAds; + +@implementation RNAdmobNativeAdsManager + +RCT_EXPORT_MODULE(); + + + +RCT_EXPORT_METHOD(setRequestConfiguration:(NSDictionary *)config) +{ + + if ([[config allKeys] containsObject:@"maxAdContentRating"]) { + + NSString *rating = [config valueForKey:@"maxAdContentRating"]; + + if ([rating isEqualToString:@"G"]) { + [[[GADMobileAds sharedInstance] requestConfiguration] setMaxAdContentRating:GADMaxAdContentRatingGeneral]; + + } else if ([rating isEqualToString:@"PG"]) { + + [[[GADMobileAds sharedInstance] requestConfiguration] setMaxAdContentRating:GADMaxAdContentRatingParentalGuidance]; + } else if ([rating isEqualToString:@"MA"]) { + + [[[GADMobileAds sharedInstance] requestConfiguration] setMaxAdContentRating:GADMaxAdContentRatingMatureAudience]; + } else if ([rating isEqualToString:@"T"]) { + [[[GADMobileAds sharedInstance] requestConfiguration] setMaxAdContentRating:GADMaxAdContentRatingTeen]; + } else if ([rating isEqualToString:@""]) { + [[[GADMobileAds sharedInstance] requestConfiguration] setMaxAdContentRating:NULL]; + + } + + }; + + if ([[config allKeys] containsObject:@"tagForChildDirectedTreatment"]) { + NSNumber *tag = [config valueForKey:@"tagForChildDirectedTreatment"]; + + if (tag.intValue == 0) { + [[[GADMobileAds sharedInstance] requestConfiguration] tagForChildDirectedTreatment:false]; + + } else { + [[[GADMobileAds sharedInstance] requestConfiguration] tagForChildDirectedTreatment:true]; + + } + }; + + if ([[config allKeys] containsObject:@"tagForUnderAgeConsent"]) { + NSNumber *tagC = [config valueForKey:@"tagForUnderAgeConsent"]; + + if (tagC.intValue == 0) { + [[[GADMobileAds sharedInstance] requestConfiguration] tagForUnderAgeOfConsent:false]; + } else { + [[[GADMobileAds sharedInstance] requestConfiguration] tagForUnderAgeOfConsent:true]; + } + }; + + if ([[config allKeys] containsObject:@"testDeviceIds"]) { + NSArray *testDevices = [config valueForKey:@"testDeviceIds"]; + + [[[GADMobileAds sharedInstance] requestConfiguration] setTestDeviceIdentifiers:testDevices]; + }; + + +} + +RCT_EXPORT_METHOD(isTestDevice:(RCTPromiseResolveBlock)resolve +rejecter:(RCTPromiseRejectBlock)reject) { + + resolve(@TRUE); +} + + + + + +@end diff --git a/ios/RNGADNativeView.h b/ios/RNGADNativeView.h index 32125463..12920217 100755 --- a/ios/RNGADNativeView.h +++ b/ios/RNGADNativeView.h @@ -23,6 +23,8 @@ @property (nonatomic, copy) NSNumber *mediaview; @property (nonatomic, copy) NSNumber *starrating; @property (nonatomic, copy) NSNumber *callToAction; +@property (nonatomic, copy) NSNumber *adChoicesPlacement; +@property (nonatomic) BOOL *requestNonPersonalizedAdsOnly; - (instancetype)initWithBridge:(RCTBridge *)bridge; diff --git a/ios/RNGADNativeView.m b/ios/RNGADNativeView.m index 12e4800b..174cd078 100755 --- a/ios/RNGADNativeView.m +++ b/ios/RNGADNativeView.m @@ -21,6 +21,9 @@ @implementation RNGADNativeView : GADUnifiedNativeAdView NSString *adUnitId; NSNumber *refreshingInterval; NSNumber *delay; +NSNumber *adChoicesPlace; +BOOL *nonPersonalizedAds; + - (instancetype)initWithBridge:(RCTBridge *)_bridge @@ -33,6 +36,16 @@ - (instancetype)initWithBridge:(RCTBridge *)_bridge return self; } +- (void)setAdChoicesPlacement:(NSNumber *)adChoicesPlacement { + + adChoicesPlace = adChoicesPlacement; +} + +- (void)setRequestNonPersonalizedAdsOnly:(BOOL *)requestNonPersonalizedAdsOnly { + + nonPersonalizedAds = requestNonPersonalizedAdsOnly; +} + - (void)setDelayAdLoad:(NSNumber *)delayAdLoad { delay = delayAdLoad; @@ -164,10 +177,6 @@ - (void)setPrice:(NSNumber *)price }]; }); - - - - } - (void)setStore:(NSNumber *)store @@ -184,10 +193,6 @@ - (void)setStore:(NSNumber *)store } }]; }); - - - - } - (void)setStarrating:(NSNumber *)starrating @@ -235,7 +240,22 @@ - (void)loadAd:(NSString *)adUnitId GADNativeAdViewAdOptions *adViewOptions = [GADNativeAdViewAdOptions new]; - adViewOptions.preferredAdChoicesPosition = GADAdChoicesPositionTopRightCorner; + + if ([adChoicesPlace isEqualToNumber:@0]) { + [adViewOptions setPreferredAdChoicesPosition:GADAdChoicesPositionTopLeftCorner]; + } else if ([adChoicesPlace isEqualToNumber:@1]) { + [adViewOptions setPreferredAdChoicesPosition:GADAdChoicesPositionTopRightCorner]; + } else if ([adChoicesPlace isEqualToNumber:@2]) { + [adViewOptions setPreferredAdChoicesPosition:GADAdChoicesPositionBottomRightCorner]; + } else if ([adChoicesPlace isEqualToNumber:@3]) { + [adViewOptions setPreferredAdChoicesPosition:GADAdChoicesPositionBottomLeftCorner]; + } else { + [adViewOptions setPreferredAdChoicesPosition:GADAdChoicesPositionTopRightCorner]; + + } + + + self.adLoader = [[GADAdLoader alloc] @@ -244,8 +264,16 @@ - (void)loadAd:(NSString *)adUnitId adTypes:@[ kGADAdLoaderAdTypeUnifiedNative ] options:@[adViewOptions]]; + self.adLoader.delegate = self; GADRequest *request = [GADRequest request]; + + if (nonPersonalizedAds) { + GADExtras *extras = [[GADExtras alloc] init]; + extras.additionalParameters = @{@"npa": @"1"}; + [request registerAdNetworkExtras:extras]; + } + GADMobileAds.sharedInstance.requestConfiguration.testDeviceIdentifiers = _testDevices; [self.adLoader loadRequest:request]; } @@ -299,7 +327,7 @@ - (void)adLoader:(GADAdLoader *)adLoader didReceiveUnifiedNativeAd:(GADUnifiedNa NSInteger val2 = @(image.image.size.height).integerValue; [imageDic setValue: [NSNumber numberWithInteger:val2] forKey:@"height"]; [images addObject:imageDic]; - + } diff --git a/ios/RNGADNativeViewManager.m b/ios/RNGADNativeViewManager.m index cd65dc83..8db61bd1 100755 --- a/ios/RNGADNativeViewManager.m +++ b/ios/RNGADNativeViewManager.m @@ -44,8 +44,8 @@ -(UIView *)view RCT_EXPORT_VIEW_PROPERTY(price, NSNumber) RCT_EXPORT_VIEW_PROPERTY(starrating, NSNumber) RCT_EXPORT_VIEW_PROPERTY(callToAction, NSNumber) - - +RCT_EXPORT_VIEW_PROPERTY(requestNonPersonalizedAdsOnly, BOOL) +RCT_EXPORT_VIEW_PROPERTY(adChoicesPlacement, NSNumber) //RCT_EXPORT_VIEW_PROPERTY(ratingStarsColor, NSString) diff --git a/package.json b/package.json index 7f1e1199..dac017cd 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-admob-native-ads", - "version": "0.2.9", + "version": "0.3.0", "description": "A simple and robust library for creating & displaying Admob Native Ads in your React Native App using Native Views", "author": "Ammar Ahmed ", "main": "index.js", diff --git a/src/AdManager.js b/src/AdManager.js new file mode 100644 index 00000000..c02e5b19 --- /dev/null +++ b/src/AdManager.js @@ -0,0 +1,38 @@ +import {NativeModules} from 'react-native'; + +const RNAdmobNativeAdsManager = NativeModules.RNAdmobNativeAdsManager; + +const MAX_AD_CONTENT_RATING = { + G: "G", + MA:"MA", + PG: "PG", + T: "T", + UNSPECIFIED: "", +} +const TAG_FOR_CHILD_DIRECTED_TREATMENT = { + TRUE:1, + FALSE:0 +} + +const TAG_FOR_UNDER_AGE_CONSENT = { + TRUE:1, + FALSE:0 +} + +function setRequestConfiguration(config) { + + RNAdmobNativeAdsManager.setRequestConfiguration(config); +} + +async function isTestDevice() { + + return await RNAdmobNativeAdsManager.isTestDevice(); +} + +export default { + setRequestConfiguration, + isTestDevice, + MAX_AD_CONTENT_RATING, + TAG_FOR_CHILD_DIRECTED_TREATMENT, + TAG_FOR_UNDER_AGE_CONSENT +} \ No newline at end of file diff --git a/src/IconView.js b/src/IconView.js index 758b9221..2060c008 100644 --- a/src/IconView.js +++ b/src/IconView.js @@ -20,7 +20,7 @@ const IconView = (props) => { _onLayout(); }, [nativeAd, nativeAdView]); - return nativeAd && nativeAd.icon ? ( + return nativeAd && nativeAd.icon && nativeAd.icon !== "empty" ? ( { onLayout={_onLayout} source={{ uri: nativeAd.icon }} /> - ) : null; + ) : ( + + ); }; export default IconView; diff --git a/src/ImageView.js b/src/ImageView.js index 5a148797..958133ed 100644 --- a/src/ImageView.js +++ b/src/ImageView.js @@ -19,7 +19,7 @@ const ImageView = (props) => { _onLayout(); }, [nativeAd, nativeAdView]); - return nativeAd && nativeAd.images[0] ? ( + return nativeAd && nativeAd.images && nativeAd.images[0] ? ( { - return Platform.OS === "android" ? ( - - ) : ( - - ); + + return }; -const AdWrapperView = - Platform.OS === "android" - ? requireNativeComponent("RNAdComponentWrapper", Wrapper) - : null; export default Wrapper; diff --git a/src/index.js b/src/index.js index aa176fbe..36465bc3 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import { Platform, requireNativeComponent } from "react-native"; import { NativeAdContext } from "./context"; +import Wrapper from "./Wrapper"; const testNativeAd = { headline: "Test Ad: Lorem ipsum dolor ", @@ -15,6 +16,8 @@ const testNativeAd = { images: ["https://dummyimage.com/qvga"], }; + + const waitAsync = (ms) => new Promise((resolve, reject) => { setTimeout(() => { @@ -66,8 +69,6 @@ const NativeAdView = (props) => { updateAd(event.nativeEvent); setTimeout(() => { setForceRefresh(!forceRefresh); - setForceRefresh(!forceRefresh); - setForceRefresh(!forceRefresh); }, 0); if (props.onUnifiedNativeAdLoaded) { @@ -105,11 +106,13 @@ const NativeAdView = (props) => { value={{ nativeAd, nativeAdView, setNativeAdView, setNativeAd }} > { nativeAdRef = ref; setNativeAdView(nativeAdRef); return nativeAdRef; }} + adUnitID={props.adUnitID} onAdLoaded={_onAdLoaded} onAdFailedToLoad={_onAdFailedToLoad} onAdClicked={_onAdClicked} @@ -121,9 +124,12 @@ const NativeAdView = (props) => { onUnifiedNativeAdLoaded={_onUnifiedNativeAdLoaded} refreshInterval={props.refreshInterval? props.refreshInterval : 60000} testDevices={props.testDevices? props.testDevices : []} - adUnitID={props.adUnitID} + requestNonPersonalizedAdsOnly={props.requestNonPersonalizedAdsOnly? true : false} + adChoicesPlacement={props.adChoicesPlacement > -1? props.adChoicesPlacement : 1} > + {props.children} + ); diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 00000000..5ba3c375 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,10 @@ + + +export const AdOptions = Object.freeze({ + adChoicesPlacement: { + TOP_LEFT:0, + TOP_RIGHT:1, + BOTTOM_RIGHT:2, + BOTTOM_LEFT: 3 + } +}) \ No newline at end of file