From 6981bfb2279e95b0c2bdca1733c3bd75d64e28dc Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Tue, 28 Dec 2021 23:21:21 -0600 Subject: [PATCH 1/4] WIP android --- android/build.gradle | 10 +- .../com/shatsy/admobflutter/AdmobBanner.kt | 86 +++++----- .../shatsy/admobflutter/AdmobBannerFactory.kt | 8 +- .../shatsy/admobflutter/AdmobFlutterPlugin.kt | 65 ++++--- .../shatsy/admobflutter/AdmobInterstitial.kt | 148 ++++++++++------ .../com/shatsy/admobflutter/AdmobReward.kt | 158 ++++++++++-------- example/android/app/build.gradle | 12 +- .../android/app/src/main/AndroidManifest.xml | 4 +- example/android/build.gradle | 2 +- example/ios/Podfile.lock | 71 -------- example/pubspec.lock | 63 ++++++- example/pubspec.yaml | 8 +- lib/src/admob_banner_size.dart | 5 +- lib/src/admob_event_handler.dart | 3 - lib/src/admob_events.dart | 1 - lib/src/admob_interstitial.dart | 6 +- lib/src/admob_reward.dart | 12 +- 17 files changed, 365 insertions(+), 297 deletions(-) delete mode 100644 example/ios/Podfile.lock diff --git a/android/build.gradle b/android/build.gradle index 62bd05d..5fff8a4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,14 +2,14 @@ group 'com.shatsy.admobflutter' version '1.0.0-beta.3' buildscript { - ext.kotlin_version = '1.3.72' + ext.kotlin_version = '1.6.10' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.3' + classpath 'com.android.tools.build:gradle:3.6.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -25,13 +25,13 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 28 + compileSdkVersion 31 sourceSets { main.java.srcDirs += 'src/main/kotlin' } defaultConfig { - minSdkVersion 16 + minSdkVersion 19 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } lintOptions { @@ -41,5 +41,5 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - api 'com.google.firebase:firebase-ads:19.1.0' + implementation 'com.google.android.gms:play-services-ads:20.5.0' } diff --git a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBanner.kt b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBanner.kt index 60873d1..a3f0051 100644 --- a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBanner.kt +++ b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBanner.kt @@ -16,58 +16,58 @@ import io.flutter.plugin.platform.PlatformView 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) + private val channel: MethodChannel = MethodChannel(messenger, "admob_flutter/banner_$id") + private val adView: AdView = AdView(context) - init { - channel.setMethodCallHandler(this) + init { + channel.setMethodCallHandler(this) - adView.adSize = getSize(context, args["adSize"] as HashMap<*, *>) - adView.adUnitId = args["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 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) + } - adView.loadAd(adRequestBuilder.build()) - } + adView.loadAd(adRequestBuilder.build()) + } - private fun getSize(context: Context, size: HashMap<*, *>) : AdSize { - val width = size["width"] as Int - val height = size["height"] as Int - val name = size["name"] as String + private fun getSize(context: Context, size: HashMap<*, *>): AdSize { + val width = size["width"] as Int + val height = size["height"] as Int + val name = size["name"] as String - return when(name) { - "BANNER" -> AdSize.BANNER - "LARGE_BANNER" -> AdSize.LARGE_BANNER - "MEDIUM_RECTANGLE" -> AdSize.MEDIUM_RECTANGLE - "FULL_BANNER" -> AdSize.FULL_BANNER - "LEADERBOARD" -> AdSize.LEADERBOARD - "SMART_BANNER" -> AdSize.SMART_BANNER - "ADAPTIVE_BANNER" -> AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, width) - else -> AdSize(width, height) + return when (name) { + "BANNER" -> AdSize.BANNER + "LARGE_BANNER" -> AdSize.LARGE_BANNER + "MEDIUM_RECTANGLE" -> AdSize.MEDIUM_RECTANGLE + "FULL_BANNER" -> AdSize.FULL_BANNER + "LEADERBOARD" -> AdSize.LEADERBOARD + "SMART_BANNER" -> AdSize.SMART_BANNER + "ADAPTIVE_BANNER" -> AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, width) + else -> AdSize(width, height) + } } - } - override fun getView(): View { - return adView - } + override fun getView(): View { + return adView + } - override fun onMethodCall(call: MethodCall, result: Result) { - when(call.method) { - "setListener" -> adView.adListener = createAdListener(channel) - "dispose" -> dispose() - else -> result.notImplemented() + override fun onMethodCall(call: MethodCall, result: Result) { + when (call.method) { + "setListener" -> adView.adListener = createAdListener(channel) + "dispose" -> dispose() + else -> result.notImplemented() + } } - } - override fun dispose() { - adView.visibility = View.GONE - adView.destroy() - channel.setMethodCallHandler(null) - } + override fun dispose() { + adView.visibility = View.GONE + adView.destroy() + channel.setMethodCallHandler(null) + } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBannerFactory.kt b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBannerFactory.kt index b65b518..53cbed7 100644 --- a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBannerFactory.kt +++ b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBannerFactory.kt @@ -6,8 +6,8 @@ import io.flutter.plugin.common.StandardMessageCodec import io.flutter.plugin.platform.PlatformView 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<*, *>) - } +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<*, *>) + } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobFlutterPlugin.kt b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobFlutterPlugin.kt index b2736e6..5c4c823 100644 --- a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobFlutterPlugin.kt +++ b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobFlutterPlugin.kt @@ -1,30 +1,33 @@ package com.shatsy.admobflutter -import android.content.Context import androidx.annotation.NonNull -import com.google.android.gms.ads.AdListener -import com.google.android.gms.ads.AdSize -import com.google.android.gms.ads.MobileAds -import com.google.android.gms.ads.RequestConfiguration +import com.google.android.gms.ads.* +import com.google.android.gms.ads.rewarded.RewardItem import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.activity.ActivityAware +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result -fun createAdListener(channel: MethodChannel): AdListener { - return object : AdListener() { +abstract class AdmobFlutterAdListener : AdListener() { + open fun onRewarded(reward: RewardItem) {} +} + +fun createAdListener(channel: MethodChannel): AdmobFlutterAdListener { + return object : AdmobFlutterAdListener() { override fun onAdLoaded() = channel.invokeMethod("loaded", null) - override fun onAdFailedToLoad(errorCode: Int) = channel.invokeMethod("failedToLoad", hashMapOf("errorCode" to errorCode)) + override fun onAdFailedToLoad(errorCode: LoadAdError) = channel.invokeMethod("failedToLoad", hashMapOf("errorCode" to errorCode)) override fun onAdClicked() = channel.invokeMethod("clicked", null) override fun onAdImpression() = channel.invokeMethod("impression", null) override fun onAdOpened() = channel.invokeMethod("opened", null) - override fun onAdLeftApplication() = channel.invokeMethod("leftApplication", null) override fun onAdClosed() = channel.invokeMethod("closed", null) + override fun onRewarded(reward: RewardItem) = channel.invokeMethod("rewarded", hashMapOf("type" to (reward.type), "amount" to (reward.amount))) } } -class AdmobFlutterPlugin : MethodCallHandler, FlutterPlugin { +class AdmobFlutterPlugin : MethodCallHandler, FlutterPlugin, ActivityAware { /// The MethodChannel that will the communication between Flutter and native Android /// /// This local reference serves to register the plugin with the Flutter Engine and unregister it @@ -32,36 +35,54 @@ class AdmobFlutterPlugin : MethodCallHandler, FlutterPlugin { private lateinit var defaultChannel: MethodChannel private lateinit var interstitialChannel: MethodChannel private lateinit var rewardChannel: MethodChannel - private var context: Context? = null + private var flutterPluginBinding: FlutterPlugin.FlutterPluginBinding? = null override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - context = flutterPluginBinding.applicationContext + this.flutterPluginBinding = flutterPluginBinding defaultChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter") defaultChannel.setMethodCallHandler(this) - interstitialChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/interstitial") - interstitialChannel.setMethodCallHandler(AdmobInterstitial(flutterPluginBinding)) + interstitialChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/interstitial") rewardChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/reward") - rewardChannel.setMethodCallHandler(AdmobReward(flutterPluginBinding)) - flutterPluginBinding.platformViewRegistry.registerViewFactory("admob_flutter/banner", AdmobBannerFactory(flutterPluginBinding.binaryMessenger)) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - defaultChannel.setMethodCallHandler(null) interstitialChannel.setMethodCallHandler(null) rewardChannel.setMethodCallHandler(null) - context = null + flutterPluginBinding = null } - override fun onMethodCall(call: MethodCall, result: Result) { - if (context == null) { - return result.error("null_android_context", "Android context is null.", "Android context is null.") + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + flutterPluginBinding?.let { + interstitialChannel.setMethodCallHandler(AdmobInterstitial(it, binding.activity)) + rewardChannel.setMethodCallHandler(AdmobReward(it, binding.activity)) } + } + + override fun onDetachedFromActivityForConfigChanges() { + // no-op + } + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + flutterPluginBinding?.let { + interstitialChannel.setMethodCallHandler(AdmobInterstitial(it, binding.activity)) + rewardChannel.setMethodCallHandler(AdmobReward(it, binding.activity)) + } + } + + override fun onDetachedFromActivity() { + interstitialChannel.setMethodCallHandler(null) + } + + override fun onMethodCall(call: MethodCall, result: Result) { + if (flutterPluginBinding == null) { + return result.error("null_android_flutterPluginBinding", "flutterPluginBinding is null.", "flutterPluginBinding is null.") + } + val context = flutterPluginBinding!!.applicationContext when (call.method) { "initialize" -> { MobileAds.initialize(context) @@ -77,7 +98,7 @@ class AdmobFlutterPlugin : MethodCallHandler, FlutterPlugin { val width = args["width"] as Int when (name) { "SMART_BANNER" -> { - val metrics = context!!.resources.displayMetrics + val metrics = context.resources.displayMetrics result.success(hashMapOf( "width" to AdSize.SMART_BANNER.getWidthInPixels(context) / metrics.density, "height" to AdSize.SMART_BANNER.getHeightInPixels(context) / metrics.density diff --git a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobInterstitial.kt b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobInterstitial.kt index 4549d9c..a5ce7f4 100644 --- a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobInterstitial.kt +++ b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobInterstitial.kt @@ -1,73 +1,111 @@ package com.shatsy.admobflutter +import android.app.Activity 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 com.google.android.gms.ads.* +import com.google.android.gms.ads.interstitial.InterstitialAd +import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel -class AdmobInterstitial(private val flutterPluginBinding: FlutterPlugin.FlutterPluginBinding): MethodChannel.MethodCallHandler { - companion object { - val allAds: MutableMap = mutableMapOf() - } - override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - when(call.method) { - "setListener" -> { - val id = call.argument("id") - if (allAds[id]!!.adListener != null) return +class AdmobInterstitial(private val flutterPluginBinding: FlutterPlugin.FlutterPluginBinding, private val activity: Activity) : MethodChannel.MethodCallHandler { - val adChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/interstitial_$id") - allAds[id]!!.adListener = createAdListener(adChannel) - } - "load" -> { - val id = call.argument("id") - val adUnitId = call.argument("adUnitId") + companion object { + val allAds: MutableMap = mutableMapOf() + } - val adRequestBuilder = AdRequest.Builder() - val npa = call.argument("nonPersonalizedAds") - if(npa == true) { - val extras = Bundle() - extras.putString("npa", "1") - adRequestBuilder.addNetworkExtrasBundle(AdMobAdapter::class.java, extras) - } + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + when (call.method) { + "setListener" -> { + val id = call.argument("id") + if (id == null) { + result.error("1", "Missing id", "Missing id") + return + } + if (allAds[id]?.adListener != null) return - if (allAds[id] == null) { - allAds[id!!] = InterstitialAd(flutterPluginBinding.applicationContext) - allAds[id]!!.adUnitId = adUnitId - } + allAds[id]!!.adListener = createAdListener(MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/interstitial_$id")) + } + "load" -> { + val id = call.argument("id") + val adUnitId = call.argument("adUnitId") + if (id == null || adUnitId == null) { + result.error("1", "Missing id and/or adUnitId", "Missing id and/or adUnitId") + return + } - // 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(adRequestBuilder.build()) - result.success(null) - } - "isLoaded" -> { - val id = call.argument("id") + val adRequestBuilder = AdRequest.Builder() + val npa = call.argument("nonPersonalizedAds") + if (npa == true) { + val extras = Bundle() + extras.putString("npa", "1") + adRequestBuilder.addNetworkExtrasBundle(AdMobAdapter::class.java, extras) + } - if (allAds[id] == null) { - return result.success(false) - } + if (allAds[id] == null) { + allAds[id] = AdmobInterstitialListener() + InterstitialAd.load(activity, adUnitId, adRequestBuilder.build(), object : InterstitialAdLoadCallback() { + override fun onAdFailedToLoad(adError: LoadAdError) { + allAds[id]?.adListener?.onAdFailedToLoad(adError) + allAds[id] = null + return result.error("2", "onAdFailedToLoad", adError.message) + } - if (allAds[id]!!.isLoaded) { - result.success(true) - } else result.success(false) - } - "show" -> { - val id = call.argument("id") + override fun onAdLoaded(interstitialAd: InterstitialAd) { + allAds[id]?.ad = interstitialAd + allAds[id]?.adListener?.onAdLoaded() + allAds[id]?.ad?.fullScreenContentCallback = object : FullScreenContentCallback() { + override fun onAdDismissedFullScreenContent() { + allAds[id]?.adListener?.onAdClosed() + } - if (allAds[id]!!.isLoaded) { - allAds[id]!!.show() - } else result.error(null, null, null) - } - "dispose" -> { - val id = call.argument("id") + override fun onAdFailedToShowFullScreenContent(adError: AdError?) { + adError?.let { + allAds[id]?.adListener?.onAdFailedToLoad(LoadAdError(it.code, it.message, "admob_flutter", adError, null)) + } + } - allAds.remove(id) - } - else -> result.notImplemented() + override fun onAdShowedFullScreenContent() { + allAds[id]?.adListener?.onAdLoaded() + + } + } + return result.success(null) + } + }) + } else { + return result.success(null) + } + } + "isLoaded" -> { + val id = call.argument("id") + + return if (allAds[id] == null) { + result.success(false) + } else { + result.success(true) + } + } + "show" -> { + val id = call.argument("id") + return if (allAds[id]?.ad != null) { + allAds[id]?.ad?.show(activity) + result.success(null) + } else result.error(null, null, null) + } + "dispose" -> { + val id = call.argument("id") + allAds.remove(id) + return result.success(null) + } + else -> result.notImplemented() + } } - } } + +class AdmobInterstitialListener { + var ad: InterstitialAd? = null + var adListener: AdListener? = null +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobReward.kt b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobReward.kt index 0fbeef1..bcb8fed 100644 --- a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobReward.kt +++ b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobReward.kt @@ -1,86 +1,106 @@ package com.shatsy.admobflutter +import android.app.Activity 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 -import com.google.android.gms.ads.reward.RewardedVideoAd -import com.google.android.gms.ads.reward.RewardedVideoAdListener +import com.google.android.gms.ads.* +import com.google.android.gms.ads.rewarded.RewardItem +import com.google.android.gms.ads.rewarded.RewardedAd +import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel -class AdmobReward(private val flutterPluginBinding: FlutterPlugin.FlutterPluginBinding): MethodChannel.MethodCallHandler, RewardedVideoAdListener { - companion object { - val allAds: MutableMap = mutableMapOf() - } - - lateinit var adChannel: MethodChannel - - override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - when(call.method) { - "setListener" -> { - val id = call.argument("id") - if (allAds[id]!!.rewardedVideoAdListener != null) return +class AdmobReward(private val flutterPluginBinding: FlutterPlugin.FlutterPluginBinding, private val activity: Activity) : MethodChannel.MethodCallHandler { + companion object { + val allAds: MutableMap = mutableMapOf() + } - adChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/reward_$id") - allAds[id]!!.rewardedVideoAdListener = this - } - "load" -> { - val id = call.argument("id") - val adUnitId = call.argument("adUnitId") - val userId = call.argument("userId") - val customData = call.argument("customData") + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + when (call.method) { + "setListener" -> { + val id = call.argument("id") + if (allAds[id]?.adListener != null) return + allAds[id]?.adListener = createAdListener(MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/reward_$id")) + } + "load" -> { + val id = call.argument("id") + val adUnitId = call.argument("adUnitId") + if (id == null || adUnitId == null) { + result.error("1", "Missing id and/or adUnitId", "Missing id and/or adUnitId") + return + } + val adRequestBuilder = AdRequest.Builder() + val npa = call.argument("nonPersonalizedAds") + if (npa == true) { + val extras = Bundle() + extras.putString("npa", "1") + adRequestBuilder.addNetworkExtrasBundle(AdMobAdapter::class.java, extras) + } - val adRequestBuilder = AdRequest.Builder() - val npa = call.argument("nonPersonalizedAds") - if(npa == true) { - val extras = Bundle() - extras.putString("npa", "1") - adRequestBuilder.addNetworkExtrasBundle(AdMobAdapter::class.java, extras) - } + if (allAds[id] == null) { + allAds[id] = AdmobRewardedListener() + RewardedAd.load(activity, adUnitId, adRequestBuilder.build(), object : RewardedAdLoadCallback() { + override fun onAdFailedToLoad(adError: LoadAdError) { + allAds[id]?.adListener?.onAdFailedToLoad(adError) + allAds[id] = null + return result.error("2", "onAdFailedToLoad", adError.message) + } - if (allAds[id] == null) allAds[id!!] = MobileAds.getRewardedVideoAdInstance(flutterPluginBinding.applicationContext) - if (userId != null) allAds[id]?.setUserId(userId) - if (customData != null) allAds[id]?.setCustomData(customData) - allAds[id]?.loadAd(adUnitId, adRequestBuilder.build()) - result.success(null) - } - "isLoaded" -> { - val id = call.argument("id") + override fun onAdLoaded(rewardedAd: RewardedAd) { + allAds[id]?.ad = rewardedAd + allAds[id]!!.ad?.fullScreenContentCallback = object : FullScreenContentCallback() { + override fun onAdDismissedFullScreenContent() { + allAds[id]?.adListener?.onAdClosed() + allAds[id] = null + } - if (allAds[id] == null) { - return result.success(false) - } + override fun onAdFailedToShowFullScreenContent(adError: AdError?) { + adError?.let { + allAds[id]?.adListener?.onAdFailedToLoad(LoadAdError(it.code, it.message, "admob_flutter", adError, null)) + } + } - if (allAds[id]!!.isLoaded) { - result.success(true) - } else result.success(false) - } - "show" -> { - val id = call.argument("id") + override fun onAdShowedFullScreenContent() { + allAds[id]?.adListener?.onAdOpened() + } + } + return result.success(null) + } + }) + } else { + return result.success(null) + } + } + "isLoaded" -> { + val id = call.argument("id") - if (allAds[id]!!.isLoaded) { - allAds[id]!!.show() - } else result.error(null, null, null) - } - "dispose" -> { - val id = call.argument("id") + return if (allAds[id] == null) { + result.success(false) + } else result.success(true) + } + "show" -> { + val id = call.argument("id") - allAds[id]!!.destroy(flutterPluginBinding.applicationContext) - allAds.remove(id) - } - else -> result.notImplemented() + if (allAds[id]?.ad != null) { + allAds[id]?.ad?.show(activity) { + fun onUserEarnedReward(rewardItem: RewardItem) { + allAds[id]?.adListener?.onRewarded(rewardItem) + } + } + } else result.error(null, null, null) + } + "dispose" -> { + val id = call.argument("id") + AdmobInterstitial.allAds.remove(id) + return result.success(null) + } + else -> result.notImplemented() + } } - } - - override fun onRewardedVideoAdClosed() = adChannel.invokeMethod("closed", null) - override fun onRewardedVideoAdLeftApplication() = adChannel.invokeMethod("leftApplication", null) - override fun onRewardedVideoAdLoaded() = adChannel.invokeMethod("loaded", null) - override fun onRewardedVideoAdOpened() = adChannel.invokeMethod("opened", null) - override fun onRewardedVideoCompleted() = adChannel.invokeMethod("completed", null) - override fun onRewarded(reward: RewardItem?) = adChannel.invokeMethod("rewarded", hashMapOf("type" to (reward?.type ?: ""), "amount" to (reward?.amount ?: 0))) - override fun onRewardedVideoStarted() = adChannel.invokeMethod("started", null) - override fun onRewardedVideoAdFailedToLoad(errorCode: Int) = adChannel.invokeMethod("failedToLoad", hashMapOf("errorCode" to errorCode)) } + +class AdmobRewardedListener { + var ad: RewardedAd? = null + var adListener: AdmobFlutterAdListener? = null +} \ No newline at end of file diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 9a4f163..81ab1b0 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 28 + compileSdkVersion 31 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -38,8 +38,8 @@ android { defaultConfig { applicationId "com.shatsy.admobflutterexample" - minSdkVersion 16 - targetSdkVersion 28 + minSdkVersion 19 + targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -60,7 +60,7 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + testImplementation 'junit:junit:4.13.1' + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 3b4a642..dc0594d 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -20,7 +20,9 @@ android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3940256099942544~3347511713" /> - + diff --git a/example/android/build.gradle b/example/android/build.gradle index 7536fb8..63d56d4 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.3.40' + ext.kotlin_version = '1.6.10' repositories { google() jcenter() diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock deleted file mode 100644 index 7d8af6e..0000000 --- a/example/ios/Podfile.lock +++ /dev/null @@ -1,71 +0,0 @@ -PODS: - - admob_flutter (1.0.0): - - Flutter - - Google-Mobile-Ads-SDK (~> 7.64) - - Flutter (1.0.0) - - Google-Mobile-Ads-SDK (7.65.0): - - GoogleAppMeasurement (~> 6.0) - - GoogleUserMessagingPlatform (~> 1.1) - - GoogleAppMeasurement (6.6.0): - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 1.30905.0) - - GoogleUserMessagingPlatform (1.1.0) - - GoogleUtilities/AppDelegateSwizzler (6.7.2): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (6.7.2): - - PromisesObjC (~> 1.2) - - GoogleUtilities/Logger (6.7.2): - - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.7.2): - - GoogleUtilities/Logger - - GoogleUtilities/Network (6.7.2): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.7.2)" - - GoogleUtilities/Reachability (6.7.2): - - GoogleUtilities/Logger - - nanopb (1.30905.0): - - nanopb/decode (= 1.30905.0) - - nanopb/encode (= 1.30905.0) - - nanopb/decode (1.30905.0) - - nanopb/encode (1.30905.0) - - PromisesObjC (1.2.10) - -DEPENDENCIES: - - admob_flutter (from `.symlinks/plugins/admob_flutter/ios`) - - Flutter (from `Flutter`) - -SPEC REPOS: - trunk: - - Google-Mobile-Ads-SDK - - GoogleAppMeasurement - - GoogleUserMessagingPlatform - - GoogleUtilities - - nanopb - - PromisesObjC - -EXTERNAL SOURCES: - admob_flutter: - :path: ".symlinks/plugins/admob_flutter/ios" - Flutter: - :path: Flutter - -SPEC CHECKSUMS: - admob_flutter: 370023e0bc3f49a44ba94f14009e0728bffe43e8 - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - Google-Mobile-Ads-SDK: af735e001ee534d9dd2ce0a87cd8c355b19844b5 - GoogleAppMeasurement: 67458367830514fb20fd9e233496f1eef9d90185 - GoogleUserMessagingPlatform: 9dc714d2e1f0aa727b9cc52d266dc1b155f4c3f2 - GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 - nanopb: c43f40fadfe79e8b8db116583945847910cbabc9 - PromisesObjC: b14b1c6b68e306650688599de8a45e49fae81151 - -PODFILE CHECKSUM: a75497545d4391e2d394c3668e20cfb1c2bbd4aa - -COCOAPODS: 1.11.2 diff --git a/example/pubspec.lock b/example/pubspec.lock index 221e8d6..76f56ce 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -2,7 +2,7 @@ # See https://dart.dev/tools/pub/glossary#lockfile packages: admob_flutter: - dependency: "direct dev" + dependency: "direct main" description: path: ".." relative: true @@ -64,6 +64,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + firebase_analytics: + dependency: "direct main" + description: + name: firebase_analytics + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.4" + firebase_analytics_platform_interface: + dependency: transitive + description: + name: firebase_analytics_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.3" + firebase_analytics_web: + dependency: transitive + description: + name: firebase_analytics_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0+4" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.6" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.3" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.3" flutter: dependency: "direct main" description: flutter @@ -74,6 +116,18 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.3" matcher: dependency: transitive description: @@ -102,6 +156,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.11.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" sky_engine: dependency: transitive description: flutter diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 8a83feb..53b8065 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -7,17 +7,19 @@ environment: sdk: '>=2.12.0 <3.0.0' dependencies: + admob_flutter: + path: ../ + cupertino_icons: ^1.0.2 + firebase_analytics: ^9.0.4 + firebase_core: ^1.10.6 flutter: sdk: flutter - cupertino_icons: ^1.0.2 dev_dependencies: pedantic: 1.11.0 flutter_test: sdk: flutter - admob_flutter: - path: ../ flutter: uses-material-design: true diff --git a/lib/src/admob_banner_size.dart b/lib/src/admob_banner_size.dart index 3627678..767a83a 100644 --- a/lib/src/admob_banner_size.dart +++ b/lib/src/admob_banner_size.dart @@ -14,10 +14,13 @@ class AdmobBannerSize { AdmobBannerSize(width: 468, height: 60, name: 'FULL_BANNER'); static const AdmobBannerSize LEADERBOARD = AdmobBannerSize(width: 728, height: 90, name: 'LEADERBOARD'); + @deprecated AdmobBannerSize.SMART_BANNER(BuildContext context): - width = MediaQuery.of(context).size.width.toInt(), height = -2, name = 'SMART_BANNER'; + width = MediaQuery.of(context).size.width.toInt(), height = -2, name = 'FLUID'; AdmobBannerSize.ADAPTIVE_BANNER({required int width}): width = width, height = -2, name = 'ADAPTIVE_BANNER'; + AdmobBannerSize.FLUID(BuildContext context): + width = MediaQuery.of(context).size.width.toInt(), height = -2, name = 'FLUID'; const AdmobBannerSize({ required this.width, diff --git a/lib/src/admob_event_handler.dart b/lib/src/admob_event_handler.dart index f921d9e..afd009e 100644 --- a/lib/src/admob_event_handler.dart +++ b/lib/src/admob_event_handler.dart @@ -32,9 +32,6 @@ abstract class AdmobEventHandler { case 'opened': _listener!(AdmobAdEvent.opened, null); break; - case 'leftApplication': - _listener!(AdmobAdEvent.leftApplication, null); - break; case 'closed': _listener!(AdmobAdEvent.closed, null); break; diff --git a/lib/src/admob_events.dart b/lib/src/admob_events.dart index 8aa2a95..566839f 100644 --- a/lib/src/admob_events.dart +++ b/lib/src/admob_events.dart @@ -4,7 +4,6 @@ enum AdmobAdEvent { clicked, impression, opened, - leftApplication, closed, completed, rewarded, diff --git a/lib/src/admob_interstitial.dart b/lib/src/admob_interstitial.dart index a0bc6f9..15fb498 100644 --- a/lib/src/admob_interstitial.dart +++ b/lib/src/admob_interstitial.dart @@ -41,7 +41,7 @@ class AdmobInterstitial extends AdmobEventHandler { return result; } - void load() async { + Future load() async { await _channel.invokeMethod('load', _channelMethodsArguments ..['adUnitId'] = adUnitId @@ -53,13 +53,13 @@ class AdmobInterstitial extends AdmobEventHandler { } } - void show() async { + Future show() async { if (await isLoaded == true) { await _channel.invokeMethod('show', _channelMethodsArguments); } } - void dispose() async { + Future dispose() async { await _channel.invokeMethod('dispose', _channelMethodsArguments); } diff --git a/lib/src/admob_reward.dart b/lib/src/admob_reward.dart index ddbe663..b406396 100644 --- a/lib/src/admob_reward.dart +++ b/lib/src/admob_reward.dart @@ -13,15 +13,11 @@ class AdmobReward extends AdmobEventHandler { final String adUnitId; final void Function(AdmobAdEvent, Map?)? listener; final bool nonPersonalizedAds; - final String userId; - final String customData; AdmobReward({ required this.adUnitId, this.listener, this.nonPersonalizedAds = false, - this.userId = '', - this.customData = '', }) : super(listener) { id = hashCode; if (listener != null) { @@ -45,21 +41,21 @@ class AdmobReward extends AdmobEventHandler { return result ?? false; } - void load() async { - await _channel.invokeMethod('load', _channelMethodsArguments..['nonPersonalizedAds'] = nonPersonalizedAds..['userId'] = userId..['customData'] = customData); + Future load() async { + await _channel.invokeMethod('load', _channelMethodsArguments..['nonPersonalizedAds'] = nonPersonalizedAds); if (listener != null) { await _channel.invokeMethod('setListener', _channelMethodsArguments); } } - void show() async { + Future show() async { if (await isLoaded == true) { await _channel.invokeMethod('show', _channelMethodsArguments); } } - void dispose() async { + Future dispose() async { await _channel.invokeMethod('dispose', _channelMethodsArguments); } From 37147bab19abf7155727e46800689bc29c13f8af Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Wed, 29 Dec 2021 10:09:07 -0600 Subject: [PATCH 2/4] WIP ios --- CHANGELOG.md | 7 + example/ios/Flutter/AppFrameworkInfo.plist | 40 ++--- example/ios/Podfile | 2 +- example/ios/Podfile.lock | 157 ++++++++++++++++++ example/ios/Runner.xcodeproj/project.pbxproj | 17 +- example/ios/Runner/Info.plist | 158 ++++++++++++++++++- example/lib/main.dart | 15 +- ios/Classes/AdmobBanner.swift | 39 ++--- ios/Classes/AdmobInterstitialPlugin.swift | 132 +++++++--------- ios/Classes/AdmobRewardPlugin.swift | 151 +++++++++++------- ios/Classes/SwiftAdmobFlutterPlugin.swift | 2 +- ios/admob_flutter.podspec | 8 +- 12 files changed, 536 insertions(+), 192 deletions(-) create mode 100644 example/ios/Podfile.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index ea5fed9..ed4569a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [3.0.0-beta.0] - 2021/12/28 +- Increase Android min api level to 19. +- Increase iOS min SDK to 12. +- Removed deprecated AdmobBannerSize.SMART_BANNER +- Removed deprecated callback `leftApplication` +- Update native SDK's to `'Google-Mobile-Ads-SDK', '~> 8.13.0'` and `com.google.android.gms:play-services-ads:20.5.0` + ## [2.0.0] - 2021/12/19 - Migrate Android to v2 embedding [287](https://github.com/kmcgill88/admob_flutter/issues/287) - Update Bitrise build machine to Xcode 13.2.x, on macOS (Monterey); Flutter 2.8.1 diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 8d4492f..658d8c4 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -2,25 +2,25 @@ - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 9.0 + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index 252d9ec..2c068c4 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '9.0' +platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock new file mode 100644 index 0000000..f8888ac --- /dev/null +++ b/example/ios/Podfile.lock @@ -0,0 +1,157 @@ +PODS: + - admob_flutter (1.0.0): + - Flutter + - Google-Mobile-Ads-SDK (~> 8.13.0) + - Firebase/Analytics (8.9.0): + - Firebase/Core + - Firebase/Core (8.9.0): + - Firebase/CoreOnly + - FirebaseAnalytics (~> 8.9.0) + - Firebase/CoreOnly (8.9.0): + - FirebaseCore (= 8.9.0) + - firebase_analytics (9.0.4): + - Firebase/Analytics (= 8.9.0) + - firebase_core + - Flutter + - firebase_core (1.10.6): + - Firebase/CoreOnly (= 8.9.0) + - Flutter + - FirebaseAnalytics (8.9.1): + - FirebaseAnalytics/AdIdSupport (= 8.9.1) + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.6) + - GoogleUtilities/MethodSwizzler (~> 7.6) + - GoogleUtilities/Network (~> 7.6) + - "GoogleUtilities/NSData+zlib (~> 7.6)" + - nanopb (~> 2.30908.0) + - FirebaseAnalytics/AdIdSupport (8.9.1): + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleAppMeasurement (= 8.9.1) + - GoogleUtilities/AppDelegateSwizzler (~> 7.6) + - GoogleUtilities/MethodSwizzler (~> 7.6) + - GoogleUtilities/Network (~> 7.6) + - "GoogleUtilities/NSData+zlib (~> 7.6)" + - nanopb (~> 2.30908.0) + - FirebaseCore (8.9.0): + - FirebaseCoreDiagnostics (~> 8.0) + - GoogleUtilities/Environment (~> 7.6) + - GoogleUtilities/Logger (~> 7.6) + - FirebaseCoreDiagnostics (8.10.0): + - GoogleDataTransport (~> 9.1) + - GoogleUtilities/Environment (~> 7.6) + - GoogleUtilities/Logger (~> 7.6) + - nanopb (~> 2.30908.0) + - FirebaseInstallations (8.10.0): + - FirebaseCore (~> 8.0) + - GoogleUtilities/Environment (~> 7.6) + - GoogleUtilities/UserDefaults (~> 7.6) + - PromisesObjC (< 3.0, >= 1.2) + - Flutter (1.0.0) + - Google-Mobile-Ads-SDK (8.13.0): + - GoogleAppMeasurement (< 9.0, >= 7.0) + - GoogleUserMessagingPlatform (>= 1.1) + - GoogleAppMeasurement (8.9.1): + - GoogleAppMeasurement/AdIdSupport (= 8.9.1) + - GoogleUtilities/AppDelegateSwizzler (~> 7.6) + - GoogleUtilities/MethodSwizzler (~> 7.6) + - GoogleUtilities/Network (~> 7.6) + - "GoogleUtilities/NSData+zlib (~> 7.6)" + - nanopb (~> 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (8.9.1): + - GoogleAppMeasurement/WithoutAdIdSupport (= 8.9.1) + - GoogleUtilities/AppDelegateSwizzler (~> 7.6) + - GoogleUtilities/MethodSwizzler (~> 7.6) + - GoogleUtilities/Network (~> 7.6) + - "GoogleUtilities/NSData+zlib (~> 7.6)" + - nanopb (~> 2.30908.0) + - GoogleAppMeasurement/WithoutAdIdSupport (8.9.1): + - GoogleUtilities/AppDelegateSwizzler (~> 7.6) + - GoogleUtilities/MethodSwizzler (~> 7.6) + - GoogleUtilities/Network (~> 7.6) + - "GoogleUtilities/NSData+zlib (~> 7.6)" + - nanopb (~> 2.30908.0) + - GoogleDataTransport (9.1.2): + - GoogleUtilities/Environment (~> 7.2) + - nanopb (~> 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUserMessagingPlatform (2.0.0) + - GoogleUtilities/AppDelegateSwizzler (7.6.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (7.6.0): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.6.0): + - GoogleUtilities/Environment + - GoogleUtilities/MethodSwizzler (7.6.0): + - GoogleUtilities/Logger + - GoogleUtilities/Network (7.6.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.6.0)" + - GoogleUtilities/Reachability (7.6.0): + - GoogleUtilities/Logger + - GoogleUtilities/UserDefaults (7.6.0): + - GoogleUtilities/Logger + - nanopb (2.30908.0): + - nanopb/decode (= 2.30908.0) + - nanopb/encode (= 2.30908.0) + - nanopb/decode (2.30908.0) + - nanopb/encode (2.30908.0) + - PromisesObjC (2.0.0) + +DEPENDENCIES: + - admob_flutter (from `.symlinks/plugins/admob_flutter/ios`) + - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - Flutter (from `Flutter`) + +SPEC REPOS: + trunk: + - Firebase + - FirebaseAnalytics + - FirebaseCore + - FirebaseCoreDiagnostics + - FirebaseInstallations + - Google-Mobile-Ads-SDK + - GoogleAppMeasurement + - GoogleDataTransport + - GoogleUserMessagingPlatform + - GoogleUtilities + - nanopb + - PromisesObjC + +EXTERNAL SOURCES: + admob_flutter: + :path: ".symlinks/plugins/admob_flutter/ios" + firebase_analytics: + :path: ".symlinks/plugins/firebase_analytics/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + Flutter: + :path: Flutter + +SPEC CHECKSUMS: + admob_flutter: 3022aa4c42c1085bfb49fe8bb9499b08dabc6843 + Firebase: 13d8d96499e2635428d5bf0ec675df21f95d9a95 + firebase_analytics: cc1ab30e479b7089c0a952c0cae7ba286d464c68 + firebase_core: c263d7daf1dc92fcd9895e6abdc04872b0ee07ff + FirebaseAnalytics: 4ab446ce08a3fe52e8a4303dd997cf26276bf968 + FirebaseCore: 599ee609343eaf4941bd188f85e3aa077ffe325b + FirebaseCoreDiagnostics: 56fb7216d87e0e6ec2feddefa9d8a392fe8b2c18 + FirebaseInstallations: 830327b45345ffc859eaa9c17bcd5ae893fd5425 + Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + Google-Mobile-Ads-SDK: 05e5d68bb42a61b2e5bef336a52789785605aa22 + GoogleAppMeasurement: 837649ad3987936c232f6717c5680216f6243d24 + GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940 + GoogleUserMessagingPlatform: ab890ce5f6620f293a21b6bdd82e416a2c73aeca + GoogleUtilities: 684ee790a24f73ebb2d1d966e9711c203f2a4237 + nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 + PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 + +PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048 + +COCOAPODS: 1.11.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index eee3adb..7b5e5f2 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -252,12 +252,20 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreDiagnostics/FirebaseCoreDiagnostics.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", + "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreDiagnostics.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", @@ -357,7 +365,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -378,6 +386,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", @@ -439,7 +448,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -489,7 +498,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -511,6 +520,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", @@ -539,6 +549,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 9487f95..2f601f6 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -6,13 +6,157 @@ This identifier will be used to deliver personalized ads to you. GADApplicationIdentifier ca-app-pub-3940256099942544~1458002511 - SKADNetworkItems - - - SKAdNetworkIdentifier - cstr6suwn9.skadnetwork - - +SKAdNetworkItems + + + SKAdNetworkIdentifier + cstr6suwn9.skadnetwork + + + SKAdNetworkIdentifier + 4fzdc2evr5.skadnetwork + + + SKAdNetworkIdentifier + 2fnua5tdw4.skadnetwork + + + SKAdNetworkIdentifier + ydx93a7ass.skadnetwork + + + SKAdNetworkIdentifier + 5a6flpkh64.skadnetwork + + + SKAdNetworkIdentifier + p78axxw29g.skadnetwork + + + SKAdNetworkIdentifier + v72qych5uu.skadnetwork + + + SKAdNetworkIdentifier + c6k4g5qg8m.skadnetwork + + + SKAdNetworkIdentifier + s39g8k73mm.skadnetwork + + + SKAdNetworkIdentifier + 3qy4746246.skadnetwork + + + SKAdNetworkIdentifier + 3sh42y64q3.skadnetwork + + + SKAdNetworkIdentifier + f38h382jlk.skadnetwork + + + SKAdNetworkIdentifier + hs6bdukanm.skadnetwork + + + SKAdNetworkIdentifier + prcb7njmu6.skadnetwork + + + SKAdNetworkIdentifier + v4nxqhlyqp.skadnetwork + + + SKAdNetworkIdentifier + wzmmz9fp6w.skadnetwork + + + SKAdNetworkIdentifier + yclnxrl5pm.skadnetwork + + + SKAdNetworkIdentifier + t38b2kh725.skadnetwork + + + SKAdNetworkIdentifier + 7ug5zh24hu.skadnetwork + + + SKAdNetworkIdentifier + 9rd848q2bz.skadnetwork + + + SKAdNetworkIdentifier + n6fk4nfna4.skadnetwork + + + SKAdNetworkIdentifier + kbd757ywx3.skadnetwork + + + SKAdNetworkIdentifier + 9t245vhmpl.skadnetwork + + + SKAdNetworkIdentifier + 4468km3ulz.skadnetwork + + + SKAdNetworkIdentifier + 2u9pt9hc89.skadnetwork + + + SKAdNetworkIdentifier + 8s468mfl3y.skadnetwork + + + SKAdNetworkIdentifier + av6w8kgt66.skadnetwork + + + SKAdNetworkIdentifier + klf5c3l5u5.skadnetwork + + + SKAdNetworkIdentifier + ppxm28t8ap.skadnetwork + + + SKAdNetworkIdentifier + 424m5254lk.skadnetwork + + + SKAdNetworkIdentifier + uw77j35x4d.skadnetwork + + + SKAdNetworkIdentifier + 578prtvx9j.skadnetwork + + + SKAdNetworkIdentifier + 4dzt52r2t5.skadnetwork + + + SKAdNetworkIdentifier + e5fvkxwrpn.skadnetwork + + + SKAdNetworkIdentifier + 8c4e2ghe7u.skadnetwork + + + SKAdNetworkIdentifier + zq492l623r.skadnetwork + + + SKAdNetworkIdentifier + 3qcr597p9d.skadnetwork + + CFBundleDevelopmentRegion en CFBundleShortVersionString diff --git a/example/lib/main.dart b/example/lib/main.dart index e2f196e..d3c72ee 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -67,8 +67,12 @@ class _MyMaterialAppState extends State { case AdmobAdEvent.closed: showSnackBar('Admob $adType Ad closed!'); break; + case AdmobAdEvent.impression: + showSnackBar('$adType made an impression.'); + break; case AdmobAdEvent.failedToLoad: showSnackBar('Admob $adType failed to load. :('); + print(args); break; case AdmobAdEvent.rewarded: showDialog( @@ -94,10 +98,13 @@ class _MyMaterialAppState extends State { ); break; default: + showSnackBar('No matching event $adType. Event: $event'); + break; } } void showSnackBar(String content) { + print(content); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(content), @@ -155,7 +162,7 @@ class _MyMaterialAppState extends State { onPressed: () async { final isLoaded = await interstitialAd.isLoaded; if (isLoaded ?? false) { - interstitialAd.show(); + await interstitialAd.show(); } else { showSnackBar( 'Interstitial ad is still loading...'); @@ -171,7 +178,7 @@ class _MyMaterialAppState extends State { child: TextButton( onPressed: () async { if (await rewardAd.isLoaded) { - rewardAd.show(); + await rewardAd.show(); } else { showSnackBar('Reward ad is still loading...'); } @@ -214,8 +221,8 @@ class _MyMaterialAppState extends State { child: Text('LEADERBOARD'), ), PopupMenuItem( - value: AdmobBannerSize.SMART_BANNER(context), - child: Text('SMART_BANNER'), + value: AdmobBannerSize.FLUID(context), + child: Text('FLUID'), ), PopupMenuItem( value: AdmobBannerSize.ADAPTIVE_BANNER( diff --git a/ios/Classes/AdmobBanner.swift b/ios/Classes/AdmobBanner.swift index 9977826..9bd8448 100644 --- a/ios/Classes/AdmobBanner.swift +++ b/ios/Classes/AdmobBanner.swift @@ -86,28 +86,27 @@ class AdmobBanner : NSObject, FlutterPlatformView { private var adSize: GADAdSize { guard let size = args["adSize"] as? [String: Any] else { assertionFailure("failed to get adSize") - return kGADAdSizeBanner // fallback value + return GADAdSizeBanner // fallback value } if let name = size["name"] as? String { switch name { case "BANNER": - return kGADAdSizeBanner + return GADAdSizeBanner case "LARGE_BANNER": - return kGADAdSizeLargeBanner + return GADAdSizeLargeBanner case "MEDIUM_RECTANGLE": - return kGADAdSizeMediumRectangle + return GADAdSizeMediumRectangle case "FULL_BANNER": - return kGADAdSizeFullBanner + return GADAdSizeFullBanner case "LEADERBOARD": - return kGADAdSizeLeaderboard - case "SMART_BANNER": - // TODO: Do we need Landscape too? - return kGADAdSizeSmartBannerPortrait + return GADAdSizeLeaderboard + case "FLUID": + return GADAdSizeFluid case "ADAPTIVE_BANNER": return GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(frame.width) default: - assertionFailure("invalid adSize.name") + assertionFailure("invalid ad size: \(name)") break } } @@ -123,28 +122,32 @@ extension AdmobBanner : GADBannerViewDelegate { channel.invokeMethod("loaded", arguments: nil) } - func adView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: GADRequestError) { + func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: Error) { + print("bannerView: Failled to load banner view: \(error.localizedDescription)") channel.invokeMethod("failedToLoad", arguments: [ - "errorCode": error.code, + "errorCode": error.localizedDescription, "error": error.localizedDescription ]) } /// Tells the delegate that a full screen view will be presented in response to the user clicking on /// an ad. The delegate may want to pause animations and time sensitive interactions. - func adViewWillPresentScreen(_ bannerView: GADBannerView) { - channel.invokeMethod("clicked", arguments: nil) + func bannerViewWillPresentScreen(_ bannerView: GADBannerView) { channel.invokeMethod("opened", arguments: nil) } - - // TODO: not sure this exists on iOS. - // channel.invokeMethod("impression", null) + func bannerViewDidRecordClick(_ bannerView: GADBannerView) { + channel.invokeMethod("clicked", arguments: nil) + } + + func bannerViewDidRecordImpression(_ bannerView: GADBannerView) { + channel.invokeMethod("impression", arguments: nil) + } func adViewWillLeaveApplication(_ bannerView: GADBannerView) { channel.invokeMethod("leftApplication", arguments: nil) } - func adViewDidDismissScreen(_ bannerView: GADBannerView) { + func bannerViewDidDismissScreen(_ bannerView: GADBannerView) { channel.invokeMethod("closed", arguments: nil) } } diff --git a/ios/Classes/AdmobInterstitialPlugin.swift b/ios/Classes/AdmobInterstitialPlugin.swift index 05ad59b..cd28280 100644 --- a/ios/Classes/AdmobInterstitialPlugin.swift +++ b/ios/Classes/AdmobInterstitialPlugin.swift @@ -22,8 +22,7 @@ import GoogleMobileAds public class AdmobIntersitialPlugin: NSObject, FlutterPlugin { - fileprivate var allIds: [Int: GADInterstitial] = [:] - fileprivate var delegates: [Int: GADInterstitialDelegate] = [:] + fileprivate var allIds: [Int: AdmobIntersitialPluginDelegate] = [:] fileprivate var pluginRegistrar: FlutterPluginRegistrar? fileprivate var interstantialAdUnitId: String? @@ -47,106 +46,95 @@ public class AdmobIntersitialPlugin: NSObject, FlutterPlugin { switch call.method { case "setListener": - let channel = FlutterMethodChannel(name: "admob_flutter/interstitial_\(id)", binaryMessenger: pluginRegistrar!.messenger()) - delegates[id] = AdmobIntersitialPluginDelegate(channel: channel) - let interstantialAd = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) - interstantialAd.delegate = delegates[id] + if allIds[id] == nil { + allIds[id] = AdmobIntersitialPluginDelegate() + } + allIds[id]!.channel = FlutterMethodChannel(name: "admob_flutter/interstitial_\(id)", binaryMessenger: pluginRegistrar!.messenger()) + result(nil) break case "load": - allIds[id] = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) - loadInterstantialAd(id: id, interstantialAdUnitId: adUnitId, nonPersonalizedAds: (args["nonPersonalizedAds"] as? Bool) ?? false) - result(nil) + let request = GADRequest() + let nonPersonalizedAds = (args["nonPersonalizedAds"] as? Bool) ?? false + + if (nonPersonalizedAds) { + let extras = GADExtras() + extras.additionalParameters = ["npa": "1"] + request.register(extras) + } + GADInterstitialAd.load(withAdUnitID: adUnitId, + request: request, + completionHandler: { [weak self] ad, error in + if let self = self { + if self.allIds[id] == nil { + self.allIds[id] = AdmobIntersitialPluginDelegate() + } + let del = self.allIds[id]! + + if let error = error { + del.channel?.invokeMethod("failedToLoad", arguments: [ + "errorCode": 1, + "error": error.localizedDescription + ]) + result(nil) + return + } + del.ad = ad + del.ad?.fullScreenContentDelegate = del + del.channel?.invokeMethod("loaded", arguments: nil) + result(nil) + } + }) break case "isLoaded": - let interstitial = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) - result(interstitial.isReady && !interstitial.hasBeenUsed) + result(allIds[id]?.ad != nil) break case "show": - let interstitial = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) - if interstitial.isReady && !interstitial.hasBeenUsed, let rootViewController = UIApplication.shared.keyWindow?.rootViewController { + if let interstitial = allIds[id]?.ad, let rootViewController = UIApplication.shared.keyWindow?.rootViewController { interstitial.present(fromRootViewController: rootViewController) + result(nil) } else { result(FlutterError(code: "Interstitial Error", message: "Failed to present interstitial", details: nil)) } break case "dispose": allIds.removeValue(forKey: id) - delegates.removeValue(forKey: id) + result(nil) break default: result(FlutterMethodNotImplemented) } } - - 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) - } +class AdmobIntersitialPluginDelegate: NSObject, GADFullScreenContentDelegate { + var channel: FlutterMethodChannel? = nil + var ad: GADInterstitialAd? = nil - interstantial.load(request) - } - - private func getInterstitialAd(id: Int, interstantialAdUnitId: String) -> GADInterstitial { - if let interstantialAd = allIds[id] { - // https://developers.google.com/admob/ios/interstitial#use_gadinterstitialdelegate_to_reload - // "GADInterstitial is a one-time-use object. This means once an interstitial is shown, hasBeenUsed returns true and the interstitial can't be used to load another ad. To request another interstitial, you'll need to create a new GADInterstitial object." - if (interstantialAd.hasBeenUsed) { - let interstantialAd = GADInterstitial(adUnitID: interstantialAdUnitId) - allIds[id] = interstantialAd - } - } else { - let interstantialAd = GADInterstitial(adUnitID: interstantialAdUnitId) - allIds[id] = interstantialAd - } - - return allIds[id]! - } -} -class AdmobIntersitialPluginDelegate: NSObject, GADInterstitialDelegate { - let channel: FlutterMethodChannel - - init(channel: FlutterMethodChannel) { - self.channel = channel - } - - // TODO: not sure this exists on iOS. - // channel.invokeMethod("impression", null) - - func interstitialWillPresentScreen(_ ad: GADInterstitial) { - channel.invokeMethod("clicked", arguments: nil) - channel.invokeMethod("opened", arguments: nil) + func adWillDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) { + // Unused } - func interstitialWillDismissScreen(_ ad: GADInterstitial) { - // Unused + func adDidPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) { + channel?.invokeMethod("opened", arguments: nil) } - - func interstitialDidDismissScreen(_ ad: GADInterstitial) { - channel.invokeMethod("closed", arguments: nil) + + func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) { + channel?.invokeMethod("closed", arguments: nil) } - - func interstitialWillLeaveApplication(_ ad: GADInterstitial) { - channel.invokeMethod("leftApplication", arguments: nil) + + func adDidRecordImpression(_ ad: GADFullScreenPresentingAd) { + channel?.invokeMethod("impression", arguments: nil) } - func interstitialDidReceiveAd(_ ad: GADInterstitial) { - channel.invokeMethod("loaded", arguments: nil) + func adDidRecordClick(_ ad: GADFullScreenPresentingAd) { + channel?.invokeMethod("clicked", arguments: nil) } - func interstitial(_ ad: GADInterstitial, didFailToReceiveAdWithError error: GADRequestError) { - channel.invokeMethod("failedToLoad", arguments: [ - "errorCode": error.code, + func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) { + channel?.invokeMethod("failedToLoad", arguments: [ + "errorCode": 1, "error": error.localizedDescription ]) } - - func interstitialDidFail(toPresentScreen ad: GADInterstitial) { - channel.invokeMethod("failedToLoad", arguments: ["errorCode": ad.isReady && ad.hasBeenUsed]) - } } diff --git a/ios/Classes/AdmobRewardPlugin.swift b/ios/Classes/AdmobRewardPlugin.swift index 01a252d..3ca8073 100644 --- a/ios/Classes/AdmobRewardPlugin.swift +++ b/ios/Classes/AdmobRewardPlugin.swift @@ -22,8 +22,7 @@ import GoogleMobileAds public class AdmobRewardPlugin: NSObject, FlutterPlugin { - fileprivate var rewardAds: [Int: GADRewardedAd] = [:] - fileprivate var delegates: [Int: AdmobRewardPluginDelegate] = [:] + fileprivate var rewardAds: [Int: AdmobRewardPluginDelegate] = [:] fileprivate var pluginRegistrar: FlutterPluginRegistrar? public static func register(with registrar: FlutterPluginRegistrar) { @@ -53,24 +52,58 @@ public class AdmobRewardPlugin: NSObject, FlutterPlugin { switch call.method { case "setListener": - let channel = FlutterMethodChannel( - name: "admob_flutter/reward_\(id)", - binaryMessenger: pluginRegistrar!.messenger() - ) - delegates[id] = AdmobRewardPluginDelegate(channel: channel) + if rewardAds[id] == nil { + rewardAds[id] = AdmobRewardPluginDelegate() + } + rewardAds[id]!.channel = FlutterMethodChannel( + name: "admob_flutter/reward_\(id)", + binaryMessenger: pluginRegistrar!.messenger() + ) + result(nil) break case "load": - loadRewardBasedVideoAd(id: id, rewardBasedVideoAdUnitId: adUnitId, nonPersonalizedAds: (args["nonPersonalizedAds"] as? Bool) ?? false, userId: (args["userId"] as? String), customData: (args["customData"] as? String)) - result(nil) + + let request = GADRequest() + let nonPersonalizedAds = (args["nonPersonalizedAds"] as? Bool) ?? false + + if (nonPersonalizedAds) { + let extras = GADExtras() + extras.additionalParameters = ["npa": "1"] + request.register(extras) + } + GADRewardedAd.load(withAdUnitID: adUnitId, + request: request, + completionHandler: { [weak self] ad, error in + if let self = self { + if self.rewardAds[id] == nil { + self.rewardAds[id] = AdmobRewardPluginDelegate() + } + let del = self.rewardAds[id]! + + if let error = error { + result(FlutterError(code: "Failed to load!", message: "Failed to load interstitial ad with error: \(error.localizedDescription)", details: error.localizedDescription)) + return + } + del.ad = ad + del.ad?.fullScreenContentDelegate = del + del.channel?.invokeMethod("loaded", arguments: nil) + result(nil) + } + + }) break case "isLoaded": - let isReady = getRewardBasedVideoAd(id: id, rewardBasedVideoAdUnitId: adUnitId).isReady - result(isReady) + result(rewardAds[id] != nil) break case "show": - let rewardVideo = getRewardBasedVideoAd(id: id, rewardBasedVideoAdUnitId: adUnitId) - if rewardVideo.isReady, let rootViewController = UIApplication.shared.keyWindow?.rootViewController { - rewardVideo.present(fromRootViewController: rootViewController, delegate: delegates[id]!) + if let del = rewardAds[id], let rootViewController = UIApplication.shared.keyWindow?.rootViewController { + if let rewardAd = del.ad, let channel = del.channel { + rewardAd.present(fromRootViewController: rootViewController) { +// channel.invokeMethod(T##method: String##String, arguments: T##Any?) + print("rewarded") + } + } + } else { result(FlutterError( code: "GADRewardBasedVideoAd Error", @@ -81,70 +114,68 @@ public class AdmobRewardPlugin: NSObject, FlutterPlugin { break case "dispose": rewardAds.removeValue(forKey: id) - delegates.removeValue(forKey: id) + result(nil) break default: result(FlutterMethodNotImplemented) } } - private func loadRewardBasedVideoAd(id: Int, rewardBasedVideoAdUnitId: String, nonPersonalizedAds: Bool, userId: String?, customData: String?) { - let ssvOptions = GADServerSideVerificationOptions() - ssvOptions.userIdentifier = userId - ssvOptions.customRewardString = customData - let video = GADRewardedAd(adUnitID: rewardBasedVideoAdUnitId) - video.serverSideVerificationOptions = ssvOptions - 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. - self?.delegates[id]?.channel.invokeMethod("failedToLoad", arguments: ["errorCode": error.localizedDescription]) - } else { - // Ad successfully loaded. - self?.delegates[id]?.channel.invokeMethod("loaded", arguments: nil) - } - } - } - - private func getRewardBasedVideoAd(id: Int, rewardBasedVideoAdUnitId: String) -> GADRewardedAd { - if rewardAds[id] == nil { - let rewardBadedVideoAd = GADRewardedAd(adUnitID: rewardBasedVideoAdUnitId) - rewardAds[id] = rewardBadedVideoAd - } - - return rewardAds[id]! - } +// private func loadRewardBasedVideoAd(id: Int, rewardBasedVideoAdUnitId: String, nonPersonalizedAds: Bool, userId: String?, customData: String?) { +// let ssvOptions = GADServerSideVerificationOptions() +// ssvOptions.userIdentifier = userId +// ssvOptions.customRewardString = customData +// let video = GADRewardedAd(adUnitID: rewardBasedVideoAdUnitId) +// video.serverSideVerificationOptions = ssvOptions +// 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. +// self?.delegates[id]?.channel.invokeMethod("failedToLoad", arguments: ["errorCode": error.localizedDescription]) +// } else { +// // Ad successfully loaded. +// self?.delegates[id]?.channel.invokeMethod("loaded", arguments: nil) +// } +// } +// } +// +// private func getRewardBasedVideoAd(id: Int, rewardBasedVideoAdUnitId: String) -> GADRewardedAd { +// if rewardAds[id] == nil { +// let rewardBadedVideoAd = GADRewardedAd(adUnitID: rewardBasedVideoAdUnitId) +// rewardAds[id] = rewardBadedVideoAd +// } +// +// return rewardAds[id]! +// } } -class AdmobRewardPluginDelegate: NSObject, GADRewardedAdDelegate { - let channel: FlutterMethodChannel - - init(channel: FlutterMethodChannel) { - self.channel = channel - } - +class AdmobRewardPluginDelegate: NSObject, GADFullScreenContentDelegate { + var channel: FlutterMethodChannel? = nil + var ad: GADRewardedAd? = nil + func rewardedAdDidPresent(_ rewardedAd: GADRewardedAd) { - channel.invokeMethod("opened", arguments: nil) + channel?.invokeMethod("opened", arguments: nil) } func rewardedAdDidDismiss(_ rewardedAd: GADRewardedAd) { - channel.invokeMethod("closed", arguments: nil) + channel?.invokeMethod("closed", arguments: nil) } func rewardedAd(_ rewardedAd: GADRewardedAd, didFailToPresentWithError error: Error) { - channel.invokeMethod("failedToLoad", arguments: ["errorCode": error.localizedDescription]) + channel?.invokeMethod("failedToLoad", arguments: ["errorCode": error.localizedDescription]) } func rewardedAd(_ rewardedAd: GADRewardedAd, userDidEarn reward: GADAdReward) { - channel.invokeMethod("rewarded", arguments: [ + print("rewarded delegate") + channel?.invokeMethod("rewarded", arguments: [ "type": reward.type, "amount": reward.amount ]) diff --git a/ios/Classes/SwiftAdmobFlutterPlugin.swift b/ios/Classes/SwiftAdmobFlutterPlugin.swift index 72dd7c9..b029e03 100644 --- a/ios/Classes/SwiftAdmobFlutterPlugin.swift +++ b/ios/Classes/SwiftAdmobFlutterPlugin.swift @@ -64,7 +64,7 @@ public class SwiftAdmobFlutterPlugin: NSObject, FlutterPlugin { return } switch name { - case "SMART_BANNER": + case "FLUID": // TODO: Do we need Landscape too? let height: CGFloat = UIApplication.shared.keyWindow?.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.compact ? 50 : 90; result([ diff --git a/ios/admob_flutter.podspec b/ios/admob_flutter.podspec index 9bceddd..48fcfe4 100644 --- a/ios/admob_flutter.podspec +++ b/ios/admob_flutter.podspec @@ -1,6 +1,3 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html -# Pod::Spec.new do |s| s.name = 'admob_flutter' s.version = '1.0.0' @@ -16,9 +13,8 @@ Admob plugin that shows banner ads using native platform views. s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' - # https://firebase.google.com/docs/ios/setup - s.dependency 'Google-Mobile-Ads-SDK', '~> 7.64' - s.ios.deployment_target = '9.0' + s.dependency 'Google-Mobile-Ads-SDK', '~> 8.13.0' + s.ios.deployment_target = '12.0' s.static_framework = true end From d4440cec824e020dd3d2b5a77debf0bf7723a882 Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Wed, 29 Dec 2021 10:59:12 -0600 Subject: [PATCH 3/4] update reward call backs --- ios/Classes/AdmobRewardPlugin.swift | 81 ++++++++++------------------- 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/ios/Classes/AdmobRewardPlugin.swift b/ios/Classes/AdmobRewardPlugin.swift index 3ca8073..0991bdc 100644 --- a/ios/Classes/AdmobRewardPlugin.swift +++ b/ios/Classes/AdmobRewardPlugin.swift @@ -62,7 +62,6 @@ public class AdmobRewardPlugin: NSObject, FlutterPlugin { result(nil) break case "load": - let request = GADRequest() let nonPersonalizedAds = (args["nonPersonalizedAds"] as? Bool) ?? false @@ -96,14 +95,18 @@ public class AdmobRewardPlugin: NSObject, FlutterPlugin { result(rewardAds[id] != nil) break case "show": - if let del = rewardAds[id], let rootViewController = UIApplication.shared.keyWindow?.rootViewController { - if let rewardAd = del.ad, let channel = del.channel { - rewardAd.present(fromRootViewController: rootViewController) { -// channel.invokeMethod(T##method: String##String, arguments: T##Any?) + if let del = rewardAds[id], let rewardAd = del.ad, let rootViewController = UIApplication.shared.keyWindow?.rootViewController { + rewardAd.present(fromRootViewController: rootViewController) { + if let channel = del.channel { print("rewarded") + channel.invokeMethod("rewarded", arguments: [ + "type": rewardAd.adReward.type, + "amount": rewardAd.adReward.amount + ]) + } else { + print("rewarded but no channel") } } - } else { result(FlutterError( code: "GADRewardBasedVideoAd Error", @@ -120,64 +123,36 @@ public class AdmobRewardPlugin: NSObject, FlutterPlugin { result(FlutterMethodNotImplemented) } } - -// private func loadRewardBasedVideoAd(id: Int, rewardBasedVideoAdUnitId: String, nonPersonalizedAds: Bool, userId: String?, customData: String?) { -// let ssvOptions = GADServerSideVerificationOptions() -// ssvOptions.userIdentifier = userId -// ssvOptions.customRewardString = customData -// let video = GADRewardedAd(adUnitID: rewardBasedVideoAdUnitId) -// video.serverSideVerificationOptions = ssvOptions -// 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. -// self?.delegates[id]?.channel.invokeMethod("failedToLoad", arguments: ["errorCode": error.localizedDescription]) -// } else { -// // Ad successfully loaded. -// self?.delegates[id]?.channel.invokeMethod("loaded", arguments: nil) -// } -// } -// } -// -// private func getRewardBasedVideoAd(id: Int, rewardBasedVideoAdUnitId: String) -> GADRewardedAd { -// if rewardAds[id] == nil { -// let rewardBadedVideoAd = GADRewardedAd(adUnitID: rewardBasedVideoAdUnitId) -// rewardAds[id] = rewardBadedVideoAd -// } -// -// return rewardAds[id]! -// } } class AdmobRewardPluginDelegate: NSObject, GADFullScreenContentDelegate { var channel: FlutterMethodChannel? = nil var ad: GADRewardedAd? = nil - func rewardedAdDidPresent(_ rewardedAd: GADRewardedAd) { + func adWillDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) { + // Unused + } + + func adDidPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) { channel?.invokeMethod("opened", arguments: nil) } - - func rewardedAdDidDismiss(_ rewardedAd: GADRewardedAd) { + + func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) { channel?.invokeMethod("closed", arguments: nil) } - - func rewardedAd(_ rewardedAd: GADRewardedAd, didFailToPresentWithError error: Error) { - channel?.invokeMethod("failedToLoad", arguments: ["errorCode": error.localizedDescription]) + + func adDidRecordImpression(_ ad: GADFullScreenPresentingAd) { + channel?.invokeMethod("impression", arguments: nil) } - - func rewardedAd(_ rewardedAd: GADRewardedAd, userDidEarn reward: GADAdReward) { - print("rewarded delegate") - channel?.invokeMethod("rewarded", arguments: [ - "type": reward.type, - "amount": reward.amount + + func adDidRecordClick(_ ad: GADFullScreenPresentingAd) { + channel?.invokeMethod("clicked", arguments: nil) + } + + func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) { + channel?.invokeMethod("failedToLoad", arguments: [ + "errorCode": 1, + "error": error.localizedDescription ]) } } From 823c1220af70ae68de9ec2f55d30170171dd687d Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Wed, 29 Dec 2021 14:00:26 -0600 Subject: [PATCH 4/4] android rewards --- CHANGELOG.md | 2 +- .../com/shatsy/admobflutter/AdmobBanner.kt | 4 +- .../shatsy/admobflutter/AdmobFlutterPlugin.kt | 9 +-- .../shatsy/admobflutter/AdmobInterstitial.kt | 58 ++++++++-------- .../com/shatsy/admobflutter/AdmobReward.kt | 66 +++++++++---------- example/android/app/build.gradle | 2 +- .../android/app/src/main/AndroidManifest.xml | 13 ++-- example/lib/main.dart | 1 - pubspec.yaml | 2 +- 9 files changed, 78 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed4569a..595405a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## [3.0.0-beta.0] - 2021/12/28 +## [3.0.0-beta.0] - 2021/12/29 - Increase Android min api level to 19. - Increase iOS min SDK to 12. - Removed deprecated AdmobBannerSize.SMART_BANNER diff --git a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBanner.kt b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBanner.kt index a3f0051..df006ec 100644 --- a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBanner.kt +++ b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobBanner.kt @@ -23,7 +23,7 @@ class AdmobBanner(context: Context, messenger: BinaryMessenger, id: Int, args: H channel.setMethodCallHandler(this) adView.adSize = getSize(context, args["adSize"] as HashMap<*, *>) - adView.adUnitId = args["adUnitId"] as String? + adView.adUnitId = args["adUnitId"] as String val adRequestBuilder = AdRequest.Builder() val npa: Boolean? = args["nonPersonalizedAds"] as Boolean? @@ -47,7 +47,7 @@ class AdmobBanner(context: Context, messenger: BinaryMessenger, id: Int, args: H "MEDIUM_RECTANGLE" -> AdSize.MEDIUM_RECTANGLE "FULL_BANNER" -> AdSize.FULL_BANNER "LEADERBOARD" -> AdSize.LEADERBOARD - "SMART_BANNER" -> AdSize.SMART_BANNER + "FLUID" -> AdSize.FLUID "ADAPTIVE_BANNER" -> AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, width) else -> AdSize(width, height) } diff --git a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobFlutterPlugin.kt b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobFlutterPlugin.kt index 5c4c823..fe8158e 100644 --- a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobFlutterPlugin.kt +++ b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobFlutterPlugin.kt @@ -42,8 +42,6 @@ class AdmobFlutterPlugin : MethodCallHandler, FlutterPlugin, ActivityAware { defaultChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter") defaultChannel.setMethodCallHandler(this) - - interstitialChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/interstitial") rewardChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/reward") flutterPluginBinding.platformViewRegistry.registerViewFactory("admob_flutter/banner", AdmobBannerFactory(flutterPluginBinding.binaryMessenger)) @@ -55,7 +53,6 @@ class AdmobFlutterPlugin : MethodCallHandler, FlutterPlugin, ActivityAware { flutterPluginBinding = null } - override fun onAttachedToActivity(binding: ActivityPluginBinding) { flutterPluginBinding?.let { interstitialChannel.setMethodCallHandler(AdmobInterstitial(it, binding.activity)) @@ -97,11 +94,11 @@ class AdmobFlutterPlugin : MethodCallHandler, FlutterPlugin, ActivityAware { val name = args["name"] as String val width = args["width"] as Int when (name) { - "SMART_BANNER" -> { + "FLUID" -> { val metrics = context.resources.displayMetrics result.success(hashMapOf( - "width" to AdSize.SMART_BANNER.getWidthInPixels(context) / metrics.density, - "height" to AdSize.SMART_BANNER.getHeightInPixels(context) / metrics.density + "width" to AdSize.FLUID.getWidthInPixels(context) / metrics.density, + "height" to AdSize.FLUID.getHeightInPixels(context) / metrics.density )) } "ADAPTIVE_BANNER" -> { diff --git a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobInterstitial.kt b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobInterstitial.kt index a5ce7f4..0194d1c 100644 --- a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobInterstitial.kt +++ b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobInterstitial.kt @@ -24,9 +24,11 @@ class AdmobInterstitial(private val flutterPluginBinding: FlutterPlugin.FlutterP result.error("1", "Missing id", "Missing id") return } - if (allAds[id]?.adListener != null) return - + if (allAds[id] == null) { + allAds[id] = AdmobInterstitialListener() + } allAds[id]!!.adListener = createAdListener(MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/interstitial_$id")) + return result.success(null) } "load" -> { val id = call.argument("id") @@ -43,41 +45,39 @@ class AdmobInterstitial(private val flutterPluginBinding: FlutterPlugin.FlutterP extras.putString("npa", "1") adRequestBuilder.addNetworkExtrasBundle(AdMobAdapter::class.java, extras) } - if (allAds[id] == null) { allAds[id] = AdmobInterstitialListener() - InterstitialAd.load(activity, adUnitId, adRequestBuilder.build(), object : InterstitialAdLoadCallback() { - override fun onAdFailedToLoad(adError: LoadAdError) { - allAds[id]?.adListener?.onAdFailedToLoad(adError) - allAds[id] = null - return result.error("2", "onAdFailedToLoad", adError.message) - } - - override fun onAdLoaded(interstitialAd: InterstitialAd) { - allAds[id]?.ad = interstitialAd - allAds[id]?.adListener?.onAdLoaded() - allAds[id]?.ad?.fullScreenContentCallback = object : FullScreenContentCallback() { - override fun onAdDismissedFullScreenContent() { - allAds[id]?.adListener?.onAdClosed() - } + } - override fun onAdFailedToShowFullScreenContent(adError: AdError?) { - adError?.let { - allAds[id]?.adListener?.onAdFailedToLoad(LoadAdError(it.code, it.message, "admob_flutter", adError, null)) - } - } + InterstitialAd.load(activity, adUnitId, adRequestBuilder.build(), object : InterstitialAdLoadCallback() { + override fun onAdFailedToLoad(adError: LoadAdError) { + allAds[id]?.adListener?.onAdFailedToLoad(adError) + allAds[id] = null + return result.error("2", "onAdFailedToLoad", adError.message) + } - override fun onAdShowedFullScreenContent() { - allAds[id]?.adListener?.onAdLoaded() + override fun onAdLoaded(interstitialAd: InterstitialAd) { + allAds[id]?.ad = interstitialAd + allAds[id]?.adListener?.onAdLoaded() + allAds[id]?.ad?.fullScreenContentCallback = object : FullScreenContentCallback() { + override fun onAdDismissedFullScreenContent() { + allAds[id]?.adListener?.onAdClosed() + } + override fun onAdFailedToShowFullScreenContent(adError: AdError?) { + adError?.let { + allAds[id]?.adListener?.onAdFailedToLoad(LoadAdError(it.code, it.message, "admob_flutter", adError, null)) } } - return result.success(null) + + override fun onAdShowedFullScreenContent() { + allAds[id]?.adListener?.onAdOpened() + } } - }) - } else { - return result.success(null) - } + return result.success(null) + } + }) + } "isLoaded" -> { val id = call.argument("id") diff --git a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobReward.kt b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobReward.kt index bcb8fed..bfdc193 100644 --- a/android/src/main/kotlin/com/shatsy/admobflutter/AdmobReward.kt +++ b/android/src/main/kotlin/com/shatsy/admobflutter/AdmobReward.kt @@ -19,14 +19,16 @@ class AdmobReward(private val flutterPluginBinding: FlutterPlugin.FlutterPluginB override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { "setListener" -> { - val id = call.argument("id") - if (allAds[id]?.adListener != null) return + val id = call.argument("id") ?: 0 + if (allAds[id] == null) { + allAds[id] = AdmobRewardedListener() + } allAds[id]?.adListener = createAdListener(MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/reward_$id")) } "load" -> { - val id = call.argument("id") + val id = call.argument("id") ?: 0 val adUnitId = call.argument("adUnitId") - if (id == null || adUnitId == null) { + if (adUnitId == null) { result.error("1", "Missing id and/or adUnitId", "Missing id and/or adUnitId") return } @@ -40,37 +42,37 @@ class AdmobReward(private val flutterPluginBinding: FlutterPlugin.FlutterPluginB if (allAds[id] == null) { allAds[id] = AdmobRewardedListener() - RewardedAd.load(activity, adUnitId, adRequestBuilder.build(), object : RewardedAdLoadCallback() { - override fun onAdFailedToLoad(adError: LoadAdError) { - allAds[id]?.adListener?.onAdFailedToLoad(adError) - allAds[id] = null - return result.error("2", "onAdFailedToLoad", adError.message) - } + } - override fun onAdLoaded(rewardedAd: RewardedAd) { - allAds[id]?.ad = rewardedAd - allAds[id]!!.ad?.fullScreenContentCallback = object : FullScreenContentCallback() { - override fun onAdDismissedFullScreenContent() { - allAds[id]?.adListener?.onAdClosed() - allAds[id] = null - } + RewardedAd.load(activity, adUnitId, adRequestBuilder.build(), object : RewardedAdLoadCallback() { + override fun onAdFailedToLoad(adError: LoadAdError) { + allAds[id]?.adListener?.onAdFailedToLoad(adError) + allAds[id] = null + return result.error("2", "onAdFailedToLoad", adError.message) + } - override fun onAdFailedToShowFullScreenContent(adError: AdError?) { - adError?.let { - allAds[id]?.adListener?.onAdFailedToLoad(LoadAdError(it.code, it.message, "admob_flutter", adError, null)) - } - } + override fun onAdLoaded(rewardedAd: RewardedAd) { + allAds[id]?.ad = rewardedAd + allAds[id]?.adListener?.onAdLoaded() + allAds[id]!!.ad?.fullScreenContentCallback = object : FullScreenContentCallback() { + override fun onAdDismissedFullScreenContent() { + allAds[id]?.adListener?.onAdClosed() + } - override fun onAdShowedFullScreenContent() { - allAds[id]?.adListener?.onAdOpened() + override fun onAdFailedToShowFullScreenContent(adError: AdError?) { + adError?.let { + allAds[id]?.adListener?.onAdFailedToLoad(LoadAdError(it.code, it.message, "admob_flutter", adError, null)) } } - return result.success(null) + + override fun onAdShowedFullScreenContent() { + allAds[id]?.adListener?.onAdOpened() + } } - }) - } else { - return result.success(null) - } + return result.success(null) + } + }) + } "isLoaded" -> { val id = call.argument("id") @@ -84,14 +86,12 @@ class AdmobReward(private val flutterPluginBinding: FlutterPlugin.FlutterPluginB if (allAds[id]?.ad != null) { allAds[id]?.ad?.show(activity) { - fun onUserEarnedReward(rewardItem: RewardItem) { - allAds[id]?.adListener?.onRewarded(rewardItem) - } + allAds[id]?.adListener?.onRewarded(it) } } else result.error(null, null, null) } "dispose" -> { - val id = call.argument("id") + val id = call.argument("id") ?: 0 AdmobInterstitial.allAds.remove(id) return result.success(null) } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 81ab1b0..181bfd2 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -38,7 +38,7 @@ android { defaultConfig { applicationId "com.shatsy.admobflutterexample" - minSdkVersion 19 + minSdkVersion 21 targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index dc0594d..e5a0cbe 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -15,12 +15,20 @@ + @@ -28,10 +36,5 @@ - - diff --git a/example/lib/main.dart b/example/lib/main.dart index d3c72ee..a4004c2 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -72,7 +72,6 @@ class _MyMaterialAppState extends State { break; case AdmobAdEvent.failedToLoad: showSnackBar('Admob $adType failed to load. :('); - print(args); break; case AdmobAdEvent.rewarded: showDialog( diff --git a/pubspec.yaml b/pubspec.yaml index 3c68389..aa50851 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: admob_flutter description: Admob plugin that shows banner ads using native platform views. -version: 2.0.0 +version: 3.0.0-beta.0 homepage: https://github.com/kmcgill88/admob_flutter environment: