Skip to content

Commit

Permalink
fix: interstitials and rewarded ads (#18)
Browse files Browse the repository at this point in the history
* fix: wrong event naming
* fix: RN prefix
* fix: add module suffix
* fix: make android work
* fix: module name
* fix: missing rctconvert
* Update android/src/main/java/io/invertase/googleads/common/RCTConvert.java
Co-authored-by: Mike Hardy <github@mikehardy.net>
dylancom authored Dec 7, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 7c7c8b5 commit 9569b98
Showing 13 changed files with 355 additions and 23 deletions.
173 changes: 173 additions & 0 deletions android/src/main/java/io/invertase/googleads/ReactNativeAppModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package io.invertase.googleads;

/*
* Copyright (c) 2016-present Invertase Limited & Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this library except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import io.invertase.googleads.common.ReactNativeApp;
import io.invertase.googleads.common.RCTConvert;
import io.invertase.googleads.common.ReactNativeEvent;
import io.invertase.googleads.common.ReactNativeEventEmitter;
import io.invertase.googleads.common.ReactNativeJSON;
import io.invertase.googleads.common.ReactNativeMeta;
import io.invertase.googleads.common.ReactNativePreferences;
import io.invertase.googleads.common.ReactNativeModule;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ReactNativeAppModule extends ReactNativeModule {
private static final String TAG = "RNAppModule";

ReactNativeAppModule(ReactApplicationContext reactContext) {
super(reactContext, TAG);
}

@Override
public void initialize() {
super.initialize();
ReactNativeEventEmitter.getSharedInstance().attachReactContext(getContext());
}

@ReactMethod
public void initializeApp(ReadableMap options, ReadableMap appConfig, Promise promise) {
// ReactNativeApp reactNativeApp =
// RCTConvertFirebase.readableMapToFirebaseApp(options, appConfig, getContext());

// WritableMap reactNativeAppMap = RCTConvertFirebase.reactNativeAppToWritableMap(reactNativeApp);
// promise.resolve(reactNativeAppMap);
promise.resolve(options);
}

@ReactMethod
public void setAutomaticDataCollectionEnabled(String appName, Boolean enabled) {
// ReactNativeApp reactNativeApp = ReactNativeApp.getInstance(appName);
// reactNativeApp.setDataCollectionDefaultEnabled(enabled);
}

@ReactMethod
public void deleteApp(String appName, Promise promise) {
// ReactNativeApp reactNativeApp = ReactNativeApp.getInstance(appName);

// if (reactNativeApp != null) {
// reactNativeApp.delete();
// }

promise.resolve(null);
}

@ReactMethod
public void eventsNotifyReady(Boolean ready) {
ReactNativeEventEmitter emitter = ReactNativeEventEmitter.getSharedInstance();
emitter.notifyJsReady(ready);
}

@ReactMethod
public void eventsGetListeners(Promise promise) {
ReactNativeEventEmitter emitter = ReactNativeEventEmitter.getSharedInstance();
promise.resolve(emitter.getListenersMap());
}

@ReactMethod
public void eventsPing(String eventName, ReadableMap eventBody, Promise promise) {
ReactNativeEventEmitter emitter = ReactNativeEventEmitter.getSharedInstance();
emitter.sendEvent(
new ReactNativeEvent(
eventName, RCTConvert.readableMapToWritableMap(eventBody)));
promise.resolve(RCTConvert.readableMapToWritableMap(eventBody));
}

@ReactMethod
public void eventsAddListener(String eventName) {
ReactNativeEventEmitter emitter = ReactNativeEventEmitter.getSharedInstance();
emitter.addListener(eventName);
}

@ReactMethod
public void eventsRemoveListener(String eventName, Boolean all) {
ReactNativeEventEmitter emitter = ReactNativeEventEmitter.getSharedInstance();
emitter.removeListener(eventName, all);
}

@ReactMethod
public void addListener(String eventName) {
// Keep: Required for RN built in Event Emitter Calls.
}

@ReactMethod
public void removeListeners(Integer count) {
// Keep: Required for RN built in Event Emitter Calls.
}

/** ------------------ META ------------------ */
@ReactMethod
public void metaGetAll(Promise promise) {
promise.resolve(ReactNativeMeta.getSharedInstance().getAll());
}

/** ------------------ JSON ------------------ */
@ReactMethod
public void jsonGetAll(Promise promise) {
promise.resolve(ReactNativeJSON.getSharedInstance().getAll());
}

/** ------------------ PREFERENCES ------------------ */
@ReactMethod
public void preferencesSetBool(String key, boolean value, Promise promise) {
ReactNativePreferences.getSharedInstance().setBooleanValue(key, value);
promise.resolve(null);
}

@ReactMethod
public void preferencesSetString(String key, String value, Promise promise) {
ReactNativePreferences.getSharedInstance().setStringValue(key, value);
promise.resolve(null);
}

@ReactMethod
public void preferencesGetAll(Promise promise) {
promise.resolve(ReactNativePreferences.getSharedInstance().getAll());
}

@ReactMethod
public void preferencesClearAll(Promise promise) {
ReactNativePreferences.getSharedInstance().clearAll();
promise.resolve(null);
}

@Override
public Map<String, Object> getConstants() {
Map<String, Object> constants = new HashMap<>();
// List<Map<String, Object>> appsList = new ArrayList<>();
// List<ReactNativeApp> reactNativeApps = ReactNativeApp.getApps(getReactApplicationContext());

// for (ReactNativeApp app : reactNativeApps) {
// appsList.add(RCTConvertFirebase.reactNativeAppToMap(app));
// }

// constants.put("NATIVE_FIREBASE_APPS", appsList);

// constants.put("FIREBASE_RAW_JSON", ReactNativeJSON.getSharedInstance().getRawJSON());

return constants;
}
}
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@
import java.util.List;

