Skip to content
This repository has been archived by the owner on Apr 27, 2022. It is now read-only.

Commit

Permalink
feat: add AdError class
Browse files Browse the repository at this point in the history
  • Loading branch information
wjaykim committed Nov 28, 2021
1 parent 75562e4 commit c92128e
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 101 deletions.
2 changes: 2 additions & 0 deletions android/src/main/java/com/rnadmob/admob/RNAdMobCommon.java
Expand Up @@ -132,4 +132,6 @@ static public AdManagerAdRequest buildAdRequest(ReadableMap requestOptions) {

return builder.build();
}


}
16 changes: 5 additions & 11 deletions android/src/main/java/com/rnadmob/admob/RNAdMobPromiseHolder.java
@@ -1,8 +1,11 @@
package com.rnadmob.admob;

import static com.rnadmob.admob.RNAdMobEventModule.AD_FAILED_TO_PRESENT;

import android.util.SparseArray;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.WritableMap;
import com.google.android.gms.ads.AdError;

import java.util.Locale;
Expand All @@ -26,19 +29,10 @@ public void resolve(int requestId) {
}
}

public void reject(int requestId, AdError adError) {
Promise promise = promiseArray.get(requestId);
if (promise != null) {
String code = String.format(Locale.getDefault(),"E_AD_PRESENT_FAILED(%d)", adError.getCode());
promise.reject(code, adError.getMessage());
promiseArray.delete(requestId);
}
}

public void reject(int requestId, String code, String message) {
public void reject(int requestId, WritableMap error) {
Promise promise = promiseArray.get(requestId);
if (promise != null) {
promise.reject(code, message);
promise.reject(AD_FAILED_TO_PRESENT, "Error occurred while presenting ad.", error);
promiseArray.delete(requestId);
}
}
Expand Down
@@ -1,6 +1,6 @@
package com.rnadmob.admob.ads.fullscreen;

import static com.rnadmob.admob.RNAdMobEventModule.AD_FAILED_TO_PRESENT;
import static com.rnadmob.admob.RNAdMobEventModule.AD_FAILED_TO_LOAD;

import android.os.Handler;

Expand All @@ -9,7 +9,6 @@
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
Expand Down Expand Up @@ -98,10 +97,9 @@ public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
@Override
protected void show(AppOpenAd ad, int requestId) {
if (isAdExpired()) {
presentPromiseHolder.reject(requestId, "E_AD_NOT_READY", "Ad is expired.");
WritableMap error = Arguments.createMap();
error.putString("message", "Ad is expired.");
sendEvent(AD_FAILED_TO_PRESENT, requestId, error);
WritableMap error = createErrorObject(null, "Ad is expired.");
sendError(AD_FAILED_TO_LOAD, requestId, null, error);

requestAd(requestId, unitId, options, null);
return;
}
Expand Down Expand Up @@ -131,5 +129,4 @@ public void onStart(@NonNull LifecycleOwner owner) {
}
}
}

}
Expand Up @@ -25,7 +25,6 @@
import com.rnadmob.admob.RNAdMobEventModule;
import com.rnadmob.admob.RNAdMobPromiseHolder;

import java.util.Locale;
import java.util.Objects;

