Skip to content

Commit

Permalink
Upgrade android v2 embedding (#289)
Browse files Browse the repository at this point in the history
* android embed v2

* update changelog

* Update build images

* lint

* more lint
  • Loading branch information
kmcgill88 authored Dec 20, 2021
1 parent 97b3f14 commit b2c54d6
Show file tree
Hide file tree
Showing 21 changed files with 171 additions and 140 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [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

## [2.0.0-nullsafety.1] - 2021/04/10
- Restored testAdUnitId [275](https://github.com/kmcgill88/admob_flutter/commit/54ae1b24b01981834ac4d7020efaf96345a86d77)
- Add SSV data support to RewardedVideoAd [274](https://github.com/kmcgill88/admob_flutter/pull/274)
Expand Down
2 changes: 1 addition & 1 deletion android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.shatsy.admobflutter">
package="com.shatsy.admobflutter">

<uses-permission android:name="android.permission.INTERNET" />
</manifest>
134 changes: 76 additions & 58 deletions android/src/main/kotlin/com/shatsy/admobflutter/AdmobFlutterPlugin.kt
Original file line number Diff line number Diff line change
@@ -1,81 +1,99 @@
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 io.flutter.embedding.engine.plugins.FlutterPlugin
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
import io.flutter.plugin.common.PluginRegistry.Registrar
import kotlin.collections.HashMap

fun createAdListener(channel: MethodChannel) : AdListener {
return object: AdListener() {
override fun onAdLoaded() = channel.invokeMethod("loaded", null)
override fun onAdFailedToLoad(errorCode: Int) = 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)
}
fun createAdListener(channel: MethodChannel): AdListener {
return object : AdListener() {
override fun onAdLoaded() = channel.invokeMethod("loaded", null)
override fun onAdFailedToLoad(errorCode: Int) = 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)
}
}

class AdmobFlutterPlugin(private val context: Context): MethodCallHandler {
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val defaultChannel = MethodChannel(registrar.messenger(), "admob_flutter")
defaultChannel.setMethodCallHandler(AdmobFlutterPlugin(registrar.context()))
class AdmobFlutterPlugin : MethodCallHandler, FlutterPlugin {
/// 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
/// when the Flutter Engine is detached from the Activity
private lateinit var defaultChannel: MethodChannel
private lateinit var interstitialChannel: MethodChannel
private lateinit var rewardChannel: MethodChannel
private var context: Context? = null

override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext

val interstitialChannel = MethodChannel(registrar.messenger(), "admob_flutter/interstitial")
interstitialChannel.setMethodCallHandler(AdmobInterstitial(registrar))
defaultChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter")
defaultChannel.setMethodCallHandler(this)

val rewardChannel = MethodChannel(registrar.messenger(), "admob_flutter/reward")
rewardChannel.setMethodCallHandler(AdmobReward(registrar))
interstitialChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/interstitial")
interstitialChannel.setMethodCallHandler(AdmobInterstitial(flutterPluginBinding))

registrar
.platformViewRegistry()
.registerViewFactory("admob_flutter/banner", AdmobBannerFactory(registrar.messenger()))
rewardChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/reward")
rewardChannel.setMethodCallHandler(AdmobReward(flutterPluginBinding))

flutterPluginBinding.platformViewRegistry.registerViewFactory("admob_flutter/banner", AdmobBannerFactory(flutterPluginBinding.binaryMessenger))
}
}

override fun onMethodCall(call: MethodCall, result: Result) {
when(call.method) {
"initialize" -> {
MobileAds.initialize(context)
@Suppress("UNCHECKED_CAST")
(call.arguments as? ArrayList<String>)?.apply {
val configuration = RequestConfiguration.Builder().setTestDeviceIds(this).build()
MobileAds.setRequestConfiguration(configuration)
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
defaultChannel.setMethodCallHandler(null)
interstitialChannel.setMethodCallHandler(null)
rewardChannel.setMethodCallHandler(null)
context = 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.")
}
}
"banner_size" -> {
val args = call.arguments as HashMap<*, *>
val name = args["name"] as String
val width = args["width"] as Int
when(name) {
"SMART_BANNER" -> {
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
))
}
"ADAPTIVE_BANNER" -> {
val adSize = AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, width)
result.success(hashMapOf(
"width" to adSize.width,
"height" to adSize.height
))
}
else -> result.error("banner_size", "not implemented name", name)

when (call.method) {
"initialize" -> {
MobileAds.initialize(context)
@Suppress("UNCHECKED_CAST")
(call.arguments as? ArrayList<String>)?.apply {
val configuration = RequestConfiguration.Builder().setTestDeviceIds(this).build()
MobileAds.setRequestConfiguration(configuration)
}
}
"banner_size" -> {
val args = call.arguments as HashMap<*, *>
val name = args["name"] as String
val width = args["width"] as Int
when (name) {
"SMART_BANNER" -> {
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
))
}
"ADAPTIVE_BANNER" -> {
val adSize = AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, width)
result.success(hashMapOf(
"width" to adSize.width,
"height" to adSize.height
))
}
else -> result.error("banner_size", "not implemented name", name)
}
}
else -> result.notImplemented()
}
}
else -> result.notImplemented()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ 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.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.PluginRegistry

