初步添加 Android 开屏广告
This commit is contained in:
parent
e3f5f1340e
commit
34f73d5c45
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Android API 34, extension level 7 Platform" project-jdk-type="Android SDK" />
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="io.flutter" />
|
||||
</component>
|
||||
</project>
|
6
android/.idea/vcs.xml
generated
Normal file
6
android/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -3,13 +3,16 @@ package com.example.union_ad_ssgf
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.util.Log
|
||||
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import com.qq.e.comm.managers.GDTAdSdk
|
||||
import com.qq.e.comm.managers.setting.GlobalSetting.setPersonalizedState
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.EventChannel.EventSink
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
|
||||
class PluginDelegate(var activity: Activity, pluginBinding: FlutterPluginBinding) :
|
||||
MethodChannel.MethodCallHandler, EventChannel.StreamHandler {
|
||||
|
||||
@ -53,7 +56,7 @@ class PluginDelegate(var activity: Activity, pluginBinding: FlutterPluginBinding
|
||||
}
|
||||
// 开屏广告
|
||||
"showSplashAd" -> {
|
||||
|
||||
showSplashAd(call, result)
|
||||
}
|
||||
// 激励广告
|
||||
"showRewardVideoAd" -> {
|
||||
@ -85,7 +88,7 @@ class PluginDelegate(var activity: Activity, pluginBinding: FlutterPluginBinding
|
||||
* @param call MethodCall
|
||||
* @param result Result
|
||||
*/
|
||||
private fun getPlatformVersion(call: MethodCall?, result: MethodChannel.Result) {
|
||||
private fun getPlatformVersion(call: MethodCall, result: MethodChannel.Result) {
|
||||
result.success(" Union AD Android " + android.os.Build.VERSION.RELEASE);
|
||||
}
|
||||
|
||||
@ -94,22 +97,23 @@ class PluginDelegate(var activity: Activity, pluginBinding: FlutterPluginBinding
|
||||
* @param call MethodCall
|
||||
* @param result Result
|
||||
*/
|
||||
private fun initAd(call: MethodCall?, result: MethodChannel.Result) {
|
||||
private fun initAd(call: MethodCall, result: MethodChannel.Result) {
|
||||
val appId = call.argument<String>("appId")
|
||||
result.success(" Union AD AppId " + appId);
|
||||
// GDTAdSdk.initWithoutStart(activity.getApplicationContext(), appId);
|
||||
// GDTAdSdk.start(new GDTAdSdk.OnStartListener() {
|
||||
// @Override
|
||||
// public void onStartSuccess() {
|
||||
// Log.e(TAG, "广告初始化成功");
|
||||
// result.success(true);
|
||||
// }
|
||||
// @Override
|
||||
// public void onStartFailed(Exception e) {
|
||||
// Log.e(TAG, "广告初始化失败", e);
|
||||
// result.success(false);
|
||||
// }
|
||||
// });
|
||||
GDTAdSdk.initWithoutStart(activity.applicationContext, appId) // 该接口不会采集用户信息
|
||||
// 调用initWithoutStart后请尽快调用start,否则可能影响广告填充,造成收入下降
|
||||
GDTAdSdk.start(
|
||||
object : GDTAdSdk.OnStartListener {
|
||||
override fun onStartSuccess() {
|
||||
// 推荐开发者在 onStartSuccess 回调后开始拉广告
|
||||
Log.e("gdt onStartSuccess", "加载成功")
|
||||
}
|
||||
|
||||
override fun onStartFailed(e: Exception) {
|
||||
Log.e("gdt onStartFailed:", e.toString())
|
||||
}
|
||||
}
|
||||
)
|
||||
result.success(true)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,9 +121,28 @@ class PluginDelegate(var activity: Activity, pluginBinding: FlutterPluginBinding
|
||||
* @param call MethodCall
|
||||
* @param result Result
|
||||
*/
|
||||
private fun setPersonalizedState(call: MethodCall?, result: MethodChannel.Result) {
|
||||
private fun setPersonalizedState(call: MethodCall, result: MethodChannel.Result) {
|
||||
val state = call.argument<Int>("state")!!
|
||||
// GlobalSetting.setPersonalizedState(state);
|
||||
setPersonalizedState(state)
|
||||
result.success(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开屏广告
|
||||
* @param call MethodCall
|
||||
* @param result Result
|
||||
*/
|
||||
private fun showSplashAd(call: MethodCall, result: MethodChannel.Result) {
|
||||
val posId: String? = call.argument(UnionADConfig.KEY_POSID)
|
||||
val logo: String? = call.argument(UnionADConfig.KEY_LOGO)
|
||||
|
||||
val fetchDelay: Double = call.argument(UnionADConfig.KEY_FETCH_DELAY)!!
|
||||
// val intent = Intent(activity, AdSplashActivity::class.java)
|
||||
// intent.putExtra(UnionADConfig.KEY_POSID, posId)
|
||||
// intent.putExtra(UnionADConfig.KEY_LOGO, logo)
|
||||
// intent.putExtra(UnionADConfig.KEY_FETCH_DELAY, fetchDelay)
|
||||
// activity.startActivity(intent)
|
||||
// result.success(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,10 +17,13 @@ class UnionAdSsgfPlugin : FlutterPlugin, ActivityAware {
|
||||
* 用于Flutter与原生Android之间通信的 MethodChannel
|
||||
* 此本地引用用于向Flutter引擎注册插件,并在Flutter引擎与Activity分离时注销它
|
||||
*/
|
||||
private var methodChannel: MethodChannel = null;
|
||||
private lateinit var methodChannel: MethodChannel
|
||||
|
||||
/**
|
||||
* 事件通道
|
||||
*
|
||||
* 用于Flutter与原生Android之间通信的 EventChannel
|
||||
* 此本地引用用于向Flutter引擎注册插件,并在Flutter引擎与Activity分离时注销它
|
||||
*/
|
||||
private lateinit var eventChannel: EventChannel
|
||||
|
||||
@ -28,7 +31,7 @@ class UnionAdSsgfPlugin : FlutterPlugin, ActivityAware {
|
||||
/**
|
||||
* 插件代理
|
||||
*/
|
||||
private var delegate: PluginDelegate? = null
|
||||
var delegate: PluginDelegate? = null
|
||||
|
||||
|
||||
/**
|
||||
@ -40,13 +43,13 @@ class UnionAdSsgfPlugin : FlutterPlugin, ActivityAware {
|
||||
* 初始化方法通道和事件通道
|
||||
*/
|
||||
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
bind = flutterPluginBinding;
|
||||
bind = flutterPluginBinding
|
||||
|
||||
// 注册 - 方法通道
|
||||
methodChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "union_ad_ssgf_method");
|
||||
methodChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "union_ad_ssgf_method")
|
||||
|
||||
// 注册 - 监听通道
|
||||
eventChannel =
|
||||
EventChannel(flutterPluginBinding.getBinaryMessenger(), "union_ad_ssgf_event");
|
||||
eventChannel = EventChannel(flutterPluginBinding.getBinaryMessenger(), "union_ad_ssgf_event")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,40 @@
|
||||
package com.example.union_ad_ssgf.config
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
|
||||
class UnionADConfig {
|
||||
|
||||
/*初始化代码块*/
|
||||
init {
|
||||
_instance = this
|
||||
}
|
||||
|
||||
companion object {
|
||||
// 插件代理对象
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private lateinit var _instance: UnionADConfig
|
||||
|
||||
fun getInstance(): UnionADConfig {
|
||||
return _instance
|
||||
}
|
||||
|
||||
// Banner View
|
||||
const val KEY_BANNER_VIEW = "flutter_qq_ads_banner"
|
||||
|
||||
// Feed View
|
||||
const val KEY_FEED_VIEW = "flutter_qq_ads_feed"
|
||||
|
||||
// 广告参数
|
||||
const val KEY_POSID = "posId"
|
||||
|
||||
// 广告参数
|
||||
const val KEY_ADID = "adId"
|
||||
|
||||
// logo 参数
|
||||
const val KEY_LOGO = "logo"
|
||||
|
||||
// fetchDelay 参数
|
||||
const val KEY_FETCH_DELAY = "fetchDelay"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.example.union_ad_ssgf.event
|
||||
|
||||
|
||||
class AdErrorEvent(posId: String?, adId: String?, viewId: Int?, errCode: Int, errMsg: String?) :
|
||||
AdEvent(posId, adId, viewId, AdEventAction.onAdError) {
|
||||
// 错误码
|
||||
private var errCode = 0
|
||||
|
||||
// 错误信息
|
||||
private var errMsg: String? = null
|
||||
|
||||
// 错误事件实体
|
||||
init {
|
||||
this.errMsg = errMsg
|
||||
this.errCode = errCode
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写 toMap 方法
|
||||
* @return 返回错误事件的map
|
||||
*/
|
||||
override fun toMap(): HashMap<String, Any?> {
|
||||
val newMap = super.toMap()
|
||||
newMap["errCode"] = errCode
|
||||
newMap["errMsg"] = errMsg
|
||||
return newMap
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.example.union_ad_ssgf.event
|
||||
|
||||
import java.util.HashMap
|
||||
|
||||
open class AdEvent(posId: String?, adId: String?, viewId: Int?, action: String?) {
|
||||
// 广告位 id
|
||||
private var posId: String? = null
|
||||
|
||||
// 广告 id
|
||||
private var adId: String? = null
|
||||
|
||||
private var viewId: Int? = null
|
||||
|
||||
// 操作
|
||||
private var action: String? = null
|
||||
|
||||
init {
|
||||
this.adId = adId
|
||||
this.posId = posId
|
||||
this.action = action
|
||||
this.viewId = viewId
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为 Map 方面传输
|
||||
* @return 转换后的 Map 对象
|
||||
*/
|
||||
open fun toMap(): HashMap<String, Any?> {
|
||||
val map = HashMap<String, Any?>()
|
||||
map["adId"] = adId
|
||||
map["posId"] = posId
|
||||
map["action"] = action
|
||||
map["viewId"] = viewId
|
||||
return map
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.example.union_ad_ssgf.event
|
||||
|
||||
class AdEventAction {
|
||||
companion object {
|
||||
// 广告错误
|
||||
val onAdError = "onAdError"
|
||||
|
||||
// 广告加载成功
|
||||
val onAdLoaded = "onAdLoaded"
|
||||
|
||||
// 广告填充
|
||||
val onAdPresent = "onAdPresent"
|
||||
|
||||
// 广告曝光
|
||||
val onAdExposure = "onAdExposure"
|
||||
|
||||
// 广告关闭
|
||||
val onAdClosed = "onAdClosed"
|
||||
|
||||
// 广告点击
|
||||
val onAdClicked = "onAdClicked"
|
||||
|
||||
// 广告跳过
|
||||
val onAdSkip = "onAdSkip"
|
||||
|
||||
// 广告播放或计时完毕
|
||||
val onAdComplete = "onAdComplete"
|
||||
|
||||
// 获得广告激励
|
||||
val onAdReward = "onAdReward"
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.example.union_ad_ssgf.event
|
||||
|
||||
|
||||
class AdEventHandler {
|
||||
companion object {
|
||||
|
||||
// 广告事件处理对象
|
||||
@Volatile private var _instance: AdEventHandler? = null
|
||||
|
||||
/**
|
||||
* 获取广告事件处理类
|
||||
* @return 广告事件处理对象
|
||||
*/
|
||||
fun getInstance(): AdEventHandler? {
|
||||
if (_instance == null) {
|
||||
synchronized(AdEventHandler::class.java) { _instance = AdEventHandler() }
|
||||
}
|
||||
return _instance
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加广告事件
|
||||
* @param event 广告事件
|
||||
*/
|
||||
fun sendEvent(event: AdEvent?) {
|
||||
if (event != null) {
|
||||
// PluginDelegate.getInstance().addEvent(event.toMap())
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package com.example.union_ad_ssgf.load
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import com.example.union_ad_ssgf.event.AdEventAction
|
||||
import com.example.union_ad_ssgf.page.BaseAdPage
|
||||
import com.qq.e.ads.nativ.ADSize
|
||||
import com.qq.e.ads.nativ.NativeExpressAD
|
||||
import com.qq.e.ads.nativ.NativeExpressADView
|
||||
import com.qq.e.comm.util.AdError
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.util.Locale
|
||||
|
||||
/** 信息流加载对象 */
|
||||
class FeedAdLoad : BaseAdPage(), NativeExpressAD.NativeExpressADListener {
|
||||
private val TAG = FeedAdLoad::class.java.getSimpleName()
|
||||
private var result: MethodChannel.Result? = null
|
||||
|
||||
/**
|
||||
* 加载信息流广告列表
|
||||
* @param call
|
||||
* @param result
|
||||
*/
|
||||
fun loadFeedAdList(activity: Activity?, call: MethodCall, result: MethodChannel.Result) {
|
||||
this.result = result
|
||||
showAd(activity, null, call)
|
||||
}
|
||||
|
||||
override fun onNoAD(error: AdError) {
|
||||
val msg =
|
||||
String.format(
|
||||
Locale.getDefault(),
|
||||
"onError, error code: %d, error msg: %s",
|
||||
error.getErrorCode(),
|
||||
error.getErrorMsg()
|
||||
)
|
||||
Log.i(TAG, "onError, adError=$msg")
|
||||
sendErrorEvent(error.getErrorCode(), error.getErrorMsg())
|
||||
this.result!!.success(ArrayList<Int>())
|
||||
}
|
||||
|
||||
override fun onADLoaded(list: MutableList<NativeExpressADView>) {
|
||||
Log.i(TAG, "onADLoaded")
|
||||
val adResultList: MutableList<Int> = ArrayList()
|
||||
|
||||
if (list.isEmpty()) {
|
||||
result!!.success(adResultList)
|
||||
return
|
||||
}
|
||||
for (adItem in list) {
|
||||
val key = adItem.hashCode()
|
||||
adResultList.add(key)
|
||||
FeedAdManager.getInstance()?.putAd(key, adItem)
|
||||
}
|
||||
// 添加广告事件
|
||||
sendEvent(AdEventAction.onAdLoaded)
|
||||
result!!.success(adResultList)
|
||||
}
|
||||
|
||||
override fun onRenderFail(nativeExpressADView: NativeExpressADView) {
|
||||
Log.i(TAG, "onRenderFail")
|
||||
sendErrorEvent(-100, "onRenderFail")
|
||||
sendBroadcastEvent(nativeExpressADView, AdEventAction.onAdError)
|
||||
}
|
||||
|
||||
private fun sendBroadcastEvent(adView: NativeExpressADView, event: String) {
|
||||
val intent = Intent()
|
||||
intent.setAction(UnionADConfig.KEY_FEED_VIEW + "_" + adView.hashCode())
|
||||
intent.putExtra("event", event)
|
||||
LocalBroadcastManager.getInstance(activity!!).sendBroadcast(intent)
|
||||
}
|
||||
|
||||
override fun onRenderSuccess(nativeExpressADView: NativeExpressADView) {
|
||||
Log.i(TAG, "onRenderSuccess")
|
||||
sendEvent(AdEventAction.onAdPresent)
|
||||
sendBroadcastEvent(nativeExpressADView, AdEventAction.onAdPresent)
|
||||
}
|
||||
|
||||
override fun onADExposure(nativeExpressADView: NativeExpressADView?) {
|
||||
Log.i(TAG, "onADExposure")
|
||||
sendEvent(AdEventAction.onAdExposure)
|
||||
}
|
||||
|
||||
override fun onADClicked(nativeExpressADView: NativeExpressADView?) {
|
||||
Log.i(TAG, "onADClicked")
|
||||
sendEvent(AdEventAction.onAdClicked)
|
||||
}
|
||||
|
||||
override fun onADClosed(nativeExpressADView: NativeExpressADView) {
|
||||
Log.i(TAG, "onADClosed")
|
||||
sendEvent(AdEventAction.onAdClosed)
|
||||
sendBroadcastEvent(nativeExpressADView, AdEventAction.onAdClosed)
|
||||
}
|
||||
|
||||
override fun onADLeftApplication(p0: NativeExpressADView?) {}
|
||||
|
||||
override fun loadAd(call: MethodCall?) {
|
||||
// 获取请求模板广告素材的尺寸
|
||||
val width: Int = call!!.argument("width")!!
|
||||
val height: Int = call.argument("height")!!
|
||||
val count: Int = call.argument("count")!!
|
||||
val ad = NativeExpressAD(activity, ADSize(width, height), posId, this)
|
||||
ad.loadAD(count)
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package com.example.union_ad_ssgf.load
|
||||
|
||||
import com.qq.e.ads.nativ.NativeExpressADView
|
||||
|
||||
|
||||
/**
|
||||
* 信息流广告管理
|
||||
*/
|
||||
class FeedAdManager {
|
||||
private val TAG = FeedAdManager::class.java.getSimpleName()
|
||||
|
||||
companion object {
|
||||
// 信息流广告管理类对象
|
||||
private var _instance: FeedAdManager? = null
|
||||
|
||||
@Synchronized
|
||||
fun getInstance(): FeedAdManager? {
|
||||
if (_instance == null) {
|
||||
synchronized(FeedAdManager::class.java) { _instance = FeedAdManager() }
|
||||
}
|
||||
return _instance
|
||||
}
|
||||
}
|
||||
|
||||
// 信息流广告列表
|
||||
private val feedAdList: MutableMap<Int, NativeExpressADView> = HashMap()
|
||||
|
||||
/**
|
||||
* 添加广告渲染对象
|
||||
*
|
||||
* @param key 广告缓存id
|
||||
* @param ad 广告渲染对象
|
||||
*/
|
||||
fun putAd(key: Int, ad: NativeExpressADView) {
|
||||
feedAdList[key] = ad
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取广告渲染对象
|
||||
*
|
||||
* @param key 广告缓存 id
|
||||
* @return 广告渲染对象
|
||||
*/
|
||||
fun getAd(key: Int): NativeExpressADView? {
|
||||
return feedAdList[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除广告渲染对象
|
||||
*
|
||||
* @param key 广告缓存id
|
||||
* @return 广告渲染对象
|
||||
*/
|
||||
fun removeAd(key: Int): NativeExpressADView? {
|
||||
return feedAdList.remove(key)
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package com.example.union_ad_ssgf.page
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import com.qq.e.ads.banner2.UnifiedBannerADListener
|
||||
import com.qq.e.ads.banner2.UnifiedBannerView
|
||||
import com.qq.e.comm.util.AdError
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
import java.util.Locale
|
||||
|
||||
import android.util.Log;
|
||||
import com.example.union_ad_ssgf.PluginDelegate
|
||||
import com.example.union_ad_ssgf.event.AdEventAction
|
||||
|
||||
class AdBannerView(
|
||||
context: Context,
|
||||
private var id: Int,
|
||||
creationParams: Map<String, Any>?,
|
||||
private var pluginDelegate: PluginDelegate?
|
||||
) : BaseAdPage(), PlatformView, UnifiedBannerADListener {
|
||||
private val TAG = AdBannerView::class.java.getSimpleName()
|
||||
private var frameLayout = FrameLayout(context)
|
||||
private var bv: UnifiedBannerView? = null
|
||||
private var params = creationParams
|
||||
|
||||
init {
|
||||
val call = MethodCall("AdBannerView", creationParams)
|
||||
showAd(this.pluginDelegate!!.activity, id, call);
|
||||
}
|
||||
|
||||
override fun loadAd(call: MethodCall?) {
|
||||
// 获取轮播时间间隔参数
|
||||
val interval = params!!["interval"] as Int
|
||||
// 加载广告 Banner
|
||||
bv = UnifiedBannerView(activity, posId, this)
|
||||
frameLayout!!.addView(bv)
|
||||
// 设置轮播时间间隔
|
||||
bv!!.setRefresh(interval)
|
||||
bv!!.loadAD()
|
||||
}
|
||||
|
||||
override fun getView(): View? {
|
||||
return frameLayout;
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
disposeAd();
|
||||
}
|
||||
|
||||
override fun onNoAD(error: AdError) {
|
||||
val msg = java.lang.String.format(
|
||||
Locale.getDefault(), "onNoAD, error code: %d, error msg: %s",
|
||||
error.getErrorCode(), error.getErrorMsg()
|
||||
)
|
||||
Log.e(TAG, msg)
|
||||
sendErrorEvent(error.getErrorCode(), error.getErrorMsg())
|
||||
disposeAd()
|
||||
}
|
||||
|
||||
override fun onADReceive() {
|
||||
Log.i(TAG, "onADReceive");
|
||||
sendEvent(AdEventAction.onAdLoaded);
|
||||
}
|
||||
|
||||
override fun onADExposure() {
|
||||
Log.i(TAG, "onADExposure");
|
||||
sendEvent(AdEventAction.onAdExposure);
|
||||
}
|
||||
|
||||
override fun onADClosed() {
|
||||
Log.i(TAG, "onADClosed");
|
||||
sendEvent(AdEventAction.onAdClosed);
|
||||
disposeAd();
|
||||
}
|
||||
|
||||
override fun onADClicked() {
|
||||
Log.i(TAG, "onADClicked");
|
||||
sendEvent(AdEventAction.onAdClicked);
|
||||
}
|
||||
|
||||
override fun onADLeftApplication() {
|
||||
Log.i(TAG, "onADLeftApplication");
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁广告
|
||||
*/
|
||||
private fun disposeAd() {
|
||||
frameLayout!!.removeAllViews()
|
||||
if (bv != null) {
|
||||
bv!!.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package com.example.union_ad_ssgf.page
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.example.union_ad_ssgf.PluginDelegate
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import com.example.union_ad_ssgf.event.AdEventAction
|
||||
import com.example.union_ad_ssgf.load.FeedAdManager
|
||||
import com.qq.e.ads.nativ.NativeExpressADView
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
|
||||
class AdFeedView(
|
||||
context: Context,
|
||||
private var id: Int,
|
||||
private var creationParams: Map<String, Any>?,
|
||||
private var pluginDelegate: PluginDelegate?
|
||||
) : BaseAdPage(), PlatformView {
|
||||
private var frameLayout = FrameLayout(context)
|
||||
private var fad: NativeExpressADView? = null
|
||||
private var receiver: BroadcastReceiver? = null
|
||||
|
||||
init {
|
||||
val call = MethodCall("AdFeedView", creationParams)
|
||||
showAd(this.pluginDelegate!!.activity, id, call)
|
||||
}
|
||||
|
||||
override fun loadAd(call: MethodCall?) {
|
||||
val key = adId!!.toInt()
|
||||
regReceiver(key)
|
||||
fad = FeedAdManager.getInstance()!!.getAd(key)
|
||||
if (fad != null) {
|
||||
if (frameLayout.childCount > 0) {
|
||||
frameLayout.removeAllViews()
|
||||
}
|
||||
fad!!.render()
|
||||
frameLayout.addView(fad)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getView(): View? {
|
||||
return frameLayout
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
removeAd()
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册广播
|
||||
* @param key key
|
||||
*/
|
||||
private fun regReceiver(key: Int) {
|
||||
// 注册广播
|
||||
receiver =
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val event = intent.getStringExtra("event")
|
||||
sendEvent(event)
|
||||
if (AdEventAction.onAdClosed == event || AdEventAction.onAdError == event) {
|
||||
this@AdFeedView.disposeAd()
|
||||
}
|
||||
}
|
||||
}
|
||||
val intentFilter = IntentFilter(UnionADConfig.KEY_FEED_VIEW + "_" + key)
|
||||
LocalBroadcastManager.getInstance(activity!!).registerReceiver(receiver!!, intentFilter)
|
||||
}
|
||||
|
||||
/** 移除广告 */
|
||||
private fun removeAd() {
|
||||
frameLayout!!.removeAllViews()
|
||||
// 注销广播
|
||||
if (receiver != null) {
|
||||
LocalBroadcastManager.getInstance(activity!!).unregisterReceiver(receiver!!)
|
||||
}
|
||||
}
|
||||
|
||||
/** 销毁广告 */
|
||||
private fun disposeAd() {
|
||||
removeAd()
|
||||
FeedAdManager.getInstance()!!.removeAd(adId!!.toInt())
|
||||
if (fad != null) {
|
||||
fad!!.destroy()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
package com.example.union_ad_ssgf.page
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import com.example.union_ad_ssgf.R
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import com.example.union_ad_ssgf.event.AdErrorEvent
|
||||
import com.example.union_ad_ssgf.event.AdEvent
|
||||
import com.example.union_ad_ssgf.event.AdEventAction
|
||||
import com.example.union_ad_ssgf.event.AdEventHandler
|
||||
import com.qq.e.ads.splash.SplashAD
|
||||
import com.qq.e.ads.splash.SplashADListener
|
||||
import com.qq.e.comm.util.AdError
|
||||
|
||||
|
||||
/**
|
||||
* 开屏广告
|
||||
*/
|
||||
class AdSplashActivity() : AppCompatActivity(), SplashADListener {
|
||||
private val TAG = AdSplashActivity::class.java.getSimpleName()
|
||||
|
||||
// 广告容器
|
||||
private var ad_container: FrameLayout? = null
|
||||
|
||||
// 自定义品牌 logo
|
||||
private var ad_logo: AppCompatImageView? = null
|
||||
|
||||
// 广告位 id
|
||||
private var posId: String? = null
|
||||
|
||||
// 是否全屏
|
||||
private var isFullScreen = false
|
||||
|
||||
// 开屏广告
|
||||
private var splashAD: SplashAD? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_ad_splash);
|
||||
initView();
|
||||
initData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化View
|
||||
*/
|
||||
private fun initView() {
|
||||
ad_container = findViewById<FrameLayout>(R.id.splash_ad_container)
|
||||
ad_logo = findViewById<AppCompatImageView>(R.id.splash_ad_logo)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化数据
|
||||
*/
|
||||
private fun initData() {
|
||||
// 获取参数
|
||||
posId = intent.getStringExtra(UnionADConfig.KEY_POSID)
|
||||
val logo = intent.getStringExtra(UnionADConfig.KEY_LOGO)
|
||||
val fetchDelay = intent.getDoubleExtra(UnionADConfig.KEY_FETCH_DELAY, 0.0)
|
||||
val absFetchDelay = (fetchDelay * 1000).toInt()
|
||||
isFullScreen = TextUtils.isEmpty(logo)
|
||||
// 创建开屏广告
|
||||
splashAD = SplashAD(this, posId, this, absFetchDelay)
|
||||
if (isFullScreen) {
|
||||
// logo 为空则加载全屏广告
|
||||
ad_logo!!.setVisibility(View.GONE)
|
||||
splashAD!!.fetchFullScreenAdOnly()
|
||||
} else {
|
||||
// 加载 logo
|
||||
val resId: Int = getMipmapId(logo!!)
|
||||
if (resId > 0) {
|
||||
ad_logo!!.setVisibility(View.VISIBLE)
|
||||
ad_logo!!.setImageResource(resId)
|
||||
}
|
||||
// 加载广告
|
||||
splashAD!!.fetchAdOnly()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onADDismissed() {
|
||||
Log.d(TAG, "onADDismissed")
|
||||
finishPage()
|
||||
AdEventHandler.getInstance()?.sendEvent(AdEvent(posId, "", 1, AdEventAction.onAdClosed));
|
||||
}
|
||||
|
||||
override fun onNoAD(adError: AdError) {
|
||||
Log.d(TAG, "onNoAD adError:" + adError.errorMsg);
|
||||
finishPage()
|
||||
AdEventHandler.getInstance()!!
|
||||
.sendEvent(AdErrorEvent(posId, "", 1, adError.errorCode, adError.errorMsg));
|
||||
}
|
||||
|
||||
override fun onADPresent() {
|
||||
Log.d(TAG, "onADPresent")
|
||||
AdEventHandler.getInstance()!!.sendEvent(AdEvent(posId, "", 1, AdEventAction.onAdPresent))
|
||||
}
|
||||
|
||||
override fun onADClicked() {
|
||||
Log.d(TAG, "onADClicked")
|
||||
AdEventHandler.getInstance()!!.sendEvent(AdEvent(posId, "", 1, AdEventAction.onAdClicked))
|
||||
}
|
||||
|
||||
override fun onADTick(millisUntilFinished: Long) {
|
||||
Log.d(TAG, "onADTick millisUntilFinished:$millisUntilFinished");
|
||||
}
|
||||
|
||||
override fun onADExposure() {
|
||||
Log.d(TAG, "onADExposure")
|
||||
AdEventHandler.getInstance()!!.sendEvent(AdEvent(posId, "", 1, AdEventAction.onAdExposure))
|
||||
}
|
||||
|
||||
override fun onADLoaded(expireTimestamp: Long) {
|
||||
Log.d(TAG, "onADLoaded expireTimestamp:$expireTimestamp")
|
||||
AdEventHandler.getInstance()!!.sendEvent(AdEvent(posId, "", 1, AdEventAction.onAdLoaded))
|
||||
if (isFullScreen) {
|
||||
splashAD!!.showFullScreenAd(ad_container)
|
||||
} else {
|
||||
splashAD!!.showAd(ad_container)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成广告,退出开屏页面
|
||||
*/
|
||||
private fun finishPage() {
|
||||
finish()
|
||||
// 设置退出动画
|
||||
overridePendingTransition(0, android.R.anim.fade_out)
|
||||
}
|
||||
|
||||
/**
|
||||
* 开屏页一定要禁止用户对返回按钮的控制,否则将可能导致用户手动退出了App而广告无法正常曝光和计费
|
||||
*/
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) {
|
||||
true
|
||||
} else super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片资源的id
|
||||
*
|
||||
* @param resName 资源名称,不带后缀
|
||||
* @return 返回资源id
|
||||
*/
|
||||
@SuppressLint("DiscouragedApi")
|
||||
private fun getMipmapId(resName: String): Int {
|
||||
return getResources().getIdentifier(resName, "mipmap", packageName)
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.example.union_ad_ssgf.page
|
||||
|
||||
import android.app.Activity
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import com.example.union_ad_ssgf.event.AdErrorEvent
|
||||
import com.example.union_ad_ssgf.event.AdEvent
|
||||
import com.example.union_ad_ssgf.event.AdEventHandler
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
|
||||
abstract class BaseAdPage {
|
||||
// 上下文
|
||||
protected var activity: Activity? = null
|
||||
|
||||
// 广告位 id
|
||||
protected var posId: String? = null
|
||||
|
||||
// 广告位 id
|
||||
protected var adId: String? = null
|
||||
|
||||
// 广告定位ID
|
||||
protected var viewId: Int? = null
|
||||
|
||||
/**
|
||||
* 显示广告
|
||||
*
|
||||
* @param activity 上下文
|
||||
* @param call 方法调用
|
||||
*/
|
||||
fun showAd(activity: Activity?, viewId: Int?, call: MethodCall) {
|
||||
this.activity = activity
|
||||
this.viewId = viewId
|
||||
this.posId = call.argument<String>(UnionADConfig.KEY_POSID)
|
||||
this.adId = call.argument<String>(UnionADConfig.KEY_ADID)
|
||||
loadAd(call)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载广告
|
||||
* @param call 方法调用
|
||||
*/
|
||||
abstract fun loadAd(call: MethodCall?)
|
||||
|
||||
/**
|
||||
* 发送广告事件
|
||||
* @param event 广告事件
|
||||
*/
|
||||
protected fun sendEvent(event: AdEvent?) {
|
||||
AdEventHandler.getInstance()?.sendEvent(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送广告事件
|
||||
*
|
||||
* @param action 操作
|
||||
*/
|
||||
protected fun sendEvent(action: String?) {
|
||||
sendEvent(AdEvent(posId, adId, viewId, action))
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送错误事件
|
||||
*
|
||||
* @param errCode 错误码
|
||||
* @param errMsg 错误事件
|
||||
*/
|
||||
protected fun sendErrorEvent(errCode: Int, errMsg: String?) {
|
||||
sendEvent(AdErrorEvent(posId, adId, viewId, errCode, errMsg))
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.example.union_ad_ssgf.page
|
||||
|
||||
import android.content.Context
|
||||
import com.example.union_ad_ssgf.PluginDelegate
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import io.flutter.plugin.common.StandardMessageCodec
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
import io.flutter.plugin.platform.PlatformViewFactory
|
||||
|
||||
/// View 名字
|
||||
class NativeViewFactory(
|
||||
private val viewName: String, // 插件代理类
|
||||
private val pluginDelegate: PluginDelegate
|
||||
) :
|
||||
PlatformViewFactory(StandardMessageCodec.INSTANCE) {
|
||||
|
||||
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
|
||||
val creationParams = args as Map<String, Object>
|
||||
return if (viewName == UnionADConfig.KEY_BANNER_VIEW) {
|
||||
AdBannerView(context, viewId, creationParams, pluginDelegate)
|
||||
} else {
|
||||
AdFeedView(context, viewId, creationParams, pluginDelegate)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
28
android/src/main/res/layout/activity_ad_splash.xml
Normal file
28
android/src/main/res/layout/activity_ad_splash.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/white"
|
||||
tools:context=".page.AdSplashActivity">
|
||||
<!-- 广告容器 -->
|
||||
<FrameLayout
|
||||
android:id="@+id/splash_ad_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/splash_ad_logo"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<!-- Logo 内容 -->
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/splash_ad_logo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="96dp"
|
||||
android:src="@mipmap/flutterads_logo"
|
||||
android:scaleType="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
BIN
android/src/main/res/mipmap-xxhdpi/flutterads_logo.png
Normal file
BIN
android/src/main/res/mipmap-xxhdpi/flutterads_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
1
android/src/main/res/values/strings.xml
Normal file
1
android/src/main/res/values/strings.xml
Normal file
@ -0,0 +1 @@
|
||||
<resources></resources>
|
17
android/src/main/res/values/themes.xml
Normal file
17
android/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
|
||||
<!-- Status bar color. -->
|
||||
<!-- <item name="android:statusBarColor" tools:targetApi="l">@android:color/transparent</item>-->
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
<!-- 开屏页主题 -->
|
||||
<style name="SplashTheme" parent="AppTheme">
|
||||
<item name="android:windowAnimationStyle">@style/WindowAnimationStyle</item>
|
||||
</style>
|
||||
<!-- 设置动画 -->
|
||||
<style name="WindowAnimationStyle">
|
||||
<item name="android:windowEnterAnimation">@android:anim/fade_in</item>
|
||||
<item name="android:windowExitAnimation">@android:anim/fade_out</item>
|
||||
</style>
|
||||
</resources>
|
@ -20,8 +20,10 @@ internal class UnionAdSsgfPluginTest {
|
||||
|
||||
val call = MethodCall("getPlatformVersion", null)
|
||||
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
|
||||
plugin.onMethodCall(call, mockResult)
|
||||
|
||||
println("--------->>>> ${plugin}, ${plugin.delegate}")
|
||||
|
||||
plugin.delegate?.onMethodCall(call, mockResult)
|
||||
Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
MethodChannelUnionAdSsgf platform = MethodChannelUnionAdSsgf();
|
||||
const MethodChannel channel = MethodChannel('union_ad_ssgf');
|
||||
const MethodChannel channel = MethodChannel('union_ad_ssgf_method');
|
||||
|
||||
setUp(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
|
Loading…
x
Reference in New Issue
Block a user