[admob][android] Add admob module to base firebase object

This commit is contained in:
Elliot Hesp 2017-05-26 15:19:48 +01:00
parent dd1262ca86
commit 0e47c57317
4 changed files with 451 additions and 0 deletions

View File

@ -0,0 +1,97 @@
package io.invertase.firebase.admob;
import android.app.Activity;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.google.android.gms.ads.AdRequest;
import com.google.firebase.database.ServerValue;
import com.google.firebase.perf.FirebasePerformance;
import com.google.firebase.perf.metrics.Trace;
import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
import io.invertase.firebase.Utils;
import io.invertase.firebase.admob.RNFirebaseAdmobInterstitial;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class RNFirebaseAdMob extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseAdmob";
public ReactApplicationContext getContext() {
return getReactApplicationContext();
}
public Activity getActivity() {
return getCurrentActivity();
}
private ReactApplicationContext context;
private HashMap<String, RNFirebaseAdmobInterstitial> interstitials = new HashMap<>();
public RNFirebaseAdMob(ReactApplicationContext reactContext) {
super(reactContext);
context = reactContext;
Log.d(TAG, "New instance");
}
@Override
public String getName() {
return TAG;
}
@ReactMethod
public void interstitialLoadAd(String adUnit, ReadableMap request) {
RNFirebaseAdmobInterstitial interstitial = getOrCreateInterstitial(adUnit);
AdRequest.Builder requestBuilder = new AdRequest.Builder();
if (request.hasKey("testDevice")) {
requestBuilder.addTestDevice(AdRequest.DEVICE_ID_EMULATOR);
}
ReadableArray keywords = request.getArray("keywords");
List<Object> keywordsList = Utils.recursivelyDeconstructReadableArray(keywords);
for (Object word : keywordsList) {
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<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("DEVICE_ID_EMULATOR", AdRequest.DEVICE_ID_EMULATOR);
return constants;
}
}

View File

