Skip to content

Commit

Permalink
Added support for non-personalized ads (#215)
Browse files Browse the repository at this point in the history
* Cleaned repetitive channel methods arguments.
And added support for non-personalised ads on the Dart side.

* Added non-personalized ads on the Android side.

* Added non-personalized ads on the iOS side.

* Updated README.md.
  • Loading branch information
Skyost committed Sep 21, 2020
1 parent 97003f8 commit 559c974
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 63 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ await Admob.requestTrackingAuthorization();
- TL;DR - Make sure you have the correct combination of id's per platform. See:[161](https://github.com/kmcgill88/admob_flutter/issues/161)
- Objective-C based project cannot build
- TL;DR - You have to enable swift support for your flutter project. See: [stackoverflow](https://stackoverflow.com/questions/52244346/how-to-enable-swift-support-for-existing-project-in-flutter) and [123](https://github.com/kmcgill88/admob_flutter/issues/123)
- How do I manage consentement for users in the European Economic Area?
- Pass `nonPersonalizedAds: false` to the classes constructor (`AdmobBanner`, `AdmobInterstitial` and `AdmobReward`) in order to not display personalized ads for users who don't give their consent. A way to ask users for their consent is to use the plugin [admob_consent](https://pub.dev/packages/admob_consent). Please note that the new recommended is to use the brand new UMP SDK ([Android](https://developers.google.com/admob/ump/android/quick-start), [iOS](https://developers.google.com/admob/ump/ios/quick-start)).

# Recipes
- [AppBar Banner](https://mcgilldevtech.com/2020/08/admob-flutter-appbar-banner-recipe/)
Expand Down
24 changes: 17 additions & 7 deletions android/src/main/kotlin/com/shatsy/admobflutter/AdmobBanner.kt
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
package com.shatsy.admobflutter

import android.content.Context
import android.os.Bundle
import android.view.View
import com.google.ads.mediation.admob.AdMobAdapter
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.AdSize
import com.google.android.gms.ads.AdView
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.platform.PlatformView

class AdmobBanner(context: Context, messenger: BinaryMessenger, id: Int, args: HashMap<*, *>?) : PlatformView, MethodCallHandler {

class AdmobBanner(context: Context, messenger: BinaryMessenger, id: Int, args: HashMap<*, *>) : PlatformView, MethodCallHandler {
private val channel: MethodChannel = MethodChannel(messenger, "admob_flutter/banner_$id")
private val adView: AdView = AdView(context)

init {
channel.setMethodCallHandler(this)

adView.adSize = getSize(context, args?.get("adSize") as HashMap<*, *>)
adView.adUnitId = args?.get("adUnitId") as String?
adView.adSize = getSize(context, args["adSize"] as HashMap<*, *>)
adView.adUnitId = args["adUnitId"] as String?

val adRequestBuilder = AdRequest.Builder()
val npa: Boolean? = args["nonPersonalizedAds"] as Boolean?
if(npa == true) {
val extras = Bundle()
extras.putString("npa", "1")
adRequestBuilder.addNetworkExtrasBundle(AdMobAdapter::class.java, extras)
}

val adRequest = AdRequest.Builder().build()
adView.loadAd(adRequest)
adView.loadAd(adRequestBuilder.build())
}

private fun getSize(context: Context, size: HashMap<*, *>) : AdSize {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import io.flutter.plugin.platform.PlatformViewFactory

class AdmobBannerFactory(private val messenger: BinaryMessenger): PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
return AdmobBanner(context, messenger, viewId, args as HashMap<*, *>?)
return AdmobBanner(context, messenger, viewId, args as HashMap<*, *>)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.shatsy.admobflutter

import android.os.Bundle
import com.google.ads.mediation.admob.AdMobAdapter
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.InterstitialAd
import io.flutter.plugin.common.MethodCall
Expand All @@ -22,7 +24,14 @@ class AdmobInterstitial(private val registrar: PluginRegistry.Registrar): Method
"load" -> {
val id = call.argument<Int>("id")
val adUnitId = call.argument<String>("adUnitId")
val adRequest = AdRequest.Builder().build()

val adRequestBuilder = AdRequest.Builder()
val npa = call.argument<Boolean>("nonPersonalizedAds")
if(npa == true) {
val extras = Bundle()
extras.putString("npa", "1")
adRequestBuilder.addNetworkExtrasBundle(AdMobAdapter::class.java, extras)
}

if (allAds[id] == null) {
allAds[id!!] = InterstitialAd(registrar.context())
Expand All @@ -32,7 +41,7 @@ class AdmobInterstitial(private val registrar: PluginRegistry.Registrar): Method
// https://developers.google.com/admob/android/interstitial#create_an_interstitial_ad_object
// Unlike iOS a new InterstitialAd object is not required
// "A single InterstitialAd object can be used to request and display multiple interstitial ads over the course of an activity's lifespan, so you only need to construct it once."
allAds[id]?.loadAd(adRequest)
allAds[id]?.loadAd(adRequestBuilder.build())
result.success(null)
}
"isLoaded" -> {
Expand Down
13 changes: 11 additions & 2 deletions android/src/main/kotlin/com/shatsy/admobflutter/AdmobReward.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.shatsy.admobflutter

import android.os.Bundle
import com.google.ads.mediation.admob.AdMobAdapter
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.MobileAds
import com.google.android.gms.ads.reward.RewardItem
Expand Down Expand Up @@ -28,10 +30,17 @@ class AdmobReward(private val registrar: PluginRegistry.Registrar): MethodChanne
"load" -> {
val id = call.argument<Int>("id")
val adUnitId = call.argument<String>("adUnitId")
val adRequest = AdRequest.Builder().build()

val adRequestBuilder = AdRequest.Builder()
val npa = call.argument<Boolean>("nonPersonalizedAds")
if(npa == true) {
val extras = Bundle()
extras.putString("npa", "1")
adRequestBuilder.addNetworkExtrasBundle(AdMobAdapter::class.java, extras)
}

if (allAds[id] == null) allAds[id!!] = MobileAds.getRewardedVideoAdInstance(registrar.context())
allAds[id]?.loadAd(adUnitId, adRequest)
allAds[id]?.loadAd(adUnitId, adRequestBuilder.build())
result.success(null)
}
"isLoaded" -> {
Expand Down
11 changes: 10 additions & 1 deletion ios/Classes/AdmobBanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,16 @@ class AdmobBanner : NSObject, FlutterPlatformView {
flutterResult(FlutterMethodNotImplemented)
}
}
adView.load(GADRequest())

let request = GADRequest()

if ((args["nonPersonalizedAds"] as? Bool) == true) {
let extras = GADExtras()
extras.additionalParameters = ["npa": "1"]
request.register(extras)
}

adView.load(request)

return adView
}
Expand Down
11 changes: 9 additions & 2 deletions ios/Classes/AdmobInterstitialPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public class AdmobIntersitialPlugin: NSObject, FlutterPlugin {
break
case "load":
allIds[id] = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId)
loadInterstantialAd(id: id, interstantialAdUnitId: adUnitId)
loadInterstantialAd(id: id, interstantialAdUnitId: adUnitId, nonPersonalizedAds: (args["nonPersonalizedAds"] as? Bool) ?? false)
result(nil)
break
case "isLoaded":
Expand All @@ -78,9 +78,16 @@ public class AdmobIntersitialPlugin: NSObject, FlutterPlugin {
}
}

private func loadInterstantialAd(id: Int, interstantialAdUnitId: String) {
private func loadInterstantialAd(id: Int, interstantialAdUnitId: String, nonPersonalizedAds: Bool) {
let interstantial = getInterstitialAd(id: id, interstantialAdUnitId: interstantialAdUnitId)
let request = GADRequest()

if (nonPersonalizedAds) {
let extras = GADExtras()
extras.additionalParameters = ["npa": "1"]
request.register(extras)
}

interstantial.load(request)
}

Expand Down
11 changes: 9 additions & 2 deletions ios/Classes/AdmobRewardPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public class AdmobRewardPlugin: NSObject, FlutterPlugin {
delegates[id] = AdmobRewardPluginDelegate(channel: channel)
break
case "load":
loadRewardBasedVideoAd(id: id, rewardBasedVideoAdUnitId: adUnitId)
loadRewardBasedVideoAd(id: id, rewardBasedVideoAdUnitId: adUnitId, nonPersonalizedAds: (args["nonPersonalizedAds"] as? Bool) ?? false)
result(nil)
break
case "isLoaded":
Expand Down Expand Up @@ -88,10 +88,17 @@ public class AdmobRewardPlugin: NSObject, FlutterPlugin {
}
}

private func loadRewardBasedVideoAd(id: Int, rewardBasedVideoAdUnitId: String) {
private func loadRewardBasedVideoAd(id: Int, rewardBasedVideoAdUnitId: String, nonPersonalizedAds: Bool) {
let video = GADRewardedAd(adUnitID: rewardBasedVideoAdUnitId)
rewardAds[id] = video
let request = GADRequest()

if (nonPersonalizedAds) {
let extras = GADExtras()
extras.additionalParameters = ["npa": "1"]
request.register(extras)
}

video.load(request) { [weak self] error in
if let error = error {
// Handle ad failed to load case.
Expand Down
18 changes: 10 additions & 8 deletions lib/src/admob_banner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ class AdmobBanner extends StatefulWidget {
final AdmobBannerSize adSize;
final void Function(AdmobAdEvent, Map<String, dynamic>) listener;
final void Function(AdmobBannerController) onBannerCreated;
final bool nonPersonalizedAds;

AdmobBanner({
Key key,
@required this.adUnitId,
@required this.adSize,
this.listener,
this.onBannerCreated,
this.nonPersonalizedAds = false,
}) : super(key: key);

@override
Expand Down Expand Up @@ -59,10 +61,7 @@ class _AdmobBannerState extends State<AdmobBanner> {
child: AndroidView(
key: _key,
viewType: 'admob_flutter/banner',
creationParams: <String, dynamic>{
'adUnitId': widget.adUnitId,
'adSize': widget.adSize.toMap,
},
creationParams: bannerCreationParams,
creationParamsCodec: const StandardMessageCodec(),
onPlatformViewCreated: _onPlatformViewCreated,
),
Expand All @@ -73,10 +72,7 @@ class _AdmobBannerState extends State<AdmobBanner> {
child: UiKitView(
key: _key,
viewType: 'admob_flutter/banner',
creationParams: <String, dynamic>{
'adUnitId': widget.adUnitId,
'adSize': widget.adSize.toMap,
},
creationParams: bannerCreationParams,
creationParamsCodec: const StandardMessageCodec(),
onPlatformViewCreated: _onPlatformViewCreated,
),
Expand All @@ -95,4 +91,10 @@ class _AdmobBannerState extends State<AdmobBanner> {
widget.onBannerCreated(_controller);
}
}

Map<String, dynamic> get bannerCreationParams => <String, dynamic>{
'adUnitId': widget.adUnitId,
'adSize': widget.adSize.toMap,
'nonPersonalizedAds': widget.nonPersonalizedAds,
};
}
32 changes: 15 additions & 17 deletions lib/src/admob_interstitial.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ class AdmobInterstitial extends AdmobEventHandler {
MethodChannel _adChannel;
final String adUnitId;
final void Function(AdmobAdEvent, Map<String, dynamic>) listener;
final bool nonPersonalizedAds;

AdmobInterstitial({
@required this.adUnitId,
this.listener,
this.nonPersonalizedAds = false,
}) : super(listener) {
id = hashCode;
if (listener != null) {
Expand All @@ -25,37 +27,33 @@ class AdmobInterstitial extends AdmobEventHandler {
}

Future<bool> get isLoaded async {
final result =
await _channel.invokeMethod('isLoaded', <String, dynamic>{
'id': id,
});
final result = await _channel.invokeMethod('isLoaded', _channelMethodsArguments);
return result;
}

void load() async {
await _channel.invokeMethod('load', <String, dynamic>{
'id': id,
'adUnitId': adUnitId,
});
await _channel.invokeMethod('load',
_channelMethodsArguments
..['adUnitId'] = adUnitId
..['nonPersonalizedAds'] = nonPersonalizedAds
);

if (listener != null) {
await _channel.invokeMethod('setListener', <String, dynamic>{
'id': id,
});
await _channel.invokeMethod('setListener', _channelMethodsArguments);
}
}

void show() async {
if (await isLoaded == true) {
await _channel.invokeMethod('show', <String, dynamic>{
'id': id,
});
await _channel.invokeMethod('show', _channelMethodsArguments);
}
}

void dispose() async {
await _channel.invokeMethod('dispose', <String, dynamic>{
'id': id,
});
await _channel.invokeMethod('dispose', _channelMethodsArguments);
}

Map<String, dynamic> get _channelMethodsArguments => <String, dynamic>{
'id': id,
};
}
33 changes: 12 additions & 21 deletions lib/src/admob_reward.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ class AdmobReward extends AdmobEventHandler {
MethodChannel _adChannel;
final String adUnitId;
final void Function(AdmobAdEvent, Map<String, dynamic>) listener;
final bool nonPersonalizedAds;

AdmobReward({
@required this.adUnitId,
this.listener,
this.nonPersonalizedAds = false,
}) : super(listener) {
id = hashCode;
if (listener != null) {
Expand All @@ -25,41 +27,30 @@ class AdmobReward extends AdmobEventHandler {
}

Future<bool> get isLoaded async {
final result =
await _channel.invokeMethod('isLoaded', <String, dynamic>{
'id': id,
'adUnitId': adUnitId,
});
final result = await _channel.invokeMethod('isLoaded', _channelMethodsArguments);
return result;
}

void load() async {
await _channel.invokeMethod('load', <String, dynamic>{
'id': id,
'adUnitId': adUnitId,
});
await _channel.invokeMethod('load', _channelMethodsArguments..['nonPersonalizedAds'] = nonPersonalizedAds);

if (listener != null) {
await _channel.invokeMethod('setListener', <String, dynamic>{
'id': id,
'adUnitId': adUnitId,
});
await _channel.invokeMethod('setListener', _channelMethodsArguments);
}
}

void show() async {
if (await isLoaded == true) {
await _channel.invokeMethod('show', <String, dynamic>{
'id': id,
'adUnitId': adUnitId,
});
await _channel.invokeMethod('show', _channelMethodsArguments);
}
}

void dispose() async {
await _channel.invokeMethod('dispose', <String, dynamic>{
'id': id,
'adUnitId': adUnitId,
});
await _channel.invokeMethod('dispose', _channelMethodsArguments);
}

Map<String, dynamic> get _channelMethodsArguments => <String, dynamic>{
'id': id,
'adUnitId': adUnitId,
};
}

0 comments on commit 559c974

Please sign in to comment.