Commit f43b9a38 authored by wanglei's avatar wanglei

...

parent 4f5c6253
...@@ -46,4 +46,8 @@ dependencies { ...@@ -46,4 +46,8 @@ dependencies {
api(libs.drouter.api) api(libs.drouter.api)
api(libs.drouter.api.page) api(libs.drouter.api.page)
api(libs.drouter.api.process) api(libs.drouter.api.process)
// 语种切换框架:https://github.com/getActivity/MultiLanguages
api("com.github.getActivity:MultiLanguages:9.3")
} }
\ No newline at end of file
...@@ -2,7 +2,7 @@ package com.koko.drouter.app ...@@ -2,7 +2,7 @@ package com.koko.drouter.app
import android.app.Activity import android.app.Activity
import com.didi.drouter.api.DRouter import com.didi.drouter.api.DRouter
import com.koko.drouter.ad.NativeAdService import java.util.Locale
val appService by lazy { val appService by lazy {
...@@ -11,7 +11,14 @@ val appService by lazy { ...@@ -11,7 +11,14 @@ val appService by lazy {
interface AppService { interface AppService {
fun eventActivity() = Unit fun getCurrentSpLocal(): Locale
fun changeActivityLanguage(
activity: Activity,
currentLocale: Locale,
changeAction: () -> Unit
): Boolean
fun finishToMain() = Unit fun finishToMain() = Unit
......
package com.koko.drouter.base package com.koko.drouter.base
import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
...@@ -10,7 +11,9 @@ import androidx.core.view.ViewCompat ...@@ -10,7 +11,9 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.didi.drouter.api.DRouter import com.didi.drouter.api.DRouter
import com.hjq.language.MultiLanguages
import com.koko.drouter.app.AppService import com.koko.drouter.app.AppService
import com.koko.drouter.app.appService
import com.koko.drouter.event.EventService import com.koko.drouter.event.EventService
import java.util.Locale import java.util.Locale
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
...@@ -27,7 +30,8 @@ abstract class BaseActivity<VB : ViewBinding>( ...@@ -27,7 +30,8 @@ abstract class BaseActivity<VB : ViewBinding>(
} }
lateinit var launcher: ActivityLauncher lateinit var launcher: ActivityLauncher
private var currentLocale: Locale? = null private var currentLocale: Locale = appService.getCurrentSpLocal()
private var isLanguageRecreate: Boolean = false//判断当前activity有没有因为切换语言重新创建
private val eventService by lazy { private val eventService by lazy {
DRouter.build(EventService::class.java).getService() DRouter.build(EventService::class.java).getService()
...@@ -42,14 +46,19 @@ abstract class BaseActivity<VB : ViewBinding>( ...@@ -42,14 +46,19 @@ abstract class BaseActivity<VB : ViewBinding>(
// LogEx.logDebug(TAG, "lifecycle $tagNo onCreate ${javaClass.simpleName} $onCreateI") // LogEx.logDebug(TAG, "lifecycle $tagNo onCreate ${javaClass.simpleName} $onCreateI")
launcher = ActivityLauncher(this) launcher = ActivityLauncher(this)
setContentView(binding.root) setContentView(binding.root)
eventActivity() eventActivity()
// currentLocale = Locale(appLanguageSp, appLanguageCountrySp) changeLanguage()
initView() initView()
initListener() initListener()
initData() initData()
} }
private fun changeLanguage() {
isLanguageRecreate = appService.changeActivityLanguage(this, currentLocale) {
currentLocale = appService.getCurrentSpLocal()
}
}
open fun initData() {} open fun initData() {}
open fun useDefaultImmersive() { open fun useDefaultImmersive() {
...@@ -62,7 +71,6 @@ abstract class BaseActivity<VB : ViewBinding>( ...@@ -62,7 +71,6 @@ abstract class BaseActivity<VB : ViewBinding>(
} }
private fun eventActivity() { private fun eventActivity() {
val eventService = DRouter.build(EventService::class.java).getService()
eventService.eventActivity(this) eventService.eventActivity(this)
} }
...@@ -81,57 +89,8 @@ abstract class BaseActivity<VB : ViewBinding>( ...@@ -81,57 +89,8 @@ abstract class BaseActivity<VB : ViewBinding>(
super.onDestroy() super.onDestroy()
} }
// override fun attachBaseContext(newBase: Context?) { override fun attachBaseContext(newBase: Context?) {
// super.attachBaseContext(MultiLanguages.attach(newBase)) super.attachBaseContext(MultiLanguages.attach(newBase))
// }
// fun changeLanguage(currentActivity: Activity, bundle: Bundle = Bundle()): Boolean {
// val spLanguage = Locale(appLanguageSp, appLanguageCountrySp)
// val flag = currentLocale != spLanguage
// val restart = MultiLanguages.setAppLanguage(this, spLanguage)
// LogEx.logDebug(
// TAG, "changeLanguage " +
// "flag=$flag restart=$restart currentLocale=$currentLocale spLanguage=$spLanguage"
// )
// if (restart) {
// currentLocale = Locale(appLanguageSp, appLanguageCountrySp)
// }
//
// if (flag || restart) {
// // 1.使用 recreate 来重启 Activity,效果差,有闪屏的缺陷
//// recreate();
//
// // 2.使用常规 startActivity 来重启 Activity,有从左向右的切换动画
// // 稍微比 recreate 的效果好一点,但是这种并不是最佳的效果
// // startActivity(new Intent(this, LanguageActivity.class));
// // finish();
//
// // 3.我们可以充分运用 Activity 跳转动画,在跳转的时候设置一个渐变的效果,相比前面的两种带来的体验是最佳的
// //需要设置启动模式,避免AndroidManifest.xml设置singleTop启动模式导致重复启动不生效
// //这种方式如果设置了singleTop或者singleTask的,再次回到该activity无法改变于语言,因为还在堆栈里没有重新创建
// needRecreate()
// if (needRecreate()) {
// LogEx.logDebug(TAG, "changeLanguage recreate")
// recreate()
// } else {
// startActivity(Intent(this, currentActivity::class.java).apply {
// putExtras(bundle)
// })
// overridePendingTransition(R.anim.activity_alpha_in, R.anim.activity_alpha_out)
// finish()
// }
// return true
// }
// return false
// }
/**
* 判断是否需要重新创建对SingleTop启动模式的有效
*/
fun needRecreate(): Boolean {
return TAG.contains("MainActivity")
} }
......
...@@ -2,21 +2,24 @@ package com.base.appzxhy.ads ...@@ -2,21 +2,24 @@ package com.base.appzxhy.ads
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.animation.ValueAnimator.INFINITE import android.animation.ValueAnimator.INFINITE
import android.annotation.SuppressLint
import android.app.AlertDialog import android.app.AlertDialog
import android.app.Dialog
import android.content.Context import android.content.Context
import android.os.CountDownTimer
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import com.base.appzxhy.databinding.DialogAdPreparingBinding import android.widget.TextView
import com.base.appzxhy.utils.DensityUtils import com.base.appzxhy.R
import com.base.appzxhy.utils.LogEx import com.base.appzxhy.databinding.DialogAdCountdownBinding
object AdDialog { object AdCountDownDialog {
private val TAG = "com.base.appzxhy.ads.AdDialog" private val TAG = "AdCountDownDialog"
fun Context.showAdPreparingDialog(where: Int = 0): AlertDialog {
LogEx.logDebug(TAG, "where=$where") fun Context.showAdCountDownDialog(): AlertDialog {
val binding = DialogAdPreparingBinding.inflate(LayoutInflater.from(this)) val binding = DialogAdCountdownBinding.inflate(LayoutInflater.from(this))
val dialog = AlertDialog.Builder(this).create() val dialog = AlertDialog.Builder(this).create()
dialog.setView(binding.root) dialog.setView(binding.root)
dialog.setCancelable(false) dialog.setCancelable(false)
...@@ -24,13 +27,14 @@ object AdDialog { ...@@ -24,13 +27,14 @@ object AdDialog {
dialog.show() dialog.show()
val params = dialog.window?.attributes val params = dialog.window?.attributes
params?.width = DensityUtils.dip2px(200f) // params?.width = LinearLayout.LayoutParams.WRAP_CONTENT
params?.height = DensityUtils.dip2px(146f) params?.width = resources.getDimensionPixelSize(R.dimen.dp_300)
// params?.height = LinearLayout.LayoutParams.WRAP_CONTENT
dialog.window?.attributes = params dialog.window?.attributes = params
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent) dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
// 创建一个旋转动画 // 创建一个旋转动画
val rotateAnimator = ObjectAnimator.ofFloat(binding.iv, "rotation", 0f, -360f) val rotateAnimator = ObjectAnimator.ofFloat(binding.ivYuan, "rotation", 0f, -360f)
rotateAnimator.setDuration(1000) // 设置动画持续时间为1000毫秒 rotateAnimator.setDuration(1000) // 设置动画持续时间为1000毫秒
rotateAnimator.repeatCount = INFINITE rotateAnimator.repeatCount = INFINITE
rotateAnimator.interpolator = LinearInterpolator() // 设置插值器为线性插值 rotateAnimator.interpolator = LinearInterpolator() // 设置插值器为线性插值
...@@ -38,4 +42,26 @@ object AdDialog { ...@@ -38,4 +42,26 @@ object AdDialog {
return dialog return dialog
} }
fun createUICountdownTimer(dialog: Dialog?, onFinish: () -> Unit) {
val tvCountdown = dialog?.findViewById<TextView>(R.id.tvCountdown)
val countDownTimer = object : CountDownTimer(4000, 1000) {
@SuppressLint("SetTextI18n")
override fun onTick(millisUntilFinished: Long) {
val s = millisUntilFinished / 1000
tvCountdown?.text = "Ads are about to be shown(${s}s)"
}
override fun onFinish() {
kotlin.runCatching {
dialog?.dismiss()
}
onFinish.invoke()
}
}
countDownTimer.start()
}
} }
\ No newline at end of file
...@@ -30,7 +30,7 @@ abstract class AdEvent { ...@@ -30,7 +30,7 @@ abstract class AdEvent {
obj1.put("req_id", reqId) obj1.put("req_id", reqId)
obj1.put("from", from) obj1.put("from", from)
EventUtils.event("ad_prepare_show", ext = obj1) EventUtils.event("ad_prepare_show", ext = obj1)
LogEx.logDebug(TAG, "ad_prepare_show $obj1") LogEx.logDebug(TAG, "ad_prepare_show_$adUnit $obj1")
} }
fun adPulStart() { fun adPulStart() {
...@@ -40,7 +40,7 @@ abstract class AdEvent { ...@@ -40,7 +40,7 @@ abstract class AdEvent {
obj.put("ad_type", adUnit) obj.put("ad_type", adUnit)
obj.put("from", from) obj.put("from", from)
EventUtils.event("ad_pull_start", ext = obj) EventUtils.event("ad_pull_start", ext = obj)
LogEx.logDebug(TAG, "ad_pull_start $obj") LogEx.logDebug(TAG, "ad_pull_start_$adUnit $obj")
} }
fun adShowError(reason: Any) { fun adShowError(reason: Any) {
...@@ -50,7 +50,7 @@ abstract class AdEvent { ...@@ -50,7 +50,7 @@ abstract class AdEvent {
obj.put("from", from) obj.put("from", from)
obj.put("reason", reason.toString()) obj.put("reason", reason.toString())
EventUtils.event("ad_show_error", ext = obj) EventUtils.event("ad_show_error", ext = obj)
LogEx.logDebug(TAG, "ad_show_error $obj") LogEx.logDebug(TAG, "ad_show_error_$adUnit $obj")
} }
fun adLimited(value: String) { fun adLimited(value: String) {
...@@ -59,7 +59,7 @@ abstract class AdEvent { ...@@ -59,7 +59,7 @@ abstract class AdEvent {
obj.put("req_id", reqId) obj.put("req_id", reqId)
obj.put("from", from) obj.put("from", from)
EventUtils.event("ad_limit", value, obj) EventUtils.event("ad_limit", value, obj)
LogEx.logDebug(TAG, "ad_limit $obj") LogEx.logDebug(TAG, "ad_limit_$adUnit $obj")
} }
......
...@@ -2,9 +2,9 @@ package com.base.appzxhy.ads ...@@ -2,9 +2,9 @@ package com.base.appzxhy.ads
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.Toast
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import com.applovin.sdk.AppLovinMediationProvider import com.applovin.sdk.AppLovinMediationProvider
import com.applovin.sdk.AppLovinSdk import com.applovin.sdk.AppLovinSdk
...@@ -29,7 +29,6 @@ import com.base.appzxhy.utils.ToastUtils.toast ...@@ -29,7 +29,6 @@ import com.base.appzxhy.utils.ToastUtils.toast
import com.google.android.gms.ads.MobileAds import com.google.android.gms.ads.MobileAds
import com.google.android.gms.ads.identifier.AdvertisingIdClient import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.google.android.gms.ads.initialization.AdapterStatus import com.google.android.gms.ads.initialization.AdapterStatus
import com.google.android.gms.ads.nativead.NativeAdView
import java.util.Collections import java.util.Collections
import java.util.concurrent.Executors import java.util.concurrent.Executors
...@@ -88,8 +87,11 @@ object AdsMgr { ...@@ -88,8 +87,11 @@ object AdsMgr {
EventUtils.event("isInBlackList", configBean.isInBlackList.toString()) EventUtils.event("isInBlackList", configBean.isInBlackList.toString())
return return
} }
initAdmob(context) if (adsConfigBean.adSwitch) {
initMax(context) initAdmob(context)
} else {
initMax(context)
}
} }
...@@ -105,8 +107,8 @@ object AdsMgr { ...@@ -105,8 +107,8 @@ object AdsMgr {
if (adsConfigBean.adSwitch) { if (adsConfigBean.adSwitch) {
admobInitCallBack?.invoke() admobInitCallBack?.invoke()
admobInitCallBack = null admobInitCallBack = null
adOpenMgr.loadAd(context, false, AdmobEvent("openAd", context::class.java.simpleName)) adOpenMgr.loadAd(context, AdmobEvent("openAd", context::class.java.simpleName))
adInsertMgr.loadAd(context, false, AdmobEvent("interAd", context::class.java.simpleName)) adInsertMgr.loadAd(context, AdmobEvent("interAd", context::class.java.simpleName))
} }
} }
...@@ -133,7 +135,7 @@ object AdsMgr { ...@@ -133,7 +135,7 @@ object AdsMgr {
isMaxInit = true isMaxInit = true
// maxOpenMgr.loadAd(context) // maxOpenMgr.loadAd(context)
if (!adsConfigBean.adSwitch) { if (!adsConfigBean.adSwitch) {
maxInsertMgr.loadAd(context, false, AdMaxEvent("interAd", context::class.java.simpleName)) maxInsertMgr.loadAd( AdMaxEvent("interAd", context::class.java.simpleName))
context.toast("max init") context.toast("max init")
maxInitCallBack?.invoke() maxInitCallBack?.invoke()
maxInitCallBack = null maxInitCallBack = null
...@@ -158,7 +160,6 @@ object AdsMgr { ...@@ -158,7 +160,6 @@ object AdsMgr {
*/ */
fun showOpen( fun showOpen(
activity: Activity, activity: Activity,
isUnLimit: Boolean = false,
showCallBack: AdsShowCallBack? = null, showCallBack: AdsShowCallBack? = null,
) { ) {
if (configBean.isInBlackList) { if (configBean.isInBlackList) {
...@@ -169,18 +170,18 @@ object AdsMgr { ...@@ -169,18 +170,18 @@ object AdsMgr {
val from = activity::class.java.simpleName val from = activity::class.java.simpleName
if (adsConfigBean.adSwitch) { if (adsConfigBean.adSwitch) {
if (isAdmobInit) { if (isAdmobInit) {
adOpenMgr.show(activity, isUnLimit, AdmobEvent("openAd", from), showCallBack) adOpenMgr.show(activity, AdmobEvent("openAd", from), showCallBack)
} else { } else {
admobInitCallBack = { admobInitCallBack = {
} }
adOpenMgr.show(activity, isUnLimit, AdmobEvent("openAd", from), showCallBack) adOpenMgr.show(activity, AdmobEvent("openAd", from), showCallBack)
} }
} else { } else {
if (isMaxInit) { if (isMaxInit) {
maxOpenMgr.show(activity, isUnLimit, AdMaxEvent("openAd", from), showCallBack) maxOpenMgr.show(activity, AdMaxEvent("openAd", from), showCallBack)
} else { } else {
maxInitCallBack = { maxInitCallBack = {
maxOpenMgr.show(activity, isUnLimit, AdMaxEvent("openAd", from), showCallBack) maxOpenMgr.show(activity, AdMaxEvent("openAd", from), showCallBack)
} }
} }
} }
...@@ -196,7 +197,6 @@ object AdsMgr { ...@@ -196,7 +197,6 @@ object AdsMgr {
*/ */
fun showInsert( fun showInsert(
activity: Activity, activity: Activity,
isUnLimit: Boolean = false,
showCallBack: AdsShowCallBack? = null, showCallBack: AdsShowCallBack? = null,
) { ) {
if (configBean.isInBlackList) { if (configBean.isInBlackList) {
...@@ -206,9 +206,9 @@ object AdsMgr { ...@@ -206,9 +206,9 @@ object AdsMgr {
LogEx.logDebug("showAd", "adSwitch=${adsConfigBean.adSwitch}") LogEx.logDebug("showAd", "adSwitch=${adsConfigBean.adSwitch}")
val from = activity::class.java.simpleName val from = activity::class.java.simpleName
if (adsConfigBean.adSwitch) { if (adsConfigBean.adSwitch) {
adInsertMgr.show(activity, isUnLimit, AdmobEvent("interAd", from), showCallBack) adInsertMgr.show(activity, AdmobEvent("interAd", from), showCallBack)
} else { } else {
maxInsertMgr.show(activity, isUnLimit, AdMaxEvent("interAd", from), showCallBack) maxInsertMgr.show(activity, AdMaxEvent("interAd", from), showCallBack)
} }
} }
...@@ -221,29 +221,25 @@ object AdsMgr { ...@@ -221,29 +221,25 @@ object AdsMgr {
fun showNative( fun showNative(
activity: Activity, activity: Activity,
parent: FrameLayout, parent: FrameLayout,
@LayoutRes layout: Int, @LayoutRes admobLayout: Int,
@LayoutRes maxLayout: Int,
nativeCallBack: ((Any?) -> Unit)? = null nativeCallBack: ((Any?) -> Unit)? = null
) { ) {
if (configBean.isInBlackList) { if (configBean.isInBlackList) {
LogEx.logDebug("showNative", "isInBlackList")
EventUtils.event("isInBlackList", configBean.isInBlackList.toString()) EventUtils.event("isInBlackList", configBean.isInBlackList.toString())
return return
} }
if (!isAdmobInit) return
val from = activity::class.java.simpleName val from = activity::class.java.simpleName
if (adsConfigBean.adSwitch) { if (adsConfigBean.adSwitch) {
adNativeMgr.show(AdmobEvent("nativeAd", from), parent, admobLayout, nativeCallBack)
val nativeView = LayoutInflater.from(parent.context).inflate(layout, parent, false) as NativeAdView
adNativeMgr.show(AdmobEvent("nativeAd", from), parent, nativeView, nativeCallBack)
} else { } else {
// maxNativeMgr.show(AdMaxEvent("nativeAd", "nativeAd"), nativeParent, layout, nativeCallBack) maxNativeMgr.show(AdMaxEvent("nativeAd", from), parent, maxLayout, nativeCallBack)
} }
} }
fun isNativeShow() = (isAdmobInit && !configBean.isInBlackList) && LimitUtils.isAdShow(AdsType.NATIVE, null)
/** /**
* 展示banner广告 * 展示banner广告
......
...@@ -4,5 +4,5 @@ abstract class AdsShowCallBack { ...@@ -4,5 +4,5 @@ abstract class AdsShowCallBack {
open fun show() {} open fun show() {}
abstract fun close(where: Int = 0) abstract fun close(where: Int = 0)
abstract fun failed(where: Int = 0) abstract fun failed(where: Int = 0)
abstract fun googleFailed(where: Int = 0) abstract fun adFailed(where: Int = 0)
} }
\ No newline at end of file
package com.base.appzxhy.ads package com.base.appzxhy.ads
import com.base.appzxhy.BuildConfig
import com.base.appzxhy.bean.config.AdConfigBean import com.base.appzxhy.bean.config.AdConfigBean
import com.base.appzxhy.utils.AppPreferences import com.base.appzxhy.utils.AppPreferences
import com.base.appzxhy.utils.KotlinExt.toFormatTime4 import com.base.appzxhy.utils.KotlinExt.toFormatTime4
...@@ -16,7 +17,7 @@ object LimitUtils { ...@@ -16,7 +17,7 @@ object LimitUtils {
const val NUM_CLICK = "local_numClickLimit" const val NUM_CLICK = "local_numClickLimit"
const val SAVE_DATE = "local_SAVE_DATE" const val SAVE_DATE = "local_saveDate"
/** /**
* 保存的时间,用来判断是否是当天,不是当天要重置计数次数 * 保存的时间,用来判断是否是当天,不是当天要重置计数次数
...@@ -34,7 +35,6 @@ object LimitUtils { ...@@ -34,7 +35,6 @@ object LimitUtils {
} }
/** /**
* 广告请求是否到达限制 * 广告请求是否到达限制
*/ */
...@@ -81,6 +81,11 @@ object LimitUtils { ...@@ -81,6 +81,11 @@ object LimitUtils {
* @return true or false * @return true or false
*/ */
fun isAdShow(adsType: AdsType, adEvent: AdEvent?): Boolean { fun isAdShow(adsType: AdsType, adEvent: AdEvent?): Boolean {
if (BuildConfig.DEBUG) {
return true
}
val currentDate = System.currentTimeMillis().toFormatTime4() val currentDate = System.currentTimeMillis().toFormatTime4()
if (saveDate != currentDate) { if (saveDate != currentDate) {
//如果已经不是今天了,就重置个数 //如果已经不是今天了,就重置个数
......
package com.base.appzxhy.ads
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.Button
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.LayoutRes
import com.applovin.mediation.MaxAd
import com.applovin.mediation.nativeAds.MaxNativeAdLoader
import com.applovin.mediation.nativeAds.MaxNativeAdView
import com.applovin.mediation.nativeAds.MaxNativeAdViewBinder
import com.base.appzxhy.R
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdView
@SuppressLint("ViewConstructor")
class NativeParentView(context: Context, attrs: AttributeSet? = null) :
FrameLayout(context, attrs) {
private val TAG = "NativeParentView"
private var removeAll = true
init {
// 获取XML属性
val a = context.obtainStyledAttributes(
attrs,
R.styleable.NativeParentView,
0,
0
)
// 从XML中读取属性值
// 例如,如果你的XML中有自定义属性 `app:customAttribute="value"`
removeAll = a.getBoolean(R.styleable.NativeParentView_removeAll, true)
// 回收TypedArray
a.recycle()
}
fun setNativeAd(
nativeAd: NativeAd,
@LayoutRes resource: Int? = null
) {
val layout = resource ?: R.layout.layout_admob_native_custom
val adView =
layout.let { LayoutInflater.from(context).inflate(it, null) } as NativeAdView
adView.mediaView = adView.findViewById(R.id.ad_media)
adView.headlineView = adView.findViewById(R.id.ad_headline)
adView.bodyView = adView.findViewById(R.id.ad_body)
adView.callToActionView = adView.findViewById(R.id.ad_call_to_action)
adView.iconView = adView.findViewById(R.id.ad_app_icon)
(adView.headlineView as TextView?)?.text = nativeAd.headline
adView.mediaView?.mediaContent = nativeAd.mediaContent
if (nativeAd.body == null) {
adView.bodyView?.visibility = View.INVISIBLE
} else {
adView.bodyView?.visibility = View.VISIBLE
(adView.bodyView as TextView?)?.text = nativeAd.body
}
if (nativeAd.callToAction == null) {
adView.callToActionView?.visibility = View.INVISIBLE
} else {
adView.callToActionView?.visibility = View.VISIBLE
(adView.callToActionView as Button?)?.text = nativeAd.callToAction
}
if (nativeAd.icon == null) {
adView.iconView?.visibility = View.GONE
} else {
(adView.iconView as ImageView?)?.setImageDrawable(
nativeAd.icon?.drawable
)
adView.iconView?.visibility = View.VISIBLE
}
adView.setNativeAd(nativeAd)
setBackgroundResource(0)
if (removeAll) {
removeAllViews()
}
addView(adView)
}
fun setNativeAd(
nativeAdLoader: MaxNativeAdLoader,
nativeAd: MaxAd,
@LayoutRes resource: Int? = null
) {
val layout = resource ?: R.layout.layout_max_native_custom
val binder: MaxNativeAdViewBinder =
MaxNativeAdViewBinder.Builder(layout)
.setTitleTextViewId(R.id.title_text_view)
.setBodyTextViewId(R.id.body_text_view)
.setAdvertiserTextViewId(R.id.advertiser_text_view)
.setIconImageViewId(R.id.icon_image_view)
.setMediaContentViewGroupId(R.id.media_view_container)
.setOptionsContentViewGroupId(R.id.options_view)
.setStarRatingContentViewGroupId(R.id.star_rating_view)
.setCallToActionButtonId(R.id.cta_button)
.build()
val adView = MaxNativeAdView(binder, context)
nativeAdLoader.render(adView, nativeAd)
setBackgroundResource(0)
removeAllViews()
addView(adView)
}
}
...@@ -3,75 +3,64 @@ package com.base.appzxhy.ads.admob ...@@ -3,75 +3,64 @@ package com.base.appzxhy.ads.admob
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import com.base.appzxhy.GlobalConfig import com.base.appzxhy.GlobalConfig
import com.base.appzxhy.ads.AdDialog.showAdPreparingDialog import com.base.appzxhy.ads.AdCountDownDialog.createUICountdownTimer
import com.base.appzxhy.ads.AdCountDownDialog.showAdCountDownDialog
import com.base.appzxhy.ads.AdEvent import com.base.appzxhy.ads.AdEvent
import com.base.appzxhy.ads.AdsShowCallBack import com.base.appzxhy.ads.AdsShowCallBack
import com.base.appzxhy.ads.AdsType import com.base.appzxhy.ads.AdsType
import com.base.appzxhy.ads.LimitUtils import com.base.appzxhy.ads.LimitUtils
import com.base.appzxhy.ads.adState
import com.google.android.gms.ads.AdError import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.FullScreenContentCallback import com.google.android.gms.ads.FullScreenContentCallback
import com.google.android.gms.ads.LoadAdError import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.interstitial.InterstitialAd import com.google.android.gms.ads.interstitial.InterstitialAd
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback
import java.lang.ref.WeakReference
/** /**
*插屏广告加载显示管理类 *插屏广告加载显示管理类
*/ */
class AdInterMgr { class AdInterMgr {
private var adState = adState<InterstitialAd>() private var adState = AdmobAdState()
private var showCallBack: AdsShowCallBack? = null
fun show(activity: Activity, isUnLimit: Boolean, adEvent: AdEvent, showCallBack: AdsShowCallBack?) { fun show(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
if (activity.isFinishing || activity.isDestroyed) { if (activity.isFinishing || activity.isDestroyed) {
showCallBack?.failed(0) showCallBack?.failed(0)
return return
} }
if (!isUnLimit) {
if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
showCallBack?.failed(2)
return
}
if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
showCallBack?.failed(3)
return
}
}
if (adState.showingAd) { if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
showCallBack?.failed(1) showCallBack?.failed(1)
adEvent.adShowError("showingAd")
adState.showingAd = false
return return
} }
if (showCallBack != null) { if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
this.showCallBack = showCallBack showCallBack?.failed(2)
adState.activityRef = WeakReference(activity) return
if (adState.adDialog == null) {
adState.adDialog = activity.showAdPreparingDialog(1)
}
adEvent.adPrepareShow()
} }
if (!adState.loadingAd) { val loadingDialog = activity.showAdCountDownDialog()
if (adState.currentAd == null) {
loadAd(activity, isUnLimit, adEvent) if (adState.currentAd == null || !adAvailable()) {
return loadAd(activity, adEvent) {
if (adState.currentAd != null) {
createUICountdownTimer(loadingDialog) {
showReadyAd(activity, adEvent, showCallBack)
}
}
} }
if (!adAvailable()) { } else {
loadAd(activity, isUnLimit, adEvent) createUICountdownTimer(loadingDialog) {
return showReadyAd(activity, adEvent, showCallBack)
} }
showReadyAd(adEvent, activity)
} }
} }
private fun showReadyAd(adEvent: AdEvent, activity: Activity) { private fun showReadyAd(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
adState.currentAd?.run { val ad = adState.currentAd as InterstitialAd?
ad?.run {
fullScreenContentCallback = object : FullScreenContentCallback() { fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdShowedFullScreenContent() { override fun onAdShowedFullScreenContent() {
super.onAdShowedFullScreenContent() super.onAdShowedFullScreenContent()
...@@ -85,9 +74,7 @@ class AdInterMgr { ...@@ -85,9 +74,7 @@ class AdInterMgr {
super.onAdFailedToShowFullScreenContent(adError) super.onAdFailedToShowFullScreenContent(adError)
adState.onAdDisplayFailed() adState.onAdDisplayFailed()
showCallBack?.googleFailed() showCallBack?.adFailed()
showCallBack = null
adEvent.adShowError(adError) adEvent.adShowError(adError)
} }
...@@ -97,8 +84,7 @@ class AdInterMgr { ...@@ -97,8 +84,7 @@ class AdInterMgr {
adState.onAdHidden() adState.onAdHidden()
showCallBack?.close() showCallBack?.close()
showCallBack = null loadAd(activity.applicationContext, AdmobEvent("interAd", "preload"))
loadAd(activity.applicationContext, false, AdmobEvent("interAd", "preload"))
} }
override fun onAdClicked() { override fun onAdClicked() {
...@@ -112,15 +98,12 @@ class AdInterMgr { ...@@ -112,15 +98,12 @@ class AdInterMgr {
} }
} }
fun loadAd(context: Context, isUnLimit: Boolean, adEvent: AdEvent) { fun loadAd(context: Context, adEvent: AdEvent, loadCallBack: (() -> Unit)? = null) {
if (!isUnLimit) { if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) { loadCallBack?.invoke()
this.showCallBack?.close(4) adState.onAdLoadFailed()
this.showCallBack = null return
adState.onAdLoadFailed()
return
}
} }
if (!adState.loadingAd) { if (!adState.loadingAd) {
...@@ -132,10 +115,7 @@ class AdInterMgr { ...@@ -132,10 +115,7 @@ class AdInterMgr {
object : InterstitialAdLoadCallback() { object : InterstitialAdLoadCallback() {
override fun onAdLoaded(ad: InterstitialAd) { override fun onAdLoaded(ad: InterstitialAd) {
adState.onAdLoaded(ad) adState.onAdLoaded(ad)
val ac = adState.activityRef?.get() loadCallBack?.invoke()
if (ac != null) {
show(ac, isUnLimit, adEvent, null)
}
(adEvent as AdmobEvent).pullAd(ad.responseInfo) (adEvent as AdmobEvent).pullAd(ad.responseInfo)
LimitUtils.addRequestNum() LimitUtils.addRequestNum()
ad.onPaidEventListener = AdmobEvent.EventOnPaidEventListener(ad) ad.onPaidEventListener = AdmobEvent.EventOnPaidEventListener(ad)
...@@ -144,8 +124,7 @@ class AdInterMgr { ...@@ -144,8 +124,7 @@ class AdInterMgr {
override fun onAdFailedToLoad(loadAdError: LoadAdError) { override fun onAdFailedToLoad(loadAdError: LoadAdError) {
adState.onAdLoadFailed() adState.onAdLoadFailed()
(adEvent as AdmobEvent).pullAd(loadAdError.responseInfo, loadAdError) (adEvent as AdmobEvent).pullAd(loadAdError.responseInfo, loadAdError)
showCallBack?.googleFailed() loadCallBack?.invoke()
showCallBack = null
} }
} }
) )
......
package com.base.appzxhy.ads.admob package com.base.appzxhy.ads.admob
import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.Button import android.widget.Button
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.view.isVisible
import com.base.appzxhy.GlobalConfig import com.base.appzxhy.GlobalConfig
import com.base.appzxhy.R import com.base.appzxhy.R
import com.base.appzxhy.ads.AdsType import com.base.appzxhy.ads.AdsType
...@@ -15,48 +15,40 @@ import com.google.android.gms.ads.AdListener ...@@ -15,48 +15,40 @@ import com.google.android.gms.ads.AdListener
import com.google.android.gms.ads.AdLoader import com.google.android.gms.ads.AdLoader
import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.LoadAdError import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.nativead.MediaView
import com.google.android.gms.ads.nativead.NativeAd import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdOptions import com.google.android.gms.ads.nativead.NativeAdOptions
import com.google.android.gms.ads.nativead.NativeAdView import com.google.android.gms.ads.nativead.NativeAdView
import java.util.concurrent.ConcurrentLinkedDeque
/** /**
*原生广告加载显示管理类 *原生广告加载显示管理类
*/ */
class AdNativeMgr { class AdNativeMgr {
/** private val TAG = this::class.java.simpleName
* 上一次的缓存成功时间
*/
protected var lastTime: Long = 0 protected var lastTime: Long = 0
private var currentAd: NativeAd? = null
/**
* 原生广告缓存队列
*/
private val cacheItems = ConcurrentLinkedDeque<NativeAd>()
private fun loadAd( private fun loadAd(
admobEvent: AdmobEvent, admobEvent: AdmobEvent,
parent: FrameLayout, context: Context,
nativeAdView: NativeAdView callback: ((nativeAd: NativeAd) -> Unit)? = null
) { ) {
admobEvent.adPulStart()
if (!LimitUtils.isAdShow(AdsType.NATIVE, admobEvent)) return if (!LimitUtils.isAdShow(AdsType.NATIVE, admobEvent)) return
admobEvent.adPulStart()
var currentNativeAd: NativeAd? = null var currentNativeAd: NativeAd? = null
val adLoader = AdLoader.Builder( val adLoader = AdLoader.Builder(
parent.context, context,
GlobalConfig.ID_ADMOB_NATIVE GlobalConfig.ID_ADMOB_NATIVE
).forNativeAd { nativeAd -> ).forNativeAd { nativeAd ->
currentNativeAd = nativeAd currentNativeAd = nativeAd
cacheItems.offer(nativeAd)
lastTime = System.currentTimeMillis() lastTime = System.currentTimeMillis()
nativeAd.setOnPaidEventListener(AdmobEvent.EventOnPaidEventListener(nativeAd)) nativeAd.setOnPaidEventListener(AdmobEvent.EventOnPaidEventListener(nativeAd))
admobEvent.pullAd(nativeAd.responseInfo) admobEvent.pullAd(nativeAd.responseInfo)
show(admobEvent, parent, nativeAdView) callback?.invoke(nativeAd)
}.withAdListener(object : AdListener() { }.withAdListener(object : AdListener() {
override fun onAdFailedToLoad(error: LoadAdError) { override fun onAdFailedToLoad(error: LoadAdError) {
...@@ -80,69 +72,68 @@ class AdNativeMgr { ...@@ -80,69 +72,68 @@ class AdNativeMgr {
fun show( fun show(
admobEvent: AdmobEvent, admobEvent: AdmobEvent,
nativeParent: FrameLayout, parent: FrameLayout,
nativeAdView: NativeAdView, layout: Int,
nativeCallBack: ((Any?) -> Unit)? = null nativeCallBack: ((Any?) -> Unit)? = null
) { ) {
admobEvent.adPrepareShow()
if (!LimitUtils.isAdShow(AdsType.NATIVE, admobEvent)) { if (!LimitUtils.isAdShow(AdsType.NATIVE, admobEvent)) {
cacheItems.clear()
return return
} }
val nativeAd = cacheItems.peek() admobEvent.adPrepareShow()
if (nativeAd == null || !adAvailable()) {
//缓存过期了就清空
cacheItems.clear()
loadAd(admobEvent, nativeParent, nativeAdView)
return
}
nativeCallBack?.invoke(nativeAd)
//添加原生到父布局
nativeParent.removeAllViews()
nativeAdView.bindNativeAd(nativeAd)
nativeParent.addView(nativeAdView)
nativeParent.isVisible = true
admobEvent.showAd(nativeAd.responseInfo) if (currentAd == null || !adAvailable()) {
loadAd(admobEvent, parent.context) { nativeAd: NativeAd ->
setNative(parent, layout, nativeAd)
admobEvent.showAd(nativeAd.responseInfo)
nativeCallBack?.invoke(nativeAd)
}
} else {
currentAd?.let {
setNative(parent, layout, it)
admobEvent.showAd(it.responseInfo)
nativeCallBack?.invoke(it)
}
}
} }
private fun NativeAdView.bindNativeAd(nativeAd: NativeAd?) { private fun setNative(parent: FrameLayout, layout: Int, nativeAd: NativeAd) = runCatching {
nativeAd ?: return
mediaView = findViewById(R.id.ad_media) currentAd = null
headlineView = findViewById(R.id.ad_headline)
bodyView = findViewById(R.id.ad_body)
callToActionView = findViewById(R.id.ad_call_to_action)
iconView = findViewById(R.id.ad_app_icon)
(headlineView as TextView?)?.text = nativeAd.headline val nativeView = LayoutInflater.from(parent.context).inflate(layout, parent, false) as NativeAdView
val mediaView = nativeView.findViewById<MediaView?>(R.id.ad_media)
mediaView?.mediaContent = nativeAd.mediaContent mediaView?.mediaContent = nativeAd.mediaContent
val headlineView = nativeView.findViewById<TextView?>(R.id.ad_headline)
headlineView?.text = nativeAd.headline
val bodyView = nativeView.findViewById<TextView?>(R.id.ad_body)
if (nativeAd.body == null) { if (nativeAd.body == null) {
bodyView?.visibility = View.INVISIBLE bodyView?.visibility = View.INVISIBLE
} else { } else {
bodyView?.visibility = View.VISIBLE bodyView?.visibility = View.VISIBLE
(bodyView as TextView?)?.text = nativeAd.body bodyView?.text = nativeAd.body
} }
val callToActionView = nativeView.findViewById<Button?>(R.id.ad_call_to_action)
if (nativeAd.callToAction == null) { if (nativeAd.callToAction == null) {
callToActionView?.visibility = View.INVISIBLE callToActionView?.visibility = View.INVISIBLE
} else { } else {
callToActionView?.visibility = View.VISIBLE callToActionView?.visibility = View.VISIBLE
(callToActionView as Button?)?.text = nativeAd.callToAction callToActionView?.text = nativeAd.callToAction
} }
val iconView = nativeView.findViewById<ImageView?>(R.id.ad_app_icon)
if (nativeAd.icon == null) { if (nativeAd.icon == null) {
iconView?.visibility = View.GONE iconView?.visibility = View.GONE
} else { } else {
(iconView as ImageView?)?.setImageDrawable( iconView?.setImageDrawable(nativeAd.icon?.drawable)
nativeAd.icon?.drawable
)
iconView?.visibility = View.VISIBLE iconView?.visibility = View.VISIBLE
} }
setNativeAd(nativeAd) parent.removeAllViews()
parent.addView(nativeView)
} }
private fun adAvailable(): Boolean { private fun adAvailable(): Boolean {
......
...@@ -3,66 +3,62 @@ package com.base.appzxhy.ads.admob ...@@ -3,66 +3,62 @@ package com.base.appzxhy.ads.admob
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import com.base.appzxhy.GlobalConfig import com.base.appzxhy.GlobalConfig
import com.base.appzxhy.ads.AdCountDownDialog.createUICountdownTimer
import com.base.appzxhy.ads.AdsType import com.base.appzxhy.ads.AdsType
import com.base.appzxhy.ads.AdEvent import com.base.appzxhy.ads.AdEvent
import com.base.appzxhy.ads.AdsShowCallBack import com.base.appzxhy.ads.AdsShowCallBack
import com.base.appzxhy.ads.LimitUtils import com.base.appzxhy.ads.LimitUtils
import com.base.appzxhy.ads.adState
import com.google.android.gms.ads.AdError import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.FullScreenContentCallback import com.google.android.gms.ads.FullScreenContentCallback
import com.google.android.gms.ads.LoadAdError import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.appopen.AppOpenAd import com.google.android.gms.ads.appopen.AppOpenAd
import java.lang.ref.WeakReference
/** /**
* 开屏广告加载显示管理类 * 开屏广告加载显示管理类
*/ */
class AdOpenMgr { class AdOpenMgr {
private val adState = adState<AppOpenAd>() private val adState = AdmobAdState()
private var showCallBack: AdsShowCallBack? = null
fun show(activity: Activity, isUnLimit: Boolean, adEvent: AdEvent, showCallBack: AdsShowCallBack?) { fun show(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
if (activity.isFinishing || activity.isDestroyed) { if (activity.isFinishing || activity.isDestroyed) {
return return
} }
if (!isUnLimit) { if (!LimitUtils.isAdShow(AdsType.OPEN, adEvent)) {
if (!LimitUtils.isAdShow(AdsType.OPEN, adEvent)) {
showCallBack?.failed()
return
}
if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
showCallBack?.failed()
return
}
}
if (adState.showingAd) {
showCallBack?.failed() showCallBack?.failed()
adEvent.adShowError("showingAd")
adState.showingAd = false
return return
} }
if (showCallBack != null) { if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
this.showCallBack = showCallBack showCallBack?.failed()
adState.activityRef = WeakReference(activity) return
adEvent.adPrepareShow()
} }
adEvent.adPrepareShow()
if (!adState.loadingAd) { if (adState.currentAd == null || !adAvailable()) {
if (!adAvailable() || adState.currentAd == null) {
loadAd(activity, isUnLimit, adEvent) loadAd(activity, adEvent) {
return if (adState.currentAd != null) {
createUICountdownTimer(null) {
showReadyAd(activity, adEvent, showCallBack)
}
} else {
showCallBack?.failed()
}
} }
showReadyAd(adEvent, activity)
}
} else {
createUICountdownTimer(null) {
showReadyAd(activity, adEvent, showCallBack)
}
}
} }
private fun showReadyAd(adEvent: AdEvent, activity: Activity) { private fun showReadyAd(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
adState.currentAd?.run { val ad = adState.currentAd as AppOpenAd?
ad?.run {
fullScreenContentCallback = object : FullScreenContentCallback() { fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdShowedFullScreenContent() { override fun onAdShowedFullScreenContent() {
...@@ -77,8 +73,7 @@ class AdOpenMgr { ...@@ -77,8 +73,7 @@ class AdOpenMgr {
override fun onAdFailedToShowFullScreenContent(adError: AdError) { override fun onAdFailedToShowFullScreenContent(adError: AdError) {
super.onAdFailedToShowFullScreenContent(adError) super.onAdFailedToShowFullScreenContent(adError)
showCallBack?.googleFailed() showCallBack?.adFailed()
showCallBack = null
adState.onAdDisplayFailed() adState.onAdDisplayFailed()
(adEvent as AdmobEvent).adShowError(adError) (adEvent as AdmobEvent).adShowError(adError)
...@@ -87,13 +82,9 @@ class AdOpenMgr { ...@@ -87,13 +82,9 @@ class AdOpenMgr {
override fun onAdDismissedFullScreenContent() { override fun onAdDismissedFullScreenContent() {
super.onAdDismissedFullScreenContent() super.onAdDismissedFullScreenContent()
showCallBack?.close() showCallBack?.close()
showCallBack = null
adState.onAdHidden() adState.onAdHidden()
loadAd(activity.applicationContext, AdmobEvent("openAd", "preload"))
loadAd(activity.applicationContext, false, AdmobEvent("openAd", "preload"))
} }
override fun onAdClicked() { override fun onAdClicked() {
...@@ -106,15 +97,12 @@ class AdOpenMgr { ...@@ -106,15 +97,12 @@ class AdOpenMgr {
} }
} }
fun loadAd(context: Context, isUnLimit: Boolean, adEvent: AdEvent) { fun loadAd(context: Context, adEvent: AdEvent, loadCallBack: (() -> Unit)? = null) {
if (!isUnLimit) { if (!LimitUtils.isAdShow(AdsType.OPEN, adEvent)) {
if (!LimitUtils.isAdShow(AdsType.OPEN, adEvent)) { loadCallBack?.invoke()
this.showCallBack?.close() adState.onAdLoadFailed()
this.showCallBack = null return
adState.onAdLoadFailed()
return
}
} }
if (!adState.loadingAd) { if (!adState.loadingAd) {
...@@ -128,19 +116,15 @@ class AdOpenMgr { ...@@ -128,19 +116,15 @@ class AdOpenMgr {
object : AppOpenAd.AppOpenAdLoadCallback() { object : AppOpenAd.AppOpenAdLoadCallback() {
override fun onAdLoaded(appOpenAd: AppOpenAd) { override fun onAdLoaded(appOpenAd: AppOpenAd) {
adState.onAdLoaded(appOpenAd) adState.onAdLoaded(appOpenAd)
val ac = adState.activityRef?.get() loadCallBack?.invoke()
if (ac != null) {
show(ac, isUnLimit, adEvent, null)
}
(adEvent as AdmobEvent).pullAd(appOpenAd.responseInfo) (adEvent as AdmobEvent).pullAd(appOpenAd.responseInfo)
appOpenAd.onPaidEventListener = AdmobEvent.EventOnPaidEventListener(appOpenAd) appOpenAd.onPaidEventListener = AdmobEvent.EventOnPaidEventListener(appOpenAd)
LimitUtils.addRequestNum() LimitUtils.addRequestNum()
} }
override fun onAdFailedToLoad(loadAdError: LoadAdError) { override fun onAdFailedToLoad(loadAdError: LoadAdError) {
showCallBack?.googleFailed()
showCallBack = null
adState.onAdLoadFailed() adState.onAdLoadFailed()
loadCallBack?.invoke()
(adEvent as AdmobEvent).pullAd(loadAdError.responseInfo, loadAdError) (adEvent as AdmobEvent).pullAd(loadAdError.responseInfo, loadAdError)
} }
} }
......
package com.base.appzxhy.ads package com.base.appzxhy.ads.admob
import android.app.Activity import android.app.Activity
import android.app.Dialog
import android.content.Context
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
class AdmobAdState {
class adState<T>() {
var adDialog: Dialog? = null
/** /**
* 当前缓存的广告 * 当前缓存的广告
*/ */
var currentAd: T? = null var currentAd: Any? = null
/** /**
* 是否正在缓存加载广告 * 是否正在缓存加载广告
...@@ -22,11 +15,6 @@ class adState<T>() { ...@@ -22,11 +15,6 @@ class adState<T>() {
var loadingAd: Boolean = false var loadingAd: Boolean = false
/**
* 是否正在显示广告
*/
var showingAd: Boolean = false
/** /**
* 用于保存引用现有页面,在此页面显示广告(因为要等待广告加载完毕) * 用于保存引用现有页面,在此页面显示广告(因为要等待广告加载完毕)
*/ */
...@@ -43,10 +31,6 @@ class adState<T>() { ...@@ -43,10 +31,6 @@ class adState<T>() {
var lastShowTime: Long = 0 var lastShowTime: Long = 0
fun onAdDisplayed() { fun onAdDisplayed() {
showingAd = true
adDialog?.dismiss()
adDialog = null
lastShowTime = System.currentTimeMillis() lastShowTime = System.currentTimeMillis()
...@@ -56,19 +40,15 @@ class adState<T>() { ...@@ -56,19 +40,15 @@ class adState<T>() {
fun onAdHidden() { fun onAdHidden() {
// 广告关闭,清空缓存数据,重新加载 // 广告关闭,清空缓存数据,重新加载
showingAd = false
} }
fun onAdDisplayFailed() { fun onAdDisplayFailed() {
adDialog?.dismiss()
adDialog = null
showingAd = false
currentAd = null currentAd = null
activityRef = null activityRef = null
} }
fun onAdLoaded(ad: T?) { fun onAdLoaded(ad: Any?) {
//这里可能提前设置,所有可以不设置,max回调的类型可能不同 //这里可能提前设置,所有可以不设置,max回调的类型可能不同
if (ad != null) { if (ad != null) {
currentAd = ad currentAd = ad
...@@ -78,70 +58,6 @@ class adState<T>() { ...@@ -78,70 +58,6 @@ class adState<T>() {
} }
fun onAdLoadFailed() { fun onAdLoadFailed() {
adDialog?.dismiss()
adDialog = null
loadingAd = false loadingAd = false
} }
} }
/**
* 特定广告管理基类
* @param T 缓存广告的类
*/
abstract class BaseAdMgr<T> {
/**
* 广告展示回调
*/
protected var showCallBack: AdsShowCallBack? = null
/**
* 当前缓存的广告
*/
protected var currentAd: T? = null
/**
* 是否正在缓存加载广告
*/
protected var loadingAd: Boolean = false
/**
* 是否正在显示广告
*/
protected var showingAd: Boolean = false
/**
* 用于保存引用现有页面,在此页面显示广告(因为要等待广告加载完毕)
*/
protected var activityRef: WeakReference<Activity>? = null
/**
* 上一次的缓存成功时间
*/
protected var lastLoadTime: Long = 0
/**
* 预加载广告
*
* @param context 加载所用的上下文,一般使用appContext
*/
abstract fun loadAd(context: Context, isUnLimit: Boolean = true, adEvent: AdEvent)
/**
* 广告显示
*
* @param activity 当前页面
*/
abstract fun show(activity: Activity, isUnLimit: Boolean = true, adEvent: AdEvent, showCallBack: AdsShowCallBack? = null)
/**
* 预加载的缓存超时判断
*
* @return true:没有超时 false:超时需要重新加载
*/
abstract fun adAvailable(): Boolean
}
\ No newline at end of file
...@@ -67,7 +67,7 @@ class AdmobEvent : AdEvent { ...@@ -67,7 +67,7 @@ class AdmobEvent : AdEvent {
obj.put("status", "2") obj.put("status", "2")
} }
EventUtils.event("ad_pull", ext = obj) EventUtils.event("ad_pull", ext = obj)
LogEx.logDebug(TAG, "ad_pull obj=$obj") LogEx.logDebug(TAG, "ad_pull_$adUnit obj=$obj")
} }
...@@ -89,9 +89,9 @@ class AdmobEvent : AdEvent { ...@@ -89,9 +89,9 @@ class AdmobEvent : AdEvent {
obj.put("from", from) obj.put("from", from)
obj.put("networkname", responseInfo?.mediationAdapterClassName) obj.put("networkname", responseInfo?.mediationAdapterClassName)
if (adUnit != "nativeAd") { if (adUnit != "nativeAd") {
EventUtils.event("ad_click", ext = obj) EventUtils.event("ad_click_$adUnit", ext = obj)
} else { } else {
EventUtils.event("ad_click", ext = obj) EventUtils.event("ad_click_$adUnit", ext = obj)
} }
} }
...@@ -115,7 +115,7 @@ class AdmobEvent : AdEvent { ...@@ -115,7 +115,7 @@ class AdmobEvent : AdEvent {
} else { } else {
EventUtils.event("ad_show", ext = obj) EventUtils.event("ad_show", ext = obj)
} }
LogEx.logDebug(TAG, "ad_show $obj") LogEx.logDebug(TAG, "ad_show_$adUnit $obj")
} }
class EventOnPaidEventListener(private val ad: Any?) : OnPaidEventListener { class EventOnPaidEventListener(private val ad: Any?) : OnPaidEventListener {
...@@ -303,7 +303,7 @@ class AdmobEvent : AdEvent { ...@@ -303,7 +303,7 @@ class AdmobEvent : AdEvent {
obj.put("from", from) obj.put("from", from)
obj.put("ad_unit", "openAd") obj.put("ad_unit", "openAd")
EventUtils.event("ad_show_error", ext = obj) EventUtils.event("ad_show_error", ext = obj)
LogEx.logDebug(TAG, "ad_show_error $obj") LogEx.logDebug(TAG, "ad_show_error_$adUnit $obj")
} }
} }
......
...@@ -10,7 +10,6 @@ import com.base.appzxhy.ads.AdEvent ...@@ -10,7 +10,6 @@ import com.base.appzxhy.ads.AdEvent
import com.base.appzxhy.ads.taichiPref import com.base.appzxhy.ads.taichiPref
import com.base.appzxhy.ads.taichiSharedPreferencesEditor import com.base.appzxhy.ads.taichiSharedPreferencesEditor
import com.base.appzxhy.helper.EventUtils import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.utils.LogEx
import com.base.appzxhy.utils.LogEx.logDebug import com.base.appzxhy.utils.LogEx.logDebug
import com.facebook.appevents.AppEventsConstants import com.facebook.appevents.AppEventsConstants
import com.facebook.appevents.AppEventsLogger import com.facebook.appevents.AppEventsLogger
......
package com.base.appzxhy.ads.applovin
import android.app.Activity
import java.lang.ref.WeakReference
class MaxAdState {
/**
* 当前缓存的广告
*/
var currentAd: Any? = null
/**
* 是否正在缓存加载广告
*/
var loadingAd: Boolean = false
/**
* 用于保存引用现有页面,在此页面显示广告(因为要等待广告加载完毕)
*/
var activityRef: WeakReference<Activity>? = null
/**
* 上一次的缓存成功时间
*/
var lastLoadTime: Long = 0
/**
* 上次展示时间
*/
var lastShowTime: Long = 0
fun onAdDisplayed() {
lastShowTime = System.currentTimeMillis()
activityRef = null
}
fun onAdHidden() {
// 广告关闭,清空缓存数据,重新加载
}
fun onAdDisplayFailed() {
activityRef = null
}
fun onAdLoaded() {
loadingAd = false
lastLoadTime = System.currentTimeMillis()
}
fun onAdLoadFailed() {
loadingAd = false
}
}
\ No newline at end of file
package com.base.appzxhy.ads.applovin package com.base.appzxhy.ads.applovin
import android.app.Activity import android.app.Activity
import android.content.Context
import com.applovin.mediation.MaxAd import com.applovin.mediation.MaxAd
import com.applovin.mediation.MaxAdListener import com.applovin.mediation.MaxAdListener
import com.applovin.mediation.MaxError import com.applovin.mediation.MaxError
import com.applovin.mediation.ads.MaxInterstitialAd import com.applovin.mediation.ads.MaxInterstitialAd
import com.base.appzxhy.GlobalConfig import com.base.appzxhy.GlobalConfig
import com.base.appzxhy.MyApplication
import com.base.appzxhy.ads.AdCountDownDialog.createUICountdownTimer
import com.base.appzxhy.ads.AdCountDownDialog.showAdCountDownDialog
import com.base.appzxhy.ads.AdsType import com.base.appzxhy.ads.AdsType
import com.base.appzxhy.ads.AdDialog.showAdPreparingDialog
import com.base.appzxhy.ads.AdEvent import com.base.appzxhy.ads.AdEvent
import com.base.appzxhy.ads.AdsShowCallBack import com.base.appzxhy.ads.AdsShowCallBack
import com.base.appzxhy.ads.LimitUtils import com.base.appzxhy.ads.LimitUtils
import com.base.appzxhy.ads.adState
import java.lang.ref.WeakReference
/** /**
*插屏广告加载显示管理类 *插屏广告加载显示管理类
*/ */
class MaxInsertMgr { class MaxInsertMgr {
private var adState = adState<MaxInterstitialAd>() private var adState = MaxAdState().apply {
private var showCallBack: AdsShowCallBack? = null currentAd = MaxInterstitialAd(GlobalConfig.ID_MAX_INTER, MyApplication.appContext)
}
fun show( fun show(
activity: Activity, activity: Activity,
isUnLimit: Boolean,
adEvent: AdEvent, adEvent: AdEvent,
showCallBack: AdsShowCallBack? showCallBack: AdsShowCallBack?
) { ) {
if (activity.isFinishing || activity.isDestroyed) { if (activity.isFinishing || activity.isDestroyed) {
showCallBack?.failed(0)
return
}
if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
showCallBack?.failed(1) showCallBack?.failed(1)
return return
} }
if (adState.showingAd) { if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
showCallBack?.failed(2) showCallBack?.failed(2)
return return
} }
if (showCallBack != null) { adEvent.adPrepareShow()
adState.activityRef = WeakReference(activity)
this.showCallBack = showCallBack
if (adState.adDialog == null) {
adState.adDialog = activity.showAdPreparingDialog(2)
}
adEvent.adPrepareShow()
}
if (!adState.loadingAd) {
if (!isUnLimit) {
if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
showCallBack?.failed(3)
return
}
if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
showCallBack?.failed(4)
return
}
}
if (!adAvailable() || adState.currentAd == null) { val loadingDialog = activity.showAdCountDownDialog()
loadAd(activity, isUnLimit, adEvent)
return
}
if (adState.currentAd?.isReady == false) { if (adState.currentAd == null || !adAvailable()) {
loadAd(activity, isUnLimit, adEvent) loadAd(adEvent) { flag: Boolean ->
return if (flag) {
createUICountdownTimer(loadingDialog) {
showReadyAd(activity, adEvent, showCallBack)
}
} else {
showCallBack?.failed()
}
} }
showReadyAd(adEvent) } else {
showReadyAd(activity, adEvent, showCallBack)
} }
} }
private fun showReadyAd(adEvent: AdEvent) { private fun showReadyAd(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
adState.currentAd?.run { val ad = adState.currentAd as MaxInterstitialAd?
ad?.run {
setListener(object : MaxAdListener { setListener(object : MaxAdListener {
override fun onAdLoaded(p0: MaxAd) = Unit override fun onAdLoaded(p0: MaxAd) = Unit
override fun onAdLoadFailed(p0: String, p1: MaxError) = Unit override fun onAdLoadFailed(p0: String, p1: MaxError) = Unit
...@@ -93,8 +83,7 @@ class MaxInsertMgr { ...@@ -93,8 +83,7 @@ class MaxInsertMgr {
override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) { override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) {
adState.onAdDisplayFailed() adState.onAdDisplayFailed()
showCallBack?.googleFailed() showCallBack?.adFailed()
showCallBack = null
(adEvent as AdMaxEvent).adShowError(error) (adEvent as AdMaxEvent).adShowError(error)
} }
...@@ -104,7 +93,7 @@ class MaxInsertMgr { ...@@ -104,7 +93,7 @@ class MaxInsertMgr {
adState.onAdHidden() adState.onAdHidden()
showCallBack?.close() showCallBack?.close()
loadAd(activity.applicationContext, false, AdMaxEvent("interAd", "preload")) loadAd(AdMaxEvent("interAd", "preload"))
} }
override fun onAdClicked(ad: MaxAd) { override fun onAdClicked(ad: MaxAd) {
...@@ -123,53 +112,43 @@ class MaxInsertMgr { ...@@ -123,53 +112,43 @@ class MaxInsertMgr {
fun loadAd( fun loadAd(
context: Context,
isUnLimit: Boolean,
adEvent: AdEvent, adEvent: AdEvent,
loadCallBack: ((flag: Boolean) -> Unit)? = null
) { ) {
if (!isUnLimit) { if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) { loadCallBack?.invoke(false)
this.showCallBack?.close(4) return
this.showCallBack = null
return
}
} }
if (!adState.loadingAd) { val ad = adState.currentAd as MaxInterstitialAd?
adState.loadingAd = true
adEvent.adPulStart()
adState.currentAd = MaxInterstitialAd(GlobalConfig.ID_MAX_INTER, context)
adState.currentAd?.setListener(object : MaxAdListener {
override fun onAdDisplayed(p0: MaxAd) = Unit ad?.setListener(object : MaxAdListener {
override fun onAdHidden(p0: MaxAd) = Unit
override fun onAdClicked(p0: MaxAd) = Unit
override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) = Unit
override fun onAdLoaded(ad: MaxAd) { override fun onAdDisplayed(p0: MaxAd) = Unit
adState.onAdLoaded(null) override fun onAdHidden(p0: MaxAd) = Unit
val ac = adState.activityRef?.get() override fun onAdClicked(p0: MaxAd) = Unit
if (ac != null) { override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) = Unit
show(ac, isUnLimit, adEvent, null)
}
(adEvent as AdMaxEvent).pullAd(ad) override fun onAdLoaded(ad: MaxAd) {
LimitUtils.addRequestNum() loadCallBack?.invoke(true)
} adState.onAdLoaded()
(adEvent as AdMaxEvent).pullAd(ad)
LimitUtils.addRequestNum()
}
override fun onAdLoadFailed(ad: String, error: MaxError) { override fun onAdLoadFailed(ad: String, error: MaxError) {
adState.onAdLoadFailed() loadCallBack?.invoke(false)
adState.onAdLoadFailed()
(adEvent as AdMaxEvent).pullAd(null, error)
(adEvent as AdMaxEvent).pullAd(null, error) }
showCallBack?.googleFailed(5) })
showCallBack = null
}
}) if (!adState.loadingAd) {
adState.currentAd?.loadAd() adState.loadingAd = true
adEvent.adPulStart()
ad?.loadAd()
} }
} }
......
package com.base.appzxhy.ads.applovin package com.base.appzxhy.ads.applovin
import android.widget.FrameLayout
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import com.applovin.mediation.MaxAd import com.applovin.mediation.MaxAd
import com.applovin.mediation.MaxError import com.applovin.mediation.MaxError
import com.applovin.mediation.nativeAds.MaxNativeAdListener import com.applovin.mediation.nativeAds.MaxNativeAdListener
import com.applovin.mediation.nativeAds.MaxNativeAdLoader import com.applovin.mediation.nativeAds.MaxNativeAdLoader
import com.applovin.mediation.nativeAds.MaxNativeAdView import com.applovin.mediation.nativeAds.MaxNativeAdView
import com.applovin.mediation.nativeAds.MaxNativeAdViewBinder
import com.base.appzxhy.GlobalConfig import com.base.appzxhy.GlobalConfig
import com.base.appzxhy.R
import com.base.appzxhy.ads.AdsType import com.base.appzxhy.ads.AdsType
import com.base.appzxhy.ads.LimitUtils import com.base.appzxhy.ads.LimitUtils
import com.base.appzxhy.ads.NativeParentView
import com.base.appzxhy.helper.EventUtils import com.base.appzxhy.helper.EventUtils
import org.json.JSONObject import org.json.JSONObject
import java.util.UUID import java.util.UUID
...@@ -30,10 +32,9 @@ class MaxNativeMgr { ...@@ -30,10 +32,9 @@ class MaxNativeMgr {
private var currentAd: MaxAd? = null private var currentAd: MaxAd? = null
private var currentLoader: MaxNativeAdLoader? = null private var currentLoader: MaxNativeAdLoader? = null
private fun loadAd( private fun loadAd(
adMaxEvent: AdMaxEvent, adMaxEvent: AdMaxEvent,
parent: NativeParentView, parent: FrameLayout,
@LayoutRes layout: Int @LayoutRes layout: Int
) { ) {
...@@ -71,7 +72,7 @@ class MaxNativeMgr { ...@@ -71,7 +72,7 @@ class MaxNativeMgr {
fun show( fun show(
adMaxEvent: AdMaxEvent, adMaxEvent: AdMaxEvent,
parent: NativeParentView, parent: FrameLayout,
@LayoutRes layout: Int, @LayoutRes layout: Int,
nativeCallBack: ((Any?) -> Unit)? = null nativeCallBack: ((Any?) -> Unit)? = null
) { ) {
...@@ -105,11 +106,41 @@ class MaxNativeMgr { ...@@ -105,11 +106,41 @@ class MaxNativeMgr {
val obj = JSONObject() val obj = JSONObject()
obj.put("ad_unit", "nativeAd") obj.put("ad_unit", "nativeAd")
EventUtils.event("ad_prepare_show", ext = obj) EventUtils.event("ad_prepare_show", ext = obj)
parent.setNativeAd(nativeLoader!!, nativeAd!!, layout) setMaxNativeAd(parent, layout)
nativeCallBack?.invoke(nativeAd) nativeCallBack?.invoke(nativeAd)
} }
private fun adAvailable(): Boolean { private fun adAvailable(): Boolean {
return ((System.currentTimeMillis() - lastTime) / 1000 / 60).toInt() < 30 return ((System.currentTimeMillis() - lastTime) / 1000 / 60).toInt() < 30
} }
private fun setMaxNativeAd(parent: FrameLayout, layout: Int) {
// val binder: MaxNativeAdViewBinder = MaxNativeAdViewBinder.Builder(R.layout.layout_max_native_custom)
// .setTitleTextViewId(R.id.title_text_view)
// .setBodyTextViewId(R.id.body_text_view)
// .setAdvertiserTextViewId(R.id.advertiser_text_view)
// .setIconImageViewId(R.id.icon_image_view)
// .setMediaContentViewGroupId(R.id.media_view_container)
// .setOptionsContentViewGroupId(R.id.options_view)
// .setStarRatingContentViewGroupId(R.id.star_rating_view)
// .setCallToActionButtonId(R.id.cta_button)
// .build()
// return MaxNativeAdView(binder, context)
val binder: MaxNativeAdViewBinder = MaxNativeAdViewBinder.Builder(layout)
.setTitleTextViewId(R.id.ad_headline)
.setBodyTextViewId(R.id.ad_body)
// .setAdvertiserTextViewId(R.id.advertiser_text_view)
.setIconImageViewId(R.id.ad_app_icon)
.setMediaContentViewGroupId(R.id.ad_media)
// .setOptionsContentViewGroupId(R.id.options_view)
// .setStarRatingContentViewGroupId(R.id.star_rating_view)
.setCallToActionButtonId(R.id.ad_call_to_action)
.build()
val maxNativeAdView = MaxNativeAdView(binder, parent.context)
parent.removeAllViews()
parent.addView(maxNativeAdView)
}
} }
\ No newline at end of file
package com.base.appzxhy.ads.applovin package com.base.appzxhy.ads.applovin
import android.app.Activity import android.app.Activity
import android.content.Context
import com.applovin.mediation.MaxAd import com.applovin.mediation.MaxAd
import com.applovin.mediation.MaxAdListener import com.applovin.mediation.MaxAdListener
import com.applovin.mediation.MaxError import com.applovin.mediation.MaxError
import com.applovin.mediation.ads.MaxAppOpenAd import com.applovin.mediation.ads.MaxAppOpenAd
import com.base.appzxhy.GlobalConfig import com.base.appzxhy.GlobalConfig
import com.base.appzxhy.MyApplication
import com.base.appzxhy.ads.AdCountDownDialog.createUICountdownTimer
import com.base.appzxhy.ads.AdCountDownDialog.showAdCountDownDialog
import com.base.appzxhy.ads.AdsType import com.base.appzxhy.ads.AdsType
import com.base.appzxhy.ads.AdEvent import com.base.appzxhy.ads.AdEvent
import com.base.appzxhy.ads.AdsShowCallBack import com.base.appzxhy.ads.AdsShowCallBack
import com.base.appzxhy.ads.LimitUtils import com.base.appzxhy.ads.LimitUtils
import com.base.appzxhy.ads.adState
import java.lang.ref.WeakReference
/** /**
* 开屏广告加载显示管理类 * 开屏广告加载显示管理类
*/ */
class MaxOpenMgr { class MaxOpenMgr {
private val adState = adState<MaxAppOpenAd>() private val adState = MaxAdState().apply {
private var showCallBack: AdsShowCallBack? = null currentAd = MaxAppOpenAd(GlobalConfig.ID_MAX_OPEN, MyApplication.appContext)
}
fun show(activity: Activity, isUnLimit: Boolean, adEvent: AdEvent, showCallBack: AdsShowCallBack?) { fun show(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
if (activity.isFinishing || activity.isDestroyed) { if (activity.isFinishing || activity.isDestroyed) {
showCallBack?.failed(0)
return return
} }
if (adState.showingAd) { if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
showCallBack?.failed() showCallBack?.failed(1)
adEvent.adShowError("showingAd")
return return
} }
if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
if (showCallBack != null) { showCallBack?.failed(2)
this.showCallBack = showCallBack return
adState.activityRef = WeakReference(activity)
adEvent.adPrepareShow()
} }
if (!adState.loadingAd) { adEvent.adPrepareShow()
if (!isUnLimit) {
if (!LimitUtils.isAdShow(AdsType.OPEN, adEvent)) {
showCallBack?.failed()
return
}
if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
showCallBack?.failed()
return
}
}
if (!adAvailable() || adState.currentAd == null) { val loadingDialog = activity.showAdCountDownDialog()
loadAd(activity, isUnLimit, adEvent)
return
}
if (adState.currentAd?.isReady != true) { if (!adAvailable()) {
loadAd(activity, isUnLimit, adEvent) loadAd(adEvent) { flag: Boolean ->
return if (flag) {
createUICountdownTimer(loadingDialog) {
showReadyAd(activity, adEvent, showCallBack)
}
} else {
showCallBack?.failed()
}
} }
showReadyAd(activity, adEvent) } else {
showReadyAd(activity, adEvent, showCallBack)
} }
} }
private fun showReadyAd(activity: Activity, adEvent: AdEvent) { private fun showReadyAd(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
adState.currentAd?.run { val ad = adState.currentAd as MaxAppOpenAd?
ad?.run {
setListener(object : MaxAdListener { setListener(object : MaxAdListener {
override fun onAdLoaded(p0: MaxAd) {
} override fun onAdLoaded(p0: MaxAd) = Unit
override fun onAdLoadFailed(p0: String, p1: MaxError) = Unit
override fun onAdDisplayed(ad: MaxAd) { override fun onAdDisplayed(ad: MaxAd) {
adState.onAdDisplayed() adState.onAdDisplayed()
showCallBack?.show() showCallBack?.show()
LimitUtils.addDisplayNum() LimitUtils.addDisplayNum()
(adEvent as AdMaxEvent).showAd(ad, activity::class.simpleName) (adEvent as AdMaxEvent).showAd(ad, activity::class.simpleName)
} }
...@@ -86,9 +78,7 @@ class MaxOpenMgr { ...@@ -86,9 +78,7 @@ class MaxOpenMgr {
override fun onAdHidden(p0: MaxAd) { override fun onAdHidden(p0: MaxAd) {
adState.onAdHidden() adState.onAdHidden()
showCallBack?.close() showCallBack?.close()
showCallBack = null loadAd(AdMaxEvent("openAd", "preload"))
loadAd(activity.applicationContext, false, AdMaxEvent("openAd", "preload"))
} }
override fun onAdClicked(ad: MaxAd) { override fun onAdClicked(ad: MaxAd) {
...@@ -97,14 +87,10 @@ class MaxOpenMgr { ...@@ -97,14 +87,10 @@ class MaxOpenMgr {
LimitUtils.addClickNum() LimitUtils.addClickNum()
} }
override fun onAdLoadFailed(p0: String, p1: MaxError) {
}
override fun onAdDisplayFailed(p0: MaxAd, error: MaxError) { override fun onAdDisplayFailed(p0: MaxAd, error: MaxError) {
adState.onAdDisplayFailed() adState.onAdDisplayFailed()
showCallBack?.googleFailed() showCallBack?.adFailed()
showCallBack = null
adEvent.adShowError(error) adEvent.adShowError(error)
} }
...@@ -114,60 +100,43 @@ class MaxOpenMgr { ...@@ -114,60 +100,43 @@ class MaxOpenMgr {
} }
} }
fun loadAd(context: Context, isUnLimit: Boolean, adEvent: AdEvent) { fun loadAd(
adEvent: AdEvent,
loadCallBack: ((flag: Boolean) -> Unit)? = null
) {
if (!isUnLimit) { if (!LimitUtils.isAdShow(AdsType.OPEN, adEvent)) {
if (!LimitUtils.isAdShow(AdsType.OPEN, adEvent)) { loadCallBack?.invoke(false)
this.showCallBack?.close() return
this.showCallBack = null
return
}
} }
val ad = adState.currentAd as MaxAppOpenAd?
ad?.setListener(object : MaxAdListener {
override fun onAdLoaded(ad: MaxAd) {
adState.onAdLoaded()
loadCallBack?.invoke(true)
(adEvent as AdMaxEvent).pullAd(ad)
LimitUtils.addRequestNum()
}
override fun onAdLoadFailed(p0: String, error: MaxError) {
adState.onAdLoadFailed()
(adEvent as AdMaxEvent).pullAd(null, error)
loadCallBack?.invoke(false)
}
override fun onAdDisplayed(p0: MaxAd) = Unit
override fun onAdHidden(p0: MaxAd) = Unit
override fun onAdClicked(p0: MaxAd) = Unit
override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) = Unit
})
if (!adState.loadingAd) { if (!adState.loadingAd) {
adState.loadingAd = true adState.loadingAd = true
adEvent.adPulStart() adEvent.adPulStart()
ad?.loadAd()
adState.currentAd = MaxAppOpenAd(GlobalConfig.ID_MAX_OPEN, context)
adState.currentAd?.setListener(object : MaxAdListener {
override fun onAdLoaded(ad: MaxAd) {
adState.onAdLoaded(null)
val ac = adState.activityRef?.get()
if (ac != null) {
show(ac, isUnLimit, adEvent, null)
}
(adEvent as AdMaxEvent).pullAd(ad)
LimitUtils.addRequestNum()
}
override fun onAdDisplayed(p0: MaxAd) {
}
override fun onAdHidden(p0: MaxAd) {
}
override fun onAdClicked(p0: MaxAd) {
}
override fun onAdLoadFailed(p0: String, error: MaxError) {
adState.onAdLoadFailed()
showCallBack?.googleFailed()
showCallBack = null
(adEvent as AdMaxEvent).pullAd(null, error)
}
override fun onAdDisplayFailed(p0: MaxAd, p1: MaxError) {
}
})
adState.currentAd?.loadAd()
} }
} }
fun adAvailable() = ((System.currentTimeMillis() - adState.lastLoadTime) / 1000 / 60).toInt() < 30 private fun adAvailable() = ((System.currentTimeMillis() - adState.lastLoadTime) / 1000 / 60).toInt() < 30
} }
\ No newline at end of file
package com.base.appzxhy.base
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding
import com.base.appzxhy.ui.main.MainActivity
import com.base.appzxhy.R
import com.base.appzxhy.bean.ConstObject.appLanguageCountrySp
import com.base.appzxhy.bean.ConstObject.appLanguageSp
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.utils.ActivityLauncher
import com.base.appzxhy.utils.ActivityManagerUtils
import com.base.appzxhy.utils.LogEx
import com.gyf.immersionbar.ktx.immersionBar
import com.hjq.language.MultiLanguages
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.Locale
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.random.Random
abstract class BaseActivity<VB : ViewBinding>(
bindingInflater: (LayoutInflater) -> VB
) : AppCompatActivity() {
open val TAG = javaClass.simpleName
private val tagNo = Random.nextInt(500)
val binding by lazy(LazyThreadSafetyMode.NONE) {
bindingInflater(layoutInflater)
}
lateinit var launcher: ActivityLauncher
private var currentLocale: Locale? = null
var onCreateI = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
configSystemBar()
onCreateI++
LogEx.logDebug(TAG, "lifecycle $tagNo onCreate ${javaClass.simpleName} $onCreateI")
launcher = ActivityLauncher(this)
setContentView(binding.root)
EventUtils.event("page_${javaClass.simpleName}")
currentLocale = Locale(appLanguageSp, appLanguageCountrySp)
initView()
initListener()
}
protected open fun initView() {}
protected open fun initListener() {}
fun finishToMain(delay: Long = 0) {
val flag = this !is MainActivity && !ActivityManagerUtils.getInstance().isActivityInStack(MainActivity::class.java)
if (flag) {
startActivity(Intent(this, MainActivity::class.java).apply {
putExtra("where", "finishToMain")
})
}
lifecycleScope.launch(Dispatchers.Main) {
delay(delay)
finish()
}
}
fun finishToMainTop() {
val intent = Intent(this, MainActivity::class.java)
intent.putExtra("where", "finishToMainTop")
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
startActivity(intent)
}
var dialog: Dialog? = null
override fun onDestroy() {
super.onDestroy()
ActivityManagerUtils.getInstance().removeActivity(this)
if (dialog != null) {
dialog?.dismiss()
dialog = null
}
}
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(MultiLanguages.attach(newBase))
}
fun changeLanguage(currentActivity: Activity, bundle: Bundle = Bundle()): Boolean {
val spLanguage = Locale(appLanguageSp, appLanguageCountrySp)
val flag = currentLocale != spLanguage
val restart = MultiLanguages.setAppLanguage(this, spLanguage)
LogEx.logDebug(
TAG, "changeLanguage " +
"flag=$flag restart=$restart currentLocale=$currentLocale spLanguage=$spLanguage"
)
if (restart) {
currentLocale = Locale(appLanguageSp, appLanguageCountrySp)
}
if (flag || restart) {
// 1.使用 recreate 来重启 Activity,效果差,有闪屏的缺陷
// recreate();
// 2.使用常规 startActivity 来重启 Activity,有从左向右的切换动画
// 稍微比 recreate 的效果好一点,但是这种并不是最佳的效果
// startActivity(new Intent(this, LanguageActivity.class));
// finish();
// 3.我们可以充分运用 Activity 跳转动画,在跳转的时候设置一个渐变的效果,相比前面的两种带来的体验是最佳的
//需要设置启动模式,避免AndroidManifest.xml设置singleTop启动模式导致重复启动不生效
//这种方式如果设置了singleTop或者singleTask的,再次回到该activity无法改变于语言,因为还在堆栈里没有重新创建
needRecreate()
if (needRecreate()) {
LogEx.logDebug(TAG, "changeLanguage recreate")
recreate()
} else {
startActivity(Intent(this, currentActivity::class.java).apply {
putExtras(bundle)
})
overridePendingTransition(R.anim.activity_alpha_in, R.anim.activity_alpha_out)
finish()
}
return true
}
return false
}
/**
* 判断是否需要重新创建对SingleTop启动模式的有效
*/
fun needRecreate(): Boolean {
return TAG.contains("MainActivity")
}
/**
* 避免在onResume--onPause周期内,多次调用
* 在onStop中重置标志
*/
private val onResumeCalled = AtomicBoolean(false)
private var onResumeOneShootI = 0
open fun onResumeOneShoot() {
onResumeOneShootI++
LogEx.logDebug(TAG, "lifecycle $tagNo onResumeOneShoot ${javaClass.simpleName} $onResumeOneShootI")
}
private var onResumeI = 0
override fun onResume() {
super.onResume()
onResumeI++
LogEx.logDebug(TAG, "lifecycle $tagNo onResume ${javaClass.simpleName} $onResumeI")
if (!onResumeCalled.get()) {
onResumeCalled.set(true)
onResumeOneShoot()
}
}
private var onStartI = 0
override fun onStart() {
super.onStart()
onStartI++
LogEx.logDebug(TAG, "lifecycle $tagNo onStart ${javaClass.simpleName} $onStartI")
}
private var onStopI = 0
override fun onStop() {
super.onStop()
onStopI++
LogEx.logDebug(TAG, "lifecycle $tagNo onStop ${javaClass.simpleName} $onStopI")
onResumeCalled.set(false)
}
private var onPauseI = 0
override fun onPause() {
super.onPause()
onPauseI++
LogEx.logDebug(TAG, "lifecycle $tagNo onPause ${javaClass.simpleName} $onPauseI")
}
open fun configSystemBar() {
immersionBar {
statusBarColor("#FFFFFFFF")
navigationBarColor("#FFFFFFFF")
statusBarDarkFont(true)
navigationBarDarkIcon(true)
fitsSystemWindows(true)
}
}
}
\ No newline at end of file
@file:Suppress("unused")
package com.base.appzxhy.base
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
/**
*
* @param VB : ViewBinding
* 基本的fragment绑定数据
*/
abstract class BaseFragment<VB : ViewBinding>(
private val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> VB
) : Fragment() {
private var _binding: VB? = null
private var fragmentInit = false
open val TAG = javaClass.simpleName
//重写binding get()方法不直接暴露真实的可空_binding数据
@Suppress("MemberVisibilityCanBePrivate")
val binding: VB
get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = bindingInflater.invoke(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
initListener()
}
protected open fun initView() {}
protected open fun initListener() {}
open fun onResumeOneShoot() = Unit
override fun onResume() {
if (!fragmentInit) {
onResumeOneShoot()
fragmentInit = true
}
super.onResume()
}
override fun onDestroyView() {
super.onDestroyView()
//要手动置null防止内存泄漏
_binding = null
}
}
inline fun <VB : ViewBinding> BaseFragment<VB>.viewBind(block: VB.() -> Unit) {
binding.apply(block)
}
inline fun Fragment.goToAc(clazz: Class<out Activity>, block: (Intent.() -> Unit) = {}) {
context?.startActivity(Intent(context, clazz).apply(block))
}
package com.base.appzxhy.ui.main package com.base.appzxhy.ui.main
import com.base.appzxhy.base.BaseFragment
import com.base.appzxhy.databinding.FragmentHomeBinding import com.base.appzxhy.databinding.FragmentHomeBinding
import com.koko.drouter.base.BaseFragment
class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) { class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) {
......
...@@ -3,17 +3,18 @@ package com.base.appzxhy.ui.main ...@@ -3,17 +3,18 @@ package com.base.appzxhy.ui.main
import android.graphics.Color import android.graphics.Color
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.TextView import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavController import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.navigation.fragment.NavHostFragment import androidx.viewpager2.widget.ViewPager2
import com.base.appzxhy.R import com.base.appzxhy.R
import com.base.appzxhy.base.BaseActivity
import com.base.appzxhy.bean.HomeTabUIBean import com.base.appzxhy.bean.HomeTabUIBean
import com.base.appzxhy.databinding.ActivityMainBinding import com.base.appzxhy.databinding.ActivityMainBinding
import com.base.appzxhy.databinding.ItemHomeTabBinding import com.base.appzxhy.databinding.ItemHomeTabBinding
import com.base.appzxhy.utils.LogEx import com.base.appzxhy.utils.LogEx
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.koko.drouter.base.BaseActivity
class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::inflate) { class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::inflate) {
...@@ -21,18 +22,58 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl ...@@ -21,18 +22,58 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
ViewModelProvider(this)[MainViewModel::class.java] ViewModelProvider(this)[MainViewModel::class.java]
} }
private lateinit var navController: NavController
private val homeTabs by lazy { private val homeTabs by lazy {
arrayOf( arrayOf(
HomeTabUIBean(R.drawable.bg_selector_home, getString(R.string.home)), HomeTabUIBean(R.drawable.bg_selector_home, getString(R.string.home)),
) )
} }
private val homeFragment by lazy(LazyThreadSafetyMode.NONE) {
HomeFragment()
}
private val fragments = arrayOf(homeFragment)
private var pageAdapter: FragmentStateAdapter? = null
override fun initView() { override fun initView() {
super.initView() super.initView()
navController = (supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment).navController initViewPager2()
initTab()
} }
private fun initViewPager2() {
pageAdapter = object : FragmentStateAdapter(this) {
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
binding.viewPager2.run {
isUserInputEnabled = false
//https://www.jianshu.com/p/f69bd30cf5b0
//FragmentStateAdapter 这里必须传人fragment
adapter = pageAdapter
offscreenPageLimit = 3
}
binding.viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
}
override fun onPageSelected(position: Int) {
val defaultTab = binding.tabLayout.getTabAt(position)
defaultTab?.select()
defaultTab?.setSelected()
}
})
}
private fun initTab() { private fun initTab() {
homeTabs.forEachIndexed { index, homeTab -> homeTabs.forEachIndexed { index, homeTab ->
val tab = binding.tabLayout.newTab() val tab = binding.tabLayout.newTab()
...@@ -47,7 +88,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl ...@@ -47,7 +88,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
} }
binding.tabLayout.addOnTabSelectedListener(object : OnTabSelectedListener { binding.tabLayout.addOnTabSelectedListener(object : OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) { override fun onTabSelected(tab: TabLayout.Tab?) {
tab?.setSelected() tab?.let {
it.setSelected()
naviFragment(tab.id)
}
} }
override fun onTabUnselected(tab: TabLayout.Tab?) { override fun onTabUnselected(tab: TabLayout.Tab?) {
...@@ -65,6 +109,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl ...@@ -65,6 +109,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
} }
private fun naviFragment(id: Int) { private fun naviFragment(id: Int) {
if (binding.viewPager2.currentItem == id) return
binding.viewPager2.setCurrentItem(id, false)
when (id) { when (id) {
0 -> { 0 -> {
} }
...@@ -87,7 +133,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl ...@@ -87,7 +133,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
val textView = customView?.findViewById<TextView>(R.id.tv_tab) val textView = customView?.findViewById<TextView>(R.id.tv_tab)
LogEx.logDebug(TAG, "setSelected ${textView?.text}") LogEx.logDebug(TAG, "setSelected ${textView?.text}")
textView?.setTextColor(Color.parseColor("#FF0000")) textView?.setTextColor(Color.parseColor("#FF0000"))
naviFragment(this.id)
} }
} }
\ No newline at end of file
...@@ -2,9 +2,14 @@ package com.base.appzxhy.ui.start ...@@ -2,9 +2,14 @@ package com.base.appzxhy.ui.start
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import com.base.appzxhy.base.BaseActivity import android.graphics.Color
import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.base.appzxhy.databinding.ActivityStartBinding import com.base.appzxhy.databinding.ActivityStartBinding
import com.koko.batteryinfo.BatteryInfoActivity import com.koko.batteryinfo.BatteryInfoActivity
import com.koko.drouter.base.BaseActivity
class StartActivity : BaseActivity<ActivityStartBinding>(ActivityStartBinding::inflate) { class StartActivity : BaseActivity<ActivityStartBinding>(ActivityStartBinding::inflate) {
...@@ -12,6 +17,16 @@ class StartActivity : BaseActivity<ActivityStartBinding>(ActivityStartBinding::i ...@@ -12,6 +17,16 @@ class StartActivity : BaseActivity<ActivityStartBinding>(ActivityStartBinding::i
super.initView() super.initView()
} }
override fun useDefaultImmersive() {
// enableEdgeToEdge(SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT))
enableEdgeToEdge(SystemBarStyle.dark(Color.TRANSPARENT))
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, 0, systemBars.right, systemBars.bottom)
insets
}
}
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun initListener() { override fun initListener() {
......
package com.base.appzxhy.ui.start
import android.os.CountDownTimer
import androidx.lifecycle.ViewModel
import com.base.appzxhy.bean.config.AdConfigBean
class StartViewModel : ViewModel() {
var jumpNext: (() -> Unit)? = null
var onTick: ((sLong: Long, total: Long, percent: Int) -> Unit)? = null
private val openAdLoading = AdConfigBean.adsConfigBean.openAdLoading * 1000L
private var countdownTimer: CountDownTimer? = null
fun startJumpCountdown() {
cancelJumpCountDown()
countdownTimer = createNewCountdownTimer()
countdownTimer?.start()
}
fun cancelJumpCountDown() {
countdownTimer?.cancel()
onTick?.invoke(0, openAdLoading, 0)
}
private fun createNewCountdownTimer(): CountDownTimer {
return object : CountDownTimer(openAdLoading, 1000) {
override fun onTick(millisUntilFinished: Long) {
val progress = (openAdLoading - millisUntilFinished)
val percent = (progress * 100 / openAdLoading).toInt()
onTick?.invoke(progress, openAdLoading, percent)
}
override fun onFinish() {
onTick?.invoke(openAdLoading, openAdLoading, 100)
jumpNext?.invoke()
jumpNext = null
}
}
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ffffff" />
<corners android:radius="15dp" />
</shape>
\ No newline at end of file
...@@ -6,14 +6,13 @@ ...@@ -6,14 +6,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView <androidx.viewpager2.widget.ViewPager2
android:id="@+id/fragment_container" android:id="@+id/viewPager2"
android:name="androidx.navigation.fragment.NavHostFragment" android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/fl_tab" app:layout_constraintBottom_toTopOf="@id/fl_tab"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
app:navGraph="@navigation/nav_graph" />
<FrameLayout <FrameLayout
android:id="@+id/fl_tab" android:id="@+id/fl_tab"
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="300dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/bg_ffffff_15"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="30dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/ivYuan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:scaleType="centerInside"
android:src="@mipmap/zhuanquan"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:text="AD LOADING"
android:textColor="#ff111111"
android:textSize="20sp"
tools:ignore="HardcodedText" />
</LinearLayout>
<TextView
android:id="@+id/tvCountdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:layout_marginBottom="24dp"
android:text="Ads are about to be shown"
android:textColor="#666666"
android:textSize="13sp"
tools:ignore="HardcodedText" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="dp_300">300dp</dimen>
</resources>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment