1.添加admob广告
2.添加广告打点
This commit is contained in:
parent
e714a1ef63
commit
905c97e415
6
.idea/studiobot.xml
generated
Normal file
6
.idea/studiobot.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="StudioBotProjectSettings">
|
||||||
|
<option name="shareContext" value="OptedIn" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@ -26,6 +26,7 @@
|
|||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||||
tools:ignore="ScopedStorage" />
|
tools:ignore="ScopedStorage" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".PRApp"
|
android:name=".PRApp"
|
||||||
@ -40,9 +41,11 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.PDFReaderPro">
|
android:theme="@style/Theme.PDFReaderPro">
|
||||||
|
<!--正式ID ca-app-pub-5717753826607607~1850604454 -->
|
||||||
|
<!--google测试ID ca-app-pub-3940256099942544~3347511713 -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
||||||
android:value="ca-app-pub-3940256099942544~3347511713" />
|
android:value="ca-app-pub-5717753826607607~1850604454" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.max_aspect"
|
android:name="android.max_aspect"
|
||||||
android:value="2.4" />
|
android:value="2.4" />
|
||||||
|
|||||||
@ -46,10 +46,11 @@ class PRApp : Application() {
|
|||||||
UpLoadManager.init(context = this, tag = "PRApp_upload_task") { _, _ -> }
|
UpLoadManager.init(context = this, tag = "PRApp_upload_task") { _, _ -> }
|
||||||
// 广告初始化
|
// 广告初始化
|
||||||
MobileAds.initialize(this)
|
MobileAds.initialize(this)
|
||||||
|
initMobileAds()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initMobileAds() {
|
private fun initMobileAds() {
|
||||||
val testDeviceIds = listOf("TEST_DEVICE_ID")
|
val testDeviceIds = listOf("9A96E667D69B45F744FD7D724DF8B093")
|
||||||
val configuration = RequestConfiguration.Builder().setTestDeviceIds(testDeviceIds).build()
|
val configuration = RequestConfiguration.Builder().setTestDeviceIds(testDeviceIds).build()
|
||||||
MobileAds.setRequestConfiguration(configuration)
|
MobileAds.setRequestConfiguration(configuration)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,48 +19,55 @@ class AdInstLoad(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadAd() {
|
private fun loadAd() {
|
||||||
//多处调用load,也不会重复、不影响缓存广告、展示安全
|
Log.d(AdsInsUtil.Placement.TAG,"广告 $placement 开始加载")
|
||||||
|
// 是否存在缓存广告、展示安全
|
||||||
val cachedAd = InstAdCacheManager.instance.getAdCache(placement)
|
val cachedAd = InstAdCacheManager.instance.getAdCache(placement)
|
||||||
if (cachedAd != null) {
|
if (cachedAd != null) {
|
||||||
Log.d("ocean","广告存在缓存,跳过加载,返回成功")
|
Log.d(AdsInsUtil.Placement.TAG,"广告存在缓存,跳过加载,返回成功")
|
||||||
//缓存广告有效,跳过加载,返回成功
|
//缓存广告有效,跳过加载,返回成功
|
||||||
adLoadListener?.loaded(cachedAd)
|
adLoadListener?.loaded(cachedAd)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果正在加载(其他地方已 load) 加入等待队列
|
||||||
|
if (InstAdCacheManager.instance.isLoading(placement)) {
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG,"广告 $placement 正在加载中,加入等待队列")
|
||||||
|
InstAdCacheManager.instance.addPendingListener(placement, adLoadListener)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 本次为真正开始加载 标记 loading(并加入当前 listener)
|
||||||
|
InstAdCacheManager.instance.markLoading(placement, adLoadListener)
|
||||||
|
|
||||||
val adUnitId = AdsInsUtil.adUnitIdMap[placement] ?: run {
|
val adUnitId = AdsInsUtil.adUnitIdMap[placement] ?: run {
|
||||||
val errorMsg = "No AdUnitId for $placement"
|
val errorMsg = "No AdUnitId for $placement"
|
||||||
Log.d("ocean","没找到对应的广告ID->$placement")
|
Log.d(AdsInsUtil.Placement.TAG,"没找到对应的广告ID->$placement")
|
||||||
adLoadListener?.loadFailed(errorMsg)
|
InstAdCacheManager.instance.notifyFail(placement, errorMsg)
|
||||||
AnalyticsUtils.logAdEvent(
|
|
||||||
placement,
|
|
||||||
AnalyticsUtils.AdEvent.LOAD_FAIL,
|
|
||||||
null,
|
|
||||||
errorMsg
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.REQ)
|
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.REQ)
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG,"adUnitId->$adUnitId")
|
||||||
InterstitialAd.load(
|
InterstitialAd.load(
|
||||||
activity,
|
activity,
|
||||||
adUnitId,
|
adUnitId,
|
||||||
AdRequest.Builder().build(),
|
AdRequest.Builder().build(),
|
||||||
object : InterstitialAdLoadCallback() {
|
object : InterstitialAdLoadCallback() {
|
||||||
override fun onAdLoaded(ad: InterstitialAd) {
|
override fun onAdLoaded(ad: InterstitialAd) {
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG,"广告 $placement 加载成功")
|
||||||
InstAdCacheManager.instance.setAdCache(placement, ad)
|
InstAdCacheManager.instance.setAdCache(placement, ad)
|
||||||
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.LOADED)
|
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.LOADED)
|
||||||
adLoadListener?.loaded(ad)
|
InstAdCacheManager.instance.notifySuccess(placement, ad)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAdFailedToLoad(adError: LoadAdError) {
|
override fun onAdFailedToLoad(adError: LoadAdError) {
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG,"广告 $placement 加载失败")
|
||||||
AnalyticsUtils.logAdEvent(
|
AnalyticsUtils.logAdEvent(
|
||||||
placement,
|
placement,
|
||||||
AnalyticsUtils.AdEvent.LOAD_FAIL,
|
AnalyticsUtils.AdEvent.LOAD_FAIL,
|
||||||
adError.code,
|
adError.code,
|
||||||
adError.message
|
adError.message
|
||||||
)
|
)
|
||||||
adLoadListener?.loadFailed(adError.toString())
|
InstAdCacheManager.instance.notifyFail(placement, adError.toString())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,38 +1,52 @@
|
|||||||
package com.all.pdfreader.pdf.reader.ad
|
package com.all.pdfreader.pdf.reader.ad
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import com.all.pdfreader.pdf.reader.ad.AdsInsUtil.AdPlacement
|
import android.util.Log
|
||||||
import com.all.pdfreader.pdf.reader.util.AnalyticsUtils
|
import com.all.pdfreader.pdf.reader.util.AnalyticsUtils
|
||||||
import com.google.android.gms.ads.AdError
|
import com.google.android.gms.ads.AdError
|
||||||
import com.google.android.gms.ads.FullScreenContentCallback
|
import com.google.android.gms.ads.FullScreenContentCallback
|
||||||
|
|
||||||
class AdInstShower(
|
class AdInstShower(
|
||||||
private val activity: Activity,
|
private val activity: Activity,
|
||||||
private val placement: AdPlacement,
|
private val placement: AdsInsUtil.AdPlacement,
|
||||||
private val showListener: ShowListener?
|
private val showListener: ShowListener?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SHOW_INTERVAL = 60_000L // 60秒间隔
|
||||||
|
private var lastShowTimestamp = 0L
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
showAd()
|
showAd()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showAd() {
|
private fun showAd() {
|
||||||
|
// ======= 新增:全局展示间隔限制 启动页不处理 =======
|
||||||
|
if(placement != AdsInsUtil.AdPlacement.SPL_AND_INTO_HOME){
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
if (now - lastShowTimestamp < SHOW_INTERVAL) {
|
||||||
|
val errorMsg = "广告展示过于频繁, 请等待 ${(SHOW_INTERVAL - (now - lastShowTimestamp)) / 1000}s(间隔限制60s)"
|
||||||
|
showListener?.onAdShowFailed(errorMsg)
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "showAd->$errorMsg")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val interstitialAd = InstAdCacheManager.instance.getAdCache(placement)
|
val interstitialAd = InstAdCacheManager.instance.getAdCache(placement)
|
||||||
?: run {
|
?: run {
|
||||||
val errorMsg = "InterstitialAd cache is null for place = $placement"
|
val errorMsg = "插页式广告缓存为空 = $placement"
|
||||||
AnalyticsUtils.logAdEvent(
|
|
||||||
placement,
|
|
||||||
AnalyticsUtils.AdEvent.SHOW_FAIL,
|
|
||||||
null,
|
|
||||||
errorMsg
|
|
||||||
)
|
|
||||||
showListener?.onAdShowFailed(errorMsg)
|
showListener?.onAdShowFailed(errorMsg)
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "showAd->$errorMsg")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
interstitialAd.fullScreenContentCallback = object : FullScreenContentCallback() {
|
interstitialAd.fullScreenContentCallback = object : FullScreenContentCallback() {
|
||||||
|
|
||||||
override fun onAdShowedFullScreenContent() {
|
override fun onAdShowedFullScreenContent() {
|
||||||
|
// ======= 新增:展示成功 记录时间 =======
|
||||||
|
lastShowTimestamp = System.currentTimeMillis()
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "广告 $placement 展示成功")
|
||||||
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.SHOW_SUC)
|
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.SHOW_SUC)
|
||||||
showListener?.onAdShown()
|
showListener?.onAdShown()
|
||||||
}
|
}
|
||||||
@ -52,6 +66,7 @@ class AdInstShower(
|
|||||||
)
|
)
|
||||||
InstAdCacheManager.instance.remove(placement)
|
InstAdCacheManager.instance.remove(placement)
|
||||||
showListener?.onAdShowFailed(adError.toString())
|
showListener?.onAdShowFailed(adError.toString())
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "广告 $placement 展示失败->${adError}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAdClicked() {
|
override fun onAdClicked() {
|
||||||
|
|||||||
@ -5,16 +5,17 @@ import android.app.Activity
|
|||||||
object AdsInsUtil {
|
object AdsInsUtil {
|
||||||
/** 广告位定义(可扩展) */
|
/** 广告位定义(可扩展) */
|
||||||
enum class AdPlacement(val tag: String) {
|
enum class AdPlacement(val tag: String) {
|
||||||
SPL_AND_INTO_HOME(Placement.SPL_AND_INTO_HOME), INT_AND_TOPDF(Placement.INT_AND_TOPDF), INT_AND_PDFTOHOME(
|
SPL_AND_INTO_HOME(Placement.SPL_AND_INTO_HOME),
|
||||||
Placement.INT_AND_PDFTOHOME
|
INT_AND_TOPDF(Placement.INT_AND_TOPDF),
|
||||||
),
|
INT_AND_PDFTOHOME(Placement.INT_AND_PDFTOHOME),
|
||||||
INT_AND_MERGE(Placement.INT_AND_MERGE), INT_AND_SPLIT(Placement.INT_AND_SPLIT), NATIVE_AND_EXIT(
|
INT_AND_MERGE(Placement.INT_AND_MERGE),
|
||||||
Placement.NATIVE_AND_EXIT
|
INT_AND_SPLIT(Placement.INT_AND_SPLIT),
|
||||||
),
|
NATIVE_AND_EXIT(Placement.NATIVE_AND_EXIT),
|
||||||
BAN_AND_HOMEPAGE(Placement.BAN_AND_HOMEPAGE)
|
BAN_AND_HOMEPAGE(Placement.BAN_AND_HOMEPAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Placement {
|
object Placement {
|
||||||
|
const val TAG = "ocean-ad"
|
||||||
/**
|
/**
|
||||||
* 启动页插页
|
* 启动页插页
|
||||||
*/
|
*/
|
||||||
@ -53,7 +54,7 @@ object AdsInsUtil {
|
|||||||
|
|
||||||
// 广告位对应的广告ID
|
// 广告位对应的广告ID
|
||||||
val adUnitIdMap: Map<AdPlacement, String> = mapOf(
|
val adUnitIdMap: Map<AdPlacement, String> = mapOf(
|
||||||
AdPlacement.SPL_AND_INTO_HOME to "ca-app-pub-5717753826607607/5211991318",
|
AdPlacement.SPL_AND_INTO_HOME to "ca-app-pub-5717753826607607/9606833210",
|
||||||
AdPlacement.INT_AND_TOPDF to "ca-app-pub-5717753826607607/5308904672",
|
AdPlacement.INT_AND_TOPDF to "ca-app-pub-5717753826607607/5308904672",
|
||||||
AdPlacement.INT_AND_PDFTOHOME to "ca-app-pub-5717753826607607/7085128570",
|
AdPlacement.INT_AND_PDFTOHOME to "ca-app-pub-5717753826607607/7085128570",
|
||||||
AdPlacement.INT_AND_MERGE to "ca-app-pub-5717753826607607/8928693282",
|
AdPlacement.INT_AND_MERGE to "ca-app-pub-5717753826607607/8928693282",
|
||||||
@ -74,9 +75,33 @@ object AdsInsUtil {
|
|||||||
return AdInstLoad(act, adPlacement, loadListener)
|
return AdInstLoad(act, adPlacement, loadListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showAd(
|
||||||
|
act: Activity, adPlacement: AdPlacement
|
||||||
|
): AdInstShower {
|
||||||
|
return AdInstShower(act, adPlacement, null)
|
||||||
|
}
|
||||||
|
|
||||||
fun showAd(
|
fun showAd(
|
||||||
act: Activity, adPlacement: AdPlacement, listener: ShowListener?
|
act: Activity, adPlacement: AdPlacement, listener: ShowListener?
|
||||||
): AdInstShower {
|
): AdInstShower {
|
||||||
return AdInstShower(act, adPlacement, listener)
|
return AdInstShower(act, adPlacement, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showFinishAd(act: Activity, adPlacement: AdPlacement, onFinish: () -> Unit) {
|
||||||
|
showAd(
|
||||||
|
act = act,
|
||||||
|
adPlacement = adPlacement,
|
||||||
|
listener = object : ShowListener {
|
||||||
|
override fun onAdShown() {}
|
||||||
|
override fun onAdClicked() {}
|
||||||
|
override fun onAdShowFailed(string: String) {
|
||||||
|
onFinish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAdClosed() {
|
||||||
|
onFinish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
package com.all.pdfreader.pdf.reader.ad
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.util.DisplayMetrics
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import com.all.pdfreader.pdf.reader.util.AnalyticsUtils
|
||||||
|
import com.google.android.gms.ads.AdListener
|
||||||
|
import com.google.android.gms.ads.AdRequest
|
||||||
|
import com.google.android.gms.ads.AdSize
|
||||||
|
import com.google.android.gms.ads.AdView
|
||||||
|
import com.google.android.gms.ads.LoadAdError
|
||||||
|
|
||||||
|
object BannerManager {
|
||||||
|
|
||||||
|
private data class BannerInfo(val adView: AdView, val container: FrameLayout, var isLoaded: Boolean = false)
|
||||||
|
private val bannerMap = mutableMapOf<String, BannerInfo>()
|
||||||
|
|
||||||
|
//因为有多个activity来展示这个banner,所以我们使用act的名称+广告位做为key
|
||||||
|
private fun key(placement: AdsInsUtil.AdPlacement, activity: Activity): String {
|
||||||
|
return "${placement.name}_${activity::class.java.simpleName}"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadBanner(
|
||||||
|
activity: Activity,
|
||||||
|
container: FrameLayout,
|
||||||
|
placement: AdsInsUtil.AdPlacement,
|
||||||
|
onLoaded: (() -> Unit)? = null
|
||||||
|
) {
|
||||||
|
val currentKey = key(placement, activity)//得到当前的key
|
||||||
|
val adUnitId = AdsInsUtil.adUnitIdMap[placement] ?: run {
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "${activity::class.java.simpleName} Banner 未找到广告ID: $placement")
|
||||||
|
container.visibility = View.GONE
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "${activity::class.java.simpleName} Banner $placement 开始加载")
|
||||||
|
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.REQ)
|
||||||
|
|
||||||
|
container.post {
|
||||||
|
container.visibility = View.GONE
|
||||||
|
|
||||||
|
val adSize = getAdSize(activity, container)
|
||||||
|
|
||||||
|
// 销毁旧 AdView
|
||||||
|
bannerMap[currentKey]?.adView?.destroy()
|
||||||
|
|
||||||
|
val adView = AdView(activity).apply {
|
||||||
|
this.adUnitId = adUnitId
|
||||||
|
setAdSize(adSize)
|
||||||
|
adListener = object : AdListener() {
|
||||||
|
override fun onAdLoaded() {
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "${activity::class.java.simpleName} Banner $placement 加载成功")
|
||||||
|
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.LOADED)
|
||||||
|
|
||||||
|
val info = bannerMap[currentKey]
|
||||||
|
info?.isLoaded = true
|
||||||
|
|
||||||
|
// 广告加载成功后显示容器
|
||||||
|
info?.container?.visibility = View.VISIBLE
|
||||||
|
info?.container?.alpha = 0f
|
||||||
|
info?.container?.animate()?.alpha(1f)?.setDuration(300)?.start()
|
||||||
|
|
||||||
|
onLoaded?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAdFailedToLoad(error: LoadAdError) {
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "${activity::class.java.simpleName} Banner $placement 加载失败: ${error.message}")
|
||||||
|
AnalyticsUtils.logAdEvent(
|
||||||
|
placement,
|
||||||
|
AnalyticsUtils.AdEvent.LOAD_FAIL,
|
||||||
|
error.code,
|
||||||
|
error.message
|
||||||
|
)
|
||||||
|
val info = bannerMap[currentKey]
|
||||||
|
info?.isLoaded = false
|
||||||
|
info?.container?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bannerMap[currentKey] = BannerInfo(adView, container)
|
||||||
|
|
||||||
|
container.removeAllViews()
|
||||||
|
container.addView(adView)
|
||||||
|
|
||||||
|
adView.loadAd(AdRequest.Builder().build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onResume(activity: Activity, placement: AdsInsUtil.AdPlacement) {
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "${activity::class.java.simpleName} Banner $placement 恢复广告")
|
||||||
|
val info = bannerMap[key(placement, activity)] ?: return
|
||||||
|
info.adView.resume()
|
||||||
|
if (info.isLoaded) {
|
||||||
|
info.container.visibility = View.VISIBLE
|
||||||
|
info.container.alpha = 0f
|
||||||
|
info.container.animate().alpha(1f).setDuration(300).start()
|
||||||
|
} else {
|
||||||
|
info.container.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onPause(activity: Activity, placement: AdsInsUtil.AdPlacement) {
|
||||||
|
val info = bannerMap[key(placement, activity)] ?: return
|
||||||
|
info.container.visibility = View.GONE//暂停后就隐藏,让回调来显示
|
||||||
|
info.adView.pause()
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "${activity::class.java.simpleName} Banner $placement 暂停广告")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDestroy(activity: Activity, placement: AdsInsUtil.AdPlacement) {
|
||||||
|
val key = key(placement, activity)
|
||||||
|
bannerMap[key]?.adView?.destroy()
|
||||||
|
bannerMap.remove(key)
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "${activity::class.java.simpleName} Banner $placement 销毁广告")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAdSize(activity: Activity, container: FrameLayout): AdSize {
|
||||||
|
val display = activity.windowManager.defaultDisplay
|
||||||
|
val outMetrics = DisplayMetrics()
|
||||||
|
display.getMetrics(outMetrics)
|
||||||
|
|
||||||
|
var adWidthPixels = container.width.toFloat()
|
||||||
|
if (adWidthPixels == 0f) adWidthPixels = outMetrics.widthPixels.toFloat()
|
||||||
|
|
||||||
|
val density = outMetrics.density
|
||||||
|
val adWidth = (adWidthPixels / density).toInt()
|
||||||
|
|
||||||
|
return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(activity, adWidth)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,14 +3,56 @@ package com.all.pdfreader.pdf.reader.ad
|
|||||||
import com.google.android.gms.ads.interstitial.InterstitialAd
|
import com.google.android.gms.ads.interstitial.InterstitialAd
|
||||||
|
|
||||||
class InstAdCacheManager {
|
class InstAdCacheManager {
|
||||||
|
|
||||||
private val mAdCacheDict: MutableMap<AdsInsUtil.AdPlacement, CachedAd> = mutableMapOf()
|
private val mAdCacheDict: MutableMap<AdsInsUtil.AdPlacement, CachedAd> = mutableMapOf()
|
||||||
|
|
||||||
|
// 是否正在加载
|
||||||
|
private val loadingSet = mutableSetOf<AdsInsUtil.AdPlacement>()
|
||||||
|
|
||||||
|
// 等待回调的 listener(多个 load 时会加入这里)
|
||||||
|
private val pendingListeners =
|
||||||
|
mutableMapOf<AdsInsUtil.AdPlacement, MutableList<LoadListener?>>()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val instance: InstAdCacheManager by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
|
val instance: InstAdCacheManager by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
|
||||||
InstAdCacheManager()
|
InstAdCacheManager()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** ============= 加载状态控制 ============= */
|
||||||
|
|
||||||
|
fun isLoading(place: AdsInsUtil.AdPlacement) = loadingSet.contains(place)
|
||||||
|
|
||||||
|
fun markLoading(place: AdsInsUtil.AdPlacement, listener: LoadListener?) {
|
||||||
|
loadingSet.add(place)
|
||||||
|
addPendingListener(place, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addPendingListener(place: AdsInsUtil.AdPlacement, listener: LoadListener?) {
|
||||||
|
if (listener == null) return
|
||||||
|
|
||||||
|
val list = pendingListeners[place] ?: mutableListOf()
|
||||||
|
if (!list.contains(listener)) {
|
||||||
|
list.add(listener)
|
||||||
|
}
|
||||||
|
pendingListeners[place] = list
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun notifySuccess(place: AdsInsUtil.AdPlacement, ad: InterstitialAd) {
|
||||||
|
pendingListeners[place]?.forEach { it?.loaded(ad) }
|
||||||
|
pendingListeners.remove(place)
|
||||||
|
loadingSet.remove(place)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyFail(place: AdsInsUtil.AdPlacement, msg: String) {
|
||||||
|
pendingListeners[place]?.forEach { it?.loadFailed(msg) }
|
||||||
|
pendingListeners.remove(place)
|
||||||
|
loadingSet.remove(place)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** ============= 广告缓存逻辑 ============= */
|
||||||
|
|
||||||
fun setAdCache(place: AdsInsUtil.AdPlacement, adCache: InterstitialAd) {
|
fun setAdCache(place: AdsInsUtil.AdPlacement, adCache: InterstitialAd) {
|
||||||
mAdCacheDict[place] = CachedAd(adCache)
|
mAdCacheDict[place] = CachedAd(adCache)
|
||||||
}
|
}
|
||||||
@ -19,7 +61,7 @@ class InstAdCacheManager {
|
|||||||
val cached = mAdCacheDict[place]
|
val cached = mAdCacheDict[place]
|
||||||
return if (cached != null && cached.isValid()) cached.ad
|
return if (cached != null && cached.isValid()) cached.ad
|
||||||
else {
|
else {
|
||||||
mAdCacheDict.remove(place) // 过期广告清理
|
mAdCacheDict.remove(place) // 清理过期
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,12 +70,14 @@ class InstAdCacheManager {
|
|||||||
mAdCacheDict.remove(place)
|
mAdCacheDict.remove(place)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包装广告 + 时间
|
||||||
|
*/
|
||||||
data class CachedAd(
|
data class CachedAd(
|
||||||
val ad: InterstitialAd,
|
val ad: InterstitialAd,
|
||||||
val loadedAt: Long = System.currentTimeMillis()
|
val loadedAt: Long = System.currentTimeMillis()
|
||||||
) {
|
) {
|
||||||
// 广告有效期, 1 小时
|
// 广告有效期 1 小时
|
||||||
fun isValid(): Boolean = System.currentTimeMillis() - loadedAt < 3600_000
|
fun isValid(): Boolean = System.currentTimeMillis() - loadedAt < 3600_000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,74 @@
|
|||||||
|
package com.all.pdfreader.pdf.reader.ad
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import com.all.pdfreader.pdf.reader.util.AnalyticsUtils
|
||||||
|
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
|
||||||
|
|
||||||
|
object NativeAdCache {
|
||||||
|
private var cachedAd: NativeAd? = null
|
||||||
|
|
||||||
|
fun load(
|
||||||
|
context: Context,
|
||||||
|
placement: AdsInsUtil.AdPlacement,
|
||||||
|
onAdLoaded: (() -> Unit)? = null,
|
||||||
|
onAdFailedToLoad: (() -> Unit)? = null
|
||||||
|
) {
|
||||||
|
if (cachedAd != null) {
|
||||||
|
onAdLoaded?.invoke()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val adUnitId = AdsInsUtil.adUnitIdMap[placement] ?: run {
|
||||||
|
val errorMsg = "No AdUnitId for $placement"
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "没找到对应的广告ID->$placement")
|
||||||
|
onAdFailedToLoad?.invoke()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.REQ)
|
||||||
|
val adLoader = AdLoader.Builder(context, adUnitId)
|
||||||
|
.forNativeAd { ad ->
|
||||||
|
if (context is Activity) {
|
||||||
|
if (context.isFinishing || context.isDestroyed) {
|
||||||
|
ad.destroy()
|
||||||
|
return@forNativeAd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachedAd?.destroy()
|
||||||
|
cachedAd = ad
|
||||||
|
onAdLoaded?.invoke()
|
||||||
|
}
|
||||||
|
.withAdListener(object : AdListener() {
|
||||||
|
override fun onAdFailedToLoad(error: LoadAdError) {
|
||||||
|
cachedAd = null
|
||||||
|
onAdFailedToLoad?.invoke()
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "$placement 广告加载失败 ${error.message}")
|
||||||
|
AnalyticsUtils.logAdEvent(
|
||||||
|
placement,
|
||||||
|
AnalyticsUtils.AdEvent.LOAD_FAIL,
|
||||||
|
error.code,
|
||||||
|
error.message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAdLoaded() {
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "$placement 广告加载成功")
|
||||||
|
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.LOADED)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
|
||||||
|
adLoader.loadAd(AdRequest.Builder().build())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAd(): NativeAd? = cachedAd
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
cachedAd?.destroy()
|
||||||
|
cachedAd = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -24,33 +24,27 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
protected val appStore by lazy { AppStore(this) }
|
protected val appStore by lazy { AppStore(this) }
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
Log.d("ocean", "🚀 ${javaClass.simpleName} onCreate")
|
|
||||||
setupBackPressedCallback()//初始化back事件
|
setupBackPressedCallback()//初始化back事件
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
Log.d("ocean", "🔄 ${javaClass.simpleName} onStart")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
Log.d("ocean", "📱 ${javaClass.simpleName} onResume")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
Log.d("ocean", "⏸️ ${javaClass.simpleName} onPause")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
Log.d("ocean", "🛑 ${javaClass.simpleName} onStop")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
Log.d("ocean", "💀 ${javaClass.simpleName} onDestroy")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun logDebug(message: String) {
|
protected fun logDebug(message: String) {
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package com.all.pdfreader.pdf.reader.ui.act
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.DisplayMetrics
|
||||||
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
@ -12,10 +14,14 @@ import androidx.lifecycle.ViewModelProvider
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.all.pdfreader.pdf.reader.PRApp
|
import com.all.pdfreader.pdf.reader.PRApp
|
||||||
import com.all.pdfreader.pdf.reader.R
|
import com.all.pdfreader.pdf.reader.R
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.AdsInsUtil
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.BannerManager
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.NativeAdCache
|
||||||
import com.all.pdfreader.pdf.reader.databinding.ActivityMainBinding
|
import com.all.pdfreader.pdf.reader.databinding.ActivityMainBinding
|
||||||
import com.all.pdfreader.pdf.reader.model.FileActionEvent
|
import com.all.pdfreader.pdf.reader.model.FileActionEvent
|
||||||
import com.all.pdfreader.pdf.reader.model.FragmentType
|
import com.all.pdfreader.pdf.reader.model.FragmentType
|
||||||
import com.all.pdfreader.pdf.reader.model.PdfPickerSource
|
import com.all.pdfreader.pdf.reader.model.PdfPickerSource
|
||||||
|
import com.all.pdfreader.pdf.reader.ui.dialog.ExitDialogFragment
|
||||||
import com.all.pdfreader.pdf.reader.ui.dialog.PermissionDialogFragment
|
import com.all.pdfreader.pdf.reader.ui.dialog.PermissionDialogFragment
|
||||||
import com.all.pdfreader.pdf.reader.ui.dialog.ProgressDialogFragment
|
import com.all.pdfreader.pdf.reader.ui.dialog.ProgressDialogFragment
|
||||||
import com.all.pdfreader.pdf.reader.ui.dialog.PromptDialogFragment
|
import com.all.pdfreader.pdf.reader.ui.dialog.PromptDialogFragment
|
||||||
@ -33,6 +39,16 @@ import com.all.pdfreader.pdf.reader.util.PdfScanner
|
|||||||
import com.all.pdfreader.pdf.reader.util.StoragePermissionHelper
|
import com.all.pdfreader.pdf.reader.util.StoragePermissionHelper
|
||||||
import com.all.pdfreader.pdf.reader.viewmodel.PdfViewModel
|
import com.all.pdfreader.pdf.reader.viewmodel.PdfViewModel
|
||||||
import com.all.pdfreader.pdf.reader.viewmodel.observeEvent
|
import com.all.pdfreader.pdf.reader.viewmodel.observeEvent
|
||||||
|
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.AdSize
|
||||||
|
import com.google.android.gms.ads.AdView
|
||||||
|
import com.google.android.gms.ads.LoadAdError
|
||||||
|
import com.google.android.gms.ads.nativead.NativeAdOptions
|
||||||
|
import com.google.android.gms.ads.nativead.NativeAdView
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -60,6 +76,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
loadAd()
|
||||||
AnalyticsUtils.logEvent(AnalyticsUtils.Event.HOME_SHOW)
|
AnalyticsUtils.logEvent(AnalyticsUtils.Event.HOME_SHOW)
|
||||||
setupDoubleBackExit()
|
setupDoubleBackExit()
|
||||||
initObserve()
|
initObserve()
|
||||||
@ -414,25 +431,6 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
|||||||
supportFragmentManager.putFragment(outState, fragmentTag, activeFragment)
|
supportFragmentManager.putFragment(outState, fragmentTag, activeFragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
logDebug("main onResume")
|
|
||||||
if (StoragePermissionHelper.hasBasicStoragePermission(this)) {
|
|
||||||
// 有授权才初始化文件变化监听器
|
|
||||||
PRApp.getInstance().startFileChangeObserving()
|
|
||||||
scanningStrategy()
|
|
||||||
binding.pnLayout.visibility = View.GONE
|
|
||||||
} else {
|
|
||||||
binding.pnLayout.visibility = View.VISIBLE
|
|
||||||
val dialog = PermissionDialogFragment()
|
|
||||||
//如果之前展示过授权对话框,则不再展示
|
|
||||||
if (!appStore.isShowPermissionsDialogPrompt) {
|
|
||||||
dialog.show(supportFragmentManager, TAG)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateSelectedNav(activeFragment)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun scanningStrategy() {
|
private fun scanningStrategy() {
|
||||||
if (StoragePermissionHelper.hasBasicStoragePermission(this)) {
|
if (StoragePermissionHelper.hasBasicStoragePermission(this)) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
@ -563,22 +561,54 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var lastBackPressedTime: Long = 0
|
|
||||||
private val EXIT_INTERVAL = 2000L // 2 秒内双击退出
|
|
||||||
|
|
||||||
private fun setupDoubleBackExit() {
|
private fun setupDoubleBackExit() {
|
||||||
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||||||
override fun handleOnBackPressed() {
|
override fun handleOnBackPressed() {
|
||||||
val currentTime = System.currentTimeMillis()
|
ExitDialogFragment(onExitClick = {
|
||||||
if (currentTime - lastBackPressedTime < EXIT_INTERVAL) {
|
|
||||||
// 双击退出
|
|
||||||
isEnabled = false // 解除拦截
|
isEnabled = false // 解除拦截
|
||||||
onBackPressedDispatcher.onBackPressed() // 调用系统默认返回逻辑
|
onBackPressedDispatcher.onBackPressed() // 调用系统默认返回逻辑
|
||||||
} else {
|
}).show(supportFragmentManager, TAG)
|
||||||
lastBackPressedTime = currentTime
|
|
||||||
showToast(getString(R.string.press_again_to_exit))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadAd() {
|
||||||
|
AdsInsUtil.loadAd(this, AdsInsUtil.AdPlacement.INT_AND_TOPDF)
|
||||||
|
AdsInsUtil.loadAd(this, AdsInsUtil.AdPlacement.INT_AND_PDFTOHOME)
|
||||||
|
|
||||||
|
NativeAdCache.load(this, AdsInsUtil.AdPlacement.NATIVE_AND_EXIT)
|
||||||
|
|
||||||
|
BannerManager.loadBanner(this, binding.bannerAdFl, AdsInsUtil.AdPlacement.BAN_AND_HOMEPAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
BannerManager.onResume(this,AdsInsUtil.AdPlacement.BAN_AND_HOMEPAGE)
|
||||||
|
logDebug("main onResume")
|
||||||
|
if (StoragePermissionHelper.hasBasicStoragePermission(this)) {
|
||||||
|
// 有授权才初始化文件变化监听器
|
||||||
|
PRApp.getInstance().startFileChangeObserving()
|
||||||
|
scanningStrategy()
|
||||||
|
binding.pnLayout.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.pnLayout.visibility = View.VISIBLE
|
||||||
|
val dialog = PermissionDialogFragment()
|
||||||
|
//如果之前展示过授权对话框,则不再展示
|
||||||
|
if (!appStore.isShowPermissionsDialogPrompt) {
|
||||||
|
dialog.show(supportFragmentManager, TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateSelectedNav(activeFragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
BannerManager.onPause(this,AdsInsUtil.AdPlacement.BAN_AND_HOMEPAGE)
|
||||||
|
super.onPause()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
BannerManager.onDestroy(this,AdsInsUtil.AdPlacement.BAN_AND_HOMEPAGE)
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import androidx.activity.result.contract.ActivityResultContract
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.all.pdfreader.pdf.reader.R
|
import com.all.pdfreader.pdf.reader.R
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.AdsInsUtil
|
||||||
import com.all.pdfreader.pdf.reader.databinding.ActivityPdfMergeBinding
|
import com.all.pdfreader.pdf.reader.databinding.ActivityPdfMergeBinding
|
||||||
import com.all.pdfreader.pdf.reader.model.PdfPickerSource
|
import com.all.pdfreader.pdf.reader.model.PdfPickerSource
|
||||||
import com.all.pdfreader.pdf.reader.room.entity.PdfDocumentEntity
|
import com.all.pdfreader.pdf.reader.room.entity.PdfDocumentEntity
|
||||||
@ -41,6 +42,7 @@ class MergePdfActivity : BaseActivity() {
|
|||||||
binding = ActivityPdfMergeBinding.inflate(layoutInflater)
|
binding = ActivityPdfMergeBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
AnalyticsUtils.logEvent(AnalyticsUtils.Event.MERGE_SHOW)
|
AnalyticsUtils.logEvent(AnalyticsUtils.Event.MERGE_SHOW)
|
||||||
|
AdsInsUtil.loadAd(this, AdsInsUtil.AdPlacement.INT_AND_MERGE)
|
||||||
setupImmersionBar {
|
setupImmersionBar {
|
||||||
statusBarView(binding.view)
|
statusBarView(binding.view)
|
||||||
statusBarDarkFont(true)
|
statusBarDarkFont(true)
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.all.pdfreader.pdf.reader.PRApp
|
import com.all.pdfreader.pdf.reader.PRApp
|
||||||
import com.all.pdfreader.pdf.reader.R
|
import com.all.pdfreader.pdf.reader.R
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.AdsInsUtil
|
||||||
import com.all.pdfreader.pdf.reader.databinding.ActivityPdfSplitResultBinding
|
import com.all.pdfreader.pdf.reader.databinding.ActivityPdfSplitResultBinding
|
||||||
import com.all.pdfreader.pdf.reader.model.PdfPageItem
|
import com.all.pdfreader.pdf.reader.model.PdfPageItem
|
||||||
import com.all.pdfreader.pdf.reader.model.PdfPickerSource
|
import com.all.pdfreader.pdf.reader.model.PdfPickerSource
|
||||||
@ -160,7 +161,11 @@ class PdfResultActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
if (source == PdfPickerSource.SPLIT) {
|
if (source == PdfPickerSource.SPLIT) {
|
||||||
val splitPassword = intent.getStringExtra(EXTRA_SPLIT_PASSWORD) ?: ""
|
val splitPassword = intent.getStringExtra(EXTRA_SPLIT_PASSWORD) ?: ""
|
||||||
runOnUiThread {
|
withContext(Dispatchers.Main) {
|
||||||
|
AdsInsUtil.showFinishAd(this@PdfResultActivity, AdsInsUtil.AdPlacement.INT_AND_SPLIT){
|
||||||
|
AdsInsUtil.loadAd(this@PdfResultActivity, AdsInsUtil.AdPlacement.INT_AND_SPLIT)
|
||||||
|
}
|
||||||
|
|
||||||
binding.progressBar.isIndeterminate = false
|
binding.progressBar.isIndeterminate = false
|
||||||
binding.progressBar.progress = 0
|
binding.progressBar.progress = 0
|
||||||
binding.progressBar.max = 100
|
binding.progressBar.max = 100
|
||||||
@ -210,7 +215,10 @@ class PdfResultActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (source == PdfPickerSource.MERGE) {
|
} else if (source == PdfPickerSource.MERGE) {
|
||||||
runOnUiThread {
|
withContext(Dispatchers.Main) {
|
||||||
|
AdsInsUtil.showFinishAd(this@PdfResultActivity, AdsInsUtil.AdPlacement.INT_AND_MERGE){
|
||||||
|
AdsInsUtil.loadAd(this@PdfResultActivity, AdsInsUtil.AdPlacement.INT_AND_MERGE)
|
||||||
|
}
|
||||||
binding.progressBar.isIndeterminate = false
|
binding.progressBar.isIndeterminate = false
|
||||||
binding.progressBar.progress = 0
|
binding.progressBar.progress = 0
|
||||||
binding.progressBar.max = 100
|
binding.progressBar.max = 100
|
||||||
|
|||||||
@ -11,13 +11,18 @@ import android.view.inputmethod.EditorInfo
|
|||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.all.pdfreader.pdf.reader.PRApp
|
||||||
import com.all.pdfreader.pdf.reader.R
|
import com.all.pdfreader.pdf.reader.R
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.AdsInsUtil
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.BannerManager
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.ShowListener
|
||||||
import com.all.pdfreader.pdf.reader.databinding.ActivityPdfViewBinding
|
import com.all.pdfreader.pdf.reader.databinding.ActivityPdfViewBinding
|
||||||
import com.all.pdfreader.pdf.reader.model.FileActionEvent
|
import com.all.pdfreader.pdf.reader.model.FileActionEvent
|
||||||
import com.all.pdfreader.pdf.reader.room.entity.PdfDocumentEntity
|
import com.all.pdfreader.pdf.reader.room.entity.PdfDocumentEntity
|
||||||
import com.all.pdfreader.pdf.reader.ui.dialog.BookmarksDialogFragment
|
import com.all.pdfreader.pdf.reader.ui.dialog.BookmarksDialogFragment
|
||||||
import com.all.pdfreader.pdf.reader.ui.dialog.ListMoreDialogFragment
|
import com.all.pdfreader.pdf.reader.ui.dialog.ListMoreDialogFragment
|
||||||
import com.all.pdfreader.pdf.reader.ui.dialog.PdfPasswordProtectionDialogFragment
|
import com.all.pdfreader.pdf.reader.ui.dialog.PdfPasswordProtectionDialogFragment
|
||||||
|
import com.all.pdfreader.pdf.reader.ui.dialog.PermissionDialogFragment
|
||||||
import com.all.pdfreader.pdf.reader.ui.dialog.ViewModelDialogFragment
|
import com.all.pdfreader.pdf.reader.ui.dialog.ViewModelDialogFragment
|
||||||
import com.all.pdfreader.pdf.reader.ui.view.CustomScrollHandle
|
import com.all.pdfreader.pdf.reader.ui.view.CustomScrollHandle
|
||||||
import com.all.pdfreader.pdf.reader.util.AnalyticsUtils
|
import com.all.pdfreader.pdf.reader.util.AnalyticsUtils
|
||||||
@ -27,6 +32,7 @@ import com.all.pdfreader.pdf.reader.util.AppUtils.showKeyboard
|
|||||||
import com.all.pdfreader.pdf.reader.util.FileUtils
|
import com.all.pdfreader.pdf.reader.util.FileUtils
|
||||||
import com.all.pdfreader.pdf.reader.util.PDFHighlighter
|
import com.all.pdfreader.pdf.reader.util.PDFHighlighter
|
||||||
import com.all.pdfreader.pdf.reader.util.PDFSearchManager
|
import com.all.pdfreader.pdf.reader.util.PDFSearchManager
|
||||||
|
import com.all.pdfreader.pdf.reader.util.StoragePermissionHelper
|
||||||
import com.all.pdfreader.pdf.reader.viewmodel.PdfViewModel
|
import com.all.pdfreader.pdf.reader.viewmodel.PdfViewModel
|
||||||
import com.all.pdfreader.pdf.reader.viewmodel.observeEvent
|
import com.all.pdfreader.pdf.reader.viewmodel.observeEvent
|
||||||
import com.github.barteksc.pdfviewer.listener.OnErrorListener
|
import com.github.barteksc.pdfviewer.listener.OnErrorListener
|
||||||
@ -48,6 +54,7 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
|
|||||||
override val TAG: String = "PdfViewActivity"
|
override val TAG: String = "PdfViewActivity"
|
||||||
override val rootBottomView: View?
|
override val rootBottomView: View?
|
||||||
get() = binding.rootBottomLayout
|
get() = binding.rootBottomLayout
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val FRAG_TAG = "PdfViewActivity"
|
const val FRAG_TAG = "PdfViewActivity"
|
||||||
private const val EXTRA_PDF_FILE_PATH = "extra_pdf_file_path"
|
private const val EXTRA_PDF_FILE_PATH = "extra_pdf_file_path"
|
||||||
@ -77,6 +84,7 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
|
|||||||
binding = ActivityPdfViewBinding.inflate(layoutInflater)
|
binding = ActivityPdfViewBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
AnalyticsUtils.logEvent(AnalyticsUtils.Event.PDF_SHOW)
|
AnalyticsUtils.logEvent(AnalyticsUtils.Event.PDF_SHOW)
|
||||||
|
BannerManager.loadBanner(this, binding.bannerAdFl, AdsInsUtil.AdPlacement.BAN_AND_HOMEPAGE)
|
||||||
setupImmersionBar {
|
setupImmersionBar {
|
||||||
statusBarView(binding.view)
|
statusBarView(binding.view)
|
||||||
statusBarDarkFont(true)
|
statusBarDarkFont(true)
|
||||||
@ -94,6 +102,13 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
|
|||||||
//加载书签数据
|
//加载书签数据
|
||||||
viewModel.getBookmarks(filePath)
|
viewModel.getBookmarks(filePath)
|
||||||
setupOnClick()
|
setupOnClick()
|
||||||
|
|
||||||
|
AdsInsUtil.showFinishAd(
|
||||||
|
this@PdfViewActivity,
|
||||||
|
AdsInsUtil.AdPlacement.INT_AND_TOPDF
|
||||||
|
) {
|
||||||
|
AdsInsUtil.loadAd(this@PdfViewActivity, AdsInsUtil.AdPlacement.INT_AND_TOPDF)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initObserve() {
|
private fun initObserve() {
|
||||||
@ -451,6 +466,15 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
|
|||||||
setSelection(0)
|
setSelection(0)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
AdsInsUtil.showFinishAd(
|
||||||
|
this@PdfViewActivity,
|
||||||
|
AdsInsUtil.AdPlacement.INT_AND_PDFTOHOME
|
||||||
|
) {
|
||||||
|
AdsInsUtil.loadAd(
|
||||||
|
this@PdfViewActivity,
|
||||||
|
AdsInsUtil.AdPlacement.INT_AND_PDFTOHOME
|
||||||
|
)
|
||||||
|
}
|
||||||
isEnabled = false // 解除拦截
|
isEnabled = false // 解除拦截
|
||||||
onBackPressedDispatcher.onBackPressed() // 调用系统默认返回逻辑
|
onBackPressedDispatcher.onBackPressed() // 调用系统默认返回逻辑
|
||||||
}
|
}
|
||||||
@ -558,10 +582,20 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
BannerManager.onDestroy(this,AdsInsUtil.AdPlacement.BAN_AND_HOMEPAGE)
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
if (::searchManager.isInitialized) {
|
if (::searchManager.isInitialized) {
|
||||||
searchManager.closeCachedDocument()
|
searchManager.closeCachedDocument()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
BannerManager.onResume( this,AdsInsUtil.AdPlacement.BAN_AND_HOMEPAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
BannerManager.onPause(this,AdsInsUtil.AdPlacement.BAN_AND_HOMEPAGE)
|
||||||
|
super.onPause()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -5,11 +5,16 @@ import android.content.Intent
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.util.Log
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.core.view.doOnPreDraw
|
import androidx.core.view.doOnPreDraw
|
||||||
import com.all.pdfreader.pdf.reader.R
|
import com.all.pdfreader.pdf.reader.R
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.AdsInsUtil
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.LoadListener
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.ShowListener
|
||||||
import com.all.pdfreader.pdf.reader.databinding.ActivitySplashBinding
|
import com.all.pdfreader.pdf.reader.databinding.ActivitySplashBinding
|
||||||
import com.all.pdfreader.pdf.reader.util.AnalyticsUtils
|
import com.all.pdfreader.pdf.reader.util.AnalyticsUtils
|
||||||
|
import com.google.android.gms.ads.interstitial.InterstitialAd
|
||||||
import com.gyf.immersionbar.BarHide
|
import com.gyf.immersionbar.BarHide
|
||||||
import com.gyf.immersionbar.ImmersionBar
|
import com.gyf.immersionbar.ImmersionBar
|
||||||
|
|
||||||
@ -21,7 +26,7 @@ class SplashActivity : BaseActivity() {
|
|||||||
private lateinit var binding: ActivitySplashBinding
|
private lateinit var binding: ActivitySplashBinding
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val SPLASH_DELAY = 3000L // 启动页显示时长
|
private const val AD_TIMEOUT = 15000L // 广告加载超时时间 15 秒
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -35,15 +40,62 @@ class SplashActivity : BaseActivity() {
|
|||||||
// 设置启动页布局
|
// 设置启动页布局
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
AnalyticsUtils.logEvent(AnalyticsUtils.Event.SPLASH_VISIBLE)
|
AnalyticsUtils.logEvent(AnalyticsUtils.Event.SPLASH_VISIBLE)
|
||||||
// 延迟跳转到权限检查
|
loadSplashAd()
|
||||||
Handler(Looper.getMainLooper()).postDelayed({
|
|
||||||
navigateToNext()
|
|
||||||
}, SPLASH_DELAY)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val adHandler = Handler(Looper.getMainLooper())
|
||||||
|
private val adTimeoutRunnable = Runnable {
|
||||||
|
navigateToNext() // 超时直接跳转
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadSplashAd() {
|
||||||
|
adHandler.postDelayed(adTimeoutRunnable, AD_TIMEOUT)
|
||||||
|
|
||||||
|
AdsInsUtil.loadAd(
|
||||||
|
act = this,
|
||||||
|
adPlacement = AdsInsUtil.AdPlacement.SPL_AND_INTO_HOME,
|
||||||
|
loadListener = object : LoadListener {
|
||||||
|
override fun loaded(ad: InterstitialAd) {
|
||||||
|
logDebug("loaded 启动页广告加载成功")
|
||||||
|
adHandler.removeCallbacks(adTimeoutRunnable)
|
||||||
|
showSplashAd()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadFailed(string: String) {
|
||||||
|
logDebug("loadFailed->$string")
|
||||||
|
adHandler.removeCallbacks(adTimeoutRunnable)
|
||||||
|
navigateToNext() // 加载失败直接跳转
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSplashAd() {
|
||||||
|
AdsInsUtil.showAd(
|
||||||
|
act = this,
|
||||||
|
adPlacement = AdsInsUtil.AdPlacement.SPL_AND_INTO_HOME,
|
||||||
|
listener = object : ShowListener {
|
||||||
|
override fun onAdShown() {
|
||||||
|
// 可以记录日志
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAdShowFailed(string: String) {
|
||||||
|
navigateToNext() // 展示失败也直接跳转
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAdClosed() {
|
||||||
|
navigateToNext() // 广告关闭后跳转
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private var hasNavigated = false
|
||||||
|
|
||||||
|
//保证只跳转一次
|
||||||
private fun navigateToNext() {
|
private fun navigateToNext() {
|
||||||
val intent = Intent(this, MainActivity::class.java)
|
if (hasNavigated) return
|
||||||
startActivity(intent)
|
hasNavigated = true
|
||||||
|
startActivity(Intent(this, MainActivity::class.java))
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,4 +103,9 @@ class SplashActivity : BaseActivity() {
|
|||||||
override fun onInterceptBackPressed() {
|
override fun onInterceptBackPressed() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
adHandler.removeCallbacks(adTimeoutRunnable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -9,6 +9,7 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.all.pdfreader.pdf.reader.R
|
import com.all.pdfreader.pdf.reader.R
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.AdsInsUtil
|
||||||
import com.all.pdfreader.pdf.reader.databinding.ActivityPdfSplitBinding
|
import com.all.pdfreader.pdf.reader.databinding.ActivityPdfSplitBinding
|
||||||
import com.all.pdfreader.pdf.reader.model.PdfPageItem
|
import com.all.pdfreader.pdf.reader.model.PdfPageItem
|
||||||
import com.all.pdfreader.pdf.reader.model.PdfPickerSource
|
import com.all.pdfreader.pdf.reader.model.PdfPickerSource
|
||||||
@ -66,6 +67,7 @@ class SplitPdfActivity : BaseActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityPdfSplitBinding.inflate(layoutInflater)
|
binding = ActivityPdfSplitBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
AdsInsUtil.loadAd(this, AdsInsUtil.AdPlacement.INT_AND_SPLIT)
|
||||||
setupImmersionBar {
|
setupImmersionBar {
|
||||||
statusBarView(binding.view)
|
statusBarView(binding.view)
|
||||||
statusBarDarkFont(true)
|
statusBarDarkFont(true)
|
||||||
|
|||||||
@ -0,0 +1,171 @@
|
|||||||
|
package com.all.pdfreader.pdf.reader.ui.dialog
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.all.pdfreader.pdf.reader.R
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.AdsInsUtil
|
||||||
|
import com.all.pdfreader.pdf.reader.ad.NativeAdCache
|
||||||
|
import com.all.pdfreader.pdf.reader.databinding.AdAdmobNativeExitLayoutBinding
|
||||||
|
import com.all.pdfreader.pdf.reader.databinding.DialogExitBinding
|
||||||
|
import com.google.android.gms.ads.nativead.NativeAd
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
class ExitDialogFragment(
|
||||||
|
private val onExitClick: () -> Unit,
|
||||||
|
) : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
private lateinit var binding: DialogExitBinding
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setStyle(STYLE_NORMAL, R.style.CustomBottomSheetDialogTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
binding = DialogExitBinding.inflate(layoutInflater)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
dialog?.window?.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
|
||||||
|
?.setBackgroundResource(R.drawable.dr_rc_top_12_bg_white)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setupOnClick()
|
||||||
|
val cached = NativeAdCache.getAd()
|
||||||
|
if (cached != null) {
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "使用缓存广告")
|
||||||
|
showNativeAd(cached)
|
||||||
|
} else {
|
||||||
|
Log.d(AdsInsUtil.Placement.TAG, "无缓存,开始加载")
|
||||||
|
loadNativeAd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupOnClick() {
|
||||||
|
binding.exitBtn.setOnClickListener {
|
||||||
|
onExitClick()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showNativeAd(nativeAd: NativeAd) {
|
||||||
|
if (!isAdded || activity == null) {
|
||||||
|
nativeAd.destroy()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requireActivity().isFinishing || requireActivity().isDestroyed) {
|
||||||
|
nativeAd.destroy()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inflate 必须在主线程
|
||||||
|
val adBinding = AdAdmobNativeExitLayoutBinding.inflate(layoutInflater)
|
||||||
|
populateNativeAdView(nativeAd, adBinding)
|
||||||
|
|
||||||
|
binding.adFl.removeAllViews()
|
||||||
|
binding.adFl.addView(adBinding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadNativeAd() {
|
||||||
|
|
||||||
|
NativeAdCache.load(requireActivity(), AdsInsUtil.AdPlacement.NATIVE_AND_EXIT, onAdLoaded = {
|
||||||
|
if (!isAdded) return@load
|
||||||
|
val ad = NativeAdCache.getAd() ?: return@load
|
||||||
|
showNativeAd(ad)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populateNativeAdView(
|
||||||
|
nativeAd: NativeAd,
|
||||||
|
unifiedAdBinding: AdAdmobNativeExitLayoutBinding
|
||||||
|
) {
|
||||||
|
val nativeAdView = unifiedAdBinding.root
|
||||||
|
|
||||||
|
// Set the media view.
|
||||||
|
nativeAdView.mediaView = unifiedAdBinding.adMedia
|
||||||
|
|
||||||
|
// Set other ad assets.
|
||||||
|
nativeAdView.headlineView = unifiedAdBinding.adHeadline
|
||||||
|
nativeAdView.bodyView = unifiedAdBinding.adBody
|
||||||
|
nativeAdView.callToActionView = unifiedAdBinding.adCallToAction
|
||||||
|
nativeAdView.iconView = unifiedAdBinding.adAppIcon
|
||||||
|
// nativeAdView.priceView = unifiedAdBinding.adPrice
|
||||||
|
nativeAdView.starRatingView = unifiedAdBinding.adStars
|
||||||
|
// nativeAdView.storeView = unifiedAdBinding.adStore
|
||||||
|
nativeAdView.advertiserView = unifiedAdBinding.adAdvertiser
|
||||||
|
|
||||||
|
// The headline and media content are guaranteed to be in every UnifiedNativeAd.
|
||||||
|
unifiedAdBinding.adHeadline.text = nativeAd.headline
|
||||||
|
nativeAd.mediaContent?.let { unifiedAdBinding.adMedia.setMediaContent(it) }
|
||||||
|
|
||||||
|
// These assets aren't guaranteed to be in every UnifiedNativeAd, so it's important to
|
||||||
|
// check before trying to display them.
|
||||||
|
if (nativeAd.body == null) {
|
||||||
|
unifiedAdBinding.adBody.visibility = View.INVISIBLE
|
||||||
|
} else {
|
||||||
|
unifiedAdBinding.adBody.visibility = View.VISIBLE
|
||||||
|
unifiedAdBinding.adBody.text = nativeAd.body
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nativeAd.callToAction == null) {
|
||||||
|
unifiedAdBinding.adCallToAction.visibility = View.INVISIBLE
|
||||||
|
} else {
|
||||||
|
unifiedAdBinding.adCallToAction.visibility = View.VISIBLE
|
||||||
|
unifiedAdBinding.adCallToAction.text = nativeAd.callToAction
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nativeAd.icon == null) {
|
||||||
|
unifiedAdBinding.adAppIcon.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
unifiedAdBinding.adAppIcon.setImageDrawable(nativeAd.icon?.drawable)
|
||||||
|
unifiedAdBinding.adAppIcon.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (nativeAd.price == null) {
|
||||||
|
// unifiedAdBinding.adPrice.visibility = View.INVISIBLE
|
||||||
|
// } else {
|
||||||
|
// unifiedAdBinding.adPrice.visibility = View.VISIBLE
|
||||||
|
// unifiedAdBinding.adPrice.text = nativeAd.price
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (nativeAd.store == null) {
|
||||||
|
// unifiedAdBinding.adStore.visibility = View.INVISIBLE
|
||||||
|
// } else {
|
||||||
|
// unifiedAdBinding.adStore.visibility = View.VISIBLE
|
||||||
|
// unifiedAdBinding.adStore.text = nativeAd.store
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (nativeAd.starRating == null) {
|
||||||
|
unifiedAdBinding.adStars.visibility = View.INVISIBLE
|
||||||
|
} else {
|
||||||
|
unifiedAdBinding.adStars.rating = nativeAd.starRating!!.toFloat()
|
||||||
|
unifiedAdBinding.adStars.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nativeAd.advertiser == null) {
|
||||||
|
unifiedAdBinding.adAdvertiser.visibility = View.INVISIBLE
|
||||||
|
} else {
|
||||||
|
unifiedAdBinding.adAdvertiser.text = nativeAd.advertiser
|
||||||
|
unifiedAdBinding.adAdvertiser.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method tells the Google Mobile Ads SDK that you have finished populating your
|
||||||
|
// native ad view with this native ad.
|
||||||
|
nativeAdView.setNativeAd(nativeAd)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
// 销毁广告,避免内存泄漏
|
||||||
|
NativeAdCache.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
9
app/src/main/res/drawable/dr_rc_top_12_bg_color.xml
Normal file
9
app/src/main/res/drawable/dr_rc_top_12_bg_color.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners
|
||||||
|
android:topLeftRadius="12dp"
|
||||||
|
android:topRightRadius="12dp" />
|
||||||
|
<solid android:color="@color/bg_color" />
|
||||||
|
|
||||||
|
</shape>
|
||||||
5
app/src/main/res/drawable/gnt_outline_shape.xml
Normal file
5
app/src/main/res/drawable/gnt_outline_shape.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
|
||||||
|
<solid android:color="@color/gnt_white" />
|
||||||
|
<stroke android:width="2dp" android:color="@color/gnt_outline"/>
|
||||||
|
</shape>
|
||||||
17
app/src/main/res/drawable/gnt_rounded_corners_shape.xml
Normal file
17
app/src/main/res/drawable/gnt_rounded_corners_shape.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#ffffffff"/>
|
||||||
|
|
||||||
|
<stroke android:width="2dp"
|
||||||
|
android:color="@color/gnt_ad_green"/>
|
||||||
|
|
||||||
|
<padding android:left="1dp"
|
||||||
|
android:top="1dp"
|
||||||
|
android:right="1dp"
|
||||||
|
android:bottom="1dp" />
|
||||||
|
|
||||||
|
<corners android:bottomRightRadius="5dp"
|
||||||
|
android:bottomLeftRadius="5dp"
|
||||||
|
android:topLeftRadius="5dp"
|
||||||
|
android:topRightRadius="5dp" />
|
||||||
|
</shape>
|
||||||
11
app/src/main/res/drawable/gnt_top_round_12_outline_shape.xml
Normal file
11
app/src/main/res/drawable/gnt_top_round_12_outline_shape.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="@color/gnt_white" />
|
||||||
|
<corners
|
||||||
|
android:topLeftRadius="12dp"
|
||||||
|
android:topRightRadius="12dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="@color/gnt_outline" />
|
||||||
|
</shape>
|
||||||
@ -8,6 +8,7 @@
|
|||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -455,6 +456,11 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/bannerAdFl"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/rootBottomLayout"
|
android:id="@+id/rootBottomLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@ -110,8 +110,8 @@
|
|||||||
android:id="@+id/selectNextBtn"
|
android:id="@+id/selectNextBtn"
|
||||||
android:layout_width="32dp"
|
android:layout_width="32dp"
|
||||||
android:layout_height="32dp"
|
android:layout_height="32dp"
|
||||||
android:background="@drawable/dr_click_effect_oval_transparent"
|
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
|
android:background="@drawable/dr_click_effect_oval_transparent"
|
||||||
android:gravity="center">
|
android:gravity="center">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@ -332,7 +332,13 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/bannerAdFl"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/rootBottomLayout"
|
android:id="@+id/rootBottomLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
113
app/src/main/res/layout/ad_admob_native_exit_layout.xml
Normal file
113
app/src/main/res/layout/ad_admob_native_exit_layout.xml
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<com.google.android.gms.ads.nativead.NativeAdView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:minHeight="50dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView style="@style/AdAttribution" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="20dp"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingRight="20dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ad_app_icon"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:paddingEnd="5dp"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:paddingBottom="5dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ad_headline"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="#0000FF"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ad_advertiser"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<RatingBar
|
||||||
|
android:id="@+id/ad_stars"
|
||||||
|
style="?android:attr/ratingBarStyleSmall"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:isIndicator="true"
|
||||||
|
android:numStars="5"
|
||||||
|
android:stepSize="0.5" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ad_body"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginRight="20dp"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<com.google.android.gms.ads.nativead.MediaView
|
||||||
|
android:id="@+id/ad_media"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="175dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="5dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="10dp">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/ad_call_to_action"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/bg_color"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.gms.ads.nativead.NativeAdView>
|
||||||
25
app/src/main/res/layout/dialog_exit.xml
Normal file
25
app/src/main/res/layout/dialog_exit.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/adFl"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/exitBtn"
|
||||||
|
style="@style/TextViewFont_PopRegular"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/tap_again_to_exit"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</LinearLayout>
|
||||||
@ -39,4 +39,14 @@
|
|||||||
<color name="ok_btn_bg_lv2_color">#FD4E1D</color>
|
<color name="ok_btn_bg_lv2_color">#FD4E1D</color>
|
||||||
<color name="no_click_btn_bg_lv1_color">#7FE43521</color>
|
<color name="no_click_btn_bg_lv1_color">#7FE43521</color>
|
||||||
<color name="no_click_btn_bg_lv2_color">#80FD4E1D</color>
|
<color name="no_click_btn_bg_lv2_color">#80FD4E1D</color>
|
||||||
|
<color name="gnt_test_background_color">#00F4F4F4</color>
|
||||||
|
<color name="gnt_test_background_color_2">#00A3A3A3</color>
|
||||||
|
<color name="gnt_black">#000000</color>
|
||||||
|
<color name="gnt_white">#FFFFFF</color>
|
||||||
|
<color name="gnt_red">#FF0000</color>
|
||||||
|
<color name="gnt_green">#00FF00</color>
|
||||||
|
<color name="gnt_blue">#4285f4</color>
|
||||||
|
<color name="gnt_ad_green">#3A6728</color>
|
||||||
|
<color name="gnt_gray">#808080</color>
|
||||||
|
<color name="gnt_outline">#E0E0E0</color>
|
||||||
</resources>
|
</resources>
|
||||||
@ -1,4 +1,23 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<dimen name="dialog_margin">16dp</dimen>
|
<dimen name="dialog_margin">16dp</dimen>
|
||||||
|
|
||||||
|
<item name="gnt_medium_template_bottom_weight" format="float" type="dimen">0.4</item>
|
||||||
|
<item name="gnt_text_row_weight" format="float" type="dimen">0.25</item>
|
||||||
|
<item name="gnt_media_view_weight" format="float" type="dimen">1.0</item>
|
||||||
|
<item name="gnt_medium_template_top_weight" format="float" type="dimen">0.2</item>
|
||||||
|
<dimen name="gnt_default_margin">10dp</dimen>
|
||||||
|
<dimen name="gnt_small_margin">5dp</dimen>
|
||||||
|
<dimen name="gnt_text_size_small">12sp</dimen>
|
||||||
|
<dimen name="gnt_text_size_large">15sp</dimen>
|
||||||
|
<dimen name="gnt_ad_indicator_width">25dp</dimen>
|
||||||
|
<dimen name="gnt_ad_indicator_height">20dp</dimen>
|
||||||
|
<dimen name="gnt_ad_indicator_bar_height">30dp</dimen>
|
||||||
|
<dimen name="gnt_ad_indicator_top_margin">0dp</dimen>
|
||||||
|
<dimen name="gnt_ad_indicator_bottom_margin">10dp</dimen>
|
||||||
|
<dimen name="gnt_ad_indicator_text_size">10sp</dimen>
|
||||||
|
<dimen name="gnt_small_cta_button_height">30dp</dimen>
|
||||||
|
<dimen name="gnt_medium_cta_button_height">50dp</dimen>
|
||||||
|
<dimen name="gnt_no_margin">0dp</dimen>
|
||||||
|
<dimen name="gnt_no_size">0dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
@ -195,4 +195,6 @@
|
|||||||
<string name="number_image_converted">%1$d imagesF converted</string>
|
<string name="number_image_converted">%1$d imagesF converted</string>
|
||||||
<string name="file_not_pdf_or_corrupted">File not in PDF format or corrupted</string>
|
<string name="file_not_pdf_or_corrupted">File not in PDF format or corrupted</string>
|
||||||
<string name="processing_failed">Processing failed</string>
|
<string name="processing_failed">Processing failed</string>
|
||||||
|
<string name="tap_again_to_exit">Tap again to exit</string>
|
||||||
|
<string name="ad_attribution" translatable="false">Ad</string>
|
||||||
</resources>
|
</resources>
|
||||||
@ -45,4 +45,16 @@
|
|||||||
<item name="android:includeFontPadding">false</item>
|
<item name="android:includeFontPadding">false</item>
|
||||||
<item name="android:fontFamily">@font/poppins_semibold</item>
|
<item name="android:fontFamily">@font/poppins_semibold</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="AdAttribution">
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:layout_gravity">left</item>
|
||||||
|
<item name="android:textColor">#FFFFFF</item>
|
||||||
|
<item name="android:textSize">12sp</item>
|
||||||
|
<item name="android:text">@string/ad_attribution</item>
|
||||||
|
<item name="android:background">#FFCC66</item>
|
||||||
|
<item name="android:width">15dp</item>
|
||||||
|
<item name="android:height">15dp</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
Reference in New Issue
Block a user