class AdmobInterstitial(private val registrar: PluginRegistry.Registrar): MethodChannel.MethodCallHandler {
class AdmobInterstitial(private val flutterPluginBinding: FlutterPlugin.FlutterPluginBinding): MethodChannel.MethodCallHandler {
companion object {
val allAds: MutableMap<Int, InterstitialAd> = mutableMapOf()
}
Expand All @@ -18,7 +18,7 @@ class AdmobInterstitial(private val registrar: PluginRegistry.Registrar): Method
val id = call.argument<Int>("id")
if (allAds[id]!!.adListener != null) return

val adChannel = MethodChannel(registrar.messenger(), "admob_flutter/interstitial_$id")
val adChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/interstitial_$id")
allAds[id]!!.adListener = createAdListener(adChannel)
}
"load" -> {
Expand All @@ -34,7 +34,7 @@ class AdmobInterstitial(private val registrar: PluginRegistry.Registrar): Method
}

if (allAds[id] == null) {
allAds[id!!] = InterstitialAd(registrar.context())
allAds[id!!] = InterstitialAd(flutterPluginBinding.applicationContext)
allAds[id]!!.adUnitId = adUnitId
}

Expand Down
10 changes: 5 additions & 5 deletions android/src/main/kotlin/com/shatsy/admobflutter/AdmobReward.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ 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 io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.PluginRegistry

class AdmobReward(private val registrar: PluginRegistry.Registrar): MethodChannel.MethodCallHandler, RewardedVideoAdListener {
class AdmobReward(private val flutterPluginBinding: FlutterPlugin.FlutterPluginBinding): MethodChannel.MethodCallHandler, RewardedVideoAdListener {
companion object {
val allAds: MutableMap<Int, RewardedVideoAd> = mutableMapOf()
}
Expand All @@ -24,7 +24,7 @@ class AdmobReward(private val registrar: PluginRegistry.Registrar): MethodChanne
val id = call.argument<Int>("id")
if (allAds[id]!!.rewardedVideoAdListener != null) return

adChannel = MethodChannel(registrar.messenger(), "admob_flutter/reward_$id")
adChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "admob_flutter/reward_$id")
allAds[id]!!.rewardedVideoAdListener = this
}
"load" -> {
Expand All @@ -41,7 +41,7 @@ class AdmobReward(private val registrar: PluginRegistry.Registrar): MethodChanne
adRequestBuilder.addNetworkExtrasBundle(AdMobAdapter::class.java, extras)
}

if (allAds[id] == null) allAds[id!!] = MobileAds.getRewardedVideoAdInstance(registrar.context())
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())
Expand All @@ -68,7 +68,7 @@ class AdmobReward(private val registrar: PluginRegistry.Registrar): MethodChanne
"dispose" -> {
val id = call.argument<Int>("id")

allAds[id]!!.destroy(registrar.context())
allAds[id]!!.destroy(flutterPluginBinding.applicationContext)
allAds.remove(id)
}
else -> result.notImplemented()
Expand Down
5 changes: 4 additions & 1 deletion bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ workflows:
- [email protected]: {}
- [email protected]:
inputs:
- version: 2.0.0
- version: 2.8.1
- [email protected]:
inputs:
- content: |-
Expand Down Expand Up @@ -43,3 +43,6 @@ app:
- opts:
is_expand: false
BITRISE_EXPORT_METHOD: development
meta:
bitrise.io:
stack: osx-xcode-13.2.x
35 changes: 34 additions & 1 deletion example/android/app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,38 @@
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET" />


<!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET" />

<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="${applicationName}"
android:label="admob_flutter_example">

<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713" />

<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
33 changes: 12 additions & 21 deletions example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,31 @@
flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET" />

<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="admob_flutter_example"
android:icon="@mipmap/ic_launcher">
android:name="${applicationName}"
android:label="admob_flutter_example">

<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713"/>
android:value="ca-app-pub-3940256099942544~3347511713" />

<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
package com.shatsy.admobflutterexample

import android.os.Bundle

import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
}
}

This file was deleted.

4 changes: 2 additions & 2 deletions example/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.3.21'
ext.kotlin_version = '1.3.40'
repositories {
google()
jcenter()
}

dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath 'com.android.tools.build:gradle:7.0.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
Loading

0 comments on commit b2c54d6

Please sign in to comment.