Commit 2080767f authored by wanglei's avatar wanglei

...

parent 2766f4c5
package com.base.locationsharewhite.ads
import com.base.locationsharewhite.utils.AppPreferences
/**
* 广告限制配置
*
......@@ -15,5 +17,20 @@ data class AdsConfigBean(
var numRequestLimit: Int = -1,
var numClickLimit: Int = -1,
var timeInterval: Int = 1,
val openAdLoading: Int = 15,
)
\ No newline at end of file
var openAdLoading: Int = 15,
) {
companion object {
fun getSpConfigBean(): AdsConfigBean {
val adsConfigBean = AdsConfigBean()
adsConfigBean.apply {
isInBlackList = AppPreferences.getInstance().getBoolean("isInBlackList", false)
numDisplayLimit = AppPreferences.getInstance().getInt("numDisplayLimit", -1)
numRequestLimit = AppPreferences.getInstance().getInt("numRequestLimit", -1)
numClickLimit = AppPreferences.getInstance().getInt("numClickLimit", -1)
timeInterval = AppPreferences.getInstance().getInt("timeInterval", 1)
openAdLoading = AppPreferences.getInstance().getInt("openAdLoading", 15)
}
return adsConfigBean
}
}
}
\ No newline at end of file
package com.base.locationsharewhite.ads
import AdInsertMgr
import android.app.Activity
import android.content.Context
import android.view.ViewGroup
import com.base.locationsharewhite.ads.admob.AdBannerMgr
import com.base.locationsharewhite.ads.admob.AdInsertMgr
import com.base.locationsharewhite.ads.admob.AdNativeMgr
import com.base.locationsharewhite.ads.admob.AdOpenMgr
import com.google.android.gms.ads.MobileAds
......@@ -70,10 +70,6 @@ object AdsMgr {
* @param showCallBack 展示回调
*/
fun showOpen(activity: Activity, showCallBack: AdsShowCallBack? = null) {
// if (!isInit || adsConfigBean?.isInBlackList != false) {
// showCallBack?.failed()
// return
// }
adOpenMgr.show(activity, showCallBack)
}
......
......@@ -3,6 +3,7 @@ package com.base.locationsharewhite.ads.admob
import android.os.Bundle
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.base.locationsharewhite.ads.AdsType
import com.base.locationsharewhite.helper.config.ConstConfig
import com.google.ads.mediation.admob.AdMobAdapter
import com.google.android.gms.ads.AdListener
......@@ -19,39 +20,37 @@ class AdBannerMgr {
private var adView: AdView? = null
private var listener: ViewTreeObserver.OnGlobalLayoutListener? = null
fun show(parent: ViewGroup) {
if (adView != null) {
adView?.destroy()
}
if (!LimitUtils.isAdShow()) {
fun show(parent: ViewGroup, adClose: (() -> Unit)? = null) {
if (!LimitUtils.isAdShow(AdsType.BANNER)) {
adView = null
return
}
parent.removeAllViews()
adView?.destroy()
adView = null
adView = AdView(parent.context)
adView?.tag = "CollapsibleBannerAd"
// val list = parent.children
// list.forEach {
// if (it.tag != "zhanweitu") {
// parent.removeView(it)
// }
// }
parent.addView(adView)
adView?.onPaidEventListener = AdmobEvent.EventOnPaidEventListener(adView)
listener = ViewTreeObserver.OnGlobalLayoutListener {
val screenPixelDensity = parent.context.resources.displayMetrics.density
val adWidth = (parent.width / screenPixelDensity).toInt()
val adSize =
AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(parent.context, adWidth)
adView?.adUnitId =
ConstConfig.bannerAdId
adView?.adUnitId = ConstConfig.bannerAdId
adView?.setAdSize(adSize)
loadAd(parent)
loadAd(adClose)
parent.viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
parent.viewTreeObserver.addOnGlobalLayoutListener(listener)
}
fun loadAd(parent: ViewGroup) {
fun loadAd(adClose: (() -> Unit)?) {
val extras = Bundle()
extras.putString("collapsible", "bottom")
extras.putString("collapsible_request_id", UUID.randomUUID().toString())
......@@ -69,15 +68,7 @@ class AdBannerMgr {
override fun onAdClosed() {
super.onAdClosed()
// val removeList = arrayListOf<View>()
// parent.children.forEach {
// if (it.tag != "CollapsibleBannerAd") {
// removeList.add(it)
// }
// }
// removeList.forEach {
// parent.removeView(it)
// }
adClose?.invoke()
}
}
adView?.loadAd(adRequest)
......
package com.base.locationsharewhite.ads.admob
import android.app.Activity
import android.content.Context
import com.base.locationsharewhite.ads.AdsShowCallBack
import com.base.locationsharewhite.ads.AdsType
import com.base.locationsharewhite.ads.BaseAdMgr
import com.base.locationsharewhite.ads.admob.LimitUtils
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.helper.config.ConstConfig
import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.AdRequest
......@@ -10,7 +13,9 @@ 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 org.json.JSONObject
import java.lang.ref.WeakReference
import java.util.UUID
/**
*插屏广告加载显示管理类
......@@ -19,9 +24,26 @@ class AdInsertMgr : BaseAdMgr<InterstitialAd>() {
private var lastOpenDate: Long = 0
private val adLoadCallback = object : InterstitialAdLoadCallback() {
override fun loadAd(context: Context) {
if (!loadingAd && LimitUtils.isAdShow(AdsType.INSERT)) {
loadingAd = true
//计数
LimitUtils.addRequestNum()
val reqId = UUID.randomUUID().toString()
val obj = JSONObject()
obj.put("req_id", reqId)
obj.put("ad_type", "interAd")
obj.put("from", context.javaClass.simpleName)
EventUtils.event("ad_pull_start", ext = obj)
InterstitialAd.load(
context,
ConstConfig.insertAdId,
AdRequest.Builder().build(),
object : InterstitialAdLoadCallback() {
override fun onAdLoaded(ad: InterstitialAd) {
super.onAdLoaded(ad)
AdmobEvent.pullAd(ad.responseInfo, "interAd", reqId = reqId)
ad.onPaidEventListener = AdmobEvent.EventOnPaidEventListener(ad)
// 开屏广告加载成功
currentAd = ad
loadingAd = false
......@@ -34,6 +56,12 @@ class AdInsertMgr : BaseAdMgr<InterstitialAd>() {
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
super.onAdFailedToLoad(loadAdError)
AdmobEvent.pullAd(
loadAdError.responseInfo,
"interAd",
loadAdError.message,
reqId = reqId
)
// 广告加载失败
// 官方不建议在此回调中重新加载广告
// 如果确实需要,则必须限制最大重试次数,避免在网络受限的情况下连续多次请求
......@@ -44,26 +72,20 @@ class AdInsertMgr : BaseAdMgr<InterstitialAd>() {
}
}
}
override fun loadAd(context: Context) {
if (!loadingAd && LimitUtils.isAdShow()) {
loadingAd = true
//计数
LimitUtils.addRequestNum()
InterstitialAd.load(
context,
ConstConfig.insertAdId,
AdRequest.Builder().build(),
adLoadCallback
)
}
}
override fun show(activity: Activity, showCallBack: AdsShowCallBack?) {
if (activity.isFinishing || activity.isDestroyed || !LimitUtils.isAdShow() || LimitUtils.isIntervalLimited(
lastOpenDate
)
) {
if (activity.isFinishing || activity.isDestroyed) {
showCallBack?.failed()
return
}
if (!LimitUtils.isAdShow(AdsType.INSERT)) {
showCallBack?.failed()
return
}
if (LimitUtils.isIntervalLimited(lastOpenDate)) {
showCallBack?.failed()
return
}
......@@ -73,7 +95,20 @@ class AdInsertMgr : BaseAdMgr<InterstitialAd>() {
}
if (showCallBack != null && this.showCallBack != showCallBack) this.showCallBack =
showCallBack
if (currentAd == null || !adAvailable()) {
if ((currentAd == null).also {
if (it) {
val obj = JSONObject()
obj.put("reason", "no_ad")
obj.put("ad_unit", "interAd")
EventUtils.event("ad_show_error", ext = obj)
}
} || (!adAvailable()).also {
if (it) {
val obj2 = JSONObject()
obj2.put("ad_unit", "interAd")
EventUtils.event("ad_expire", ext = obj2)
}
}) {
//缓存广告过期
currentAd = null
if (activityRef == null) {
......@@ -84,9 +119,16 @@ class AdInsertMgr : BaseAdMgr<InterstitialAd>() {
activityRef = null
this.showCallBack?.failed()
this.showCallBack = null
val obj = JSONObject()
obj.put("reason", "no_ad")
obj.put("ad_unit", "interAd")
EventUtils.event("ad_show_error", ext = obj)
}
return
}
val obj1 = JSONObject()
obj1.put("ad_unit", "interAd")
EventUtils.event("ad_prepare_show", ext = obj1)
currentAd?.run {
fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdShowedFullScreenContent() {
......@@ -96,7 +138,7 @@ class AdInsertMgr : BaseAdMgr<InterstitialAd>() {
currentAd = null
activityRef = null
this@AdInsertMgr.showCallBack?.show()
AdmobEvent.showAd(this@run.responseInfo, "interAd", activity)
//计数
LimitUtils.addDisplayNum()
}
......@@ -109,6 +151,11 @@ class AdInsertMgr : BaseAdMgr<InterstitialAd>() {
activityRef = null
this@AdInsertMgr.showCallBack?.googleFailed()
this@AdInsertMgr.showCallBack = null
val obj = JSONObject()
obj.put("reason", adError.message)
obj.put("code", adError.code)
obj.put("ad_unit", "interAd")
EventUtils.event("ad_show_error", ext = obj)
}
override fun onAdDismissedFullScreenContent() {
......@@ -123,6 +170,7 @@ class AdInsertMgr : BaseAdMgr<InterstitialAd>() {
override fun onAdClicked() {
super.onAdClicked()
AdmobEvent.clickAd(this@run.responseInfo, "interAd")
//计数
LimitUtils.addClickNum()
}
......
......@@ -3,14 +3,17 @@ package com.base.locationsharewhite.ads.admob
import android.content.Context
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.base.locationsharewhite.ads.AdsType
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.helper.config.ConstConfig
import com.example.mydemo.strategy.ads.admob.NativeView
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.NativeAd
import com.google.android.gms.ads.nativead.NativeAdOptions
import org.json.JSONObject
import java.util.UUID
import java.util.concurrent.ConcurrentLinkedDeque
/**
......@@ -29,6 +32,12 @@ class AdNativeMgr {
private val cacheItems = ConcurrentLinkedDeque<NativeAd>()
fun loadAd(context: Context, parent: ViewGroup? = null, layout: Int? = null) {
if (LimitUtils.isAdShow(AdsType.NATIVE)) return
val reqId = UUID.randomUUID().toString()
val obj = JSONObject()
obj.put("req_id", reqId)
obj.put("ad_type", "nativeAd")
val adLoader = AdLoader.Builder(
context,
ConstConfig.nativeAdId
......@@ -36,15 +45,19 @@ class AdNativeMgr {
.forNativeAd { nativeAd ->
cacheItems.offer(nativeAd)
lastTime = System.currentTimeMillis()
nativeAd.setOnPaidEventListener(AdmobEvent.EventOnPaidEventListener(nativeAd))
AdmobEvent.pullAd(nativeAd.responseInfo, "nativeAd", reqId = reqId)
if (parent != null && layout != null) show(parent, layout)
}
.withAdListener(object : AdListener() {
override fun onAdFailedToLoad(error: LoadAdError) {
AdmobEvent.pullAd(error.responseInfo, "nativeAd", error.message, reqId = reqId)
}
override fun onAdClicked() {
super.onAdClicked()
AdmobEvent.clickAd(cacheItems.lastOrNull()?.responseInfo, "nativeAd")
}
override fun onAdClosed() {
......@@ -63,22 +76,39 @@ class AdNativeMgr {
}
fun show(parent: ViewGroup, layout: Int) {
if (!LimitUtils.isAdShow()) {
if (!LimitUtils.isAdShow(AdsType.NATIVE)) {
cacheItems.clear()
return
}
val nativeAd = cacheItems.peek()
if (nativeAd == null || !adAvailable()) {
if ((nativeAd == null).also {
if (it) {
val obj2 = JSONObject()
obj2.put("reason", "no_ad")
obj2.put("ad_unit", "nativeAd")
EventUtils.event("ad_show_error", ext = obj2)
}
} || (!adAvailable()).also {
if (it) {
val obj2 = JSONObject()
obj2.put("ad_unit", "nativeAd")
EventUtils.event("ad_expire", ext = obj2)
}
}) {
//缓存过期了就清空
cacheItems.clear()
loadAd(parent.context.applicationContext, parent, layout)
return
}
val obj = JSONObject()
obj.put("ad_unit", "nativeAd")
EventUtils.event("ad_prepare_show_native", ext = obj)
NativeView(parent.context, layout).run {
parent.removeAllViews()
setNativeAd(nativeAd)
parent.addView(this)
parent.isVisible = true
AdmobEvent.showAd(nativeAd?.responseInfo, "nativeAd")
}
loadAd(parent.context.applicationContext)
}
......
......@@ -3,26 +3,41 @@ package com.base.locationsharewhite.ads.admob
import android.app.Activity
import android.content.Context
import com.base.locationsharewhite.ads.AdsShowCallBack
import com.base.locationsharewhite.ads.AdsType
import com.base.locationsharewhite.ads.BaseAdMgr
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.helper.config.ConstConfig
import com.base.locationsharewhite.utils.LogEx
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 org.json.JSONObject
import java.lang.ref.WeakReference
import java.util.UUID
/**
* 开屏广告加载显示管理类
*/
class AdOpenMgr : BaseAdMgr<AppOpenAd>() {
private val TAG = "AdOpenMgr"
private var lastOpenDate: Long = 0
private val adLoadCallback = object : AppOpenAd.AppOpenAdLoadCallback() {
override fun loadAd(context: Context) {
if (!loadingAd && LimitUtils.isAdShow(AdsType.OPEN)) {
loadingAd = true
//计数
LimitUtils.addRequestNum()
val reqId = UUID.randomUUID().toString()
val obj = JSONObject()
obj.put("req_id", reqId)
obj.put("ad_type", "openAd")
EventUtils.event("ad_pull_start", ext = obj)
AppOpenAd.load(
context,
ConstConfig.openAdId,
AdRequest.Builder().build(),
object : AppOpenAd.AppOpenAdLoadCallback() {
override fun onAdLoaded(appOpenAd: AppOpenAd) {
super.onAdLoaded(appOpenAd)
// 开屏广告加载成功
......@@ -33,6 +48,9 @@ class AdOpenMgr : BaseAdMgr<AppOpenAd>() {
if (ac != null) {
show(ac)
}
AdmobEvent.pullAd(appOpenAd.responseInfo, "openAd", reqId = reqId)
appOpenAd.onPaidEventListener =
AdmobEvent.EventOnPaidEventListener(appOpenAd)
}
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
......@@ -45,39 +63,53 @@ class AdOpenMgr : BaseAdMgr<AppOpenAd>() {
showCallBack?.googleFailed()
showCallBack = null
}
AdmobEvent.pullAd(
loadAdError.responseInfo,
"openAd",
loadAdError.message,
reqId = reqId
)
}
}
override fun loadAd(context: Context) {
if (!loadingAd && LimitUtils.isAdShow()) {
loadingAd = true
//计数
LimitUtils.addRequestNum()
AppOpenAd.load(
context,
ConstConfig.openAdId,
AdRequest.Builder().build(),
adLoadCallback
)
}
}
override fun show(activity: Activity, showCallBack: AdsShowCallBack?) {
if (activity.isFinishing || activity.isDestroyed || !LimitUtils.isAdShow() || LimitUtils.isIntervalLimited(
lastOpenDate
)
) {
LogEx.logDebug(TAG, "failed 1")
if (activity.isFinishing || activity.isDestroyed) {
showCallBack?.failed()
return
}
if (!LimitUtils.isAdShow(AdsType.OPEN)) {
showCallBack?.failed()
return
}
if (LimitUtils.isIntervalLimited(lastOpenDate)) {
showCallBack?.failed()
return
}
if (showingAd) {
// 开屏广告正在展示
return
}
if (showCallBack != null && this.showCallBack != showCallBack) this.showCallBack =
showCallBack
if (currentAd == null || !adAvailable()) {
if ((currentAd == null).also {
if (it) {
val obj = JSONObject()
obj.put("reason", "no_ad")
obj.put("ad_unit", "openAd")
EventUtils.event("ad_show_error", ext = obj)
}
} || (!adAvailable()).also {
if (it) {
val obj2 = JSONObject()
obj2.put("ad_unit", "openAd")
EventUtils.event("ad_expire", ext = obj2)
}
}) {
//缓存广告过期
currentAd = null
if (activityRef == null) {
......@@ -86,12 +118,18 @@ class AdOpenMgr : BaseAdMgr<AppOpenAd>() {
loadAd(activity.applicationContext)
} else {
activityRef = null
LogEx.logDebug(TAG, "failed 2")
this.showCallBack?.failed()
this.showCallBack = null
val obj = JSONObject()
obj.put("reason", "no_ad")
obj.put("ad_unit", "openAd")
EventUtils.event("ad_show_error", ext = obj)
}
return
}
val obj1 = JSONObject()
obj1.put("ad_unit", "openAd")
EventUtils.event("ad_prepare_show", ext = obj1)
currentAd?.run {
fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdShowedFullScreenContent() {
......@@ -101,9 +139,9 @@ class AdOpenMgr : BaseAdMgr<AppOpenAd>() {
currentAd = null
activityRef = null
this@AdOpenMgr.showCallBack?.show()
//计数
LimitUtils.addDisplayNum()
AdmobEvent.showAd(this@run.responseInfo, "openAd", activity)
}
override fun onAdFailedToShowFullScreenContent(adError: AdError) {
......@@ -114,6 +152,11 @@ class AdOpenMgr : BaseAdMgr<AppOpenAd>() {
activityRef = null
this@AdOpenMgr.showCallBack?.googleFailed()
this@AdOpenMgr.showCallBack = null
val obj = JSONObject()
obj.put("reason", adError.message)
obj.put("code", adError.code)
obj.put("ad_unit", "openAd")
EventUtils.event("ad_show_error", ext = obj)
}
override fun onAdDismissedFullScreenContent() {
......@@ -127,6 +170,7 @@ class AdOpenMgr : BaseAdMgr<AppOpenAd>() {
override fun onAdClicked() {
super.onAdClicked()
AdmobEvent.clickAd(this@run.responseInfo, "openAd")
//计数
LimitUtils.addClickNum()
}
......
package com.base.locationsharewhite.ads.admob
import android.app.Activity
import android.os.Bundle
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.helper.MyApplication
import com.base.locationsharewhite.utils.LogEx
import com.facebook.appevents.AppEventsConstants
import com.facebook.appevents.AppEventsLogger
import com.google.android.gms.ads.AdValue
import com.google.android.gms.ads.AdView
import com.google.android.gms.ads.OnPaidEventListener
import com.google.android.gms.ads.ResponseInfo
import com.google.android.gms.ads.appopen.AppOpenAd
import com.google.android.gms.ads.interstitial.InterstitialAd
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.rewarded.RewardedAd
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
import org.json.JSONObject
object AdmobEvent {
private val TAG = "AdmobEvent"
fun pullAd(
responseInfo: ResponseInfo?,
adUnit: String,
error: String? = null,
reqId: String? = null
) {
val obj = JSONObject()
if (responseInfo != null) {
val response = responseInfo.adapterResponses.getOrNull(0)
if (response != null) {
obj.put("source", response.adSourceName)
val credentials = mapOf(
"placementid" to response.credentials.get("placementid"),
"appid" to response.credentials.get("appid"),
"pubid" to response.credentials.get("pubid")
)
obj.put("credentials", credentials.toString())
}
obj.put("session_id", responseInfo.responseId)
}
obj.put("networkname", responseInfo?.mediationAdapterClassName)
obj.put("ad_unit", adUnit)
obj.put("req_id", reqId)
if (error == null) {
obj.put("status", "1")
} else {
obj.put("errMsg", error)
obj.put("status", "2")
}
LogEx.logDebug(TAG, "obj=$obj")
EventUtils.event("ad_pull", ext = obj)
}
private val taichiPref by lazy {
MyApplication.appContext.getSharedPreferences("TaichiTroasCache", 0)
}
private val taichiSharedPreferencesEditor by lazy {
taichiPref.edit()
}
class EventOnPaidEventListener(private val ad: Any?) : OnPaidEventListener {
override fun onPaidEvent(adValue: AdValue) {
val valueMicros = adValue.valueMicros
val currencyCode = adValue.currencyCode
val precision = adValue.precisionType
val obj = JSONObject()
obj.put("valueMicros", valueMicros)
obj.put("currencyCode", currencyCode)
obj.put("precision", precision)
Firebase.analytics.logEvent("ad_price", Bundle().apply {
putDouble("valueMicros", valueMicros / 1000000.0)
})
val params = Bundle()
val currentImpressionRevenue = adValue.valueMicros.toDouble() / 1000000.0
params.putDouble(FirebaseAnalytics.Param.VALUE, currentImpressionRevenue)
params.putString(FirebaseAnalytics.Param.CURRENCY, "USD")
LogEx.logDebug("EventOnPaidEventListener", "precisionType=${adValue.precisionType}")
val precisionType = when (adValue.precisionType) {
0 -> "UNKNOWN"
1 -> "ESTIMATED"
2 -> "PUBLISHER_PROVIDED"
3 -> "PRECISE"
else -> "Invalid"
}
params.putString("precisionType", precisionType)
Firebase.analytics.logEvent("Ad_Impression_Revenue", params)
val previousTaichiTroasCache = taichiPref.getFloat("TaichiTroasCache", 0f)
val currentTaichiTroasCache = (previousTaichiTroasCache +
currentImpressionRevenue).toFloat()
if (currentTaichiTroasCache >= 0.01) {//如果超过0.01就触发一次tROAS taichi事件
val roasbundle = Bundle()
roasbundle.putDouble(
FirebaseAnalytics.Param.VALUE,
currentTaichiTroasCache.toDouble()
)
roasbundle.putString(FirebaseAnalytics.Param.CURRENCY, "USD")
Firebase.analytics.logEvent("Total_Ads_Revenue_001", roasbundle)
taichiSharedPreferencesEditor.putFloat("TaichiTroasCache", 0f)//重新清零,开始计算
val logger = AppEventsLogger.newLogger(MyApplication.appContext)
val parameters = Bundle()
parameters.putString(AppEventsConstants.EVENT_PARAM_CURRENCY, "USD")
logger.logEvent("ad_value", currentTaichiTroasCache.toDouble(), parameters)
} else {
taichiSharedPreferencesEditor.putFloat("TaichiTroasCache", currentTaichiTroasCache)
}
taichiSharedPreferencesEditor.commit()
var key = "ad_price"
when (ad) {
is AppOpenAd -> {
val adUnitId = ad.adUnitId
val loadedAdapterResponseInfo = ad.responseInfo.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo.responseId
val extras = ad.responseInfo.responseExtras
val mediationGroupName = extras.getString("mediation_group_name")
val mediationABTestName = extras.getString("mediation_ab_test_name")
val mediationABTestVariant = extras.getString("mediation_ab_test_variant")
obj.put("ad_unit", "openAd")
obj.put("adUnitId", adUnitId)
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
is InterstitialAd -> {
val adUnitId = ad.adUnitId
val loadedAdapterResponseInfo = ad.responseInfo.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo.responseId
val extras = ad.responseInfo.responseExtras
val mediationGroupName = extras.getString("mediation_group_name")
val mediationABTestName = extras.getString("mediation_ab_test_name")
val mediationABTestVariant = extras.getString("mediation_ab_test_variant")
obj.put("ad_unit", "interAd")
obj.put("adUnitId", adUnitId)
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
is RewardedAd -> {
val loadedAdapterResponseInfo = ad.responseInfo.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo.responseId
val extras = ad.responseInfo.responseExtras
val mediationGroupName = extras.getString("mediation_group_name")
val mediationABTestName = extras.getString("mediation_ab_test_name")
val mediationABTestVariant = extras.getString("mediation_ab_test_variant")
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
is NativeAd -> {
key = "ad_price"
val loadedAdapterResponseInfo = ad.responseInfo?.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = ad.responseInfo?.responseId
val extras = ad.responseInfo?.responseExtras
val mediationGroupName = extras?.getString("mediation_group_name")
val mediationABTestName = extras?.getString("mediation_ab_test_name")
val mediationABTestVariant = extras?.getString("mediation_ab_test_variant")
obj.put("adUnitId", "nativeAd")
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
else -> {
runCatching {
val adView = ad as AdView
val adUnitId = adView.adUnitId
val loadedAdapterResponseInfo = adView.responseInfo?.loadedAdapterResponseInfo
val adSourceName = loadedAdapterResponseInfo?.adSourceName
val adSourceId = loadedAdapterResponseInfo?.adSourceId
val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName
val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId
val sessionId = adView.responseInfo?.responseId
val extras = adView.responseInfo?.responseExtras
val mediationGroupName = extras?.getString("mediation_group_name")
val mediationABTestName = extras?.getString("mediation_ab_test_name")
val mediationABTestVariant = extras?.getString("mediation_ab_test_variant")
obj.put("ad_unit", "banner")
obj.put("adUnitId", adUnitId)
obj.put("adSourceName", adSourceName)
obj.put("adSourceId", adSourceId)
obj.put("adSourceInstanceName", adSourceInstanceName)
obj.put("adSourceInstanceId", adSourceInstanceId)
obj.put("mediationGroupName", mediationGroupName)
obj.put("mediationABTestName", mediationABTestName)
obj.put("mediationABTestVariant", mediationABTestVariant)
obj.put("session_id", sessionId)
}
}
}
EventUtils.event(key, ext = obj)
}
}
fun clickAd(responseInfo: ResponseInfo?, adUnit: String) {
val response = responseInfo?.adapterResponses?.getOrNull(0)
val obj = JSONObject()
obj.put("source", response?.adSourceName)
obj.put("ad_unit", adUnit)
val credentials = mapOf(
"placementid" to response?.credentials?.get("placementid"),
"appid" to response?.credentials?.get("appid"),
"pubid" to response?.credentials?.get("pubid")
)
obj.put("credentials", credentials.toString())
obj.put("session_id", responseInfo?.responseId)
obj.put("networkname", responseInfo?.mediationAdapterClassName)
if (adUnit != "nativeAd") {
EventUtils.event("ad_click", ext = obj)
} else {
EventUtils.event("bigimage_ad_click", ext = obj)
}
}
fun showAd(responseInfo: ResponseInfo?, adUnit: String, activity: Activity? = null) {
val response = responseInfo?.adapterResponses?.getOrNull(0)
val obj = JSONObject()
obj.put("source", response?.adSourceName)
obj.put("ad_unit", adUnit)
obj.put("networkname", responseInfo?.mediationAdapterClassName)
val credentials = mapOf(
"placementid" to response?.credentials?.get("placementid"),
"appid" to response?.credentials?.get("appid"),
"pubid" to response?.credentials?.get("pubid")
)
obj.put("credentials", credentials.toString())
obj.put("session_id", responseInfo?.responseId)
obj.put("from", activity?.javaClass?.simpleName)
if (adUnit != "nativeAd") {
EventUtils.event("ad_show", ext = obj)
} else {
EventUtils.event("ad_show", ext = obj)
}
}
}
\ No newline at end of file
package com.base.locationsharewhite.ads.admob
import com.base.locationsharewhite.ads.AdsMgr
import com.base.locationsharewhite.ads.AdsType
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.KotlinExt.toFormatTime4
/**
* 控制广告计数与判断显示条件
*
......@@ -56,12 +59,22 @@ object LimitUtils {
.getInt(NUM_CLICK, 0) >= maxCount
}
private fun getAdEventMsg(adsType: AdsType): String {
return when (adsType) {
AdsType.OPEN -> "Open"
AdsType.INSERT -> "Inter"
AdsType.NATIVE -> "Native"
else -> "Banner"
}
}
/**
* 是否显示广告
*
* @return true or false
*/
fun isAdShow(): Boolean {
fun isAdShow(adsType: AdsType): Boolean {
val currentDate = System.currentTimeMillis().toFormatTime4()
if (saveDate != currentDate) {
//如果已经不是今天了,就重置个数
......@@ -70,7 +83,31 @@ object LimitUtils {
AppPreferences.getInstance().put(NUM_REQUEST, 0)
AppPreferences.getInstance().put(NUM_CLICK, 0)
}
return !(isDisplayLimited || isClickLimited || isRequestLimited)
return !(isDisplayLimited.also {
if (it) EventUtils.event(
"ad_limit",
"current${getAdEventMsg(adsType)}Show=${
AppPreferences.getInstance()
.getInt(NUM_DISPLAY, 0)
} ${getAdEventMsg(adsType).lowercase()}_max_show=${AdsMgr.adsConfigBean?.numDisplayLimit}"
)
} || isClickLimited.also {
if (it) EventUtils.event(
"ad_limit",
"current${getAdEventMsg(adsType)}Click=${
AppPreferences.getInstance()
.getInt(NUM_CLICK, 0)
} ${getAdEventMsg(adsType).lowercase()}_max_click=${AdsMgr.adsConfigBean?.numClickLimit}"
)
} || isRequestLimited.also {
if (it) EventUtils.event(
"ad_limit",
"current${getAdEventMsg(adsType)}Request=${
AppPreferences.getInstance()
.getInt(NUM_REQUEST, 0)
} ${getAdEventMsg(adsType).lowercase()}_max_request=${AdsMgr.adsConfigBean?.numRequestLimit}"
)
})
}
......
package com.example.mydemo.strategy.ads.admob
package com.base.locationsharewhite.ads.admob
import android.annotation.SuppressLint
import android.content.Context
......
package com.base.locationsharewhite.fcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.KotlinExt.toFormatTime4
/**
*电量监听
*/
class BatteryStatusReceiver(private val configBean: MsgConfigBean) : BroadcastReceiver() {
private var count
get() = AppPreferences.getInstance().getInt("battery_count", 0)
set(value) = AppPreferences.getInstance().putInt("battery_count", value)
private var date
get() = AppPreferences.getInstance().getLong("battery_date", 0)
set(value) = AppPreferences.getInstance().putLong("battery_date", value)
companion object {
fun registerBatteryReceiver(context: Context, configBean: MsgConfigBean) {
val intentFilter = IntentFilter().apply {
addAction(Intent.ACTION_BATTERY_CHANGED)
}
val applicationContext = context.applicationContext
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
applicationContext.registerReceiver(
BatteryStatusReceiver(configBean),
intentFilter,
Context.RECEIVER_EXPORTED
)
} else {
applicationContext.registerReceiver(BatteryStatusReceiver(configBean), intentFilter)
}
}
}
override fun onReceive(context: Context, intent: Intent?) {
val action = intent?.action
if (action == Intent.ACTION_BATTERY_CHANGED) {
val batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val batteryScale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
val batteryPercentage = (batteryLevel / batteryScale.toFloat()) * 100
if (batteryPercentage < 21) {
//当电量小于21%就会有推送
val currentMillis = System.currentTimeMillis()
val currentDate = currentMillis.toFormatTime4()
if (currentDate != MsgMgr.saveDate) {
//不是当天就重置
MsgMgr.saveDate = currentDate
count = 0
}
if ((configBean.value2 < 0 || count < configBean.value2) && ((currentMillis - date) / 1000 / 60).toInt() > configBean.value1) {
//推送次数没有达到限制并且展示的最小时间间隔大于配置时间(分钟)
count += 1
date = System.currentTimeMillis()
MsgMgr.sendNotification(context)
}
}
}
}
}
\ No newline at end of file
package com.base.locationsharewhite.fcm;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApp;
import com.google.firebase.messaging.FirebaseMessaging;
public class FCMManager {
public static void initFirebase(Context context) {
FirebaseApp.initializeApp(context);
}
public static void subscribeToTopic(String topic) {
FirebaseMessaging.getInstance().subscribeToTopic(topic)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d("FCMUtil", "suc:" + topic);
// EventUtils.INSTANCE.event("FCM_Topic_" + topic, null, null, false);
} else {
Log.d("FCMUtil", "fail");
}
}
});
}
public static void unsubscribeFromTopic(String topic) {
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
} else {
}
}
});
}
}
package com.base.locationsharewhite.fcm;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.base.locationsharewhite.utils.LogEx;
public class FcmReceiver extends BroadcastReceiver {
private String TAG = "FcmReceiver";
@Override
public void onReceive(Context context, Intent intent) {
LogEx.INSTANCE.logDebug(TAG, "onReceive", false);
}
}
package com.base.locationsharewhite.fcm;
import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
@SuppressLint("MissingFirebaseInstanceTokenRefresh")
public class MessagingService extends FirebaseMessagingService {
private static final String TAG = "MessagingService";
@Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
}
}
\ No newline at end of file
package com.base.locationsharewhite.fcm
import android.util.SparseArray
import androidx.core.util.isNotEmpty
import com.base.locationsharewhite.notification.NotificationBean
/**
* 消息触发配置与消息通知栏配置项目
*
* @property timer 定时器场景触发配置
* @property battery 电量场景触发配置
* @property unlock 解锁场景触发配置
* @property packageChange app安装与卸载触发场景
* @property hover 通知栏悬停配置
* @property remoteViewContainer 自定义通知栏注册容器
*/
data class MsgConfig(
var timer: MsgConfigBean, var battery: MsgConfigBean,
var unlock: MsgConfigBean, var packageChange: MsgConfigBean,
var hover: MsgConfigBean, val remoteViewContainer: SparseArray<NotificationBean>?
) {
class Builder {
private var timer: MsgConfigBean? = null
private var battery: MsgConfigBean? = null
private var unlock: MsgConfigBean? = null
private var packageChange: MsgConfigBean? = null
private var hover: MsgConfigBean? = null
private val remoteViewContainer = SparseArray<NotificationBean>()
fun setConfig(configBean: MsgConfigBean, msgType: MsgType): Builder {
when (msgType) {
MsgType.TIMER -> timer = configBean
MsgType.BATTERY -> battery = configBean
MsgType.UNLOCK -> unlock = configBean
MsgType.PACKAGE -> packageChange = configBean
}
return this
}
/**
* 设置悬停通知配置
*
* @param configBean
* @return
*/
fun setHover(configBean: MsgConfigBean): Builder {
hover = configBean
return this
}
fun addRemoteBean(actionId: Int, bean: NotificationBean): Builder {
remoteViewContainer.put(actionId, bean)
return this
}
fun build(): MsgConfig {
val config = MsgConfig(
//默认情况下,1.timer默认首次3分钟,之后7分钟。
timer ?: MsgConfigBean(3, 7),
//2.解锁场景,默认间隔1分钟,默认无次数限制。
battery ?: MsgConfigBean(60),
//3.电量场景,默认间隔1小时,默认无次数限制。
unlock ?: MsgConfigBean(1),
//4.安装与卸载场景,默认间隔5分钟,默认无次数限制。
packageChange ?: MsgConfigBean(5),
//设置悬停通知配置,默认开启,0代表关闭,1代表开启,持续时间默认为5s
hover ?: MsgConfigBean(1, 5),
if (remoteViewContainer.isNotEmpty()) remoteViewContainer else null
)
return config
}
}
}
\ No newline at end of file
package com.base.locationsharewhite.fcm
/**
* 消息推送显示策略
*
* @param value1 第一个控制参数项目(一般是代表时间间隔限制,如果是悬停通知配置,这个0代表关闭,1代表开启)
* @param value2 第二个控制参数项目(一般是配置的次数限制,-1代表无限制,0代表彻底关闭,如果悬停配置,这个代表悬停时间)
*/
data class MsgConfigBean(val value1: Int, val value2: Int = -1)
\ No newline at end of file
package com.base.locationsharewhite.fcm
import android.app.NotificationManager
import android.content.Context
import android.util.SparseArray
import androidx.core.util.isEmpty
import androidx.core.util.size
import com.base.locationsharewhite.notification.NotificationBean
import com.base.locationsharewhite.notification.NotificationHoverUtils
import com.base.locationsharewhite.notification.NotificationMgr
import com.base.locationsharewhite.service.StayNotificationService.Companion.startStayNotification
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.KotlinExt.toFormatMinute
import com.base.locationsharewhite.utils.KotlinExt.toFormatTime4
/**
* 推送消息通知栏相关管理类
*/
object MsgMgr {
const val SAVE_DATE = "SAVE_DATE"
/**
* 保存的时间,用来判断是否是当天,不是当天要重置计数次数,这里的和admob的广告当天记录时间用的一样的哦
*/
var saveDate
get() = AppPreferences.getInstance()
.getString(SAVE_DATE, System.currentTimeMillis().toFormatTime4())
set(value) = AppPreferences.getInstance().put(SAVE_DATE, value)
/**
* FCM主题订阅 Topic 包名+首次启动的当前的分钟
*/
private var topic
get() = AppPreferences.getInstance().getString("topic", "")
set(value) = AppPreferences.getInstance().put("topic", value)
var hoverActionId = -1
private set
private var actionKeyIndex = 0
private lateinit var hoverConfig: MsgConfigBean
var remoteViewContainer: SparseArray<NotificationBean>? = null
fun init(context: Context, packageName: String, msgConfig: MsgConfig) {
hoverConfig = msgConfig.hover
remoteViewContainer = msgConfig.remoteViewContainer
//初始化SDK
FCMManager.initFirebase(context)
//注册fcm主题订阅
var topic = topic
if (topic.isEmpty()) {
val topicNumber = System.currentTimeMillis().toFormatMinute()
topic = packageName + "_$topicNumber"
MsgMgr.topic = topic
}
FCMManager.subscribeToTopic(topic)
//判断是否配置自定义通知栏事件
if (remoteViewContainer?.isEmpty() != false) return
//注册几个状态广播监听
ScreenStatusReceiver.registerScreenReceiver(context, msgConfig.unlock)
PackageStatusReceiver.registerBatteryReceiver(context, msgConfig.packageChange)
BatteryStatusReceiver.registerBatteryReceiver(context, msgConfig.battery)
//根据配置设置定时推送
if (!TimerManager.isTaskTimerActive) {
TimerManager
.scheduleTask(
(msgConfig.timer.value1 * 60 * 1000).toLong(),
(msgConfig.timer.value2 * 60 * 1000).toLong()
)
}
}
/**
* 发送通知
*
* @param context
* @param actionId 通知栏的事件id,这个是自己定的,每个应用可能不一样
* @param hoverOpen 悬停通知是否关闭,0=关闭,1=开启
*/
fun sendNotification(
context: Context,
actionId: Int = getNextActionId(),
hoverOpen: Int = if (this::hoverConfig.isInitialized) hoverConfig.value1 else 0
) {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (!notificationManager.areNotificationsEnabled()) return
//先判断是否有自定义通知栏装入
if (remoteViewContainer?.isEmpty() != false) return
val bean = remoteViewContainer?.get(actionId) ?: return
NotificationMgr.sendNotification(
context,
bean.intent,
bean.bigRemoteViews,
bean.smallRemoteViews
)
if (hoverOpen == 1) {
if (hoverActionId != -1) {
//如果当前悬浮有事件,就重置整个悬浮逻辑
stopHoverNotification()
}
//记录当前悬停的通知事件id
hoverActionId = actionId
//悬停通知开启了
//计算次数,因为系统默认是5s,这里计算多久再次发送通知
val count = hoverConfig.value2 / 5
//如果悬浮通知配置小于了系统的默认值那就没必要在发送延迟了
if (count <= 1) return
//发送延迟通知
NotificationHoverUtils.sendHoverNotification(context, count - 1, 5 - 1)
}
}
/**
* 开启常驻通知栏
*/
fun startPermanentNotification(context: Context) {
context.startStayNotification()
}
/**
* 停止悬停通知的延迟队列
*/
fun stopHoverNotification() {
hoverActionId = -1
NotificationHoverUtils.stopNotificationHandler()
}
/**
*循环输出通知栏不同的事件
* @return 当前循环输出的actionId
*/
private fun getNextActionId(): Int {
val size = remoteViewContainer?.size ?: 0
val actionId = remoteViewContainer?.keyAt(actionKeyIndex) ?: 0
if (size > actionKeyIndex + 1) {
actionKeyIndex++
} else actionKeyIndex = 0
return actionId
}
}
\ No newline at end of file
package com.base.locationsharewhite.fcm
/**
* 推送类型
* 0=定时、1=fcm、2=解锁、3=电量、4=安装与卸载
*/
@JvmInline
value class MsgType private constructor(val value: Int) {
companion object {
val TIMER = MsgType(0)
val FCM = MsgType(1)
val UNLOCK = MsgType(2)
val BATTERY = MsgType(3)
val PACKAGE = MsgType(4)
fun from(adsType: Int): MsgType {
return when (adsType) {
TIMER.value -> TIMER
FCM.value -> FCM
UNLOCK.value -> UNLOCK
BATTERY.value -> BATTERY
else -> PACKAGE
}
}
}
}
\ No newline at end of file
package com.base.locationsharewhite.fcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.KotlinExt.toFormatTime4
class PackageStatusReceiver(private val configBean: MsgConfigBean) : BroadcastReceiver() {
private var count
get() = AppPreferences.getInstance().getInt("package_count", 0)
set(value) = AppPreferences.getInstance().putInt("package_count", value)
private var date
get() = AppPreferences.getInstance().getLong("package_date", 0)
set(value) = AppPreferences.getInstance().putLong("package_date", value)
companion object {
fun registerBatteryReceiver(context: Context, configBean: MsgConfigBean) {
val intentFilter = IntentFilter().apply {
addAction(Intent.ACTION_PACKAGE_ADDED)
addAction(Intent.ACTION_PACKAGE_REMOVED)
addDataScheme("package")
}
val applicationContext = context.applicationContext
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
applicationContext.registerReceiver(
PackageStatusReceiver(configBean),
intentFilter,
Context.RECEIVER_EXPORTED
)
} else {
applicationContext.registerReceiver(PackageStatusReceiver(configBean), intentFilter)
}
}
}
override fun onReceive(context: Context, intent: Intent?) {
val action = intent?.action
if (action == Intent.ACTION_PACKAGE_ADDED || action == Intent.ACTION_PACKAGE_REMOVED) {
val currentMillis = System.currentTimeMillis()
val currentDate = currentMillis.toFormatTime4()
if (currentDate != MsgMgr.saveDate) {
//不是当天就重置
MsgMgr.saveDate = currentDate
count = 0
}
if ((configBean.value2 < 0 || count < configBean.value2) && ((currentMillis - date) / 1000 / 60).toInt() > configBean.value1) {
//推送次数没有达到限制并且展示的最小时间间隔大于配置时间(分钟)
count += 1
date = System.currentTimeMillis()
MsgMgr.sendNotification(context)
}
}
}
}
\ No newline at end of file
package com.base.locationsharewhite.fcm
/**
* 云控返回的json数据格式
*
* @property battery
* @property hover
* @property pkChange
* @property timer
* @property unlock
*/
data class RemoteConfigBean(
val battery: MsgConfigBean?,
val hover: MsgConfigBean?,
val pkChange: MsgConfigBean?,
val timer: MsgConfigBean?,
val unlock: MsgConfigBean?
)
\ No newline at end of file
package com.base.locationsharewhite.fcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.KotlinExt.toFormatTime4
class ScreenStatusReceiver(private val configBean: MsgConfigBean) : BroadcastReceiver() {
private var count
get() = AppPreferences.getInstance().getInt("screen_count", 0)
set(value) = AppPreferences.getInstance().putInt("screen_count", value)
private var date
get() = AppPreferences.getInstance().getLong("screen_date", 0)
set(value) = AppPreferences.getInstance().putLong("screen_date", value)
companion object {
var isDeviceInteractive = true
private set
var isSecureLockActive = false
private set
fun registerScreenReceiver(context: Context, configBean: MsgConfigBean) {
val intentFilter = IntentFilter().apply {
addAction(Intent.ACTION_SCREEN_OFF)
addAction(Intent.ACTION_SCREEN_ON)
addAction(Intent.ACTION_USER_PRESENT)
}
val applicationContext = context.applicationContext
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
applicationContext.registerReceiver(
ScreenStatusReceiver(configBean),
intentFilter,
Context.RECEIVER_EXPORTED
)
} else {
applicationContext.registerReceiver(ScreenStatusReceiver(configBean), intentFilter)
}
}
}
override fun onReceive(context: Context, intent: Intent?) {
val action = intent?.action
when (action) {
Intent.ACTION_SCREEN_ON -> {
isDeviceInteractive = true
}
Intent.ACTION_SCREEN_OFF -> {
isDeviceInteractive = false
isSecureLockActive = true
}
Intent.ACTION_USER_PRESENT -> {
isSecureLockActive = false
if (isDeviceInteractive) {
val currentMillis = System.currentTimeMillis()
val currentDate = currentMillis.toFormatTime4()
if (currentDate != MsgMgr.saveDate) {
//不是当天就重置
MsgMgr.saveDate = currentDate
count = 0
}
if ((configBean.value2 < 0 || count < configBean.value2) && ((currentMillis - date) / 1000 / 60).toInt() > configBean.value1) {
//推送次数没有达到限制并且展示的最小时间间隔大于配置时间(分钟)
count += 1
date = System.currentTimeMillis()
MsgMgr.sendNotification(context)
}
}
}
}
}
}
\ No newline at end of file
package com.base.locationsharewhite.fcm
import com.base.locationsharewhite.helper.GoogleSdkMgr
import java.util.Timer
import java.util.TimerTask
object TimerManager {
private var taskTimer: Timer? = null
var isTaskTimerActive: Boolean = false
private set
fun scheduleTask(delay: Long, period: Long) {
synchronized(TimerManager::class.java) {
ensureTimerIsStopped() // 确保定时器未运行
taskTimer = Timer() // 创建新的 Timer 实例
val task: TimerTask = object : TimerTask() {
override fun run() {
//Log.d("glc", "Scheduled task is running")
if (!ScreenStatusReceiver.isSecureLockActive && ScreenStatusReceiver.isDeviceInteractive) {
// 确保设备处于交互状态,未锁定,且应用未暂停
MsgMgr.sendNotification(GoogleSdkMgr.context)
}
}
}
taskTimer?.schedule(task, delay, period) // 调度任务
isTaskTimerActive = true // 设置定时器状态为活跃
}
}
private fun ensureTimerIsStopped() {
if (isTaskTimerActive) {
if (taskTimer != null) {
taskTimer?.cancel()
taskTimer?.purge() // 清除所有取消的任务
}
isTaskTimerActive = false // 重置定时器状态
}
}
fun stopTaskTimer() {
synchronized(TimerManager::class.java) {
ensureTimerIsStopped() // 停止定时器
}
}
}
\ No newline at end of file
package com.base.locationsharewhite.helper
import android.annotation.SuppressLint
import android.content.Context
import com.base.locationsharewhite.ads.AdsConfigBean
import com.base.locationsharewhite.ads.AdsMgr
import com.base.locationsharewhite.fcm.MsgConfig
import com.base.locationsharewhite.fcm.MsgConfigBean
import com.base.locationsharewhite.fcm.MsgMgr
import com.base.locationsharewhite.fcm.MsgType
import com.base.locationsharewhite.fcm.RemoteConfigBean
import com.base.locationsharewhite.helper.config.ConstConfig
import com.base.locationsharewhite.notification.NotificationMgr
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.gson.Gson
/**
* admob广告与fireBase与FCM的初始化等管理类
*/
@SuppressLint("StaticFieldLeak")
object GoogleSdkMgr {
lateinit var context: Context
fun init(
context: Context, packageName: String,
msgConfig: MsgConfig = MsgConfig.Builder()
//这里只是展示默认值并没有配置自定义通知栏视图即addRemoteBean方法,默认情况下,1.timer默认首次3分钟,之后7分钟。
.setConfig(MsgConfigBean(3, 7), MsgType.TIMER)
//2.解锁场景,默认间隔1分钟,默认无次数限制。
.setConfig(MsgConfigBean(1), MsgType.UNLOCK)
//3.电量场景,默认间隔1小时,默认无次数限制。
.setConfig(MsgConfigBean(60), MsgType.BATTERY)
//4.安装与卸载场景,默认间隔5分钟,默认无次数限制。
.setConfig(MsgConfigBean(5), MsgType.PACKAGE)
//设置悬停通知配置,默认开启,0代表关闭,1代表开启,持续时间默认为5s
.setHover(MsgConfigBean(1, 5))
.build(),
//默认情况下,广告展示,点击和请求都没有次数限制,间隔时间为1分钟。
adsConfigBean: AdsConfigBean = AdsConfigBean()
) {
GoogleSdkMgr.context = context.applicationContext
//初始化注册通知栏通知渠道等
NotificationMgr.init(context)
//先注册自定义通知栏,这个不需要等云控数据回调
MsgMgr.remoteViewContainer = msgConfig.remoteViewContainer
val remoteConfig = FirebaseRemoteConfig.getInstance()
remoteConfig.fetchAndActivate().addOnCompleteListener { task ->
if (task.isSuccessful) {
//解析云控消息配置
val config = remoteConfig.getString(ConstConfig.REMOTE_MSG)
//Log.d(this::class.java.simpleName, config)
val bean: RemoteConfigBean? = Gson().fromJson(config, RemoteConfigBean::class.java)
// Log.d(this::class.java.simpleName, bean.toString())
bean?.apply {
hover?.let {
msgConfig.hover = it
}
unlock?.let {
msgConfig.unlock = it
}
battery?.let {
msgConfig.battery = it
}
pkChange?.let {
msgConfig.packageChange = it
}
timer?.let {
msgConfig.timer = it
}
}
//解析云控广告配置
val adConfig = remoteConfig.getString(ConstConfig.REMOTE_AD)
//Log.d(this::class.java.simpleName, adConfig)
val adBean: AdsConfigBean? = Gson().fromJson(adConfig, AdsConfigBean::class.java)
//Log.d(this::class.java.simpleName, adBean.toString())
adBean?.apply {
adsConfigBean.isInBlackList = isInBlackList
adsConfigBean.numDisplayLimit = numDisplayLimit
adsConfigBean.numRequestLimit = numRequestLimit
adsConfigBean.numClickLimit = numClickLimit
adsConfigBean.timeInterval = timeInterval
}
}
//Log.d(this::class.java.simpleName, msgConfig.toString())
//Log.d(this::class.java.simpleName, adsConfigBean.toString())
//初始化通知栏推送相关业务
MsgMgr.init(context, packageName, msgConfig)
//初始化广告相关业务
AdsMgr.init(context, adsConfigBean)
}
}
}
\ No newline at end of file
......@@ -16,7 +16,7 @@ object InstallHelps {
fun init() {
val referrerClient = InstallReferrerClient.newBuilder(MyApplication.context).build()
val referrerClient = InstallReferrerClient.newBuilder(MyApplication.appContext).build()
referrerClient.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
try {
......
......@@ -5,13 +5,10 @@ import android.app.Application
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import android.widget.RemoteViews
import com.base.locationsharewhite.R
import com.base.locationsharewhite.ads.AdsConfigBean
import com.base.locationsharewhite.ads.AdsMgr
import com.base.locationsharewhite.bean.ConstObject.topic_number
import com.base.locationsharewhite.fcm.MsgConfig
import com.base.locationsharewhite.helper.config.AppConfig
import com.base.locationsharewhite.helper.config.ConstConfig
import com.base.locationsharewhite.notification.NotificationBean
import com.base.locationsharewhite.ui.splash.SplashActivity
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.KotlinExt.toFormatMinute
......@@ -25,8 +22,8 @@ class MyApplication : Application() {
var uuid = ""
companion object {
lateinit var context: MyApplication
lateinit var appContext: MyApplication
var splashLanguage: String = Locale.getDefault().language + "_" + Locale.getDefault().country
var mainLanguage: String = Locale.getDefault().language + "_" + Locale.getDefault().country
......@@ -45,7 +42,7 @@ class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
context = this
appContext = this
initUUid()
initApp()
}
......@@ -148,50 +145,10 @@ class MyApplication : Application() {
})
}
fun initAdSdk() {
GoogleSdkMgr.init(
this, this.packageName, MsgConfig.Builder()
//设置自己的自定义通知栏
.addRemoteBean(
//自定义的通知事件id,可以自己添加
ConstConfig.ACTION_SHARE_LOCATION, NotificationBean(
Intent(this, SplashActivity::class.java).apply {
//悬停点击标记,用来进入当相关页面进行悬停关闭
putExtra("hover", true)
putExtra("actionId", ConstConfig.ACTION_SHARE_LOCATION)
},
RemoteViews(packageName, R.layout.notification_share_location).apply {
},
RemoteViews(packageName, R.layout.notification_share_location).apply {
}
)
)
.addRemoteBean(
ConstConfig.ACTION_ENABLE_LOCATION, NotificationBean(
Intent(this, SplashActivity::class.java).apply {
putExtra("hover", true)
putExtra("actionId", ConstConfig.ACTION_ENABLE_LOCATION)
},
RemoteViews(packageName, R.layout.notification_enable_location).apply {
},
RemoteViews(packageName, R.layout.notification_enable_location).apply {
}
)
)
.addRemoteBean(
ConstConfig.ACTION_COPY_CODE, NotificationBean(
Intent(this, SplashActivity::class.java).apply {
putExtra("hover", true)
putExtra("actionId", ConstConfig.ACTION_COPY_CODE)
},
RemoteViews(packageName, R.layout.notification_copy_code).apply {
},
RemoteViews(packageName, R.layout.notification_copy_code).apply {
}
)
)
.build()
)
private fun initAdSdk() {
val adsConfigBean = AdsConfigBean.getSpConfigBean()
//初始化广告相关业务
AdsMgr.init(appContext, adsConfigBean)
}
}
\ No newline at end of file
......@@ -18,7 +18,6 @@ import androidx.core.graphics.drawable.IconCompat
import com.base.locationsharewhite.R
import com.base.locationsharewhite.bean.ConstObject
import com.base.locationsharewhite.helper.config.ConstConfig
import com.base.locationsharewhite.helper.GoogleSdkMgr
import com.base.locationsharewhite.ui.main.MainActivity
import com.base.locationsharewhite.ui.splash.SplashActivity
import kotlin.random.Random
......
......@@ -22,7 +22,7 @@ public class AppPreferences {
public static synchronized AppPreferences getInstance() {
if (sInstance == null) {
sInstance = new AppPreferences(MyApplication.context.getApplicationContext());
sInstance = new AppPreferences(MyApplication.appContext.getApplicationContext());
}
return sInstance;
}
......
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