public abstract class RNAdMobFullScreenAdModule<T> extends ActivityAwareJavaModule {
Expand Down Expand Up @@ -60,12 +59,33 @@ protected void sendEvent(String eventName, int requestId, @Nullable WritableMap
RNAdMobEventModule.sendEvent(eventName, getAdType(), requestId, data);
}

protected Activity getCurrentActivity(Promise promise) {
Activity activity = super.getCurrentActivity();
if (activity == null && promise != null) {
promise.reject("E_NULL_ACTIVITY", "Cannot process Ad because the current Activity is null.");
protected void sendError(String eventName, int requestId, @Nullable Promise promise, WritableMap error) {
if (promise != null) {
String message;
if (eventName.equals(AD_FAILED_TO_LOAD)) {
message = "Error occurred while loading ad.";
} else {
message = "Error occurred while showing ad.";
}
promise.reject(eventName, message, error.copy());
} else if (eventName.equals(AD_FAILED_TO_PRESENT)) {
presentPromiseHolder.reject(requestId, error.copy());
}
return activity;
sendEvent(eventName, requestId, error.copy());
}

protected WritableMap createErrorObject(@Nullable Integer code, String message) {
WritableMap error = Arguments.createMap();
if (code == null)
error.putNull("code");
else
error.putInt("code", code);
error.putString("message", message);
return error;
}

protected WritableMap createErrorObject(AdError adError) {
return createErrorObject(adError.getCode(), adError.getMessage());
}

private AdLoadCallback<T> getAdLoadCallback(int requestId, ReadableMap options, Promise promise) {
Expand Down Expand Up @@ -95,15 +115,8 @@ public void onAdLoaded(@NonNull T ad) {

@Override
public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
if (promise != null) {
String code = String.format(Locale.getDefault(), "E_AD_LOAD_FAILED(%d)", loadAdError.getCode());
promise.reject(code, loadAdError.getMessage());
}

WritableMap error = Arguments.createMap();
error.putInt("code", loadAdError.getCode());
error.putString("message", loadAdError.getMessage());
sendEvent(AD_FAILED_TO_LOAD, requestId, error);
WritableMap error = createErrorObject(loadAdError);
sendError(AD_FAILED_TO_LOAD, requestId, promise, error);

if (getAdType().equals(RNAdMobAppOpenAdModule.AD_TYPE)) {
if (!RNAdMobAppOpenAdModule.appStarted) {
Expand Down Expand Up @@ -136,27 +149,24 @@ public void onAdDismissedFullScreenContent() {

@Override
public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) {
presentPromiseHolder.reject(requestId, adError);

WritableMap error = Arguments.createMap();
error.putInt("code", adError.getCode());
error.putString("message", adError.getMessage());
sendEvent(AD_FAILED_TO_PRESENT, requestId, error);
WritableMap error = createErrorObject(adError);
sendError(AD_FAILED_TO_PRESENT, requestId, null, error);

adHolder.remove(requestId);
}

@Override
public void onAdShowedFullScreenContent() {
presentPromiseHolder.resolve(requestId);

sendEvent(AD_PRESENTED, requestId, null);
}
};
}

protected void requestAd(int requestId, String unitId, ReadableMap options, final Promise promise) {
if (currentActivity == null) {
WritableMap error = createErrorObject(null, "Current activity is null.");
sendError(AD_FAILED_TO_LOAD, requestId, promise, error);
return;
}

Expand All @@ -171,6 +181,8 @@ protected void requestAd(int requestId, String unitId, ReadableMap options, fina

protected void presentAd(int requestId, final Promise promise) {
if (currentActivity == null) {
WritableMap error = createErrorObject(null, "Current activity is null.");
sendError(AD_FAILED_TO_PRESENT, requestId, promise, error);
return;
}

Expand All @@ -180,18 +192,16 @@ protected void presentAd(int requestId, final Promise promise) {
presentPromiseHolder.add(requestId, promise);
show(ad, requestId);
} else {
if (promise != null) {
promise.reject("E_AD_NOT_READY", "Ad is not ready.");
}
WritableMap error = Arguments.createMap();
error.putString("message", "Ad is not ready.");
sendEvent(AD_FAILED_TO_PRESENT, requestId, error);
WritableMap error = createErrorObject(null, "Ad is not loaded.");
sendError(AD_FAILED_TO_PRESENT, requestId, promise, error);
}
});
}

protected void destroyAd(int requestId) {
adHolder.remove(requestId);
presentPromiseHolder.reject(requestId, "E_AD_DESTROYED", "Ad has been destroyed.");

WritableMap error = createErrorObject(null, "Ad has been destroyed.");
presentPromiseHolder.reject(requestId, error);
}
}
6 changes: 6 additions & 0 deletions docs/docs/usage/appopen.mdx
Expand Up @@ -40,6 +40,12 @@ An example of a cold start is when a user opens your app for the first time. Wit

The preferred way to use app open ads on cold starts is to use a loading screen to load your game or app assets, and to only show the ad from the loading screen. If your app has completed loading and has sent the user to the main content of your app, do not show the ad.

## Accessing Ad's status

You can access to ad's status by using derived values of `useAppOpenAd` hook.

If you are using Class API instead of using hook, add listeners to listen for ad's status changes such as `adDismissed` event.

## Usage Example

Example below uses external library [react-native-bootsplash](https://github.com/zoontek/react-native-bootsplash) to implement splash screen.
Expand Down
7 changes: 3 additions & 4 deletions ios/Ads/FullScreen/RNAdMobAppOpen.swift
Expand Up @@ -55,10 +55,9 @@ class RNAdMobAppOpen: RNAdMobFullScreenAd<GADAppOpenAd> {

override func show(ad: GADAppOpenAd, viewController: UIViewController, requestId: Int) {
if (isAdExpired()) {
presentPromiseHolder.reject(requestId: requestId, code: "E_AD_NOT_READY", message: "Ad is expired")
var error = Dictionary<String, Any>()
error.updateValue("Ad is expired", forKey: "message")
sendEvent(eventName: kEventAdFailedToPresent, requestId: requestId, data: error)
let errorData = createErrorData(code: nil, message: "Ad is expired.")
sendError(eventName: kEventAdFailedToPresent, requestId: requestId, reject: nil, errorData: errorData)

requestAd(requestId, unitId: unitId!, options: options!, resolve: nil, reject: nil)
return
}
Expand Down
66 changes: 40 additions & 26 deletions ios/Ads/FullScreen/RNAdMobFullScreenAd.swift
Expand Up @@ -22,6 +22,35 @@ class RNAdMobFullScreenAd<T>: NSObject {
RNAdMobEvent.send(eventName, type: getAdType(), requestId: NSNumber(value: requestId), data: data)
}

func sendError(eventName: String, requestId: Int, reject: RCTPromiseRejectBlock?, errorData: Dictionary<String, Any>) {
let error = NSError.init(domain: "com.rnadmob.admob", code: 0, userInfo: errorData)
if (reject != nil) {
var message = ""
if (eventName == kEventAdFailedToLoad) {
message = "Error occurred while loading ad."
} else {
message = "Error occurred while showing ad."
}
reject!(eventName, message, error)
} else if (eventName == kEventAdFailedToPresent) {
presentPromiseHolder.reject(requestId: requestId, errorData: errorData)
}
sendEvent(eventName: eventName, requestId: requestId, data: errorData)
}

func createErrorData(code: Int?, message: String) -> Dictionary<String, Any> {
var error = Dictionary<String, Any>()
if (code != nil) {
error.updateValue(code!, forKey: "code")
}
error.updateValue(message, forKey: "message")
return error
}

func createErrorData(error: Error) -> Dictionary<String, Any> {
return createErrorData(code: (error as NSError).code, message: error.localizedDescription)
}

func getViewController(reject: RCTPromiseRejectBlock?) -> UIViewController? {
var viewController = RCTKeyWindow()?.rootViewController
while true {
Expand All @@ -35,9 +64,6 @@ class RNAdMobFullScreenAd<T>: NSObject {
break
}
}
if (viewController == nil && reject != nil) {
reject!("E_NIL_VC", "Cannot process Ad because the current View Controller is nil.", nil)
}
return viewController
}

Expand Down Expand Up @@ -76,15 +102,8 @@ class RNAdMobFullScreenAd<T>: NSObject {
}
}
func onAdFailedToLoad(error: Error) {
if (reject != nil) {
let code = String.localizedStringWithFormat("E_AD_LOAD_FAILED(%d)", (error as NSError).code)
reject!(code, error.localizedDescription, error)
}

var data = Dictionary<String, Any>()
data.updateValue((error as NSError).code, forKey: "code")
data.updateValue(error.localizedDescription, forKey: "message")
module.sendEvent(eventName: kEventAdFailedToLoad, requestId: requestId, data: data)
let errorData = module.createErrorData(error: error)
module.sendError(eventName: kEventAdFailedToLoad, requestId: requestId, reject: reject, errorData: errorData)

if (module.getAdType() == RNAdMobAppOpen.AD_TYPE) {
if (!RNAdMobAppOpen.appStarted) {
Expand All @@ -110,13 +129,8 @@ class RNAdMobFullScreenAd<T>: NSObject {
module.sendEvent(eventName: kEventAdPresented, requestId: requestId, data: nil)
}
func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
module.presentPromiseHolder.reject(requestId: requestId, error: error)
module.sendEvent(eventName: kEventAdFailedToPresent, requestId: requestId, data: nil)

var data = Dictionary<String, Any>()
data.updateValue((error as NSError).code, forKey: "code")
data.updateValue(error.localizedDescription, forKey: "message")
module.sendEvent(eventName: kEventAdFailedToLoad, requestId: requestId, data: data)
let errorData = module.createErrorData(error: error)
module.sendError(eventName: kEventAdFailedToPresent, requestId: requestId, reject: nil, errorData: errorData)

module.adHolder.remove(requestId: requestId)
}
Expand Down Expand Up @@ -155,6 +169,8 @@ class RNAdMobFullScreenAd<T>: NSObject {
DispatchQueue.main.async { [self] in
let viewController = getViewController(reject: reject)
if (viewController == nil) {
let errorData = createErrorData(code: nil, message: "Current view controller is nil.")
sendError(eventName: kEventAdFailedToPresent, requestId: requestId, reject: reject, errorData: errorData)
return
}

Expand All @@ -165,18 +181,16 @@ class RNAdMobFullScreenAd<T>: NSObject {
}
self.show(ad: ad!, viewController: viewController!, requestId: requestId)
} else {
if (reject != nil) {
reject!("E_AD_NOT_READY", "Ad is not ready", nil)
}
var error = Dictionary<String, Any>()
error.updateValue("Ad is not ready", forKey: "message")
sendEvent(eventName: kEventAdFailedToPresent, requestId: requestId, data: error)
let errorData = createErrorData(code: nil, message: "Ad is not loaded.")
sendError(eventName: kEventAdFailedToPresent, requestId: requestId, reject: nil, errorData: errorData)
}
}
}

func destroyAd(_ requestId: Int) {
adHolder.remove(requestId: requestId)
presentPromiseHolder.reject(requestId: requestId, code: "E_AD_DESTROYED", message: "Ad has been destroyed")

let errorData = createErrorData(code: nil, message: "Ad has been destroyed.")
presentPromiseHolder.reject(requestId: requestId, errorData: errorData)
}
}
15 changes: 3 additions & 12 deletions ios/RNAdMobPromiseHolder.swift
Expand Up @@ -23,20 +23,11 @@ class RNAdMobPromiseHolder {
rejectArray.removeValue(forKey: requestId)
}

func reject(requestId: Int, error: Error) {
func reject(requestId: Int, errorData: Dictionary<String, Any>) {
let rejectBlock = rejectArray[requestId]
if (rejectBlock != nil) {
let code = String.localizedStringWithFormat("E_AD_PRESENT_FAILED(%d)", (error as NSError).code)
rejectBlock!(code, error.localizedDescription, error)
}
resolveArray.removeValue(forKey: requestId)
rejectArray.removeValue(forKey: requestId)
}

func reject(requestId: Int, code: String, message: String) {
let rejectBlock = rejectArray[requestId]
if (rejectBlock != nil) {
rejectBlock!(code, message, nil)
let error = NSError.init(domain: "com.rnadmob.admob", code: 0, userInfo: errorData)
rejectBlock!(kEventAdFailedToPresent, "Error occurred while showing ad.", error)
}
resolveArray.removeValue(forKey: requestId)
rejectArray.removeValue(forKey: requestId)
Expand Down
6 changes: 6 additions & 0 deletions src/AdError.ts
@@ -0,0 +1,6 @@
export default class AdError extends Error {
name = 'AdError';
constructor(public readonly message: string, public readonly code?: number) {
super();
}
}

0 comments on commit c92128e

Please sign in to comment.