diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..56a6d29 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/android/.idea/vcs.xml b/android/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/android/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/PluginDelegate.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/PluginDelegate.kt index 9216e7f..82aaaa3 100644 --- a/android/src/main/kotlin/com/example/union_ad_ssgf/PluginDelegate.kt +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/PluginDelegate.kt @@ -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("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("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) + } + } diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/UnionAdSsgfPlugin.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/UnionAdSsgfPlugin.kt index 3a78631..8523fa9 100644 --- a/android/src/main/kotlin/com/example/union_ad_ssgf/UnionAdSsgfPlugin.kt +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/UnionAdSsgfPlugin.kt @@ -14,13 +14,16 @@ class UnionAdSsgfPlugin : FlutterPlugin, ActivityAware { /** * 方法通道: * - * 用于Flutter与原生Android之间通信的MethodChannel + * 用于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") } /** diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/config/UnionADConfig.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/config/UnionADConfig.kt new file mode 100644 index 0000000..69cd85d --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/config/UnionADConfig.kt @@ -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" + } + +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdErrorEvent.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdErrorEvent.kt new file mode 100644 index 0000000..ccacf37 --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdErrorEvent.kt @@ -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 { + val newMap = super.toMap() + newMap["errCode"] = errCode + newMap["errMsg"] = errMsg + return newMap + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdEvent.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdEvent.kt new file mode 100644 index 0000000..4382717 --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdEvent.kt @@ -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 { + val map = HashMap() + map["adId"] = adId + map["posId"] = posId + map["action"] = action + map["viewId"] = viewId + return map + } +} diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdEventAction.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdEventAction.kt new file mode 100644 index 0000000..1f92f89 --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdEventAction.kt @@ -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" + } +} diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdEventHandler.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdEventHandler.kt new file mode 100644 index 0000000..21dccc8 --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/event/AdEventHandler.kt @@ -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()) + } + } +} diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/load/FeedAdLoad.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/load/FeedAdLoad.kt new file mode 100644 index 0000000..80e271d --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/load/FeedAdLoad.kt @@ -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()) + } + + override fun onADLoaded(list: MutableList) { + Log.i(TAG, "onADLoaded") + val adResultList: MutableList = 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) + } +} diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/load/FeedAdManager.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/load/FeedAdManager.kt new file mode 100644 index 0000000..d4d0e9c --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/load/FeedAdManager.kt @@ -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 = 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) + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/page/AdBannerView.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/page/AdBannerView.kt new file mode 100644 index 0000000..8d71c96 --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/page/AdBannerView.kt @@ -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?, + 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() + } + } + +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/page/AdFeedView.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/page/AdFeedView.kt new file mode 100644 index 0000000..b0b139f --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/page/AdFeedView.kt @@ -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?, + 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() + } + } +} diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/page/AdSplashActivity.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/page/AdSplashActivity.kt new file mode 100644 index 0000000..07e9acd --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/page/AdSplashActivity.kt @@ -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(R.id.splash_ad_container) + ad_logo = findViewById(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) + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/page/BaseAdPage.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/page/BaseAdPage.kt new file mode 100644 index 0000000..2769a65 --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/page/BaseAdPage.kt @@ -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(UnionADConfig.KEY_POSID) + this.adId = call.argument(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)) + } +} diff --git a/android/src/main/kotlin/com/example/union_ad_ssgf/page/NativeViewFactory.kt b/android/src/main/kotlin/com/example/union_ad_ssgf/page/NativeViewFactory.kt new file mode 100644 index 0000000..600baed --- /dev/null +++ b/android/src/main/kotlin/com/example/union_ad_ssgf/page/NativeViewFactory.kt @@ -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 + return if (viewName == UnionADConfig.KEY_BANNER_VIEW) { + AdBannerView(context, viewId, creationParams, pluginDelegate) + } else { + AdFeedView(context, viewId, creationParams, pluginDelegate) + } + } + +} \ No newline at end of file diff --git a/android/src/main/res/layout/activity_ad_splash.xml b/android/src/main/res/layout/activity_ad_splash.xml new file mode 100644 index 0000000..d053f0a --- /dev/null +++ b/android/src/main/res/layout/activity_ad_splash.xml @@ -0,0 +1,28 @@ + + + + + + + \ No newline at end of file diff --git a/android/src/main/res/mipmap-xxhdpi/flutterads_logo.png b/android/src/main/res/mipmap-xxhdpi/flutterads_logo.png new file mode 100644 index 0000000..f290fda Binary files /dev/null and b/android/src/main/res/mipmap-xxhdpi/flutterads_logo.png differ diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml new file mode 100644 index 0000000..73862c4 --- /dev/null +++ b/android/src/main/res/values/strings.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/android/src/main/res/values/themes.xml b/android/src/main/res/values/themes.xml new file mode 100644 index 0000000..020f1da --- /dev/null +++ b/android/src/main/res/values/themes.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/android/src/test/kotlin/com/example/union_ad_ssgf/UnionAdSsgfPluginTest.kt b/android/src/test/kotlin/com/example/union_ad_ssgf/UnionAdSsgfPluginTest.kt index 1008f0e..8d73f4d 100644 --- a/android/src/test/kotlin/com/example/union_ad_ssgf/UnionAdSsgfPluginTest.kt +++ b/android/src/test/kotlin/com/example/union_ad_ssgf/UnionAdSsgfPluginTest.kt @@ -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) } } diff --git a/test/union_ad_ssgf_method_channel_test.dart b/test/union_ad_ssgf_method_channel_test.dart index 4e6089f..b11eb19 100644 --- a/test/union_ad_ssgf_method_channel_test.dart +++ b/test/union_ad_ssgf_method_channel_test.dart @@ -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(