@ -0,0 +1,303 @@
package io.invertase.firebase.admob;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.facebook.react.views.view.ReactViewGroup;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdSize;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.MobileAds;
import java.util.Map;
public class RNFirebaseAdMobBanner extends SimpleViewManager<ReactViewGroup> implements View.OnLayoutChangeListener {
public static final String REACT_CLASS = "RNFirebaseAdMobBanner";
public static final String BANNER_EVENT = "bannerEvent";
public enum Events {
EVENT_AD_SIZE_CHANGE("onSizeChange"),
EVENT_AD_LOADED("onAdLoaded"),
EVENT_AD_FAILED_TO_LOAD("onAdFailedToLoad"),
EVENT_AD_OPENED("onAdOpened"),
EVENT_AD_CLOSED("onAdClosed"),
EVENT_AD_LEFT_APPLICATION("onAdLeftApplication");
private final String event;
Events(final String name) {
event = name;
}
@Override
public String toString() {
return event;
}
}
private ThemedReactContext context;
private ReactViewGroup viewGroup;
private RCTEventEmitter emitter;
private String size;
@Override
public String getName() {
return REACT_CLASS;
}
/**
* Create & return view instance
* @param themedReactContext
* @return
*/
@Override
public ReactViewGroup createViewInstance(ThemedReactContext themedReactContext) {
context = themedReactContext;
viewGroup = new ReactViewGroup(themedReactContext);
emitter = themedReactContext.getJSModule(RCTEventEmitter.class);
attachAdViewToViewGroup();
return viewGroup;
}
/**
* Declare custom events
* @return
*/
@Override
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
builder.put(BANNER_EVENT, MapBuilder.of("registrationName", BANNER_EVENT));
return builder.build();
}
/**
* If the React View changes, reset the Ad size
* @param view
* @param left
* @param top
* @param right
* @param bottom
* @param oldLeft
* @param oldTop
* @param oldRight
* @param oldBottom
*/
@Override
public void onLayoutChange(View view, final int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
Log.d("Ads", "onLayoutChange");
// If the view has changed at all, recalculate what banner we need
if (left != oldLeft || right != oldRight || top != oldTop || bottom != oldBottom) {
setSize(viewGroup, null);
}
}
/**
* Handle unitId prop
* @param view
* @param unitId
*/
@ReactProp(name = "unitId")
public void setUnitId(final ReactViewGroup view, final String unitId) {
Log.d("Ads", "Prop unitId something " + unitId);
AdView adViewView = (AdView) view.getChildAt(0);
adViewView.setAdUnitId(unitId);
requestAd();
}
/**
* Handle size prop
* @param view
* @param value
*/
@ReactProp(name = "size")
public void setSize(final ReactViewGroup view, final @Nullable String value) {
if (value != null) {
size = value;
}
AdSize adSize = propToAdSize(size.toUpperCase());
Log.d("Ads", "Prop size something " + adSize.toString());
AdView adViewView = (AdView) view.getChildAt(0);
adViewView.setAdSize(adSize);
// Send the width & height back to the JS
int width;
int height;
WritableMap payload = Arguments.createMap();
if (adSize == AdSize.SMART_BANNER) {
width = (int) PixelUtil.toDIPFromPixel(adSize.getWidthInPixels(context));
height = (int) PixelUtil.toDIPFromPixel(adSize.getHeightInPixels(context));
} else {
width = adSize.getWidth();
height = adSize.getHeight();
}
payload.putDouble("width", width);
payload.putDouble("height", height);
sendEvent(Events.EVENT_AD_SIZE_CHANGE.toString(), payload);
requestAd();
}
/**
* Creates a new instance of the AdView and attaches it to the
* current ReactViewGroup
*/
void attachAdViewToViewGroup() {
removeAdFromViewGroup();
final AdView adView = new AdView(context);
viewGroup.addView(adView);
setAdListener();
}
/**
* Removes the AdView from the ViewGroup
*/
void removeAdFromViewGroup() {
AdView adView = (AdView) viewGroup.getChildAt(0);
viewGroup.removeAllViews();
if (adView != null) {
adView.destroy();
}
}
/**
* Loads a new ad into a viewGroup
*/
void requestAd() {
AdView adView = (AdView) viewGroup.getChildAt(0);
if (adView.getAdSize() == null || adView.getAdUnitId() == null) {
return;
}
AdRequest.Builder adRequestBuilder = new AdRequest.Builder();
adRequestBuilder.addTestDevice(AdRequest.DEVICE_ID_EMULATOR);
AdRequest adRequest = adRequestBuilder.build();
adView.loadAd(adRequest);
}
/**
* Listen to Ad events
*/
void setAdListener() {
final AdView adView = (AdView) viewGroup.getChildAt(0);
adView.setAdListener(new AdListener() {
@Override
public void onAdLoaded() {
int left = adView.getLeft();
int top = adView.getTop();
int width = adView.getAdSize().getWidthInPixels(context);
int height = adView.getAdSize().getHeightInPixels(context);
adView.measure(width, height);
adView.layout(left, top, left + width, top + height);
sendEvent(Events.EVENT_AD_LOADED.toString(), null);
}
@Override
public void onAdFailedToLoad(int errorCode) {
WritableMap payload = Arguments.createMap();
// TODO Error code converter
switch (errorCode) {
case AdRequest.ERROR_CODE_INTERNAL_ERROR:
payload.putString("code", "admob/error-code-internal-error");
payload.putString("message", "Something happened internally; for instance, an invalid response was received from the ad server.");
break;
case AdRequest.ERROR_CODE_INVALID_REQUEST:
payload.putString("code", "admob/error-code-invalid-request");
payload.putString("message", "The ad request was invalid; for instance, the ad unit ID was incorrect.");
break;
case AdRequest.ERROR_CODE_NETWORK_ERROR:
payload.putString("code", "admob/error-code-network-error");
payload.putString("message", "The ad request was unsuccessful due to network connectivity.");
break;
case AdRequest.ERROR_CODE_NO_FILL:
payload.putString("code", "admob/error-code-no-fill");
payload.putString("message", "The ad request was successful, but no ad was returned due to lack of ad inventory.");
break;
}
sendEvent(Events.EVENT_AD_FAILED_TO_LOAD.toString(), payload);
}
@Override
public void onAdOpened() {
sendEvent(Events.EVENT_AD_OPENED.toString(), null);
}
@Override
public void onAdClosed() {
sendEvent(Events.EVENT_AD_CLOSED.toString(), null);
}
@Override
public void onAdLeftApplication() {
sendEvent(Events.EVENT_AD_LEFT_APPLICATION.toString(), null);
}
});
}
/**
* Sends an event back to the JS component to handle
* @param type
* @param payload
*/
void sendEvent(String type, final @Nullable WritableMap payload) {
WritableMap event = Arguments.createMap();
event.putString("type", type);
if (payload != null) {
event.putMap("payload", payload);
}
emitter.receiveEvent(viewGroup.getId(), BANNER_EVENT, event);
}
/**
* Map the size prop to the AdSize
* @param prop
* @return
*/
AdSize propToAdSize(String prop) {
switch (prop) {
default:
case "BANNER":
return AdSize.BANNER;
case "LARGE_BANNER":
return AdSize.LARGE_BANNER;
case "MEDIUM_RECTANGLE":
return AdSize.MEDIUM_RECTANGLE;
case "FULL_BANNER":
return AdSize.FULL_BANNER;
case "LEADERBOARD":
return AdSize.LEADERBOARD;
case "SMART_BANNER":
return AdSize.SMART_BANNER;
}
}
}

View File

@ -0,0 +1,47 @@
package io.invertase.firebase.admob;
import android.app.Activity;
import android.util.Log;
import com.facebook.react.bridge.ReactApplicationContext;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.InterstitialAd;
class RNFirebaseAdmobInterstitial {
private InterstitialAd interstitialAd;
private RNFirebaseAdMob adMob;
RNFirebaseAdmobInterstitial(String adUnit, RNFirebaseAdMob adMobInstance) {
adMob = adMobInstance;
interstitialAd = new InterstitialAd(adMob.getContext());
interstitialAd.setAdUnitId(adUnit);
}
void loadAd(final AdRequest adRequest) {
Activity activity = adMob.getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
interstitialAd.loadAd(adRequest);
}
});
}
}
void show() {
Activity activity = adMob.getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (interstitialAd.isLoaded()) {
interstitialAd.show();
}
}
});
}
}
}

View File

@ -16,6 +16,7 @@ import Analytics from './modules/analytics';
import Crash from './modules/crash'; import Crash from './modules/crash';
import RemoteConfig from './modules/config'; import RemoteConfig from './modules/config';
import Performance from './modules/perf'; import Performance from './modules/perf';
import AdMob, { statics as AdMobStatics } from './modules/admob';
const instances: Object = { default: null }; const instances: Object = { default: null };
const FirebaseModule = NativeModules.RNFirebase; const FirebaseModule = NativeModules.RNFirebase;
@ -36,6 +37,7 @@ export default class Firebase {
_config: ?Object; _config: ?Object;
_crash: ?Object; _crash: ?Object;
_perf: ?Object; _perf: ?Object;
_admob: ?Object;
auth: Function; auth: Function;
crash: Function; crash: Function;
@ -45,6 +47,7 @@ export default class Firebase {
messaging: Function; messaging: Function;
config: Function; config: Function;
perf: Function; perf: Function;
admob: Function;
eventHandlers: Object; eventHandlers: Object;
debug: boolean; debug: boolean;
@ -90,6 +93,7 @@ export default class Firebase {
this.crash = this._staticsOrInstance('crash', {}, Crash); this.crash = this._staticsOrInstance('crash', {}, Crash);
this.config = this._staticsOrInstance('config', {}, RemoteConfig); this.config = this._staticsOrInstance('config', {}, RemoteConfig);
this.perf = this._staticsOrInstance('perf', {}, Performance); this.perf = this._staticsOrInstance('perf', {}, Performance);
this.admob = this._staticsOrInstance('admob', AdMobStatics, AdMob);
// init auth to start listeners // init auth to start listeners
this.auth(); this.auth();