Search code examples
flutterkotlininterstitial

How to get activity in Flutter Android?


I am creating Flutter plugin that displays banner and interstitial ads in Flutter UI using Kotlin and Swift. When trying to show interstitial ad using Kotlin, I need to use Activity for that, which I am trying to get using onAttachedToActivity, but my activity variable is always null.

I checked other questions related to this, but they are either using configureFlutterEngine or have only one class.

I am using init method instead of FlutterEngine to add a layout in Flutter UI using Kotlin, which is later used to display banner (and banner works without any problems, so I removed that code part). I am adding all three Kotlin classes used for displaying ads.

Plugin class:

class SecondPluginAttemptPlugin: FlutterPlugin {

  private var Tag: String = "MyTag"
  override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    Log.d(Tag, "SecondPluginAttemptPlugin attached to Flutter engine")
    try {
      binding.platformViewRegistry.registerViewFactory(
        "plugins.example/second_attempt", MyViewFactory(binding.binaryMessenger)
      )
      Log.d(Tag, "MyViewFactory registered")
    } catch (e: Exception) {
      Log.e(Tag, "Error during registration: ${e.message}")
    }
  }
  
  override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    Log.d(Tag, "SecondPluginAttemptPlugin detached from Flutter engine")
  }
}

Factory class:

class MyViewFactory (private val messenger: BinaryMessenger): PlatformViewFactory(StandardMessageCodec.INSTANCE){
        override fun create(context: Context, id: Int, o: Any?) : PlatformView {
            return MyView(context, messenger, id)
        }
}

And my view class

class MyView internal constructor(context: Context, messenger: BinaryMessenger, id: Int) :
    PlatformView, MethodCallHandler, ActivityAware {
    private var applicationContext: Context = context
    private var messageLayout: ViewGroup?
    private val channel = MethodChannel(messenger, "plugins.example/second_attempt_$id")

    private var mAdManagerInterstitialAd: AdManagerInterstitialAd? = null
    private lateinit var myActivity: Activity
    private val Tag = "MyTag"

    init {
        try {
            Log.d(Tag, "init")
            channel.setMethodCallHandler(this)

        } catch (e: Exception) {
            Log.e(Tag, "Error setting method call handler: $e")
        }
        messageLayout = FrameLayout(applicationContext)
        messageLayout?.setBackgroundColor(Color.BLUE)
        val params = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.WRAP_CONTENT,
            FrameLayout.LayoutParams.WRAP_CONTENT
        )
        params.gravity = Gravity.BOTTOM
    }

    override fun getView(): View? {
        Log.d(Tag, "View")
        return messageLayout
    }

    override fun dispose() {
        Log.d(Tag, "Dispose")
        messageLayout = null
    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        Log.d(Tag, "onMethodCall " )
        when (call.method) {
            "setParams" -> settingParameters(call)
            else -> result.notImplemented()
        }
    }

    private fun settingParameters(call: MethodCall) {
        val arguments = call.arguments as? Map<*, *>
        val accountId = arguments?.get("prebidAccountId") as? String ?: ""
        val adUnitId = arguments?.get("adUnitId") as? String ?: ""
        val configId = arguments?.get("configId") as? String ?: ""
        val width = arguments?.get("width") as? Int ?: 0
        val height = arguments?.get("height") as? Int ?: 0

        messageLayout?.removeAllViews()

        val textView = TextView(applicationContext)
        textView.setTextColor(Color.RED)
        textView.gravity = Gravity.CENTER

        //Skipped some code used to check if variables are not empty and if they are - a message is displayed inside messageLayout

        createInterstitial(adUnitId)
    }

    private fun createInterstitial(AD_UNIT_ID: String) {
        val adRequest = AdManagerAdRequest.Builder().build()

        AdManagerInterstitialAd.load(applicationContext, AD_UNIT_ID, adRequest,
            object : AdManagerInterstitialAdLoadCallback() {
                override fun onAdLoaded(interstitialAd: AdManagerInterstitialAd) {
                    mAdManagerInterstitialAd = interstitialAd
                    Log.i(Tag, "onAdLoaded")
                    showInterstitial()
                }

                override fun onAdFailedToLoad(loadAdError: LoadAdError) {
                    Log.d(Tag, loadAdError.toString())
                    mAdManagerInterstitialAd = null
                }
            })
    }

    private fun showInterstitial() {
        if (mAdManagerInterstitialAd != null) {
            mAdManagerInterstitialAd?.show(myActivity)
        } else {
            Log.d("TAG", "The interstitial ad wasn't ready yet.")
        }
    }

    override fun onAttachedToActivity(binding: ActivityPluginBinding) {
        myActivity = binding.activity
    }

    override fun onDetachedFromActivityForConfigChanges() {
        TODO("Not yet implemented")
    }

    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
        TODO("Not yet implemented")
    }

    override fun onDetachedFromActivity() {
        TODO("Not yet implemented")
    }
}

Edit: so it seems my problem was adding ActivityAware inside MyView class instead of SecondPluginAttemptPlugin. As for passing activity value to MyView class, I used lateinit and CompletableFuture<Activity>().


Solution

  • Use ActivityAware

    import io.flutter.embedding.engine.plugins.activity.ActivityAware
    
    
    
    class SecondPluginAttemptPlugin : FlutterPlugin, ActivityAware {
        private var activity: Activity? = null
    
        //* * *
    
        override fun onAttachedToActivity(binding: ActivityPluginBinding) {
            activity = binding.activity
        }
        override fun onReattachedToActivityForConfigChanges(binding:ActivityPluginBinding) {
            activity = binding.activity
        }
    
        override fun onDetachedFromActivityForConfigChanges() {}
    
        override fun onDetachedFromActivity() {}
    }