Skip to content
This repository was archived by the owner on Apr 4, 2023. It is now read-only.

Commit 033bada

Browse files
Implementing missing callback function for Ads #1475
1 parent f1a55cb commit 033bada

File tree

6 files changed

+146
-45
lines changed

6 files changed

+146
-45
lines changed

demo/app/main-view-model.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,11 @@ export class HelloWorldModel extends Observable {
608608
"fee4cf319a242eab4701543e4c16db89c722731f", // Eddy's iPad Pro
609609
"a4cbb499e279054b55c206528f8510ff7fbf20c8", // Eddy's iPhone X
610610
],
611-
keywords: ["keyword1", "keyword2"] // add keywords for ad targeting
611+
keywords: ["keyword1", "keyword2"], // add keywords for ad targeting
612+
onClicked: () => console.log("Ad clicked"),
613+
onLeftApplication: () => console.log("Ad left application (opened a browser, likely)"),
614+
onOpened: () => console.log("Ad opened"),
615+
onClosed: () => console.log("Ad closed")
612616
}).then(
613617
() => {
614618
alert({
@@ -636,8 +640,7 @@ export class HelloWorldModel extends Observable {
636640
iosTestDeviceIds: [
637641
"45d77bf513dfabc2949ba053da83c0c7b7e87715", // Eddy's iPhone 6s
638642
"fee4cf319a242eab4701543e4c16db89c722731f" // Eddy's iPad Pro
639-
],
640-
onAdClosed: () => console.log("Interstitial closed")
643+
]
641644
}).then(
642645
() => {
643646
console.log("AdMob interstitial showing");
@@ -662,7 +665,10 @@ export class HelloWorldModel extends Observable {
662665
"45d77bf513dfabc2949ba053da83c0c7b7e87715", // Eddy's iPhone 6s
663666
"fee4cf319a242eab4701543e4c16db89c722731f" // Eddy's iPad Pro
664667
],
665-
onAdClosed: () => console.log("Interstitial closed")
668+
onClosed: () => console.log("Interstitial closed"),
669+
onClicked: () => console.log("Interstitial clicked"),
670+
onLeftApplication: () => console.log("Interstitial left application (opened a browser, likely)"),
671+
onOpened: () => console.log("Interstitial opened")
666672
}).then(
667673
() => console.log("AdMob interstitial preloaded"),
668674
errorMessage => {

demo/app/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
},
2727
"homepage": "https://github.com/NativeScript/template-hello-world-ts",
2828
"android": {
29-
"v8Flags": "--expose_gc"
29+
"v8Flags": "--expose_gc",
30+
"markingMode": "none"
3031
},
3132
"discardUncaughtJsExceptions": false,
3233
"devDependencies": {

docs/ADMOB.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,10 @@ Go [manage your AdMob app](https://apps.admob.com) and grab the banner, then sho
6969
"45d77bf513dfabc2949ba053da83c0c7b7e87715", // Eddy's iPhone 6s
7070
"fee4cf319a242eab4701543e4c16db89c722731f" // Eddy's iPad Pro
7171
],
72-
keywords: ["keyword1", "keyword2"] // add keywords for ad targeting
72+
keywords: ["keyword1", "keyword2"], // add keywords for ad targeting
73+
onOpened: () => console.log("Ad opened"),
74+
onClicked: () => console.log("Ad clicked"),
75+
onLeftApplication: () => console.log("Ad left application")
7376
}).then(
7477
function () {
7578
console.log("AdMob banner showing");
@@ -123,7 +126,10 @@ There's two ways how you can use this function:
123126
"45d77bf513dfabc2949ba053da83c0c7b7e87715", // Eddy's iPhone 6s
124127
"fee4cf319a242eab4701543e4c16db89c722731f" // Eddy's iPad Pro
125128
],
126-
onAdClosed: () => console.log("Interstitial closed")
129+
onClosed: () => console.log("Interstitial closed"),
130+
onClicked: () => console.log("Interstitial clicked"),
131+
onOpened: () => console.log("Interstitial opened"),
132+
onLeftApplication: () => console.log("Interstitial left application")
127133
}).then(
128134
function () {
129135
console.log("AdMob interstitial preloaded, you can now call 'showInterstitial' at any time to show it without delay.");
@@ -188,11 +194,11 @@ firebase.admob.showRewardedVideoAd({
188194
console.log("onRewarded called with amount " + reward.amount);
189195
console.log("onRewarded called with type " + reward.type);
190196
},
191-
onLeftApplication: () => console.log("onLeftApplication"),
192-
onClosed: () => console.log("onClosed"),
193197
onOpened: () => console.log("onOpened"),
198+
onClosed: () => console.log("onClosed"),
194199
onStarted: () => console.log("onStarted"),
195200
onCompleted: () => console.log("onCompleted"),
201+
onLeftApplication: () => console.log("onLeftApplication")
196202
}).then(
197203
function() {
198204
console.log("RewardedVideoAd showing");

src/admob/admob.android.ts

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,20 @@ export function showBanner(arg: BannerOptions): Promise<any> {
3434
const BannerAdListener = com.google.android.gms.ads.AdListener.extend({
3535
resolve: null,
3636
reject: null,
37-
onAdLoaded: () => {
38-
this.resolve();
39-
},
40-
onAdFailedToLoad: errorCode => {
41-
this.reject(errorCode);
37+
onAdLoaded: () => this.resolve(),
38+
onAdFailedToLoad: errorCode => this.reject(errorCode),
39+
onAdClicked: () => arg.onClicked && arg.onClicked(),
40+
onAdOpened: () => arg.onOpened && arg.onOpened(),
41+
onAdLeftApplication: () => arg.onLeftApplication && arg.onLeftApplication(),
42+
onAdClosed: () => {
43+
if (firebase.admob.adView) {
44+
firebase.admob.adView.setAdListener(null);
45+
firebase.admob.adView = null;
46+
}
47+
arg.onClosed && arg.onClosed();
4248
}
4349
});
50+
4451
firebase.admob.adView.setAdListener(new BannerAdListener());
4552

4653
const ad = _buildAdRequest(settings);
@@ -104,18 +111,18 @@ export function preloadInterstitial(arg: InterstitialOptions): Promise<any> {
104111

105112
// Interstitial ads must be loaded before they can be shown, so adding a listener
106113
const InterstitialAdListener = com.google.android.gms.ads.AdListener.extend({
107-
onAdLoaded: () => {
108-
this.resolve();
109-
},
110-
onAdFailedToLoad: errorCode => {
111-
this.reject(errorCode);
112-
},
114+
onAdLoaded: () => this.resolve(),
115+
onAdFailedToLoad: errorCode => this.reject(errorCode),
116+
onAdClicked: () => arg.onClicked && arg.onClicked(),
117+
onAdOpened: () => arg.onOpened && arg.onOpened(),
118+
onAdLeftApplication: () => arg.onLeftApplication && arg.onLeftApplication(),
113119
onAdClosed: () => {
114120
if (firebase.admob.interstitialView) {
115121
firebase.admob.interstitialView.setAdListener(null);
116122
firebase.admob.interstitialView = null;
117123
}
118-
arg.onAdClosed && arg.onAdClosed();
124+
arg.onAdClosed && arg.onAdClosed(); // TODO remove one day
125+
arg.onClosed && arg.onClosed();
119126
}
120127
});
121128
firebase.admob.interstitialView.setAdListener(new InterstitialAdListener());
@@ -157,15 +164,17 @@ export function showInterstitial(arg?: InterstitialOptions): Promise<any> {
157164
}
158165
resolve();
159166
},
160-
onAdFailedToLoad: errorCode => {
161-
reject(errorCode);
162-
},
167+
onAdFailedToLoad: errorCode => reject(errorCode),
168+
onAdClicked: () => arg.onClicked && arg.onClicked(),
169+
onAdOpened: () => arg.onOpened && arg.onOpened(),
170+
onAdLeftApplication: () => arg.onLeftApplication && arg.onLeftApplication(),
163171
onAdClosed: () => {
164172
if (firebase.admob.interstitialView) {
165173
firebase.admob.interstitialView.setAdListener(null);
166174
firebase.admob.interstitialView = null;
167175
}
168-
arg.onAdClosed && arg.onAdClosed();
176+
arg.onAdClosed && arg.onAdClosed(); // TODO remove one day
177+
arg.onClosed && arg.onClosed();
169178
}
170179
});
171180
firebase.admob.interstitialView.setAdListener(new InterstitialAdListener());

src/admob/admob.d.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,35 @@ export enum ERROR_CODES {
2323
ERROR_CODE_NO_FILL
2424
}
2525

26-
export interface BannerOptions {
26+
interface AdLifeCycleEvents {
27+
/**
28+
* Called when the user is about to return to the application after clicking on an ad.
29+
* For all callbacks, see https://developers.google.com/android/reference/com/google/android/gms/ads/AdListener
30+
*/
31+
onClosed?: () => void;
32+
33+
/**
34+
* @Deprecated please use onClosed instead. Simply remove these two letters: 'Ad'.
35+
*/
36+
onAdClosed?: () => void;
37+
38+
/**
39+
* Called when a click is recorded for an ad.
40+
*/
41+
onClicked?: () => void;
42+
43+
/**
44+
* Called when an ad opens an overlay that covers the screen.
45+
*/
46+
onOpened?: () => void;
47+
48+
/**
49+
* Called when an ad leaves the application (e.g., to go to the browser).
50+
*/
51+
onLeftApplication?: () => void;
52+
}
53+
54+
export interface BannerOptions extends AdLifeCycleEvents {
2755
/**
2856
* The layout of the banner.
2957
* Default AD_SIZE.SMART_BANNER
@@ -77,7 +105,7 @@ export interface BannerOptions {
77105
keywords?: string[];
78106
}
79107

80-
export interface InterstitialOptions {
108+
export interface InterstitialOptions extends AdLifeCycleEvents {
81109
/**
82110
* When false (default) you'll get real banners.
83111
*/
@@ -100,11 +128,6 @@ export interface InterstitialOptions {
100128
* ["ce97330130c9047ce0d4430d37d713b1", ".."]
101129
*/
102130
iosTestDeviceIds?: string[];
103-
104-
/**
105-
* Invoked when the user closes the interstitial.
106-
*/
107-
onAdClosed?: () => void;
108131
}
109132

110133
export interface PreloadRewardedVideoAdOptions {
@@ -142,12 +165,9 @@ export interface RewardedVideoAdReward {
142165
type: string;
143166
}
144167

145-
export interface RewardedVideoAdCallbacks {
146-
onOpened?: () => void;
168+
export interface RewardedVideoAdCallbacks extends AdLifeCycleEvents {
147169
onStarted?: () => void;
148170
onCompleted?: () => void;
149-
onClosed?: () => void;
150-
onLeftApplication?: () => void;
151171
onLoaded?: () => void,
152172
onFailedToLoad?: (err) => void,
153173
onRewarded?: (reward: RewardedVideoAdReward) => void;

src/admob/admob.ios.ts

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,25 @@ export function showBanner(arg: BannerOptions): Promise<any> {
6565

6666
firebase.admob.adView.loadRequest(adRequest);
6767

68-
// TODO consider listening to delegate features like 'ad loaded' (Android resolves when the banner is actually showing)
69-
// adView.delegate = self;
68+
// with interstitials you MUST wait for the ad to load before showing it, so requiring this delegate
69+
let delegate = GADBannerViewDelegateImpl.new().initWithOptionsAndCallback(
70+
arg,
71+
(ad: GADBannerView, error: GADRequestError) => {
72+
if (error) {
73+
reject(error.localizedDescription);
74+
} else {
75+
resolve();
76+
}
77+
}, () => {
78+
arg.onClosed && arg.onClosed();
79+
CFRelease(delegate);
80+
delegate = undefined;
81+
});
7082

71-
view.addSubview(firebase.admob.adView);
83+
CFRetain(delegate);
84+
firebase.admob.adView.delegate = delegate;
7285

73-
resolve();
86+
view.addSubview(firebase.admob.adView);
7487
} catch (ex) {
7588
console.log("Error in firebase.admob.showBanner: " + ex);
7689
reject(ex);
@@ -90,15 +103,17 @@ export function preloadInterstitial(arg: InterstitialOptions): Promise<any> {
90103
firebase.admob.interstitialView = GADInterstitial.alloc().initWithAdUnitID(settings.iosInterstitialId);
91104

92105
// with interstitials you MUST wait for the ad to load before showing it, so requiring this delegate
93-
let delegate = GADInterstitialDelegateImpl.new().initWithCallback(
106+
let delegate = GADInterstitialDelegateImpl.new().initWithOptionsAndCallback(
107+
arg,
94108
(ad: GADInterstitial, error: GADRequestError) => {
95109
if (error) {
96110
reject(error.localizedDescription);
97111
} else {
98112
resolve();
99113
}
100114
}, () => {
101-
arg.onAdClosed && arg.onAdClosed();
115+
arg.onAdClosed && arg.onAdClosed(); // TODO remove one day
116+
arg.onClosed && arg.onClosed();
102117
CFRelease(delegate);
103118
delegate = undefined;
104119
});
@@ -152,7 +167,7 @@ export function showInterstitial(arg?: InterstitialOptions): Promise<any> {
152167
firebase.admob.interstitialView = GADInterstitial.alloc().initWithAdUnitID(settings.iosInterstitialId);
153168

154169
// with interstitials you MUST wait for the ad to load before showing it, so requiring this delegate
155-
let delegate = GADInterstitialDelegateImpl.new().initWithCallback((ad: GADInterstitial, error: GADRequestError) => {
170+
let delegate = GADInterstitialDelegateImpl.new().initWithOptionsAndCallback(arg, (ad: GADInterstitial, error: GADRequestError) => {
156171
if (error) {
157172
reject(error.localizedDescription);
158173
} else {
@@ -331,7 +346,8 @@ function _getBannerType(size): any {
331346

332347
class GADInterstitialDelegateImpl extends NSObject implements GADInterstitialDelegate {
333348
public static ObjCProtocols = [];
334-
onAdCloseCallback: () => void;
349+
private options: InterstitialOptions;
350+
private onAdCloseCallback: () => void;
335351

336352
static new(): GADInterstitialDelegateImpl {
337353
if (GADInterstitialDelegateImpl.ObjCProtocols.length === 0 && typeof (GADInterstitialDelegate) !== "undefined") {
@@ -342,7 +358,8 @@ class GADInterstitialDelegateImpl extends NSObject implements GADInterstitialDel
342358

343359
private callback: (ad: GADInterstitial, error?: GADRequestError) => void;
344360

345-
public initWithCallback(callback: (ad: GADInterstitial, error?: GADRequestError) => void, onAdCloseCallback: () => void = null): GADInterstitialDelegateImpl {
361+
public initWithOptionsAndCallback(options: InterstitialOptions, callback: (ad: GADInterstitial, error?: GADRequestError) => void, onAdCloseCallback: () => void = null): GADInterstitialDelegateImpl {
362+
this.options = options;
346363
this.callback = callback;
347364
this.onAdCloseCallback = onAdCloseCallback;
348365
return this;
@@ -359,6 +376,48 @@ class GADInterstitialDelegateImpl extends NSObject implements GADInterstitialDel
359376
public interstitialDidFailToReceiveAdWithError(ad: GADInterstitial, error: GADRequestError): void {
360377
this.callback(ad, error);
361378
}
379+
380+
public interstitialWillLeaveApplication(ad: GADInterstitial): void {
381+
this.options.onLeftApplication && this.options.onLeftApplication();
382+
}
383+
}
384+
385+
class GADBannerViewDelegateImpl extends NSObject implements GADBannerViewDelegate {
386+
public static ObjCProtocols = [];
387+
private onAdCloseCallback: () => void;
388+
private options: BannerOptions;
389+
390+
static new(): GADBannerViewDelegateImpl {
391+
if (GADBannerViewDelegateImpl.ObjCProtocols.length === 0 && typeof (GADBannerViewDelegate) !== "undefined") {
392+
GADBannerViewDelegateImpl.ObjCProtocols.push(GADBannerViewDelegate);
393+
}
394+
return <GADBannerViewDelegateImpl>super.new();
395+
}
396+
397+
private callback: (ad: GADBannerView, error: GADRequestError) => void;
398+
399+
public initWithOptionsAndCallback(options: BannerOptions, callback: (ad: GADBannerView, error: GADRequestError) => void, onAdCloseCallback: () => void = null): GADBannerViewDelegateImpl {
400+
this.options = options;
401+
this.callback = callback;
402+
this.onAdCloseCallback = onAdCloseCallback;
403+
return this;
404+
}
405+
406+
public adViewDidReceiveAd(bannerView: GADBannerView): void {
407+
this.callback(bannerView, null);
408+
}
409+
410+
public adViewDidFailToReceiveAdWithError(bannerView: GADBannerView, error: GADRequestError): void {
411+
this.callback(bannerView, error);
412+
}
413+
414+
public adViewDidDismissScreen(bannerView: GADBannerView): void {
415+
this.onAdCloseCallback();
416+
}
417+
418+
public adViewWillLeaveApplication(bannerView: GADBannerView): void {
419+
this.options.onLeftApplication && this.options.onLeftApplication();
420+
}
362421
}
363422

364423
class GADRewardBasedVideoAdDelegateImpl extends NSObject implements GADRewardBasedVideoAdDelegate {

0 commit comments

Comments
 (0)