Commit f43b9a38 authored by wanglei's avatar wanglei

...

parent 4f5c6253
......@@ -46,4 +46,8 @@ dependencies {
api(libs.drouter.api)
api(libs.drouter.api.page)
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
import android.app.Activity
import com.didi.drouter.api.DRouter
import com.koko.drouter.ad.NativeAdService
import java.util.Locale
val appService by lazy {
......@@ -11,7 +11,14 @@ val appService by lazy {
interface AppService {
fun eventActivity() = Unit
fun getCurrentSpLocal(): Locale
fun changeActivityLanguage(
activity: Activity,
currentLocale: Locale,
changeAction: () -> Unit
): Boolean
fun finishToMain() = Unit
......
package com.koko.drouter.base
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
......@@ -10,7 +11,9 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.viewbinding.ViewBinding
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.event.EventService
import java.util.Locale
import java.util.concurrent.atomic.AtomicBoolean
......@@ -27,7 +30,8 @@ abstract class BaseActivity<VB : ViewBinding>(
}
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 {
DRouter.build(EventService::class.java).getService()
......@@ -42,14 +46,19 @@ abstract class BaseActivity<VB : ViewBinding>(
// LogEx.logDebug(TAG, "lifecycle $tagNo onCreate ${javaClass.simpleName} $onCreateI")
launcher = ActivityLauncher(this)
setContentView(binding.root)
eventActivity()
// currentLocale = Locale(appLanguageSp, appLanguageCountrySp)
changeLanguage()
initView()
initListener()
initData()
}
private fun changeLanguage() {
isLanguageRecreate = appService.changeActivityLanguage(this, currentLocale) {
currentLocale = appService.getCurrentSpLocal()
}
}
open fun initData() {}
open fun useDefaultImmersive() {
......@@ -62,7 +71,6 @@ abstract class BaseActivity<VB : ViewBinding>(
}
private fun eventActivity() {
val eventService = DRouter.build(EventService::class.java).getService()
eventService.eventActivity(this)
}
......@@ -81,57 +89,8 @@ abstract class BaseActivity<VB : ViewBinding>(
super.onDestroy()
}
// 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")
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(MultiLanguages.attach(newBase))
}
......
......@@ -2,21 +2,24 @@ package com.base.appzxhy.ads
import android.animation.ObjectAnimator
import android.animation.ValueAnimator.INFINITE
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.app.Dialog
import android.content.Context
import android.os.CountDownTimer
import android.view.LayoutInflater
import android.view.animation.LinearInterpolator
import com.base.appzxhy.databinding.DialogAdPreparingBinding
import com.base.appzxhy.utils.DensityUtils
import com.base.appzxhy.utils.LogEx
import android.widget.TextView
import com.base.appzxhy.R
import com.base.appzxhy.databinding.DialogAdCountdownBinding
object AdDialog {
object AdCountDownDialog {
private val TAG = "com.base.appzxhy.ads.AdDialog"
fun Context.showAdPreparingDialog(where: Int = 0): AlertDialog {
LogEx.logDebug(TAG, "where=$where")
val binding = DialogAdPreparingBinding.inflate(LayoutInflater.from(this))
private val TAG = "AdCountDownDialog"
fun Context.showAdCountDownDialog(): AlertDialog {
val binding = DialogAdCountdownBinding.inflate(LayoutInflater.from(this))
val dialog = AlertDialog.Builder(this).create()
dialog.setView(binding.root)
dialog.setCancelable(false)
......@@ -24,13 +27,14 @@ object AdDialog {
dialog.show()
val params = dialog.window?.attributes
params?.width = DensityUtils.dip2px(200f)
params?.height = DensityUtils.dip2px(146f)
// params?.width = LinearLayout.LayoutParams.WRAP_CONTENT
params?.width = resources.getDimensionPixelSize(R.dimen.dp_300)
// params?.height = LinearLayout.LayoutParams.WRAP_CONTENT
dialog.window?.attributes = params
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.repeatCount = INFINITE
rotateAnimator.interpolator = LinearInterpolator() // 设置插值器为线性插值
......@@ -38,4 +42,26 @@ object AdDialog {
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 {
obj1.put("req_id", reqId)
obj1.put("from", from)
EventUtils.event("ad_prepare_show", ext = obj1)
LogEx.logDebug(TAG, "ad_prepare_show $obj1")
LogEx.logDebug(TAG, "ad_prepare_show_$adUnit $obj1")
}
fun adPulStart() {
......@@ -40,7 +40,7 @@ abstract class AdEvent {
obj.put("ad_type", adUnit)
obj.put("from", from)
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) {
......@@ -50,7 +50,7 @@ abstract class AdEvent {
obj.put("from", from)
obj.put("reason", reason.toString())
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) {
......@@ -59,7 +59,7 @@ abstract class AdEvent {
obj.put("req_id", reqId)
obj.put("from", from)
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
import android.app.Activity
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast
import androidx.annotation.LayoutRes
import com.applovin.sdk.AppLovinMediationProvider
import com.applovin.sdk.AppLovinSdk
......@@ -29,7 +29,6 @@ import com.base.appzxhy.utils.ToastUtils.toast
import com.google.android.gms.ads.MobileAds
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.google.android.gms.ads.initialization.AdapterStatus
import com.google.android.gms.ads.nativead.NativeAdView
import java.util.Collections
import java.util.concurrent.Executors
......@@ -88,8 +87,11 @@ object AdsMgr {
EventUtils.event("isInBlackList", configBean.isInBlackList.toString())
return
}
initAdmob(context)
initMax(context)
if (adsConfigBean.adSwitch) {
initAdmob(context)
} else {
initMax(context)
}
}
......@@ -105,8 +107,8 @@ object AdsMgr {
if (adsConfigBean.adSwitch) {
admobInitCallBack?.invoke()
admobInitCallBack = null
adOpenMgr.loadAd(context, false, AdmobEvent("openAd", context::class.java.simpleName))
adInsertMgr.loadAd(context, false, AdmobEvent("interAd", context::class.java.simpleName))
adOpenMgr.loadAd(context, AdmobEvent("openAd", context::class.java.simpleName))
adInsertMgr.loadAd(context, AdmobEvent("interAd", context::class.java.simpleName))
}
}
......@@ -133,7 +135,7 @@ object AdsMgr {
isMaxInit = true
// maxOpenMgr.loadAd(context)
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")
maxInitCallBack?.invoke()
maxInitCallBack = null
......@@ -158,7 +160,6 @@ object AdsMgr {
*/
fun showOpen(
activity: Activity,
isUnLimit: Boolean = false,
showCallBack: AdsShowCallBack? = null,
) {
if (configBean.isInBlackList) {
......@@ -169,18 +170,18 @@ object AdsMgr {
val from = activity::class.java.simpleName
if (adsConfigBean.adSwitch) {
if (isAdmobInit) {
adOpenMgr.show(activity, isUnLimit, AdmobEvent("openAd", from), showCallBack)
adOpenMgr.show(activity, AdmobEvent("openAd", from), showCallBack)
} else {
admobInitCallBack = {
}
adOpenMgr.show(activity, isUnLimit, AdmobEvent("openAd", from), showCallBack)
adOpenMgr.show(activity, AdmobEvent("openAd", from), showCallBack)
}
} else {
if (isMaxInit) {
maxOpenMgr.show(activity, isUnLimit, AdMaxEvent("openAd", from), showCallBack)
maxOpenMgr.show(activity, AdMaxEvent("openAd", from), showCallBack)
} else {
maxInitCallBack = {
maxOpenMgr.show(activity, isUnLimit, AdMaxEvent("openAd", from), showCallBack)
maxOpenMgr.show(activity, AdMaxEvent("openAd", from), showCallBack)
}
}
}
......@@ -196,7 +197,6 @@ object AdsMgr {
*/
fun showInsert(
activity: Activity,
isUnLimit: Boolean = false,
showCallBack: AdsShowCallBack? = null,
) {
if (configBean.isInBlackList) {
......@@ -206,9 +206,9 @@ object AdsMgr {
LogEx.logDebug("showAd", "adSwitch=${adsConfigBean.adSwitch}")
val from = activity::class.java.simpleName
if (adsConfigBean.adSwitch) {
adInsertMgr.show(activity, isUnLimit, AdmobEvent("interAd", from), showCallBack)
adInsertMgr.show(activity, AdmobEvent("interAd", from), showCallBack)
} else {
maxInsertMgr.show(activity, isUnLimit, AdMaxEvent("interAd", from), showCallBack)
maxInsertMgr.show(activity, AdMaxEvent("interAd", from), showCallBack)
}
}
......@@ -221,29 +221,25 @@ object AdsMgr {
fun showNative(
activity: Activity,
parent: FrameLayout,
@LayoutRes layout: Int,
@LayoutRes admobLayout: Int,
@LayoutRes maxLayout: Int,
nativeCallBack: ((Any?) -> Unit)? = null
) {
if (configBean.isInBlackList) {
LogEx.logDebug("showNative", "isInBlackList")
EventUtils.event("isInBlackList", configBean.isInBlackList.toString())
return
}
if (!isAdmobInit) return
val from = activity::class.java.simpleName
if (adsConfigBean.adSwitch) {
val nativeView = LayoutInflater.from(parent.context).inflate(layout, parent, false) as NativeAdView
adNativeMgr.show(AdmobEvent("nativeAd", from), parent, nativeView, nativeCallBack)
adNativeMgr.show(AdmobEvent("nativeAd", from), parent, admobLayout, nativeCallBack)
} 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广告
......
......@@ -4,5 +4,5 @@ abstract class AdsShowCallBack {
open fun show() {}
abstract fun close(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
import com.base.appzxhy.BuildConfig
import com.base.appzxhy.bean.config.AdConfigBean
import com.base.appzxhy.utils.AppPreferences
import com.base.appzxhy.utils.KotlinExt.toFormatTime4
......@@ -16,7 +17,7 @@ object LimitUtils {
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 {
}
/**
* 广告请求是否到达限制
*/
......@@ -81,6 +81,11 @@ object LimitUtils {
* @return true or false
*/
fun isAdShow(adsType: AdsType, adEvent: AdEvent?): Boolean {
if (BuildConfig.DEBUG) {
return true
}
val currentDate = System.currentTimeMillis().toFormatTime4()
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
import android.app.Activity
import android.content.Context
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.AdsShowCallBack
import com.base.appzxhy.ads.AdsType
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.AdRequest
import com.google.android.gms.ads.FullScreenContentCallback
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.interstitial.InterstitialAd
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback
import java.lang.ref.WeakReference
/**
*插屏广告加载显示管理类
*/
class AdInterMgr {
private var adState = adState<InterstitialAd>()
private var showCallBack: AdsShowCallBack? = null
private var adState = AdmobAdState()
fun show(activity: Activity, isUnLimit: Boolean, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
fun show(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
if (activity.isFinishing || activity.isDestroyed) {
showCallBack?.failed(0)
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)
adEvent.adShowError("showingAd")
adState.showingAd = false
return
}
if (showCallBack != null) {
this.showCallBack = showCallBack
adState.activityRef = WeakReference(activity)
if (adState.adDialog == null) {
adState.adDialog = activity.showAdPreparingDialog(1)
}
adEvent.adPrepareShow()
if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
showCallBack?.failed(2)
return
}
if (!adState.loadingAd) {
if (adState.currentAd == null) {
loadAd(activity, isUnLimit, adEvent)
return
val loadingDialog = activity.showAdCountDownDialog()
if (adState.currentAd == null || !adAvailable()) {
loadAd(activity, adEvent) {
if (adState.currentAd != null) {
createUICountdownTimer(loadingDialog) {
showReadyAd(activity, adEvent, showCallBack)
}
}
}
if (!adAvailable()) {
loadAd(activity, isUnLimit, adEvent)
return
} else {
createUICountdownTimer(loadingDialog) {
showReadyAd(activity, adEvent, showCallBack)
}
showReadyAd(adEvent, activity)
}
}
private fun showReadyAd(adEvent: AdEvent, activity: Activity) {
adState.currentAd?.run {
private fun showReadyAd(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
val ad = adState.currentAd as InterstitialAd?
ad?.run {
fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdShowedFullScreenContent() {
super.onAdShowedFullScreenContent()
......@@ -85,9 +74,7 @@ class AdInterMgr {
super.onAdFailedToShowFullScreenContent(adError)
adState.onAdDisplayFailed()
showCallBack?.googleFailed()
showCallBack = null
showCallBack?.adFailed()
adEvent.adShowError(adError)
}
......@@ -97,8 +84,7 @@ class AdInterMgr {
adState.onAdHidden()
showCallBack?.close()
showCallBack = null
loadAd(activity.applicationContext, false, AdmobEvent("interAd", "preload"))
loadAd(activity.applicationContext, AdmobEvent("interAd", "preload"))
}
override fun onAdClicked() {
......@@ -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)) {
this.showCallBack?.close(4)
this.showCallBack = null
adState.onAdLoadFailed()
return
}
if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
loadCallBack?.invoke()
adState.onAdLoadFailed()
return
}
if (!adState.loadingAd) {
......@@ -132,10 +115,7 @@ class AdInterMgr {
object : InterstitialAdLoadCallback() {
override fun onAdLoaded(ad: InterstitialAd) {
adState.onAdLoaded(ad)
val ac = adState.activityRef?.get()
if (ac != null) {
show(ac, isUnLimit, adEvent, null)
}
loadCallBack?.invoke()
(adEvent as AdmobEvent).pullAd(ad.responseInfo)
LimitUtils.addRequestNum()
ad.onPaidEventListener = AdmobEvent.EventOnPaidEventListener(ad)
......@@ -144,8 +124,7 @@ class AdInterMgr {
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
adState.onAdLoadFailed()
(adEvent as AdmobEvent).pullAd(loadAdError.responseInfo, loadAdError)
showCallBack?.googleFailed()
showCallBack = null
loadCallBack?.invoke()
}
}
)
......
package com.base.appzxhy.ads.admob
import android.content.Context
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.core.view.isVisible
import com.base.appzxhy.GlobalConfig
import com.base.appzxhy.R
import com.base.appzxhy.ads.AdsType
......@@ -15,48 +15,40 @@ import com.google.android.gms.ads.AdListener
import com.google.android.gms.ads.AdLoader
import com.google.android.gms.ads.AdRequest
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.NativeAdOptions
import com.google.android.gms.ads.nativead.NativeAdView
import java.util.concurrent.ConcurrentLinkedDeque
/**
*原生广告加载显示管理类
*/
class AdNativeMgr {
/**
* 上一次的缓存成功时间
*/
private val TAG = this::class.java.simpleName
protected var lastTime: Long = 0
/**
* 原生广告缓存队列
*/
private val cacheItems = ConcurrentLinkedDeque<NativeAd>()
private var currentAd: NativeAd? = null
private fun loadAd(
admobEvent: AdmobEvent,
parent: FrameLayout,
nativeAdView: NativeAdView
context: Context,
callback: ((nativeAd: NativeAd) -> Unit)? = null
) {
admobEvent.adPulStart()
if (!LimitUtils.isAdShow(AdsType.NATIVE, admobEvent)) return
admobEvent.adPulStart()
var currentNativeAd: NativeAd? = null
val adLoader = AdLoader.Builder(
parent.context,
context,
GlobalConfig.ID_ADMOB_NATIVE
).forNativeAd { nativeAd ->
currentNativeAd = nativeAd
cacheItems.offer(nativeAd)
lastTime = System.currentTimeMillis()
nativeAd.setOnPaidEventListener(AdmobEvent.EventOnPaidEventListener(nativeAd))
admobEvent.pullAd(nativeAd.responseInfo)
show(admobEvent, parent, nativeAdView)
callback?.invoke(nativeAd)
}.withAdListener(object : AdListener() {
override fun onAdFailedToLoad(error: LoadAdError) {
......@@ -80,69 +72,68 @@ class AdNativeMgr {
fun show(
admobEvent: AdmobEvent,
nativeParent: FrameLayout,
nativeAdView: NativeAdView,
parent: FrameLayout,
layout: Int,
nativeCallBack: ((Any?) -> Unit)? = null
) {
admobEvent.adPrepareShow()
if (!LimitUtils.isAdShow(AdsType.NATIVE, admobEvent)) {
cacheItems.clear()
return
}
val nativeAd = cacheItems.peek()
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.adPrepareShow()
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?) {
nativeAd ?: return
private fun setNative(parent: FrameLayout, layout: Int, nativeAd: NativeAd) = runCatching {
mediaView = findViewById(R.id.ad_media)
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)
currentAd = null
(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
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) {
bodyView?.visibility = View.INVISIBLE
} else {
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) {
callToActionView?.visibility = View.INVISIBLE
} else {
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) {
iconView?.visibility = View.GONE
} else {
(iconView as ImageView?)?.setImageDrawable(
nativeAd.icon?.drawable
)
iconView?.setImageDrawable(nativeAd.icon?.drawable)
iconView?.visibility = View.VISIBLE
}
setNativeAd(nativeAd)
parent.removeAllViews()
parent.addView(nativeView)
}
private fun adAvailable(): Boolean {
......
......@@ -3,66 +3,62 @@ package com.base.appzxhy.ads.admob
import android.app.Activity
import android.content.Context
import com.base.appzxhy.GlobalConfig
import com.base.appzxhy.ads.AdCountDownDialog.createUICountdownTimer
import com.base.appzxhy.ads.AdsType
import com.base.appzxhy.ads.AdEvent
import com.base.appzxhy.ads.AdsShowCallBack
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.AdRequest
import com.google.android.gms.ads.FullScreenContentCallback
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.appopen.AppOpenAd
import java.lang.ref.WeakReference
/**
* 开屏广告加载显示管理类
*/
class AdOpenMgr {
private val adState = adState<AppOpenAd>()
private var showCallBack: AdsShowCallBack? = null
private val adState = AdmobAdState()
fun show(activity: Activity, isUnLimit: Boolean, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
fun show(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
if (activity.isFinishing || activity.isDestroyed) {
return
}
if (!isUnLimit) {
if (!LimitUtils.isAdShow(AdsType.OPEN, adEvent)) {
showCallBack?.failed()
return
}
if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
showCallBack?.failed()
return
}
}
if (adState.showingAd) {
if (!LimitUtils.isAdShow(AdsType.OPEN, adEvent)) {
showCallBack?.failed()
adEvent.adShowError("showingAd")
adState.showingAd = false
return
}
if (showCallBack != null) {
this.showCallBack = showCallBack
adState.activityRef = WeakReference(activity)
adEvent.adPrepareShow()
if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
showCallBack?.failed()
return
}
adEvent.adPrepareShow()
if (!adState.loadingAd) {
if (!adAvailable() || adState.currentAd == null) {
loadAd(activity, isUnLimit, adEvent)
return
if (adState.currentAd == null || !adAvailable()) {
loadAd(activity, adEvent) {
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) {
adState.currentAd?.run {
private fun showReadyAd(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
val ad = adState.currentAd as AppOpenAd?
ad?.run {
fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdShowedFullScreenContent() {
......@@ -77,8 +73,7 @@ class AdOpenMgr {
override fun onAdFailedToShowFullScreenContent(adError: AdError) {
super.onAdFailedToShowFullScreenContent(adError)
showCallBack?.googleFailed()
showCallBack = null
showCallBack?.adFailed()
adState.onAdDisplayFailed()
(adEvent as AdmobEvent).adShowError(adError)
......@@ -87,13 +82,9 @@ class AdOpenMgr {
override fun onAdDismissedFullScreenContent() {
super.onAdDismissedFullScreenContent()
showCallBack?.close()
showCallBack = null
adState.onAdHidden()
loadAd(activity.applicationContext, false, AdmobEvent("openAd", "preload"))
loadAd(activity.applicationContext, AdmobEvent("openAd", "preload"))
}
override fun onAdClicked() {
......@@ -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)) {
this.showCallBack?.close()
this.showCallBack = null
adState.onAdLoadFailed()
return
}
if (!LimitUtils.isAdShow(AdsType.OPEN, adEvent)) {
loadCallBack?.invoke()
adState.onAdLoadFailed()
return
}
if (!adState.loadingAd) {
......@@ -128,19 +116,15 @@ class AdOpenMgr {
object : AppOpenAd.AppOpenAdLoadCallback() {
override fun onAdLoaded(appOpenAd: AppOpenAd) {
adState.onAdLoaded(appOpenAd)
val ac = adState.activityRef?.get()
if (ac != null) {
show(ac, isUnLimit, adEvent, null)
}
loadCallBack?.invoke()
(adEvent as AdmobEvent).pullAd(appOpenAd.responseInfo)
appOpenAd.onPaidEventListener = AdmobEvent.EventOnPaidEventListener(appOpenAd)
LimitUtils.addRequestNum()
}
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
showCallBack?.googleFailed()
showCallBack = null
adState.onAdLoadFailed()
loadCallBack?.invoke()
(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.Dialog
import android.content.Context
import java.lang.ref.WeakReference
class adState<T>() {
var adDialog: Dialog? = null
class AdmobAdState {
/**
* 当前缓存的广告
*/
var currentAd: T? = null
var currentAd: Any? = null
/**
* 是否正在缓存加载广告
......@@ -22,11 +15,6 @@ class adState<T>() {
var loadingAd: Boolean = false
/**
* 是否正在显示广告
*/
var showingAd: Boolean = false
/**
* 用于保存引用现有页面,在此页面显示广告(因为要等待广告加载完毕)
*/
......@@ -43,10 +31,6 @@ class adState<T>() {
var lastShowTime: Long = 0
fun onAdDisplayed() {
showingAd = true
adDialog?.dismiss()
adDialog = null
lastShowTime = System.currentTimeMillis()
......@@ -56,19 +40,15 @@ class adState<T>() {
fun onAdHidden() {
// 广告关闭,清空缓存数据,重新加载
showingAd = false
}
fun onAdDisplayFailed() {
adDialog?.dismiss()
adDialog = null
showingAd = false
currentAd = null
activityRef = null
}
fun onAdLoaded(ad: T?) {
fun onAdLoaded(ad: Any?) {
//这里可能提前设置,所有可以不设置,max回调的类型可能不同
if (ad != null) {
currentAd = ad
......@@ -78,70 +58,6 @@ class adState<T>() {
}
fun onAdLoadFailed() {
adDialog?.dismiss()
adDialog = null
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 {
obj.put("status", "2")
}
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 {
obj.put("from", from)
obj.put("networkname", responseInfo?.mediationAdapterClassName)
if (adUnit != "nativeAd") {
EventUtils.event("ad_click", ext = obj)
EventUtils.event("ad_click_$adUnit", ext = obj)
} else {
EventUtils.event("ad_click", ext = obj)
EventUtils.event("ad_click_$adUnit", ext = obj)
}
}
......@@ -115,7 +115,7 @@ class AdmobEvent : AdEvent {
} else {
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 {
......@@ -303,7 +303,7 @@ class AdmobEvent : AdEvent {
obj.put("from", from)
obj.put("ad_unit", "openAd")
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
import com.base.appzxhy.ads.taichiPref
import com.base.appzxhy.ads.taichiSharedPreferencesEditor
import com.base.appzxhy.helper.EventUtils
import com.base.appzxhy.utils.LogEx
import com.base.appzxhy.utils.LogEx.logDebug
import com.facebook.appevents.AppEventsConstants
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
import android.app.Activity
import android.content.Context
import com.applovin.mediation.MaxAd
import com.applovin.mediation.MaxAdListener
import com.applovin.mediation.MaxError
import com.applovin.mediation.ads.MaxInterstitialAd
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.AdDialog.showAdPreparingDialog
import com.base.appzxhy.ads.AdEvent
import com.base.appzxhy.ads.AdsShowCallBack
import com.base.appzxhy.ads.LimitUtils
import com.base.appzxhy.ads.adState
import java.lang.ref.WeakReference
/**
*插屏广告加载显示管理类
*/
class MaxInsertMgr {
private var adState = adState<MaxInterstitialAd>()
private var showCallBack: AdsShowCallBack? = null
private var adState = MaxAdState().apply {
currentAd = MaxInterstitialAd(GlobalConfig.ID_MAX_INTER, MyApplication.appContext)
}
fun show(
activity: Activity,
isUnLimit: Boolean,
adEvent: AdEvent,
showCallBack: AdsShowCallBack?
) {
if (activity.isFinishing || activity.isDestroyed) {
showCallBack?.failed(0)
return
}
if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
showCallBack?.failed(1)
return
}
if (adState.showingAd) {
if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
showCallBack?.failed(2)
return
}
if (showCallBack != null) {
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
}
}
adEvent.adPrepareShow()
if (!adAvailable() || adState.currentAd == null) {
loadAd(activity, isUnLimit, adEvent)
return
}
val loadingDialog = activity.showAdCountDownDialog()
if (adState.currentAd?.isReady == false) {
loadAd(activity, isUnLimit, adEvent)
return
if (adState.currentAd == null || !adAvailable()) {
loadAd(adEvent) { flag: Boolean ->
if (flag) {
createUICountdownTimer(loadingDialog) {
showReadyAd(activity, adEvent, showCallBack)
}
} else {
showCallBack?.failed()
}
}
showReadyAd(adEvent)
} else {
showReadyAd(activity, adEvent, showCallBack)
}
}
private fun showReadyAd(adEvent: AdEvent) {
adState.currentAd?.run {
private fun showReadyAd(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
val ad = adState.currentAd as MaxInterstitialAd?
ad?.run {
setListener(object : MaxAdListener {
override fun onAdLoaded(p0: MaxAd) = Unit
override fun onAdLoadFailed(p0: String, p1: MaxError) = Unit
......@@ -93,8 +83,7 @@ class MaxInsertMgr {
override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) {
adState.onAdDisplayFailed()
showCallBack?.googleFailed()
showCallBack = null
showCallBack?.adFailed()
(adEvent as AdMaxEvent).adShowError(error)
}
......@@ -104,7 +93,7 @@ class MaxInsertMgr {
adState.onAdHidden()
showCallBack?.close()
loadAd(activity.applicationContext, false, AdMaxEvent("interAd", "preload"))
loadAd(AdMaxEvent("interAd", "preload"))
}
override fun onAdClicked(ad: MaxAd) {
......@@ -123,53 +112,43 @@ class MaxInsertMgr {
fun loadAd(
context: Context,
isUnLimit: Boolean,
adEvent: AdEvent,
loadCallBack: ((flag: Boolean) -> Unit)? = null
) {
if (!isUnLimit) {
if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
this.showCallBack?.close(4)
this.showCallBack = null
return
}
if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
loadCallBack?.invoke(false)
return
}
if (!adState.loadingAd) {
adState.loadingAd = true
adEvent.adPulStart()
adState.currentAd = MaxInterstitialAd(GlobalConfig.ID_MAX_INTER, context)
adState.currentAd?.setListener(object : MaxAdListener {
val ad = adState.currentAd as MaxInterstitialAd?
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
ad?.setListener(object : MaxAdListener {
override fun onAdLoaded(ad: MaxAd) {
adState.onAdLoaded(null)
val ac = adState.activityRef?.get()
if (ac != null) {
show(ac, isUnLimit, adEvent, null)
}
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
(adEvent as AdMaxEvent).pullAd(ad)
LimitUtils.addRequestNum()
}
override fun onAdLoaded(ad: MaxAd) {
loadCallBack?.invoke(true)
adState.onAdLoaded()
(adEvent as AdMaxEvent).pullAd(ad)
LimitUtils.addRequestNum()
}
override fun onAdLoadFailed(ad: String, error: MaxError) {
adState.onAdLoadFailed()
override fun onAdLoadFailed(ad: String, error: MaxError) {
loadCallBack?.invoke(false)
adState.onAdLoadFailed()
(adEvent as AdMaxEvent).pullAd(null, error)
(adEvent as AdMaxEvent).pullAd(null, error)
}
showCallBack?.googleFailed(5)
showCallBack = null
}
})
})
adState.currentAd?.loadAd()
if (!adState.loadingAd) {
adState.loadingAd = true
adEvent.adPulStart()
ad?.loadAd()
}
}
......
package com.base.appzxhy.ads.applovin
import android.widget.FrameLayout
import androidx.annotation.LayoutRes
import com.applovin.mediation.MaxAd
import com.applovin.mediation.MaxError
import com.applovin.mediation.nativeAds.MaxNativeAdListener
import com.applovin.mediation.nativeAds.MaxNativeAdLoader
import com.applovin.mediation.nativeAds.MaxNativeAdView
import com.applovin.mediation.nativeAds.MaxNativeAdViewBinder
import com.base.appzxhy.GlobalConfig
import com.base.appzxhy.R
import com.base.appzxhy.ads.AdsType
import com.base.appzxhy.ads.LimitUtils
import com.base.appzxhy.ads.NativeParentView
import com.base.appzxhy.helper.EventUtils
import org.json.JSONObject
import java.util.UUID
......@@ -30,10 +32,9 @@ class MaxNativeMgr {
private var currentAd: MaxAd? = null
private var currentLoader: MaxNativeAdLoader? = null
private fun loadAd(
adMaxEvent: AdMaxEvent,
parent: NativeParentView,
parent: FrameLayout,
@LayoutRes layout: Int
) {
......@@ -71,7 +72,7 @@ class MaxNativeMgr {
fun show(
adMaxEvent: AdMaxEvent,
parent: NativeParentView,
parent: FrameLayout,
@LayoutRes layout: Int,
nativeCallBack: ((Any?) -> Unit)? = null
) {
......@@ -105,11 +106,41 @@ class MaxNativeMgr {
val obj = JSONObject()
obj.put("ad_unit", "nativeAd")
EventUtils.event("ad_prepare_show", ext = obj)
parent.setNativeAd(nativeLoader!!, nativeAd!!, layout)
setMaxNativeAd(parent, layout)
nativeCallBack?.invoke(nativeAd)
}
private fun adAvailable(): Boolean {
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
import android.app.Activity
import android.content.Context
import com.applovin.mediation.MaxAd
import com.applovin.mediation.MaxAdListener
import com.applovin.mediation.MaxError
import com.applovin.mediation.ads.MaxAppOpenAd
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.AdEvent
import com.base.appzxhy.ads.AdsShowCallBack
import com.base.appzxhy.ads.LimitUtils
import com.base.appzxhy.ads.adState
import java.lang.ref.WeakReference
/**
* 开屏广告加载显示管理类
*/
class MaxOpenMgr {
private val adState = adState<MaxAppOpenAd>()
private var showCallBack: AdsShowCallBack? = null
private val adState = MaxAdState().apply {
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) {
showCallBack?.failed(0)
return
}
if (adState.showingAd) {
showCallBack?.failed()
adEvent.adShowError("showingAd")
if (!LimitUtils.isAdShow(AdsType.INSERT, adEvent)) {
showCallBack?.failed(1)
return
}
if (showCallBack != null) {
this.showCallBack = showCallBack
adState.activityRef = WeakReference(activity)
adEvent.adPrepareShow()
if (LimitUtils.isIntervalLimited(adState.lastShowTime, adEvent)) {
showCallBack?.failed(2)
return
}
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) {
loadAd(activity, isUnLimit, adEvent)
return
}
val loadingDialog = activity.showAdCountDownDialog()
if (adState.currentAd?.isReady != true) {
loadAd(activity, isUnLimit, adEvent)
return
if (!adAvailable()) {
loadAd(adEvent) { flag: Boolean ->
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) {
adState.currentAd?.run {
private fun showReadyAd(activity: Activity, adEvent: AdEvent, showCallBack: AdsShowCallBack?) {
val ad = adState.currentAd as MaxAppOpenAd?
ad?.run {
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) {
adState.onAdDisplayed()
showCallBack?.show()
LimitUtils.addDisplayNum()
(adEvent as AdMaxEvent).showAd(ad, activity::class.simpleName)
}
......@@ -86,9 +78,7 @@ class MaxOpenMgr {
override fun onAdHidden(p0: MaxAd) {
adState.onAdHidden()
showCallBack?.close()
showCallBack = null
loadAd(activity.applicationContext, false, AdMaxEvent("openAd", "preload"))
loadAd(AdMaxEvent("openAd", "preload"))
}
override fun onAdClicked(ad: MaxAd) {
......@@ -97,14 +87,10 @@ class MaxOpenMgr {
LimitUtils.addClickNum()
}
override fun onAdLoadFailed(p0: String, p1: MaxError) {
}
override fun onAdDisplayFailed(p0: MaxAd, error: MaxError) {
adState.onAdDisplayFailed()
showCallBack?.googleFailed()
showCallBack = null
showCallBack?.adFailed()
adEvent.adShowError(error)
}
......@@ -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)) {
this.showCallBack?.close()
this.showCallBack = null
return
}
if (!LimitUtils.isAdShow(AdsType.OPEN, adEvent)) {
loadCallBack?.invoke(false)
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) {
adState.loadingAd = true
adEvent.adPulStart()
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()
ad?.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
import com.base.appzxhy.base.BaseFragment
import com.base.appzxhy.databinding.FragmentHomeBinding
import com.koko.drouter.base.BaseFragment
class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) {
......
......@@ -3,17 +3,18 @@ package com.base.appzxhy.ui.main
import android.graphics.Color
import android.view.LayoutInflater
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.base.appzxhy.R
import com.base.appzxhy.base.BaseActivity
import com.base.appzxhy.bean.HomeTabUIBean
import com.base.appzxhy.databinding.ActivityMainBinding
import com.base.appzxhy.databinding.ItemHomeTabBinding
import com.base.appzxhy.utils.LogEx
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.koko.drouter.base.BaseActivity
class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::inflate) {
......@@ -21,18 +22,58 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
ViewModelProvider(this)[MainViewModel::class.java]
}
private lateinit var navController: NavController
private val homeTabs by lazy {
arrayOf(
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() {
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() {
homeTabs.forEachIndexed { index, homeTab ->
val tab = binding.tabLayout.newTab()
......@@ -47,7 +88,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
}
binding.tabLayout.addOnTabSelectedListener(object : OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
tab?.setSelected()
tab?.let {
it.setSelected()
naviFragment(tab.id)
}
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
......@@ -65,6 +109,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
}
private fun naviFragment(id: Int) {
if (binding.viewPager2.currentItem == id) return
binding.viewPager2.setCurrentItem(id, false)
when (id) {
0 -> {
}
......@@ -87,7 +133,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
val textView = customView?.findViewById<TextView>(R.id.tv_tab)
LogEx.logDebug(TAG, "setSelected ${textView?.text}")
textView?.setTextColor(Color.parseColor("#FF0000"))
naviFragment(this.id)
}
}
\ No newline at end of file
......@@ -2,9 +2,14 @@ package com.base.appzxhy.ui.start
import android.annotation.SuppressLint
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.koko.batteryinfo.BatteryInfoActivity
import com.koko.drouter.base.BaseActivity
class StartActivity : BaseActivity<ActivityStartBinding>(ActivityStartBinding::inflate) {
......@@ -12,6 +17,16 @@ class StartActivity : BaseActivity<ActivityStartBinding>(ActivityStartBinding::i
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")
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 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager2"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/fl_tab"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
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