public class ReactNativeGoogleAdsConsentModule extends ReactNativeModule {
private static final String TAG = "GoogleAdsConsent";
private static final String TAG = "RNGoogleAdsConsentModule";
private ConsentInformation consentInformation;
private ConsentForm consentForm;

Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@
import javax.annotation.Nullable;

public class ReactNativeGoogleAdsInterstitialModule extends ReactNativeModule {
private static final String SERVICE = "GoogleAdsInterstitial";
private static final String SERVICE = "RNGoogleAdsInterstitialModule";
private static SparseArray<InterstitialAd> interstitialAdArray = new SparseArray<>();

public ReactNativeGoogleAdsInterstitialModule(ReactApplicationContext reactContext) {
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@
import java.util.Objects;

public class ReactNativeGoogleAdsModule extends ReactNativeModule {
private static final String SERVICE = "GoogleAds";
private static final String SERVICE = "RNGoogleAdsModule";

ReactNativeGoogleAdsModule(ReactApplicationContext reactContext) {
super(reactContext, SERVICE);
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ public class ReactNativeGoogleAdsPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new ReactNativeAppModule(reactContext));
modules.add(new ReactNativeGoogleAdsModule(reactContext));
modules.add(new ReactNativeGoogleAdsConsentModule(reactContext));
modules.add(new ReactNativeGoogleAdsInterstitialModule(reactContext));
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@
import io.invertase.googleads.common.ReactNativeModule;

public class ReactNativeGoogleAdsRewardedModule extends ReactNativeModule {
private static final String SERVICE = "GoogleAdsRewarded";
private static final String SERVICE = "RNGoogleAdsRewardedModule";
private static SparseArray<RewardedAd> rewardedAdArray = new SparseArray<>();

public ReactNativeGoogleAdsRewardedModule(ReactApplicationContext reactContext) {
118 changes: 118 additions & 0 deletions android/src/main/java/io/invertase/googleads/common/RCTConvert.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package io.invertase.googleads.common;

/*
* Copyright (c) 2016-present Invertase Limited & Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this library except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import android.content.Context;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

/**
* Utilities to convert to and from React Native bridge formats.
*/
public class RCTConvert {
private static String TAG = "RCTConvert";

/**
* Takes a value and calls the appropriate setter for its type on the target map + key
*
* @param key String key to set on target map
* @param value Object value to set on target map
* @param map WritableMap target map to write the value to
*/
@SuppressWarnings("unchecked")
public static WritableMap mapPutValue(String key, @Nullable Object value, WritableMap map) {
if (value == null) {
map.putNull(key);
return map;
}

String type = value.getClass().getName();

switch (type) {
case "java.lang.Boolean":
map.putBoolean(key, (Boolean) value);
break;
case "java.lang.Long":
Long longVal = (Long) value;
map.putDouble(key, (double) longVal);
break;
case "java.lang.Float":
float floatVal = (float) value;
map.putDouble(key, (double) floatVal);
break;
case "java.lang.Double":
map.putDouble(key, (Double) value);
break;
case "java.lang.Integer":
map.putInt(key, (int) value);
break;
case "java.lang.String":
map.putString(key, (String) value);
break;
case "org.json.JSONObject$1":
map.putString(key, value.toString());
break;
default:
if (List.class.isAssignableFrom(value.getClass())) {
map.putArray(key, Arguments.makeNativeArray((List<Object>) value));
} else if (Map.class.isAssignableFrom(value.getClass())) {
WritableMap childMap = Arguments.createMap();
Map<String, Object> valueMap = (Map<String, Object>) value;

for (Map.Entry<String, Object> entry : valueMap.entrySet()) {
mapPutValue(entry.getKey(), entry.getValue(), childMap);
}

map.putMap(key, childMap);
} else {
Log.d(TAG, "utils:mapPutValue:unknownType:" + type);
map.putNull(key);
}
}

return map;
}

// TODO Remove me - also in SharedUtils
public static WritableMap readableMapToWritableMap(ReadableMap map) {
WritableMap writableMap = Arguments.createMap();
// https://github.com/facebook/react-native/blob/main/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeMap.java#L58
writableMap.merge(map);
return writableMap;
}

public static Map<String, Object> toHashMap(ReadableMap readableMap) {
// https://github.com/facebook/react-native/blob/main/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java#L263
return readableMap.toHashMap();
}

public static List<Object> toArrayList(ReadableArray readableArray) {
// https://github.com/facebook/react-native/blob/main/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java#L140
return readableArray.toArrayList();
}
}
Original file line number Diff line number Diff line change
@@ -139,7 +139,7 @@ private boolean emit(final NativeEvent event) {
try {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(event.getEventName(), event.getEventBody());
.emit("rnapp_" + event.getEventName(), event.getEventBody());
} catch (Exception e) {
Log.wtf("RN_EVENT_EMITTER", "Error sending Event " + event.getEventName(), e);
return false;
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ public Activity getActivity() {
@Nonnull
@Override
public String getName() {
return "RNAdMob" + moduleName + "Module";
return moduleName;
}

@Override
2 changes: 1 addition & 1 deletion ios/RNGoogleAds/common/RNRCTEventEmitter.m
Original file line number Diff line number Diff line change
@@ -75,7 +75,7 @@ - (void)notifyJsReady:(BOOL)jsReady {
- (void)sendEventWithName:(NSString *)eventName body:(id)body {
@synchronized(self.jsListeners) {
if (self.bridge && self.isObserving && self.jsListeners[eventName] != nil) {
NSString *prefixedEventName = [@"rnfb_" stringByAppendingString:eventName];
NSString *prefixedEventName = [@"rnapp_" stringByAppendingString:eventName];
[self.bridge enqueueJSCall:@"RCTDeviceEventEmitter"
method:@"emit"
args:body ? @[ prefixedEventName, body ] : @[ prefixedEventName ]
44 changes: 39 additions & 5 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -18,17 +18,45 @@
import { Module } from './internal';
import validateAdRequestConfiguration from './validateAdRequestConfiguration';
import version from './version';
import AdEventType from './AdEventType';
import AdsConsentDebugGeography from './AdsConsentDebugGeography';
import AdsConsentStatus from './AdsConsentStatus';
import MaxAdContentRating from './MaxAdContentRating';
import RewardedAdEventType from './RewardedAdEventType';
import BannerAdSize from './BannerAdSize';
import TestIds from './TestIds';

const statics = {
AdsConsentDebugGeography,
AdsConsentStatus,
AdEventType,
RewardedAdEventType,
MaxAdContentRating,
TestIds,
BannerAdSize,
};

const namespace = 'google_ads';

const nativeModuleName = [
'RNGoogleAdsModule',
'RNGoogleAdsInterstitialModule',
'RNGoogleAdsRewardedModule',
];

class GoogleAdsModule extends Module {
constructor(...args) {
super(...args);

this.emitter.addListener('interstitial_event', event => {
this.emitter.emit(`interstitial_event:${event.adUnitId}:${event.requestId}`, event);
this.emitter.addListener('google_ads_interstitial_event', event => {
this.emitter.emit(
`google_ads_interstitial_event:${event.adUnitId}:${event.requestId}`,
event,
);
});

this.emitter.addListener('rewarded_event', event => {
this.emitter.emit(`rewarded_event:${event.adUnitId}:${event.requestId}`, event);
this.emitter.addListener('google_ads_rewarded_event', event => {
this.emitter.emit(`google_ads_rewarded_event:${event.adUnitId}:${event.requestId}`, event);
});
}

@@ -47,7 +75,13 @@ class GoogleAdsModule extends Module {
// import { SDK_VERSION } from '@invertase/react-native-google-ads';
export const SDK_VERSION = version;

export default new GoogleAdsModule();
export default new GoogleAdsModule('AppName', {
statics,
version,
namespace,
nativeModuleName,
nativeEvents: ['google_ads_interstitial_event', 'google_ads_rewarded_event'],
});

export { default as AdsConsentDebugGeography } from './AdsConsentDebugGeography';
export { default as AdsConsentStatus } from './AdsConsentStatus';
20 changes: 10 additions & 10 deletions lib/internal/GoogleAdsNativeEventEmitter.js
Original file line number Diff line number Diff line change
@@ -17,35 +17,35 @@

import { NativeEventEmitter, NativeModules } from 'react-native';

const { RNGoogleAdsModule } = NativeModules;
const { RNAppModule } = NativeModules;

class GoogleAdsNativeEventEmitter extends NativeEventEmitter {
constructor() {
super(RNGoogleAdsModule);
super(RNAppModule);
this.ready = false;
}

addListener(eventType, listener, context) {
if (!this.ready) {
RNGoogleAdsModule.eventsNotifyReady(true);
RNAppModule.eventsNotifyReady(true);
this.ready = true;
}
RNGoogleAdsModule.eventsAddListener(eventType);
RNAppModule.eventsAddListener(eventType);

let subscription = super.addListener(`google_ads_${eventType}`, listener, context);
let subscription = super.addListener(`rnapp_${eventType}`, listener, context);

// React Native 0.65+ altered EventEmitter:
// - removeSubscription is gone
// - addListener returns an unsubscriber instead of a more complex object with eventType etc

// make sure eventType for backwards compatibility just in case
subscription.eventType = `google_ads_${eventType}`;
subscription.eventType = `rnapp_${eventType}`;

// New style is to return a remove function on the object, just in csae people call that,
// we will modify it to do our native unsubscription then call the original
let originalRemove = subscription.remove;
let newRemove = () => {
RNGoogleAdsModule.eventsRemoveListener(eventType, false);
RNAppModule.eventsRemoveListener(eventType, false);
if (super.removeSubscription != null) {
// This is for RN <= 0.64 - 65 and greater no longer have removeSubscription
super.removeSubscription(subscription);
@@ -59,13 +59,13 @@ class GoogleAdsNativeEventEmitter extends NativeEventEmitter {
}

removeAllListeners(eventType) {
RNGoogleAdsModule.eventsRemoveListener(eventType, true);
super.removeAllListeners(`google_ads_${eventType}`);
RNAppModule.eventsRemoveListener(eventType, true);
super.removeAllListeners(`rnapp_${eventType}`);
}

// This is likely no longer ever called, but it is here for backwards compatibility with RN <= 0.64
removeSubscription(subscription) {
RNGoogleAdsModule.eventsRemoveListener(subscription.eventType.replace('google_ads_'), false);
RNAppModule.eventsRemoveListener(subscription.eventType.replace('rnapp_'), false);
if (super.removeSubscription) {
super.removeSubscription(subscription);
}
8 changes: 7 additions & 1 deletion lib/internal/registry/nativeModule.js
Original file line number Diff line number Diff line change
@@ -88,7 +88,9 @@ function initialiseNativeModule(module) {
const config = module._config;
const key = nativeModuleKey(module);
const { namespace, nativeEvents, nativeModuleName } = config;
const nativeModuleNames = [nativeModuleName];
const multiModuleRoot = {};
const multiModule = Array.isArray(nativeModuleName);
const nativeModuleNames = multiModule ? nativeModuleName : [nativeModuleName];

for (let i = 0; i < nativeModuleNames.length; i++) {
const nativeModule = NativeModules[nativeModuleNames[i]];
@@ -99,6 +101,10 @@ function initialiseNativeModule(module) {
throw new Error(getMissingModuleHelpText(namespace));
}

if (multiModule) {
multiModuleRoot[nativeModuleNames[i]] = !!nativeModule;
}

const argToPrepend = [];

Object.assign(multiModuleRoot, nativeModuleWrapped(namespace, nativeModule, argToPrepend));

0 comments on commit 9569b98

Please sign in to comment.