Commit 8b1c7c11 authored by wanglei's avatar wanglei

...

parent 7ed2238e
......@@ -78,6 +78,7 @@ dependencies {
implementation("com.google.firebase:firebase-messaging")
implementation("com.google.firebase:firebase-analytics-ktx")
implementation("com.google.firebase:firebase-crashlytics")
implementation("com.google.firebase:firebase-config")
//facebook
implementation("com.facebook.android:facebook-android-sdk:[8,9)")
......
package com.base.locationsharewhite.ads
import android.animation.ObjectAnimator
import android.animation.ValueAnimator.INFINITE
import android.app.AlertDialog
import android.content.Context
import android.view.LayoutInflater
import android.view.animation.LinearInterpolator
import com.base.locationsharewhite.R
import com.base.locationsharewhite.databinding.DialogAdPreparingBinding
object AdDialog {
fun Context.showAdPreparingDialog(): AlertDialog {
val binding = DialogAdPreparingBinding.inflate(LayoutInflater.from(this))
val dialog = AlertDialog.Builder(this).create()
dialog.setView(binding.root)
dialog.setCancelable(false)
dialog.setCanceledOnTouchOutside(false)
dialog.show()
val params = dialog.window?.attributes
params?.width = resources.getDimensionPixelOffset(R.dimen.dp_200)
params?.height = resources.getDimensionPixelOffset(R.dimen.dp_146)
dialog.window?.attributes = params
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
// 创建一个旋转动画
val rotateAnimator = ObjectAnimator.ofFloat(binding.iv, "rotation", 0f, -360f)
rotateAnimator.setDuration(1000) // 设置动画持续时间为1000毫秒
rotateAnimator.repeatCount = INFINITE
rotateAnimator.interpolator = LinearInterpolator() // 设置插值器为线性插值
rotateAnimator.start()
return dialog
}
}
\ No newline at end of file
package com.base.pdfreader2.ads
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.LogEx
import com.base.pdfreader2.ads.AdmobHelper.inter_limit_click
import com.base.pdfreader2.ads.AdmobHelper.inter_limit_request
import com.base.pdfreader2.ads.AdmobHelper.inter_limit_show
import com.base.pdfreader2.ads.AdmobHelper.native_limit_request
import com.base.pdfreader2.ads.AdmobHelper.native_limit_show
import com.base.pdfreader2.ads.AdmobHelper.open_limit_click
import com.base.pdfreader2.ads.AdmobHelper.open_limit_request
import com.base.pdfreader2.ads.AdmobHelper.open_limit_show
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
object AdDisplayUtils {
//region open
private val open_max_request = AppPreferences.getInstance().getString(open_limit_request, "15").toInt()
private val open_max_show = AppPreferences.getInstance().getString(open_limit_show, "10").toInt()
private val open_max_click = AppPreferences.getInstance().getString(open_limit_click, "1").toInt()
fun incrementOpenRequestCount() {
currentOpenRequest += 1
}
fun incrementOpenShow() {
currentOpenShow += 1
}
fun incrementClickShow() {
currentOpenClick += 1
}
//当前开屏请求次数
private var currentOpenRequest = 0
get() {
return AppPreferences.getInstance().getInt("currentOpenRequest_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("currentOpenRequest_${currentDate()}", value, true)
}
//当前开屏展示次数
private var currentOpenShow = 0
get() {
return AppPreferences.getInstance().getInt("currentOpenShow_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("currentOpenShow_${currentDate()}", value, true)
}
//当前开屏点击次数
private var currentOpenClick = 0
get() {
return AppPreferences.getInstance().getInt("currentOpenClick_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("currentOpenClick_${currentDate()}", value, true)
}
fun shouldShowOpenAd(): Boolean {
if (currentOpenRequest > open_max_request) {
LogEx.logDebug(TAG, "currentOpenRequest=$currentOpenRequest open_max_request=$open_max_request")
EventUtils.event("ad_limit", "currentOpenRequest=$currentOpenRequest open_max_request=$open_max_request")
return false
}
if (currentOpenShow > open_max_show) {
LogEx.logDebug(TAG, "currentOpenShow=$currentOpenShow open_max_show=$open_max_show")
EventUtils.event("ad_limit", "currentOpenShow=$currentOpenShow open_max_show=$open_max_show")
return false
}
if (currentOpenClick > open_max_click) {
LogEx.logDebug(TAG, "currentOpenClick=$currentOpenClick open_max_click=$open_max_click")
EventUtils.event("ad_limit", "currentOpenClick=$currentOpenClick open_max_click=$open_max_click")
return false
}
return true
}
//endregion
//region inter
private val inter_max_request = AppPreferences.getInstance().getString(inter_limit_request, "15").toInt()
private val inter_max_show = AppPreferences.getInstance().getString(inter_limit_show, "10").toInt()
private val inter_max_click = AppPreferences.getInstance().getString(inter_limit_click, "1").toInt()
fun incrementInterRequestCount() {
currentInterRequest += 1
}
fun incrementInterShowCount() {
currentInterShow += 1
}
fun incrementInterClickCount() {
currentInterClick += 1
}
//当前插页请求次数
private var currentInterRequest = 0
get() {
return AppPreferences.getInstance().getInt("currentInterRequest_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("currentInterRequest_${currentDate()}", value, true)
}
//当前插页展示次数
private var currentInterShow = 0
get() {
return AppPreferences.getInstance().getInt("currentInterShow_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("currentInterShow_${currentDate()}", value, true)
}
//当前插页点击次数
private var currentInterClick = 0
get() {
return AppPreferences.getInstance().getInt("currentInterClick_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("currentInterClick_${currentDate()}", value, true)
}
fun shouldShowInterAd(): Boolean {
if (currentInterRequest > inter_max_request) {
LogEx.logDebug(TAG, "currentInterRequest=$currentInterRequest inter_max_request=$inter_max_request")
EventUtils.event("ad_limit", "currentInterRequest=$currentInterRequest inter_max_request=$inter_max_request")
return false
}
if (currentInterShow > inter_max_show) {
LogEx.logDebug(TAG, "currentInterShow=$currentInterShow inter_max_show=$inter_max_show")
EventUtils.event("ad_limit", "currentInterShow=$currentInterShow inter_max_show=$inter_max_show")
return false
}
if (currentInterClick > inter_max_click) {
LogEx.logDebug(TAG, "currentInterClick=$currentInterClick inter_max_click=$inter_max_click")
EventUtils.event("ad_limit", "currentInterClick=$currentInterClick inter_max_click=$inter_max_click")
return false
}
return true
}
//endregion
//region native
private val native_max_request = AppPreferences.getInstance().getString(native_limit_request, "15").toInt()
private val native_max_show = AppPreferences.getInstance().getString(native_limit_show, "10").toInt()
private val native_max_click = AppPreferences.getInstance().getString(native_limit_show, "1").toInt()
fun incrementNativeRequestCount() {
currentNativeRequest += 1
}
fun incrementNativeShowCount() {
currentNativeShow += 1
}
fun incrementNativeClickCount() {
currentNativeClick += 1
}
private var currentNativeRequest = 0
get() {
return AppPreferences.getInstance().getInt("currentNativeRequest_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("currentNativeRequest_${currentDate()}", value, true)
}
private var currentNativeShow = 0
get() {
return AppPreferences.getInstance().getInt("currentNativeShow_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("currentNativeShow_${currentDate()}", value, true)
}
private var currentNativeClick = 0
get() {
return AppPreferences.getInstance().getInt("currentNativeClick_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("currentNativeClick_${currentDate()}", value, true)
}
fun shouldShowNative(): Boolean {
if (currentNativeRequest > native_max_request) {
LogEx.logDebug(TAG, "currentNativeRequest=$currentNativeRequest native_max_request=$native_max_request")
EventUtils.event("ad_limit", "currentNativeRequest=$currentNativeRequest native_max_request=$native_max_request")
return false
}
if (currentNativeShow > native_max_show) {
LogEx.logDebug(TAG, "currentNativeShow=$currentNativeShow native_max_show=$native_max_show")
EventUtils.event("ad_limit", "currentNativeShow=$currentNativeShow native_max_show=$native_max_show")
return false
}
if (currentNativeClick > native_max_click) {
LogEx.logDebug(TAG, "currentNativeClick=$currentNativeClick native_max_click=$native_max_click")
EventUtils.event("ad_limit", "currentNativeClick=$currentNativeClick native_max_click=$native_max_click")
return false
}
return true
}
//endregion
private val TAG = "AdDisplayUtils"
private fun currentDate(): String {
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val currentDate = Calendar.getInstance().time
return dateFormat.format(currentDate)
}
}
package com.base.pdfreader2.ads
import android.app.Activity
import com.base.locationsharewhite.ads.admob.AdmobInterstitialUtils
import com.base.locationsharewhite.ads.admob.AdmobNativeUtils
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.helper.MyApplication
import com.base.locationsharewhite.utils.AppPreferences
import com.google.android.gms.ads.MobileAds
import java.util.concurrent.atomic.AtomicBoolean
object AdmobHelper {
//开屏限制
const val open_limit_request = "open_limit_request"
const val open_limit_show = "open_limit_show"
const val open_limit_click = "open_limit_click"
//插页限制
const val inter_limit_request = "inter_limit_request"
const val inter_limit_show = "inter_limit_show"
const val inter_limit_click = "inter_limit_click"
//原生广告限制
const val native_limit_request = "native_limit_request"
const val native_limit_show = "native_limit_show"
const val native_limit_click = "native_limit_click"
//开屏加载ad时间
val open_ad_loading = "open_ad_loading"
var isAdInit = AtomicBoolean(false)
fun initAdmobAd(activity: Activity) {
MobileAds.initialize(MyApplication.context) { initializationStatus ->
isAdInit.set(true)
EventUtils.event("AdmobInit", "AdmobInit")
AdmobNativeUtils.loadNativeAd()
AdmobInterstitialUtils.loadInterstitialAd(activity)
}
}
//上次展示广告时间关闭赋值,通用开屏和插页
var lastShowedOnHiddenTime = 0L
get() {
return AppPreferences.getInstance().getLong("lastShowedOnHiddenTime", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("lastShowedOnHiddenTime", value, true)
}
/**
* 通用广告条件判断
*/
fun canCommonShowAd(): Boolean {
val interval = AppPreferences.getInstance().getString("ad_interval", "10").toInt()
if (System.currentTimeMillis() - lastShowedOnHiddenTime < interval * 1000L) {
return false
}
return true
}
//上次scan展示ad时间
var lastScanShowAd = 0L
get() {
return AppPreferences.getInstance().getLong("lastScanShowAd", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("lastScanShowAd", value, true)
}
//是否显示扫描功能ad
fun isShowScanInter(): Boolean {
val interval = AppPreferences.getInstance().getString("scan_ad_interval", "10").toInt()
return System.currentTimeMillis() - lastScanShowAd > interval * 1000L
}
//上次打开文档展示ad时间
var lastOpenDocumentShowAd = 0L
get() {
return AppPreferences.getInstance().getLong("lastOpenDocumentShowAd", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("lastOpenDocumentShowAd", value, true)
}
//打开文档是否展示广告
fun isShowOpenDocumentInter(): Boolean {
val interval = AppPreferences.getInstance().getString("open_document_ad_interval", "10").toInt()
val openStatus = AppPreferences.getInstance().getString("open_document_ad_status", "1").toInt()
return openStatus == 1 && System.currentTimeMillis() - lastOpenDocumentShowAd > interval * 1000L
}
var lastCloseDocumentShowAd = 0L
get() {
return AppPreferences.getInstance().getLong("lastCloseDocumentShowAd", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("lastCloseDocumentShowAd", value, true)
}
fun isShowCloseDocumentInter(): Boolean {
val interval = AppPreferences.getInstance().getString("close_document_ad_interval", "10").toInt()
return System.currentTimeMillis() - lastCloseDocumentShowAd > interval * 1000L
}
fun isShowCloseDocument(): Boolean {
val status = AppPreferences.getInstance().getString("close_document_ad_show", "0").toInt()
return status == 1
}
fun isShowRvNativeAd(): Boolean {
val status = AppPreferences.getInstance().getString("rv_native_ad_show", "0").toInt()
return status == 1
}
fun isBackShowAd(): Boolean {
val status = AppPreferences.getInstance().getString("is_back_show_ad", "0").toInt()
return status == 1
}
}
\ No newline at end of file
package com.example.mydemo.strategy.ads
/**
* 广告限制配置
*
* @property isInBlackList 是否在黑名单
* @property numDisplayLimit 展示次数限制 -1为不限制,0为彻底关闭显示
* @property numRequestLimit 请求次数限制 -1为不限制,0为彻底关闭显示
* @property numClickLimit 点击次数限制 -1为不限制,0为彻底关闭显示
* @property timeInterval 广告间隔时间
*/
data class AdsConfigBean(
var isInBlackList: Boolean = false,
var numDisplayLimit: Int = -1,
var numRequestLimit: Int = -1,
var numClickLimit: Int = -1,
var timeInterval: Int = 1
)
\ No newline at end of file
package com.example.mydemo.strategy.ads
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.example.mydemo.strategy.ads.admob.AdOpenMgr
import com.google.android.gms.ads.MobileAds
import com.google.android.gms.ads.initialization.AdapterStatus
/**
* 广告管理类
*/
object AdsMgr {
private val adOpenMgr by lazy {
AdOpenMgr()
}
private val adInsertMgr by lazy {
AdInsertMgr()
}
private val adNativeMgr by lazy {
AdNativeMgr()
}
private val adBannerMgr by lazy {
AdBannerMgr()
}
/**
* 是否初始化
*/
var isInit = false
private set
/**
* 广告配置项目
*/
var adsConfigBean: AdsConfigBean? = null
private set
/**
* Init 初始化
*
* @param context 这里最好是appContext,因为是耗时操作,等它初始化完毕马上加载开屏和插屏广告
*/
fun init(context: Context, adsConfigBean: AdsConfigBean) {
if (adsConfigBean.isInBlackList) return
this.adsConfigBean = adsConfigBean
MobileAds.initialize(context) {
val readyAdapter = it.adapterStatusMap.entries.find { entry ->
entry.value.initializationState == AdapterStatus.State.READY
}
isInit = readyAdapter != null
if (isInit) {
//成功初始化就提前预加载开屏广告和插页广告
adOpenMgr.loadAd(context)
adInsertMgr.loadAd(context)
}
}
}
/**
* 展示开屏广告
*
* @param activity 当前页面
* @param showCallBack 展示回调
*/
fun showOpen(activity: Activity, showCallBack: AdsShowCallBack? = null) {
if (!isInit || adsConfigBean?.isInBlackList != false) {
showCallBack?.failed()
return
}
adOpenMgr.show(activity, showCallBack)
}
/**
* 展示插屏广告
*
* @param activity 当前页面
* @param showCallBack 展示回调
*/
fun showInsert(activity: Activity, showCallBack: AdsShowCallBack? = null) {
if (!isInit || adsConfigBean?.isInBlackList != false) {
showCallBack?.failed()
return
}
adInsertMgr.show(activity, showCallBack)
}
/**
* 展示原生广告
*
* @param parent 需要展示广告的父布局容器
* @param layout 原生广告布局 ,这里传入的layout要和com.example.mydemo.strategy.ads.admob.NativeView里的id一致
*/
fun showNative(parent: ViewGroup, layout: Int) {
if (!isInit || adsConfigBean?.isInBlackList != false) return
adNativeMgr.show(parent, layout)
}
/**
* 展示banner广告
*
* @param parent 展示广告的父布局容器
*/
fun showBanner(parent: ViewGroup) {
if (!isInit || adsConfigBean?.isInBlackList != false) return
adBannerMgr.show(parent)
}
}
\ No newline at end of file
package com.base.locationsharewhite.ads
abstract class AdsShowCallBack {
open fun show() {}
abstract fun close()
abstract fun failed()
}
\ No newline at end of file
package com.base.locationsharewhite.ads
/**
* 广告类型
* 0=开屏广告、1=插屏广告、2=原生广告、3=Banner横幅广告
*/
@JvmInline
value class AdsType private constructor(val value: Int) {
companion object {
val OPEN = AdsType(0)
val INSERT = AdsType(1)
val NATIVE = AdsType(2)
val BANNER = AdsType(3)
fun from(adsType: Int): AdsType {
return when (adsType) {
OPEN.value -> OPEN
INSERT.value -> INSERT
NATIVE.value -> NATIVE
else -> BANNER
}
}
}
}
\ No newline at end of file
package com.base.locationsharewhite.ads
import android.app.Activity
import android.content.Context
import java.lang.ref.WeakReference
/**
* 特定广告管理基类
* @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 lastTime: Long = 0
/**
* 预加载广告
*
* @param context 加载所用的上下文,一般使用appContext
*/
abstract fun loadAd(context: Context)
/**
* 广告显示
*
* @param activity 当前页面
*/
abstract fun show(activity: Activity, showCallBack: AdsShowCallBack? = null)
/**
* 预加载的缓存超时判断
*
* @return true:没有超时 false:超时需要重新加载
*/
abstract fun adAvailable(): Boolean
}
\ No newline at end of file
package com.base.locationsharewhite.ads.admob
import android.content.Context
import android.os.Bundle
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.base.locationsharewhite.BuildConfig
import com.base.locationsharewhite.helper.ConfigHelper
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.LogEx
import com.base.locationsharewhite.helper.ConstConfig
import com.google.ads.mediation.admob.AdMobAdapter
import com.google.android.gms.ads.AdListener
import com.google.android.gms.ads.AdRequest
......@@ -15,43 +11,47 @@ import com.google.android.gms.ads.AdSize
import com.google.android.gms.ads.AdView
import java.util.UUID
object AdmobBannerUtils {
/**
*banner广告加载显示管理类
*/
class AdBannerMgr {
private const val TAG = "AdmobBannerUtils"
private var adView: AdView? = null
private var listener: ViewTreeObserver.OnGlobalLayoutListener? = null
fun showCollapsibleBannerAd(context: Context, parent: ViewGroup, adClose: (() -> Unit)? = null) {
val isShowBanner = AppPreferences.getInstance().getString("isShowBanner", "0").toInt()
if (isShowBanner == 0) {
return
}
parent.removeAllViews()
fun show(parent: ViewGroup) {
if (adView != null) {
adView?.destroy()
}
if (!LimitUtils.isAdShow()) {
adView = null
adView = AdView(context)
// adView?.apply {
// onPaidEventListener = com.base.locationsharewhite.ads.admob.AdmobEvent.EventOnPaidEventListener(this)
return
}
adView = AdView(parent.context)
adView?.tag = "CollapsibleBannerAd"
// val list = parent.children
// list.forEach {
// if (it.tag != "zhanweitu") {
// parent.removeView(it)
// }
// }
parent.addView(adView)
listener = ViewTreeObserver.OnGlobalLayoutListener {
val screenPixelDensity = context.resources.displayMetrics.density
val screenPixelDensity = parent.context.resources.displayMetrics.density
val adWidth = (parent.width / screenPixelDensity).toInt()
val adSize = AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, adWidth)
adView?.adUnitId = if (BuildConfig.DEBUG) ConfigHelper.bannerAdmobIdTest else ConfigHelper.bannerAdmobId
val adSize =
AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(parent.context, adWidth)
adView?.adUnitId =
ConstConfig.bannerAdId
adView?.setAdSize(adSize)
loadCollapsibleBanner(adClose)
loadAd(parent)
parent.viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
parent.viewTreeObserver.addOnGlobalLayoutListener(listener)
}
private fun loadCollapsibleBanner(adClose: (() -> Unit)?) {
fun loadAd(parent: ViewGroup) {
val extras = Bundle()
extras.putString("collapsible", "bottom")
extras.putString("collapsible_request_id", UUID.randomUUID().toString())
......@@ -60,20 +60,27 @@ object AdmobBannerUtils {
adView?.adListener =
object : AdListener() {
override fun onAdLoaded() {
LogEx.logDebug(TAG, "onAdLoaded")
}
override fun onAdOpened() {
LogEx.logDebug(TAG, "onAdOpened")
}
override fun onAdClosed() {
super.onAdClosed()
LogEx.logDebug(TAG, "onAdClosed")
adClose?.invoke()
// val removeList = arrayListOf<View>()
// parent.children.forEach {
// if (it.tag != "CollapsibleBannerAd") {
// removeList.add(it)
// }
// }
// removeList.forEach {
// parent.removeView(it)
// }
}
}
adView?.loadAd(adRequest)
}
}
}
\ No newline at end of file
package com.base.locationsharewhite.ads.admob
import android.app.Activity
import android.content.Context
import com.base.locationsharewhite.helper.ConstConfig
import com.example.mydemo.strategy.ads.AdsShowCallBack
import com.example.mydemo.strategy.ads.BaseAdMgr
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 AdInsertMgr : BaseAdMgr<InterstitialAd>() {
private var lastOpenDate: Long = 0
private val adLoadCallback = object : InterstitialAdLoadCallback() {
override fun onAdLoaded(ad: InterstitialAd) {
super.onAdLoaded(ad)
// 开屏广告加载成功
currentAd = ad
loadingAd = false
lastTime = System.currentTimeMillis()
val ac = activityRef?.get()
if (ac != null) {
show(ac)
}
}
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
super.onAdFailedToLoad(loadAdError)
// 广告加载失败
// 官方不建议在此回调中重新加载广告
// 如果确实需要,则必须限制最大重试次数,避免在网络受限的情况下连续多次请求
loadingAd = false
if (activityRef != null && !showingAd && showCallBack != null) {
showCallBack?.failed()
showCallBack = null
}
}
}
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
)
) {
showCallBack?.failed()
return
}
if (showingAd) {
// 开屏广告正在展示
return
}
if (showCallBack != null && this.showCallBack != showCallBack) this.showCallBack =
showCallBack
if (currentAd == null || !adAvailable()) {
//缓存广告过期
currentAd = null
if (activityRef == null) {
activityRef = WeakReference(activity)
// 开屏广告不可用或者缓存超时过期,重新加载
loadAd(activity.applicationContext)
} else {
activityRef = null
this.showCallBack?.failed()
this.showCallBack = null
}
return
}
currentAd?.run {
fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdShowedFullScreenContent() {
super.onAdShowedFullScreenContent()
lastOpenDate = System.currentTimeMillis()
// 广告展示
currentAd = null
activityRef = null
this@AdInsertMgr.showCallBack?.show()
//计数
LimitUtils.addDisplayNum()
}
override fun onAdFailedToShowFullScreenContent(adError: AdError) {
super.onAdFailedToShowFullScreenContent(adError)
// 广告展示失败,清空缓存数据,重新加载
showingAd = false
currentAd = null
activityRef = null
this@AdInsertMgr.showCallBack?.failed()
this@AdInsertMgr.showCallBack = null
}
override fun onAdDismissedFullScreenContent() {
super.onAdDismissedFullScreenContent()
// 广告关闭,清空缓存数据,重新加载
showingAd = false
this@AdInsertMgr.showCallBack?.close()
this@AdInsertMgr.showCallBack = null
loadAd(activity.applicationContext)
}
override fun onAdClicked() {
super.onAdClicked()
//计数
LimitUtils.addClickNum()
}
}
showingAd = true
show(activity)
}
}
override fun adAvailable() = ((System.currentTimeMillis() - lastTime) / 1000 / 60).toInt() < 30
}
\ No newline at end of file
package com.base.locationsharewhite.ads.admob
import android.content.Context
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.base.locationsharewhite.helper.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 java.util.concurrent.ConcurrentLinkedDeque
/**
*原生广告加载显示管理类
*/
class AdNativeMgr {
/**
* 上一次的缓存成功时间
*/
protected var lastTime: Long = 0
/**
* 原生广告缓存队列
*/
private val cacheItems = ConcurrentLinkedDeque<NativeAd>()
fun loadAd(context: Context, parent: ViewGroup? = null, layout: Int? = null) {
val adLoader = AdLoader.Builder(
context,
ConstConfig.nativeAdId
)
.forNativeAd { nativeAd ->
cacheItems.offer(nativeAd)
lastTime = System.currentTimeMillis()
if (parent != null && layout != null) show(parent, layout)
}
.withAdListener(object : AdListener() {
override fun onAdFailedToLoad(error: LoadAdError) {
}
override fun onAdClicked() {
super.onAdClicked()
}
override fun onAdClosed() {
super.onAdClosed()
}
})
.withNativeAdOptions(
NativeAdOptions.Builder()
// .setAdChoicesPlacement(NativeAdOptions.ADCHOICES_TOP_RIGHT)
// .setMediaAspectRatio(NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_SQUARE)
.build()
)
.build()
// Load AdMob native advanced ads
adLoader.loadAds(AdRequest.Builder().build(), 1)
}
fun show(parent: ViewGroup, layout: Int) {
if (!LimitUtils.isAdShow()) {
cacheItems.clear()
return
}
val nativeAd = cacheItems.peek()
if (nativeAd == null || !adAvailable()) {
//缓存过期了就清空
cacheItems.clear()
loadAd(parent.context.applicationContext, parent, layout)
return
}
NativeView(parent.context, layout).run {
parent.removeAllViews()
setNativeAd(nativeAd)
parent.addView(this)
parent.isVisible = true
}
loadAd(parent.context.applicationContext)
}
fun adAvailable(): Boolean {
return ((System.currentTimeMillis() - lastTime) / 1000 / 60).toInt() < 30
}
}
\ No newline at end of file
package com.example.mydemo.strategy.ads.admob
import android.app.Activity
import android.content.Context
import com.base.locationsharewhite.ads.admob.LimitUtils
import com.base.locationsharewhite.helper.ConstConfig
import com.example.mydemo.strategy.ads.AdsShowCallBack
import com.example.mydemo.strategy.ads.BaseAdMgr
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 : BaseAdMgr<AppOpenAd>() {
private var lastOpenDate: Long = 0
private val adLoadCallback = object : AppOpenAd.AppOpenAdLoadCallback() {
override fun onAdLoaded(appOpenAd: AppOpenAd) {
super.onAdLoaded(appOpenAd)
// 开屏广告加载成功
currentAd = appOpenAd
loadingAd = false
lastTime = System.currentTimeMillis()
val ac = activityRef?.get()
if (ac != null) {
show(ac)
}
}
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
super.onAdFailedToLoad(loadAdError)
// 开屏广告加载失败
// 官方不建议在此回调中重新加载广告
// 如果确实需要,则必须限制最大重试次数,避免在网络受限的情况下连续多次请求
loadingAd = false
if (activityRef != null && !showingAd && showCallBack != null) {
showCallBack?.failed()
showCallBack = null
}
}
}
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
)
) {
showCallBack?.failed()
return
}
if (showingAd) {
// 开屏广告正在展示
return
}
if (showCallBack != null && this.showCallBack != showCallBack) this.showCallBack =
showCallBack
if (currentAd == null || !adAvailable()) {
//缓存广告过期
currentAd = null
if (activityRef == null) {
activityRef = WeakReference(activity)
// 开屏广告不可用或者缓存超时过期,重新加载
loadAd(activity.applicationContext)
} else {
activityRef = null
this.showCallBack?.failed()
this.showCallBack = null
}
return
}
currentAd?.run {
fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdShowedFullScreenContent() {
super.onAdShowedFullScreenContent()
lastOpenDate = System.currentTimeMillis()
// 广告展示
currentAd = null
activityRef = null
this@AdOpenMgr.showCallBack?.show()
//计数
LimitUtils.addDisplayNum()
}
override fun onAdFailedToShowFullScreenContent(adError: AdError) {
super.onAdFailedToShowFullScreenContent(adError)
// 广告展示失败,清空缓存数据,重新加载
showingAd = false
currentAd = null
activityRef = null
this@AdOpenMgr.showCallBack?.failed()
this@AdOpenMgr.showCallBack = null
}
override fun onAdDismissedFullScreenContent() {
super.onAdDismissedFullScreenContent()
// 广告关闭,清空缓存数据,重新加载
showingAd = false
this@AdOpenMgr.showCallBack?.close()
this@AdOpenMgr.showCallBack = null
loadAd(activity.applicationContext)
}
override fun onAdClicked() {
super.onAdClicked()
//计数
LimitUtils.addClickNum()
}
}
showingAd = true
show(activity)
}
}
override fun adAvailable() = ((System.currentTimeMillis() - lastTime) / 1000 / 60).toInt() < 30
}
\ No newline at end of file
package com.base.locationsharewhite.ads.admob
import android.app.Activity
import android.app.Dialog
import android.widget.Toast
import com.base.locationsharewhite.BuildConfig
import com.base.locationsharewhite.ads.AdDialog.showAdPreparingDialog
import com.base.locationsharewhite.ads.admob.AdmobEvent.clickAd
import com.base.locationsharewhite.ads.admob.AdmobEvent.pullAd
import com.base.locationsharewhite.ads.admob.AdmobEvent.showAd
import com.base.locationsharewhite.helper.ConfigHelper
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.helper.MyApplication
import com.base.pdfreader2.ads.AdDisplayUtils
import com.base.pdfreader2.ads.AdmobHelper.lastShowedOnHiddenTime
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 org.json.JSONObject
import java.util.UUID
object AdmobInterstitialUtils {
private var interAd: InterstitialAd? = null
private var interLoadTime = Long.MAX_VALUE
private var adLastDisplayTime: Long = 0
private val mRequest = AdRequest.Builder().build()
private fun isAdExpired(): Boolean {
return System.currentTimeMillis() - interLoadTime > 1000 * 60 * 60
}
fun showInterstitialAd(
activity: Activity,
isReLoadAd: Boolean = false,
isShowDialog: Boolean = true,
onHidden: ((showed: Boolean) -> Unit)? = null
) {
if (activity.isFinishing || activity.isDestroyed) {
return
}
if (isAdExpired()) {
val obj2 = JSONObject()
obj2.put("ad_unit", "interAd")
EventUtils.event("ad_expire", ext = obj2)
interAd = null
loadInterstitialAd(activity)
onHidden?.invoke(false)
return
}
if (!AdDisplayUtils.shouldShowInterAd()) {
onHidden?.invoke(false)
return
}
val obj1 = JSONObject()
obj1.put("ad_unit", "interAd")
EventUtils.event("ad_prepare_show", ext = obj1)
if (interAd != null) {
var dialog: Dialog? = null
if (!activity.isFinishing && !activity.isDestroyed) {
dialog = activity.showAdPreparingDialog()
}
displayInterstitialAd(activity, dialog, onHidden)
} else {
showAdDialogAndLoadInterstitial(activity, isReLoadAd, isShowDialog, onHidden)
}
}
fun loadInterstitialAd(activity: Activity, onLoad: (() -> Unit)? = null) {
if (interAd != null) {
onLoad?.invoke()
return
}
if (!AdDisplayUtils.shouldShowInterAd()) {
onLoad?.invoke()
return
}
val reqId = UUID.randomUUID().toString()
val obj = JSONObject()
obj.put("req_id", reqId)
obj.put("ad_type", "interAd")
obj.put("from", activity.javaClass.simpleName)
EventUtils.event("ad_pull_start", ext = obj)
InterstitialAd.load(
activity,
if (BuildConfig.DEBUG) ConfigHelper.interAdmobIdTest else ConfigHelper.interAdmobId,
mRequest,
object : InterstitialAdLoadCallback() {
override fun onAdFailedToLoad(p0: LoadAdError) {
interAd = null
onLoad?.invoke()
pullAd(p0.responseInfo, "interAd", p0.message, reqId = reqId)
if (BuildConfig.DEBUG) {
Toast.makeText(
MyApplication.context, "拉取失败" + p0.message, Toast.LENGTH_SHORT
).show()
}
}
override fun onAdLoaded(ad: InterstitialAd) {
interAd = ad
onLoad?.invoke()
interLoadTime = System.currentTimeMillis()
pullAd(ad.responseInfo, "interAd", reqId = reqId)
ad.onPaidEventListener = AdmobEvent.EventOnPaidEventListener(ad)
AdDisplayUtils.incrementInterRequestCount()
}
})
}
private fun showAdDialogAndLoadInterstitial(
activity: Activity,
isReLoadAd: Boolean,
isShowDialog: Boolean,
onHidden: ((showed: Boolean) -> Unit)?
) {
if (!isShowDialog) {
onHidden?.invoke(false)
return
}
var mDialog: Dialog? = null
if (!activity.isFinishing && !activity.isDestroyed) {
mDialog = activity.showAdPreparingDialog()
mDialog.show()
}
loadInterstitialAd(activity) {
mDialog?.dismiss()
if (!isReLoadAd) {
showInterstitialAd(activity, true, false) {
onHidden?.invoke(it)
}
}
}
if (isReLoadAd) {
mDialog?.dismiss()
onHidden?.invoke(false)
}
}
private fun displayInterstitialAd(
activity: Activity,
dialog: Dialog? = null,
onHidden: ((showed: Boolean) -> Unit)? = null
) {
val thisInterAd = interAd
interAd = null
thisInterAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdClicked() {
clickAd(thisInterAd?.responseInfo, "interAd")
AdDisplayUtils.incrementInterClickCount()
}
override fun onAdDismissedFullScreenContent() {
dialog?.dismiss()
interAd = null
onHidden?.invoke(true)
loadInterstitialAd(activity)
lastShowedOnHiddenTime = System.currentTimeMillis()
}
override fun onAdFailedToShowFullScreenContent(p0: AdError) {
dialog?.dismiss()
interAd = null
onHidden?.invoke(false)
loadInterstitialAd(activity)
}
override fun onAdShowedFullScreenContent() {
dialog?.dismiss()
showAd(thisInterAd?.responseInfo, "interAd", activity)
AdDisplayUtils.incrementInterShowCount()
adLastDisplayTime = System.currentTimeMillis() / 1000
}
}
thisInterAd?.show(activity)
}
fun haveReadAd(): Boolean {
return interAd != null
}
}
\ No newline at end of file
package com.base.locationsharewhite.ads.admob
import android.app.Activity
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.base.locationsharewhite.BuildConfig
import com.base.locationsharewhite.R
import com.base.locationsharewhite.ads.admob.AdmobEvent.clickAd
import com.base.locationsharewhite.ads.admob.AdmobEvent.pullAd
import com.base.locationsharewhite.ads.admob.AdmobEvent.showAd
import com.base.locationsharewhite.helper.ConfigHelper
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.helper.MyApplication
import com.base.locationsharewhite.utils.LogEx
import com.base.pdfreader2.ads.AdDisplayUtils
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 org.json.JSONObject
import java.util.UUID
object AdmobNativeUtils {
private const val TAG = "AdmobNativeUtils"
private var nativeAd: NativeAd? = null
private var isLoading = false
private var nativeLoadTime = Long.MAX_VALUE
private var loadingListener: (() -> Unit)? = null
private val mRequest = AdRequest.Builder().build()
var onAdLoaded: (() -> Unit)? = null
fun loadNativeAd() {
if (nativeAd != null) {
return
}
if (isLoading) {
return
}
isLoading = true
if (!AdDisplayUtils.shouldShowNative()) {
return
}
val reqId = UUID.randomUUID().toString()
val obj = JSONObject()
obj.put("req_id", reqId)
obj.put("ad_type", "nativeAd")
val adLoader = AdLoader.Builder(
MyApplication.context,
if (BuildConfig.DEBUG) ConfigHelper.nativeAdmobIdTest else ConfigHelper.nativeAdmobId
).forNativeAd {
nativeLoadTime = System.currentTimeMillis()
nativeAd = it
it.setOnPaidEventListener(AdmobEvent.EventOnPaidEventListener(it))
LogEx.logDebug(TAG, "nativeAd=${nativeAd.toString()}")
isLoading = false
loadingListener?.invoke()
loadingListener = null
pullAd(it.responseInfo, "nativeAd", reqId = reqId)
}.withAdListener(object : AdListener() {
override fun onAdLoaded() {
super.onAdLoaded()
onAdLoaded?.invoke()
onAdLoaded = null
AdDisplayUtils.incrementNativeRequestCount()
}
override fun onAdClicked() {
clickAd(nativeAd?.responseInfo, "nativeAd")
AdDisplayUtils.incrementNativeClickCount()
}
override fun onAdFailedToLoad(p0: LoadAdError) {
LogEx.logDebug(TAG, "onAdFailedToLoad=${p0.message}")
nativeAd = null
isLoading = false
pullAd(p0.responseInfo, "nativeAd", p0.message, reqId = reqId)
// Log.e("MXL", "NativeAdFailedToLoad: " + p0.message)
}
}).build()
adLoader.loadAd(mRequest)
}
fun showNativeAd(activity: Activity?, parent: ViewGroup, layout: Int = R.layout.layout_admob_native_custom) {
val obj = JSONObject()
obj.put("ad_unit", "nativeAd")
EventUtils.event("ad_prepare_show_native", ext = obj)
if (!AdDisplayUtils.shouldShowNative()) {
return
}
loadingListener = {
if (System.currentTimeMillis() - nativeLoadTime <= 1000 * 60 * 60) {
nativeAd?.let {
NativeView(parent.context, layout).run {
parent.removeAllViews()
setNativeAd(it)
parent.addView(this)
parent.isVisible = true
showAd(nativeAd?.responseInfo, "nativeAd", activity)
}
}
}
nativeAd = null
loadNativeAd()
}
if (nativeAd == null) {
loadNativeAd()
val obj2 = JSONObject()
obj2.put("reason", "no_ad")
obj2.put("ad_unit", "nativeAd")
EventUtils.event("ad_show_error", ext = obj2)
} else {
loadingListener?.invoke()
loadingListener = null
}
}
fun showReadyNativeAd(
activity: Activity?,
readyNativeAd: NativeAd?,
parent: ViewGroup,
layout: Int = R.layout.layout_admob_native_custom
) {
readyNativeAd?.let {
NativeView(parent.context, layout).run {
parent.removeAllViews()
setNativeAd(it)
parent.addView(this)
parent.isVisible = true
showAd(nativeAd?.responseInfo, "nativeAd", activity)
}
}
}
fun onDestroy() {
nativeAd?.destroy()
nativeAd = null
}
}
\ No newline at end of file
package com.base.locationsharewhite.ads.admob
import android.app.Activity
import com.base.locationsharewhite.BuildConfig
import com.base.locationsharewhite.ads.admob.AdmobEvent.clickAd
import com.base.locationsharewhite.ads.admob.AdmobEvent.pullAd
import com.base.locationsharewhite.ads.admob.AdmobEvent.showAd
import com.base.locationsharewhite.helper.ConfigHelper
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.helper.MyApplication
import com.base.locationsharewhite.utils.LogEx
import com.base.pdfreader2.ads.AdDisplayUtils
import com.base.pdfreader2.ads.AdmobHelper.lastShowedOnHiddenTime
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.util.UUID
object AdmobOpenUtils {
private const val TAG = "AdmobOpenUtils"
private val mRequest = AdRequest.Builder().build()
private var openLoadTime = Long.MAX_VALUE
private var mOpenAd: AppOpenAd? = null
private fun isAdExpired(): Boolean {
return System.currentTimeMillis() - openLoadTime > 1000 * 60 * 60
}
fun haveReadAd(): Boolean {
return mOpenAd != null
}
fun loadAppOpenAd(onLoad: ((loaded: Boolean) -> Unit)? = null) {
if (mOpenAd != null) {
onLoad?.invoke(true)
return
}
if (!AdDisplayUtils.shouldShowOpenAd()) {
onLoad?.invoke(false)
return
}
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(
MyApplication.context,
if (BuildConfig.DEBUG) ConfigHelper.openAdmobIdTest else ConfigHelper.openAdmobId,
mRequest,
object : AppOpenAd.AppOpenAdLoadCallback() {
override fun onAdLoaded(ad: AppOpenAd) {
LogEx.logDebug(TAG, "onAdLoaded")
openLoadTime = System.currentTimeMillis()
mOpenAd = ad
onLoad?.invoke(true)
pullAd(ad.responseInfo, "openAd", reqId = reqId)
ad.onPaidEventListener = AdmobEvent.EventOnPaidEventListener(ad)
AdDisplayUtils.incrementOpenRequestCount()
}
override fun onAdFailedToLoad(p0: LoadAdError) {
LogEx.logDebug(TAG, "LoadAdError ${p0.message}")
mOpenAd = null
onLoad?.invoke(false)
pullAd(p0.responseInfo, "openAd", p0.message, reqId = reqId)
}
})
}
fun showAppOpenAd(
activity: Activity,
isRetry: Boolean = false,
showBefore: ((flag: Boolean) -> Unit)? = null,
onHidden: ((showed: Boolean) -> Unit)? = null
) {
if (activity.isFinishing || activity.isDestroyed) {
LogEx.logDebug(TAG, "activity isDestroyed")
return
}
if (!AdDisplayUtils.shouldShowOpenAd()) {
onHidden?.invoke(false)
return
}
if (isAdExpired()) {
LogEx.logDebug(TAG, "openLoadTime out time")
mOpenAd = null
loadAppOpenAd()
onHidden?.invoke(false)
val obj2 = JSONObject()
obj2.put("ad_unit", "openAd")
EventUtils.event("ad_expire", ext = obj2)
return
}
if (!isRetry) {
val obj1 = JSONObject()
obj1.put("ad_unit", "openAd")
EventUtils.event("ad_prepare_show", ext = obj1)
LogEx.logDebug(TAG, "open ad_prepare_show")
}
if (mOpenAd != null) {
LogEx.logDebug(TAG, "mOpenAd!=null")
val thisMOpenAd = mOpenAd
mOpenAd = null
thisMOpenAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdClicked() {
clickAd(thisMOpenAd?.responseInfo, "openAd")
AdDisplayUtils.incrementClickShow()
}
override fun onAdDismissedFullScreenContent() {
mOpenAd = null
onHidden?.invoke(true)
loadAppOpenAd()
lastShowedOnHiddenTime = System.currentTimeMillis()
}
override fun onAdFailedToShowFullScreenContent(p0: AdError) {
mOpenAd = null
onHidden?.invoke(false)
loadAppOpenAd()
val obj = JSONObject()
obj.put("reason", p0.message)
obj.put("code", p0.code)
obj.put("ad_unit", "openAd")
EventUtils.event("ad_show_error", ext = obj)
}
override fun onAdShowedFullScreenContent() {
showBefore?.invoke(true)
showAd(thisMOpenAd?.responseInfo, "openAd", activity)
AdDisplayUtils.incrementOpenShow()
}
}
thisMOpenAd?.show(activity)
} else {
LogEx.logDebug(TAG, "mOpenAd=null")
loadAppOpenAd {
if (mOpenAd != null) {
showAppOpenAd(activity, true, showBefore, onHidden)
} else {
val obj = JSONObject()
obj.put("reason", "no_ad")
obj.put("ad_unit", "openAd")
EventUtils.event("ad_show_error", ext = obj)
onHidden?.invoke(false)
}
}
}
}
}
\ No newline at end of file
package com.base.locationsharewhite.ads.admob
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.KotlinExt.toFormatTime4
import com.example.mydemo.strategy.ads.AdsMgr
/**
* 控制广告计数与判断显示条件
*
*/
object LimitUtils {
const val NUM_DISPLAY = "numDisplayLimit"
const val NUM_REQUEST = "numRequestLimit"
const val NUM_CLICK = "numClickLimit"
const val SAVE_DATE = "SAVE_DATE"
/**
* 保存的时间,用来判断是否是当天,不是当天要重置计数次数
*/
private var saveDate
get() = AppPreferences.getInstance()
.getString(SAVE_DATE, System.currentTimeMillis().toFormatTime4())
set(value) = AppPreferences.getInstance().put(SAVE_DATE, value)
/**
* 广告请求是否到达限制
*/
private inline val isRequestLimited: Boolean
get() {
val maxCount = AdsMgr.adsConfigBean?.numRequestLimit ?: 100
return maxCount > -1 && AppPreferences.getInstance()
.getInt(NUM_REQUEST, 0) >= maxCount
}
/**
* 广告展示是否到达限制
*/
private inline val isDisplayLimited: Boolean
get() {
val maxCount = AdsMgr.adsConfigBean?.numDisplayLimit ?: 100
return maxCount > -1 && AppPreferences.getInstance()
.getInt(NUM_DISPLAY, 0) >= maxCount
}
/**
* 广告点击是否到达限制
*/
private inline val isClickLimited: Boolean
get() {
val maxCount = AdsMgr.adsConfigBean?.numClickLimit ?: 100
return maxCount > -1 && AppPreferences.getInstance()
.getInt(NUM_CLICK, 0) >= maxCount
}
/**
* 是否显示广告
*
* @return true or false
*/
fun isAdShow(): Boolean {
val currentDate = System.currentTimeMillis().toFormatTime4()
if (saveDate != currentDate) {
//如果已经不是今天了,就重置个数
saveDate = currentDate
AppPreferences.getInstance().put(NUM_DISPLAY, 0)
AppPreferences.getInstance().put(NUM_REQUEST, 0)
AppPreferences.getInstance().put(NUM_CLICK, 0)
}
return !(isDisplayLimited || isClickLimited || isRequestLimited)
}
private fun addNum(key: String) {
val currentDate = System.currentTimeMillis().toFormatTime4()
if (saveDate != currentDate) {
//如果已经不是今天了,就重置个数
saveDate = currentDate
AppPreferences.getInstance().put(key, 1)
return
}
AppPreferences.getInstance()
.put(key, (AppPreferences.getInstance().getInt(key, 0) + 1))
}
fun addDisplayNum() {
addNum(NUM_DISPLAY)
}
fun addRequestNum() {
addNum(NUM_REQUEST)
}
fun addClickNum() {
addNum(NUM_CLICK)
}
/**
* 开屏和插页广告的显示间隔限制
*
* @param lastTime 上一次显示的时间
*/
fun isIntervalLimited(lastTime: Long) =
((System.currentTimeMillis() - lastTime) / 1000 / 60).toInt() < (AdsMgr.adsConfigBean?.timeInterval
?: 1)
}
\ No newline at end of file
package com.base.locationsharewhite.ads.admob
package com.example.mydemo.strategy.ads.admob
import android.annotation.SuppressLint
import android.content.Context
......@@ -11,14 +11,17 @@ import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import com.base.locationsharewhite.R
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdView
@SuppressLint("ViewConstructor")
class NativeView(context: Context, val layout: Int, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
class NativeView(context: Context, val layout: Int, attrs: AttributeSet? = null) :
FrameLayout(context, attrs) {
init {
layoutParams = LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
layoutParams =
LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
fun setNativeAd(nativeAd: NativeAd?) {
......@@ -27,35 +30,32 @@ class NativeView(context: Context, val layout: Int, attrs: AttributeSet? = null)
val adView = LayoutInflater.from(context)
.inflate(layout, this, false) as NativeAdView
// runCatching {
// adView.advertiserView = adView.findViewById(R.id.ad_advertiser)
// }
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
adView.mediaView!!.mediaContent = nativeAd.mediaContent
if (nativeAd.body == null) {
adView.bodyView?.visibility = View.INVISIBLE
adView.bodyView!!.visibility = View.INVISIBLE
} else {
adView.bodyView?.visibility = View.VISIBLE
adView.bodyView!!.visibility = View.VISIBLE
(adView.bodyView as TextView?)?.text = nativeAd.body
}
if (nativeAd.callToAction == null) {
adView.callToActionView?.visibility = View.INVISIBLE
adView.callToActionView!!.visibility = View.INVISIBLE
} else {
adView.callToActionView?.visibility = View.VISIBLE
adView.callToActionView!!.visibility = View.VISIBLE
(adView.callToActionView as Button?)?.text = nativeAd.callToAction
}
if (nativeAd.icon == null) {
adView.iconView?.visibility = View.GONE
adView.iconView!!.visibility = View.GONE
} else {
(adView.iconView as ImageView?)?.setImageDrawable(
nativeAd.icon?.drawable
nativeAd.icon!!.drawable
)
adView.iconView?.visibility = View.VISIBLE
adView.iconView!!.visibility = View.VISIBLE
}
adView.setNativeAd(nativeAd)
......
package com.base.locationsharewhite.fcm
package com.example.mydemo.strategy.fcm
import android.content.BroadcastReceiver
import android.content.Context
......@@ -6,37 +6,62 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import com.example.mydemo.strategy.utils.AppPreferences
import com.example.mydemo.strategy.utils.toFormatTime4
/**
*电量监听
*/
class BatteryStatusReceiver : BroadcastReceiver() {
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) {
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(),
BatteryStatusReceiver(configBean),
intentFilter,
Context.RECEIVER_EXPORTED
)
} else {
applicationContext.registerReceiver(BatteryStatusReceiver(), intentFilter)
applicationContext.registerReceiver(BatteryStatusReceiver(configBean), intentFilter)
}
}
}
override fun onReceive(context: Context?, intent: Intent?) {
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)
}
}
}
}
......
......@@ -5,7 +5,6 @@ import android.util.Log;
import androidx.annotation.NonNull;
import com.base.locationsharewhite.utils.LogEx;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApp;
......@@ -23,7 +22,7 @@ public class FCMManager {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
LogEx.INSTANCE.logDebug("FCMUtil", "suc:" + topic, false);
Log.d("FCMUtil", "suc:" + topic);
// EventUtils.INSTANCE.event("FCM_Topic_" + topic, null, null, false);
} else {
Log.d("FCMUtil", "fail");
......
package com.example.mydemo.strategy.fcm
import android.util.SparseArray
import androidx.core.util.isNotEmpty
import com.example.mydemo.strategy.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.example.mydemo.strategy.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
package com.example.mydemo.strategy.fcm
import android.app.NotificationManager
import android.content.Context
import com.base.locationsharewhite.helper.ConfigHelper
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.KotlinExt.toFormatMinute
import android.util.SparseArray
import androidx.core.util.isEmpty
import androidx.core.util.size
import com.example.mydemo.strategy.notification.NotificationBean
import com.example.mydemo.strategy.notification.NotificationHoverUtils
import com.example.mydemo.strategy.notification.NotificationMgr
import com.example.mydemo.strategy.service.StayNotificationService.Companion.startStayNotification
import com.example.mydemo.strategy.utils.AppPreferences
import com.example.mydemo.strategy.utils.toFormatMinute
import com.example.mydemo.strategy.utils.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 包名+首次启动的当前的分钟
*/
......@@ -15,14 +36,113 @@ object MsgMgr {
set(value) = AppPreferences.getInstance().put("topic", value)
fun init(context: Context) {
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 = this.topic
if (topic.isEmpty()) {
val topicNumber = System.currentTimeMillis().toFormatMinute()
topic = ConfigHelper.packageName + "_$topicNumber"
topic = packageName + "_$topicNumber"
this.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.example.mydemo.strategy.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.annotation.SuppressLint
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Icon
import android.os.Build
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import androidx.core.graphics.drawable.IconCompat
import com.base.locationsharewhite.BuildConfig
import com.base.locationsharewhite.R
import com.base.locationsharewhite.fcm.PopupConstObject.NOTIFICATION_ACTION_MY_CODE
import com.base.locationsharewhite.fcm.PopupConstObject.POPUP_WHERE_FCM
import com.base.locationsharewhite.fcm.PopupConstObject.POPUP_WHERE_LOCK
import com.base.locationsharewhite.fcm.PopupConstObject.POPUP_WHERE_TIMBER
import com.base.locationsharewhite.fcm.PopupConstObject.popup_count
import com.base.locationsharewhite.fcm.PopupConstObject.popup_end
import com.base.locationsharewhite.fcm.PopupConstObject.popup_fcm_interval
import com.base.locationsharewhite.fcm.PopupConstObject.popup_interval
import com.base.locationsharewhite.fcm.PopupConstObject.popup_lock_interval
import com.base.locationsharewhite.fcm.PopupConstObject.popup_start
import com.base.locationsharewhite.fcm.PopupConstObject.popup_status
import com.base.locationsharewhite.fcm.PopupConstObject.popup_timer_interval
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.helper.MyApplication
import com.base.locationsharewhite.ui.splash.SplashActivity
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.LogEx
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import kotlin.random.Random
/**
* 构建发送通知 UI
* 用的actionId代替notificationId使用
*/
object NotificationUiUtil {
private val TAG = "NotificationUiUtil"
var NOTIFICATION_ID = Random.nextInt(1000, 1800)
private fun currentDate(): String {
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val currentDate = Calendar.getInstance().time
return dateFormat.format(currentDate)
}
//当天推送次数
private var dayPopupCount = 0
get() {
return AppPreferences.getInstance().getInt("dayPopupCount_${currentDate()}", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("dayPopupCount_${currentDate()}", value, true)
}
private var lastPopupTime = 0L
get() {
return AppPreferences.getInstance().getLong("lastPopupTime", field)
}
set(value) {
field = value
AppPreferences.getInstance().put("lastPopupTime", value, true)
}
private fun canSendNotification(where: String, actionId: String): Boolean {
//是否开启推送
val status = AppPreferences.getInstance().getString(popup_status, "1").toInt()
if (status == 0) {
EventUtils.event("Notification_Error", "status=$status")
LogEx.logDebug("canSendNotification", "status")
return false
}
//当天推送次数
val count = AppPreferences.getInstance().getString(popup_count, "20").toInt()
if (dayPopupCount > count) {
LogEx.logDebug("canSendNotification", "count")
EventUtils.event("Notification_Error", "dayPopupCount=$dayPopupCount count=$count where=$where actionId=$actionId")
return false
}
//判断是否在时间区域
val start = AppPreferences.getInstance().getString(popup_start, "0").toInt()
val end = AppPreferences.getInstance().getString(popup_end, "24").toInt()
val calendar = Calendar.getInstance()
val currentHour = calendar.get(Calendar.HOUR_OF_DAY)
if (currentHour !in start until end) {
LogEx.logDebug("canSendNotification", "start-end currentHour=$currentHour start=$start end=$end")
EventUtils.event("Notification_Error", "start=$start end=$end currentHour=$currentHour where=$where actionId=$actionId")
return false
}
//单位分钟
var interval = AppPreferences.getInstance().getString(popup_interval, "1").toInt()
if (where == POPUP_WHERE_TIMBER) {
interval = AppPreferences.getInstance().getString(popup_timer_interval, "7").toInt()
}
if (where == POPUP_WHERE_LOCK) {
interval = AppPreferences.getInstance().getString(popup_lock_interval, "1").toInt()
}
if (where == POPUP_WHERE_FCM) {
interval = AppPreferences.getInstance().getString(popup_fcm_interval, "1").toInt()
}
val passedTime = System.currentTimeMillis() - lastPopupTime
if (passedTime < interval * 60 * 1000L) {
EventUtils.event("Notification_Error", "where=$where actionId=$actionId interval=$interval passedTime=$passedTime")
LogEx.logDebug("canSendNotification", "interval where=$where passedTime=$passedTime interval=$interval")
return false
}
return true
}
var hoverActionId = ""
@SuppressLint("RemoteViewLayout")
fun sendNotificationIfCan(context: Context, actionId: String, where: String = "") {
if (!canSendNotification(where, actionId)) return
//发送通知
setActionNotification(context, actionId)
//上报通知
EventUtils.event("Notification_Popup", "where=$where actionId=$actionId")
//当天次数加一
dayPopupCount += 1
//推送时间
lastPopupTime = System.currentTimeMillis()
//悬停通知
hoverActionId = actionId
NotificationHoverUtils.sendHoverNotification(context)
}
fun setActionNotification(context: Context, actionId: String) {
// val bigRemoteViews = RemoteViews(MyApplication.context.packageName, R.layout.notification_message)
// val smallRemoteViews = RemoteViews(MyApplication.context.packageName, R.layout.notification_message)
val intent = Intent(context, SplashActivity::class.java)
intent.putExtra("actionId", actionId)
when (actionId) {
else -> {
EventUtils.event("Notification_Error", "unKnow actionId actionId=$actionId")
}
}
}
private fun sendCustomNotification(
context: Context,
intent: Intent,
bigRemoteViews: RemoteViews,
smallRemoteViews: RemoteViews,
) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = context.resources.getString(R.string.app_name).replace(" ", "_") + "_channel_id"
val channelName = context.resources.getString(R.string.app_name).replace(" ", "_") + "_channel"
//创建channel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
channelId, channelName, NotificationManager.IMPORTANCE_HIGH
)
channel.lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
notificationManager.createNotificationChannel(channel)
}
// Create the notification
val builder: NotificationCompat.Builder = NotificationCompat.Builder(context, channelId)
//设置状态栏内的小图标
val smallIcon = IconCompat.createFromIcon(
context, Icon.createWithResource(
context, R.mipmap.logo
)
)
smallIcon?.let {
builder.setSmallIcon(smallIcon)
}
builder.setContentTitle(context.resources.getString(R.string.app_name))
.setContentText("notification")
val requestCode = Random.nextInt(1000)
val pendingIntent = PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE)
builder.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setAutoCancel(true)
var small: RemoteViews? = bigRemoteViews
//Android 12以下需要适配小RemoteViews
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
small = smallRemoteViews
}
// 设置小视图
builder.setCustomContentView(smallRemoteViews)
// 设置悬浮通知视图(Android 12 及以上)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
builder.setCustomHeadsUpContentView(bigRemoteViews)
}
// 设置大视图
builder.setCustomBigContentView(bigRemoteViews)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
val looper_actionId = listOf(
NOTIFICATION_ACTION_MY_CODE
)
var actionIdList = arrayListOf<String>()
fun getNextActionId(): String {
if (actionIdList.isEmpty()) {
actionIdList.addAll(looper_actionId)
}
val next = actionIdList[0]
actionIdList.removeAt(0)
if (BuildConfig.DEBUG) {
// return NOTIFICATION_ACTION_WEATHER
}
return next
}
}
\ No newline at end of file
package com.base.locationsharewhite.fcm
package com.example.mydemo.strategy.fcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import com.example.mydemo.strategy.utils.AppPreferences
import com.example.mydemo.strategy.utils.toFormatTime4
class PackageStatusReceiver : BroadcastReceiver() {
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) {
fun registerBatteryReceiver(context: Context, configBean: MsgConfigBean) {
val intentFilter = IntentFilter().apply {
addAction(Intent.ACTION_PACKAGE_ADDED)
addAction(Intent.ACTION_PACKAGE_REMOVED)
......@@ -18,20 +28,32 @@ class PackageStatusReceiver : BroadcastReceiver() {
val applicationContext = context.applicationContext
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
applicationContext.registerReceiver(
PackageStatusReceiver(),
PackageStatusReceiver(configBean),
intentFilter,
Context.RECEIVER_EXPORTED
)
} else {
applicationContext.registerReceiver(PackageStatusReceiver(), intentFilter)
applicationContext.registerReceiver(PackageStatusReceiver(configBean), intentFilter)
}
}
}
override fun onReceive(context: Context?, intent: Intent?) {
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)
}
}
}
......
package com.base.locationsharewhite.fcm
object PopupConstObject {
const val NOTIFICATION_ACTION_HOME = "notification_action_home"
const val NOTIFICATION_ACTION_MY_CODE = "notification_action_my_code"
const val NOTIFICATION_ACTION_SHARE = "notification_action_share"
const val NOTIFICATION_ACTION_SETTINGS = "notification_action_settings"
const val POPUP_WHERE_TIMBER = "Timer"
const val POPUP_WHERE_LOCK = "Lock"
const val POPUP_WHERE_FCM = "fcm"
//推送总开关 0 关 1开
val popup_status = "popup_status"
//推送总数量现在
val popup_count = "popup_count"//所有常规推送的当日推送次数限制,0 为不限制
val popup_start = "popup_start"
val popup_end = "popup_end"
val popup_interval = "popup_interval"
val popup_timer_interval = "popup_timber_interval"
val popup_lock_interval = "popup_lock_interval"
val popup_fcm_interval = "popup_fcm_interval"
}
\ No newline at end of file
package com.example.mydemo.strategy.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;
package com.example.mydemo.strategy.fcm
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import com.example.mydemo.strategy.utils.AppPreferences
import com.example.mydemo.strategy.utils.toFormatTime4
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
class ScreenStatusReceiver(private val configBean: MsgConfigBean) : BroadcastReceiver() {
import com.base.locationsharewhite.utils.AppPreferences;
private var count
get() = AppPreferences.getInstance().getInt("screen_count", 0)
set(value) = AppPreferences.getInstance().putInt("screen_count", value)
import java.util.Objects;
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
public class ScreenStatusReceiver extends BroadcastReceiver {
private static boolean isDeviceInteractive = true;
private static boolean isSecureLockActive = false;
public static void setupScreenStatusListener(Context context) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_USER_PRESENT);
final Context applicationContext = context.getApplicationContext();
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(new ScreenStatusReceiver(), intentFilter, Context.RECEIVER_EXPORTED);
applicationContext.registerReceiver(
ScreenStatusReceiver(configBean),
intentFilter,
Context.RECEIVER_EXPORTED
)
} else {
applicationContext.registerReceiver(new ScreenStatusReceiver(), intentFilter);
applicationContext.registerReceiver(ScreenStatusReceiver(configBean), intentFilter)
}
}
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (Objects.requireNonNull(action)) {
case Intent.ACTION_SCREEN_ON:
setDeviceInteractive(true);
break;
case Intent.ACTION_SCREEN_OFF:
setDeviceInteractive(false);
setSecureLockActive(true);
break;
case Intent.ACTION_USER_PRESENT:
setSecureLockActive(false);
if (isDeviceInteractive() && !isSecureLockActive()) {
int secureSetting = Integer.parseInt(AppPreferences.getInstance().getString("lockS", "1"));
if (secureSetting == 1) {
// String actionId = NotificationUiUtil.INSTANCE.getNextActionId();
// NotificationUiUtil.INSTANCE.sendNotificationIfCan(context, actionId, PopupConstObject.POPUP_WHERE_LOCK);
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
}
break;
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)
}
private void setDeviceInteractive(boolean interactive) {
isDeviceInteractive = interactive;
}
public static boolean isDeviceInteractive() {
return isDeviceInteractive;
}
private void setSecureLockActive(boolean active) {
isSecureLockActive = active;
}
public static boolean isSecureLockActive() {
return isSecureLockActive;
}
}
\ No newline at end of file
package com.base.locationsharewhite.fcm;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
public class TimerManager {
private static TimerManager instance;
private Timer taskTimer;
private boolean isTimerActive;
private TimerManager() {
// 私有构造方法
}
public static synchronized TimerManager getInstance() {
if (instance == null) {
instance = new TimerManager();
package com.example.mydemo.strategy.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)
}
return instance;
}
public void scheduleTask(long delay, long period) {
synchronized (TimerManager.class) {
ensureTimerIsStopped(); // 确保定时器未运行
taskTimer = new Timer(); // 创建新的 Timer 实例
TimerTask task = new TimerTask() {
@Override
public void run() {
Log.d("glc", "Scheduled task is running");
// 确保设备处于交互状态,未锁定,且应用未暂停
}
};
taskTimer.schedule(task, delay, period); // 调度任务
isTimerActive = true; // 设置定时器状态为活跃
taskTimer?.schedule(task, delay, period) // 调度任务
isTaskTimerActive = true // 设置定时器状态为活跃
}
}
private void ensureTimerIsStopped() {
if (isTimerActive) {
private fun ensureTimerIsStopped() {
if (isTaskTimerActive) {
if (taskTimer != null) {
taskTimer.cancel();
taskTimer.purge(); // 清除所有取消的任务
taskTimer?.cancel()
taskTimer?.purge() // 清除所有取消的任务
}
isTimerActive = false; // 重置定时器状态
isTaskTimerActive = false // 重置定时器状态
}
}
public void stopTaskTimer() {
synchronized (TimerManager.class) {
ensureTimerIsStopped(); // 停止定时器
}
fun stopTaskTimer() {
synchronized(TimerManager::class.java) {
ensureTimerIsStopped() // 停止定时器
}
public boolean isTaskTimerActive() {
return isTimerActive;
}
}
\ No newline at end of file
package com.base.locationsharewhite.helper
import com.base.locationsharewhite.BuildConfig
import com.base.locationsharewhite.R
/**
* 部分常量配置相关,如admob广告位id与通知渠道等
*/
object ConstConfig {
//用于替换广告位,区分debug与正式广告位id
inline val openAdId get() = if (BuildConfig.DEBUG) openAdmobIdTest else openAdmobId
inline val bannerAdId get() = if (BuildConfig.DEBUG) bannerAdmobIdTest else bannerAdmobId
inline val insertAdId get() = if (BuildConfig.DEBUG) interAdmobIdTest else interAdmobId
inline val nativeAdId get() = if (BuildConfig.DEBUG) nativeAdmobIdTest else nativeAdmobId
//云控remoteConfig对应的key
const val REMOTE_MSG = "MsgConfig"//云控消息配置对应的key
const val REMOTE_AD = "AdmobConfig"//云控广告配置对应的key
//admob test id
const val openAdmobIdTest = "ca-app-pub-3940256099942544/9257395921"
const val bannerAdmobIdTest = "ca-app-pub-3940256099942544/9214589741"
const val interAdmobIdTest = "ca-app-pub-3940256099942544/1033173712"
const val nativeAdmobIdTest = "ca-app-pub-3940256099942544/2247696110"
// admob广告id
const val interAdmobId = "ca-app-pub-3940256099942544/1033173111"
const val nativeAdmobId = "ca-app-pub-3940256099942544/2247696111"
const val openAdmobId = "/6499/example/app-open"
const val bannerAdmobId = "ca-app-pub-3940256099942544/9214581111"
//通知相关配置
const val CHANNEL_ID = "msg_notification_id" // 通知渠道ID
const val CHANNEL_NAME = "msg_fcm_channel" // 通知渠道名称
const val NOTIFICATION_ID = 8889//普通通知id
const val NOTIFICATION_PERMANENT_ID = 10001//常驻通知id
val NOTIFICATION_LOGO = R.mipmap.logo
//通知栏的一些应用事件,自己定咯,不同事件和不同的自定义通知栏视图
const val ACTION_0 = 0
const val ACTION_1 = 1
const val ACTION_2 = 2
}
\ No newline at end of file
package com.base.locationsharewhite.helper
import android.annotation.SuppressLint
import android.content.Context
import com.example.mydemo.strategy.ads.AdsConfigBean
import com.example.mydemo.strategy.ads.AdsMgr
import com.example.mydemo.strategy.fcm.MsgConfig
import com.example.mydemo.strategy.fcm.MsgConfigBean
import com.example.mydemo.strategy.fcm.MsgMgr
import com.example.mydemo.strategy.fcm.MsgType
import com.example.mydemo.strategy.fcm.RemoteConfigBean
import com.example.mydemo.strategy.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
......@@ -5,11 +5,16 @@ 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.bean.ConstObject.topic_number
import com.base.locationsharewhite.ui.main.MainActivity
import com.base.locationsharewhite.ui.splash.SplashActivity
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.KotlinExt.toFormatMinute
import com.base.locationsharewhite.utils.LogEx
import com.example.mydemo.strategy.fcm.MsgConfig
import com.example.mydemo.strategy.notification.NotificationBean
import java.util.Locale
import java.util.UUID
......@@ -70,6 +75,8 @@ class MyApplication : Application() {
// InstallHelps.init()
initLifeListener()
// ScreenStatusReceiver.setupScreenStatusListener(this)
initAdSdk()
}
......@@ -141,4 +148,59 @@ class MyApplication : Application() {
})
}
fun initAdSdk() {
GoogleSdkMgr.init(
this, this.packageName, MsgConfig.Builder()
//设置自己的自定义通知栏
// .addRemoteBean(
// //自定义的通知事件id,可以自己添加
// ConstConfig.ACTION_0, NotificationBean(
// Intent(this, MainActivity::class.java).apply {
// //悬停点击标记,用来进入当相关页面进行悬停关闭
// putExtra("hover", true)
// },
// RemoteViews(packageName, R.layout.notification_message).apply {
// setTextViewText(R.id.tv_tittle, "大标题0")
// setTextViewText(R.id.tv_desc, "大内容0")
// },
// RemoteViews(packageName, R.layout.notification_message).apply {
// setTextViewText(R.id.tv_tittle, "小标题0")
// setTextViewText(R.id.tv_desc, "小内容0")
// }
// )
// )
// .addRemoteBean(
// ConstConfig.ACTION_1, NotificationBean(
// Intent(this, MainActivity::class.java).apply {
// putExtra("hover", true)
// },
// RemoteViews(packageName, R.layout.notification_message).apply {
// setTextViewText(R.id.tv_tittle, "大标题1")
// setTextViewText(R.id.tv_desc, "大内容1")
// },
// RemoteViews(packageName, R.layout.notification_message).apply {
// setTextViewText(R.id.tv_tittle, "小标题1")
// setTextViewText(R.id.tv_desc, "小内容1")
// }
// )
// )
// .addRemoteBean(
// ConstConfig.ACTION_2, NotificationBean(
// Intent(this, MainActivity::class.java).apply {
// putExtra("hover", true)
// },
// RemoteViews(packageName, R.layout.notification_junk_big).apply {
// setTextViewText(R.id.tv_size, "大标题2")
// setTextViewText(R.id.tv_desc, "大内容2")
// },
// RemoteViews(packageName, R.layout.notification_junk_small).apply {
// setTextViewText(R.id.tv_size, "小标题2")
// setTextViewText(R.id.tv_desc, "小内容2")
// }
// )
// )
.build()
)
}
}
\ No newline at end of file
package com.example.mydemo.strategy.notification
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.example.mydemo.strategy.fcm.MsgMgr
/**
*通知刪除监听
*/
class DeleteNotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
MsgMgr.stopHoverNotification()
}
}
\ No newline at end of file
package com.example.mydemo.strategy.notification
import android.content.Intent
import android.widget.RemoteViews
/**
* 自定义通知栏的配置
* @property intent 自定义通知栏传入的跳转或者传参之类
* @property bigRemoteViews
* @property smallRemoteViews
*/
class NotificationBean(
val intent: Intent, val bigRemoteViews: RemoteViews,
val smallRemoteViews: RemoteViews
)
\ No newline at end of file
package com.base.locationsharewhite.fcm
package com.example.mydemo.strategy.notification
import android.content.Context
import android.os.Handler
import android.os.HandlerThread
import com.base.locationsharewhite.helper.MyApplication
import com.base.locationsharewhite.utils.AppPreferences
import com.base.locationsharewhite.utils.LogEx
import com.example.mydemo.strategy.fcm.MsgMgr
object NotificationHoverUtils {
......@@ -17,13 +15,7 @@ object NotificationHoverUtils {
/**
* 发送悬停通知
*/
fun sendHoverNotification(context: Context) {
val hoverCount = AppPreferences.getInstance().getString("popup_hover_count", "0").toInt()
val hoverDelay = AppPreferences.getInstance().getString("popup_hover_delay", "0").toLong()
val hoverStatus = AppPreferences.getInstance().getString("popup_hover_status", "0").toInt()
if (hoverStatus == 0) return
fun sendHoverNotification(context: Context, hoverCount: Int, hoverDelay: Int) {
if (handlerThread == null) {
handlerThread = HandlerThread("NotificationHandlerThread")
handlerThread?.start()
......@@ -37,17 +29,10 @@ object NotificationHoverUtils {
}
for (i in 1..hoverCount) {
val time = i * hoverDelay
val time = (i * hoverDelay * 1000 + 300).toLong()
handler?.postDelayed(Runnable {
LogEx.logDebug(TAG, "handler ${MyApplication.PAUSED_VALUE}")
if (MyApplication.PAUSED_VALUE == 1) {
handler?.removeCallbacksAndMessages(null)
return@Runnable
}
if (MyApplication.PAUSED_VALUE != 1) {
LogEx.logDebug(TAG, "handler send notification")
NotificationUiUtil.setActionNotification(context, NotificationUiUtil.hoverActionId)
}
//发送普通通知,不需要在悬停,这里就关闭了悬停
MsgMgr.sendNotification(context, MsgMgr.hoverActionId, 0)
}, time)
}
}
......
package com.example.mydemo.strategy.notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Icon
import android.os.Build
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import androidx.core.graphics.drawable.IconCompat
import com.base.locationsharewhite.helper.ConstConfig
import com.example.mydemo.R
import kotlin.random.Random
/**
*通知管理类
*/
object NotificationMgr {
fun init(context: Context) {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//创建channel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
ConstConfig.CHANNEL_ID,
ConstConfig.CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH
)
channel.lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
channel.enableLights(false)
channel.enableVibration(false)
channel.vibrationPattern = longArrayOf(0)
channel.setSound(null, null)
notificationManager.createNotificationChannel(channel)
}
}
fun sendNotification(
context: Context, intent: Intent,
bigRemoteViews: RemoteViews,
smallRemoteViews: RemoteViews
) {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Create the notification
val builder: NotificationCompat.Builder =
NotificationCompat.Builder(context, ConstConfig.CHANNEL_ID)
//设置状态栏内的小图标
val smallIcon = IconCompat.createFromIcon(
context, Icon.createWithResource(
context, ConstConfig.NOTIFICATION_LOGO
)
)
smallIcon?.let {
builder.setSmallIcon(smallIcon)
}
builder.setContentTitle(context.resources.getString(R.string.app_name))
.setContentText("notification")
val requestCode = Random.nextInt(1000)
val pendingIntent =
PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE)
val deleteIntent =
PendingIntent.getBroadcast(
context,
requestCode,
Intent(context, DeleteNotificationReceiver::class.java),
PendingIntent.FLAG_IMMUTABLE
)
builder.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setAutoCancel(true)
.setVibrate(longArrayOf(0))
.setSound(null)
.setDeleteIntent(deleteIntent)
// 设置小视图
builder.setCustomContentView(smallRemoteViews)
// 设置悬浮通知视图(Android 12 及以上)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
builder.setCustomHeadsUpContentView(bigRemoteViews)
}
// 设置大视图
builder.setCustomBigContentView(bigRemoteViews)
notificationManager.notify(ConstConfig.NOTIFICATION_ID, builder.build())
}
}
\ No newline at end of file
package com.base.locationsharewhite.service
package com.example.mydemo.strategy.service
import android.app.Notification
import android.app.NotificationChannel
......@@ -15,14 +15,10 @@ import android.os.IBinder
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import androidx.core.graphics.drawable.IconCompat
import com.base.locationsharewhite.R
import com.base.locationsharewhite.fcm.PopupConstObject
import com.base.locationsharewhite.helper.EventUtils
import com.base.locationsharewhite.helper.MyApplication
import com.base.locationsharewhite.ui.main.MainActivity
import com.base.locationsharewhite.ui.splash.SplashActivity
import com.base.locationsharewhite.utils.LogEx
import kotlin.random.Random
import com.base.locationsharewhite.helper.ConstConfig
import com.base.locationsharewhite.helper.GoogleSdkMgr
import com.example.mydemo.MainActivity
import com.example.mydemo.R
/**
......@@ -61,72 +57,16 @@ class StayNotificationService : Service() {
fun createPermanentNotification(context: Context): Notification {
val channelName = "PDF Reader Foreground Service Channel"
val channelId = "PDF_Reader_Service_Id"
val channelName = "Permanent Foreground Service Channel"
val channelId = "permanent_channel"
val contentView = RemoteViews(context.packageName, R.layout.stay_notification_big)
val expendView = RemoteViews(context.packageName, R.layout.stay_notification_big)
val requestCode1 = Random.nextInt(1800)
val intent1 = Intent(context, SplashActivity::class.java).apply {
putExtra("actionId", PopupConstObject.NOTIFICATION_ACTION_HOME)
}
val pendingIntent1 =
PendingIntent.getActivity(
context,
requestCode1,
intent1,
PendingIntent.FLAG_IMMUTABLE
)
contentView.setOnClickPendingIntent(R.id.ll_1, pendingIntent1)
expendView.setOnClickPendingIntent(R.id.ll_1, pendingIntent1)
val requestCode2 = Random.nextInt(1800)
val intent2 = Intent(context, SplashActivity::class.java).apply {
putExtra("actionId", PopupConstObject.NOTIFICATION_ACTION_MY_CODE)
}
val pendingIntent2 =
PendingIntent.getActivity(
context,
requestCode2,
intent2,
PendingIntent.FLAG_IMMUTABLE
)
contentView.setOnClickPendingIntent(R.id.ll_2, pendingIntent2)
expendView.setOnClickPendingIntent(R.id.ll_2, pendingIntent2)
val requestCode3 = Random.nextInt(1800)
val intent3 = Intent(context, SplashActivity::class.java).apply {
putExtra("actionId", PopupConstObject.NOTIFICATION_ACTION_SHARE)
}
val pendingIntent3 =
PendingIntent.getActivity(
context,
requestCode3,
intent3,
PendingIntent.FLAG_IMMUTABLE
)
contentView.setOnClickPendingIntent(R.id.ll_3, pendingIntent3)
expendView.setOnClickPendingIntent(R.id.ll_3, pendingIntent3)
val requestCode4 = Random.nextInt(1800)
val intent4 = Intent(context, SplashActivity::class.java).apply {
putExtra("actionId", PopupConstObject.NOTIFICATION_ACTION_SETTINGS)
}
val pendingIntent4 =
PendingIntent.getActivity(
context,
requestCode4,
intent4,
PendingIntent.FLAG_IMMUTABLE
)
contentView.setOnClickPendingIntent(R.id.ll_4, pendingIntent4)
expendView.setOnClickPendingIntent(R.id.ll_4, pendingIntent4)
val builder = NotificationCompat.Builder(context, channelId)
val smallIcon = IconCompat.createFromIcon(
context, Icon.createWithResource(
MyApplication.context, R.mipmap.logo
context, ConstConfig.NOTIFICATION_LOGO
)
)
......@@ -137,11 +77,17 @@ class StayNotificationService : Service() {
val nfIntent = Intent(context, MainActivity::class.java)
val pendingIntent =
PendingIntent.getActivity(context, 0, nfIntent, PendingIntent.FLAG_IMMUTABLE)
builder.setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.logo))
builder.setLargeIcon(
BitmapFactory.decodeResource(
context.resources,
ConstConfig.NOTIFICATION_LOGO
)
)
builder.setContentTitle(context.resources.getString(R.string.app_name))
builder.setContentIntent(pendingIntent) //设置PendingIntent
builder.setVisibility(NotificationCompat.VISIBILITY_PRIVATE) //设置通知公开可见
builder.setAutoCancel(false)
builder.setOngoing(true)
builder.setPriority(NotificationCompat.PRIORITY_MAX) //优先级为:重要通知
builder.setWhen(System.currentTimeMillis())
builder.setCustomContentView(contentView)
......@@ -163,13 +109,13 @@ class StayNotificationService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
LogEx.logDebug(TAG, "onStartCommand intent=$intent")
//LogEx.logDebug(TAG, "onStartCommand intent=$intent")
if (intent == null) {
EventUtils.event("onStartCommand", "Foreground System auto launch intent=null isRunning=$isRunning")
// EventUtils.event("onStartCommand", "Foreground System auto launch intent=null isRunning=$isRunning")
return START_NOT_STICKY
}
if (!isRunning) {
LogEx.logDebug(TAG, "onStartCommand startForeground")
// LogEx.logDebug(TAG, "onStartCommand startForeground")
startForeground()
isRunning = true
......@@ -179,11 +125,15 @@ class StayNotificationService : Service() {
}
private fun startForeground() {
val notification = createPermanentNotification(MyApplication.context)
val notification = createPermanentNotification(GoogleSdkMgr.context)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(100, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
startForeground(
ConstConfig.NOTIFICATION_PERMANENT_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
)
} else {
startForeground(100, notification)
startForeground(ConstConfig.NOTIFICATION_PERMANENT_ID, notification)
}
isRunning = true
}
......@@ -197,5 +147,4 @@ class StayNotificationService : Service() {
isRunning = false
super.onDestroy()
}
}
\ 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