diff --git a/android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdMob.java b/android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdMob.java index a954632c..9c7f1651 100644 --- a/android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdMob.java +++ b/android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdMob.java @@ -30,6 +30,7 @@ public class RNFirebaseAdMob extends ReactContextBaseJavaModule { } private HashMap interstitials = new HashMap<>(); + private HashMap rewardedVideos = new HashMap<>(); public RNFirebaseAdMob(ReactApplicationContext reactContext) { super(reactContext); @@ -44,6 +45,53 @@ public class RNFirebaseAdMob extends ReactContextBaseJavaModule { @ReactMethod public void interstitialLoadAd(String adUnit, ReadableMap request) { RNFirebaseAdmobInterstitial interstitial = getOrCreateInterstitial(adUnit); + interstitial.loadAd(buildRequest(request).build()); + } + + @ReactMethod + public void interstitialShowAd(String adUnit) { + RNFirebaseAdmobInterstitial interstitial = getOrCreateInterstitial(adUnit); + interstitial.show(); + } + + @ReactMethod + public void rewardedVideoLoadAd(String adUnit, ReadableMap request) { + RNFirebaseRewardedVideo rewardedVideo = getOrCreateRewardedVideo(adUnit); + rewardedVideo.loadAd(buildRequest(request).build()); + } + + @ReactMethod + public void rewardedVideoShowAd(String adUnit) { + RNFirebaseRewardedVideo rewardedVideo = getOrCreateRewardedVideo(adUnit); + rewardedVideo.show(); + } + + private RNFirebaseAdmobInterstitial getOrCreateInterstitial(String adUnit) { + if (interstitials.containsKey(adUnit)) { + return interstitials.get(adUnit); + } + RNFirebaseAdmobInterstitial interstitial = new RNFirebaseAdmobInterstitial(adUnit, this); + interstitials.put(adUnit, interstitial); + return interstitial; + } + + private RNFirebaseRewardedVideo getOrCreateRewardedVideo(String adUnit) { + if (rewardedVideos.containsKey(adUnit)) { + return rewardedVideos.get(adUnit); + } + RNFirebaseRewardedVideo rewardedVideo = new RNFirebaseRewardedVideo(adUnit, this); + rewardedVideos.put(adUnit, rewardedVideo); + return rewardedVideo; + } + + @Override + public Map getConstants() { + final Map constants = new HashMap<>(); + constants.put("DEVICE_ID_EMULATOR", AdRequest.DEVICE_ID_EMULATOR); + return constants; + } + + AdRequest.Builder buildRequest(ReadableMap request) { AdRequest.Builder requestBuilder = new AdRequest.Builder(); if (request.hasKey("testDevice")) { @@ -57,30 +105,6 @@ public class RNFirebaseAdMob extends ReactContextBaseJavaModule { requestBuilder.addKeyword((String) word); } - interstitial.loadAd(requestBuilder.build()); - } - - @ReactMethod - public void interstitialShowAd(String adUnit) { - RNFirebaseAdmobInterstitial interstitial = getOrCreateInterstitial(adUnit); - interstitial.show(); - } - - private RNFirebaseAdmobInterstitial getOrCreateInterstitial(String adUnit) { - if (interstitials.containsKey(adUnit)) { - return interstitials.get(adUnit); - } - RNFirebaseAdmobInterstitial interstitial = new RNFirebaseAdmobInterstitial(adUnit, this); - interstitials.put(adUnit, interstitial); - return interstitial; - } - - - - @Override - public Map getConstants() { - final Map constants = new HashMap<>(); - constants.put("DEVICE_ID_EMULATOR", AdRequest.DEVICE_ID_EMULATOR); - return constants; + return requestBuilder; } } diff --git a/android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdMobUtils.java b/android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdMobUtils.java new file mode 100644 index 00000000..7ce6310c --- /dev/null +++ b/android/src/main/java/io/invertase/firebase/admob/RNFirebaseAdMobUtils.java @@ -0,0 +1,39 @@ +package io.invertase.firebase.admob; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; +import com.google.android.gms.ads.AdRequest; + +class RNFirebaseAdMobUtils { + + /** + * Convert common AdMob errors into a standard format + * @param errorCode + * @return + */ + static WritableMap errorCodeToMap(int errorCode) { + WritableMap map = Arguments.createMap(); + + switch (errorCode) { + case AdRequest.ERROR_CODE_INTERNAL_ERROR: + map.putString("code", "admob/error-code-internal-error"); + map.putString("message", "Something happened internally; for instance, an invalid response was received from the ad server."); + break; + case AdRequest.ERROR_CODE_INVALID_REQUEST: + map.putString("code", "admob/error-code-invalid-request"); + map.putString("message", "The ad request was invalid; for instance, the ad unit ID was incorrect."); + break; + case AdRequest.ERROR_CODE_NETWORK_ERROR: + map.putString("code", "admob/error-code-network-error"); + map.putString("message", "The ad request was unsuccessful due to network connectivity."); + break; + case AdRequest.ERROR_CODE_NO_FILL: + map.putString("code", "admob/error-code-no-fill"); + map.putString("message", "The ad request was successful, but no ad was returned due to lack of ad inventory."); + break; + } + + return map; + } + +} diff --git a/android/src/main/java/io/invertase/firebase/admob/RNFirebaseRewardedVideo.java b/android/src/main/java/io/invertase/firebase/admob/RNFirebaseRewardedVideo.java new file mode 100644 index 00000000..136d8d7b --- /dev/null +++ b/android/src/main/java/io/invertase/firebase/admob/RNFirebaseRewardedVideo.java @@ -0,0 +1,121 @@ +package io.invertase.firebase.admob; + + +import android.app.Activity; +import android.support.annotation.Nullable; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; +import com.google.android.gms.ads.AdListener; +import com.google.android.gms.ads.AdRequest; +import com.google.android.gms.ads.InterstitialAd; +import com.google.android.gms.ads.MobileAds; +import com.google.android.gms.ads.reward.RewardItem; +import com.google.android.gms.ads.reward.RewardedVideoAd; +import com.google.android.gms.ads.reward.RewardedVideoAdListener; + +import io.invertase.firebase.Utils; + +public class RNFirebaseRewardedVideo implements RewardedVideoAdListener { + + private RewardedVideoAd mAd; + private String adUnit; + private RNFirebaseAdMob adMob; + private RewardedVideoAd rewardedVideo; + + RNFirebaseRewardedVideo(final String adUnitString, final RNFirebaseAdMob adMobInstance) { + adUnit = adUnitString; + adMob = adMobInstance; + + rewardedVideo = MobileAds.getRewardedVideoAdInstance(adMob.getContext()); + rewardedVideo.setRewardedVideoAdListener(this); + } + + /** + * Load an Ad with a AdRequest instance + * @param adRequest + */ + void loadAd(final AdRequest adRequest) { + Activity activity = adMob.getActivity(); + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + rewardedVideo.loadAd(adUnit, adRequest); + } + }); + } + } + + /** + * Show the loaded interstitial, if it's loaded + */ + void show() { + Activity activity = adMob.getActivity(); + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (rewardedVideo.isLoaded()) { + rewardedVideo.show(); + } + } + }); + } + } + + @Override + public void onRewarded(RewardItem reward) { + sendEvent("onRewarded", null); + } + + @Override + public void onRewardedVideoAdLeftApplication() { + sendEvent("onRewardedVideoAdLeftApplication", null); + } + + @Override + public void onRewardedVideoAdClosed() { + sendEvent("onRewardedVideoAdClosed", null); + } + + @Override + public void onRewardedVideoAdFailedToLoad(int errorCode) { + WritableMap payload = RNFirebaseAdMobUtils.errorCodeToMap(errorCode); + sendEvent("onRewardedVideoAdFailedToLoad", payload); + } + + @Override + public void onRewardedVideoAdLoaded() { + sendEvent("onRewardedVideoAdLoaded", null); + } + + @Override + public void onRewardedVideoAdOpened() { + sendEvent("onRewardedVideoAdOpened", null); + } + + @Override + public void onRewardedVideoStarted() { + sendEvent("onRewardedVideoStarted", null); + } + + // TODO onResume etc??? https://developers.google.com/admob/android/rewarded-video + + /** + * Send a native event over the bridge with a type and optional payload + * @param type + * @param payload + */ + void sendEvent(String type, final @Nullable WritableMap payload) { + WritableMap map = Arguments.createMap(); + map.putString("type", type); + map.putString("adunit", adUnit); + + if (payload != null) { + map.putMap("payload", payload); + } + + Utils.sendEvent(adMob.getContext(), "rewarded_video_event", map); + } +} diff --git a/docs/modules/admob.md b/docs/modules/admob.md index fec3e753..3c50dfc5 100644 --- a/docs/modules/admob.md +++ b/docs/modules/admob.md @@ -4,10 +4,66 @@ The admob allows you to display adverts in your app, using your account from [Ad RNFirebase allows you to display Banners, Interstitials, Native Ads & Rewarded Videos. -## Banner +### Banner -## Interstitial +AdMob Banners in RNFirebase are exported as a usable React component, allowing you to integrate it easily into your existing app very easily. -## Native +```js +const Banner = firebase.admob.Banner; +... +render() { + return ( + + ); +} + +``` + +### Interstitial + +An interstitial is a full screen advert which creates a new activity on top of React. As they need to be controlled, +allowing the developer to choose when to display them they're not available as a component. Instead they're controlled via +method calls. + +To request an interstitial from AdMob, the `loadAd` method must be called with an instance of `AdRequest` (see below for full API): + +```js +const advert = firebase.admob().interstitial('ca-app-pub-3940256099942544/1033173712'); + +const AdRequest = firebase.admob.AdRequest; +const request = new AdRequest(); +request.addKeyword('foo').addKeyword('bar'); + +// Load the advert with our AdRequest +advert.loadAd(request.build()); + +// Simulate the interstitial being shown "sometime" later during the apps lifecycle +setTimeout(() => { + if (advert.isLoaded()) { + advert.show(); + } else { + // Unable to show interstitial - not loaded yet. + } +}, 1000); + +``` + +### Native + +### Rewarded Video + +## Statics + +### Banner +> Accessed via `firebase.admob.Banner`. + +Exports a React component with the following PropTypes: + + +### AdRequest +> Accessed via `firebase.admob.AdRequest`. + +Used to build a request object to pass into AdMob requests. Exposes the following chainable methods: -## Rewarded Video diff --git a/lib/modules/admob/index.js b/lib/modules/admob/index.js index a3224527..2020eea1 100644 --- a/lib/modules/admob/index.js +++ b/lib/modules/admob/index.js @@ -44,4 +44,13 @@ export const statics = { onAdClosed: 'onAdClosed', onAdFailedToLoad: 'onAdFailedToLoad', }, + RewardedEventTypes: { + onRewarded: 'onRewarded', + onRewardedVideoAdLeftApplication: 'onRewardedVideoAdLeftApplication', + onRewardedVideoAdClosed: 'onRewardedVideoAdClosed', + onRewardedVideoAdFailedToLoad: 'onRewardedVideoAdFailedToLoad', + onRewardedVideoAdLoaded: 'onRewardedVideoAdLoaded', + onRewardedVideoAdOpened: 'onRewardedVideoAdOpened', + onRewardedVideoStarted: 'onRewardedVideoStarted', + }, };