diff --git a/.idea/studiobot.xml b/.idea/studiobot.xml
new file mode 100644
index 0000000..539e3b8
--- /dev/null
+++ b/.idea/studiobot.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c7d19bc..b454418 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -26,6 +26,7 @@
+
+
+
+ android:value="ca-app-pub-5717753826607607~1850604454" />
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/PRApp.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/PRApp.kt
index 36c7d0e..4cfe60d 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/PRApp.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/PRApp.kt
@@ -46,10 +46,11 @@ class PRApp : Application() {
UpLoadManager.init(context = this, tag = "PRApp_upload_task") { _, _ -> }
// 广告初始化
MobileAds.initialize(this)
+ initMobileAds()
}
- private fun initMobileAds(){
- val testDeviceIds = listOf("TEST_DEVICE_ID")
+ private fun initMobileAds() {
+ val testDeviceIds = listOf("9A96E667D69B45F744FD7D724DF8B093")
val configuration = RequestConfiguration.Builder().setTestDeviceIds(testDeviceIds).build()
MobileAds.setRequestConfiguration(configuration)
}
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdInstLoad.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdInstLoad.kt
index e06bb73..792c469 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdInstLoad.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdInstLoad.kt
@@ -19,48 +19,55 @@ class AdInstLoad(
}
private fun loadAd() {
- //多处调用load,也不会重复、不影响缓存广告、展示安全
+ Log.d(AdsInsUtil.Placement.TAG,"广告 $placement 开始加载")
+ // 是否存在缓存广告、展示安全
val cachedAd = InstAdCacheManager.instance.getAdCache(placement)
if (cachedAd != null) {
- Log.d("ocean","广告存在缓存,跳过加载,返回成功")
+ Log.d(AdsInsUtil.Placement.TAG,"广告存在缓存,跳过加载,返回成功")
//缓存广告有效,跳过加载,返回成功
adLoadListener?.loaded(cachedAd)
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 errorMsg = "No AdUnitId for $placement"
- Log.d("ocean","没找到对应的广告ID->$placement")
- adLoadListener?.loadFailed(errorMsg)
- AnalyticsUtils.logAdEvent(
- placement,
- AnalyticsUtils.AdEvent.LOAD_FAIL,
- null,
- errorMsg
- )
+ Log.d(AdsInsUtil.Placement.TAG,"没找到对应的广告ID->$placement")
+ InstAdCacheManager.instance.notifyFail(placement, errorMsg)
return
}
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.REQ)
-
+ Log.d(AdsInsUtil.Placement.TAG,"adUnitId->$adUnitId")
InterstitialAd.load(
activity,
adUnitId,
AdRequest.Builder().build(),
object : InterstitialAdLoadCallback() {
override fun onAdLoaded(ad: InterstitialAd) {
+ Log.d(AdsInsUtil.Placement.TAG,"广告 $placement 加载成功")
InstAdCacheManager.instance.setAdCache(placement, ad)
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.LOADED)
- adLoadListener?.loaded(ad)
+ InstAdCacheManager.instance.notifySuccess(placement, ad)
}
override fun onAdFailedToLoad(adError: LoadAdError) {
+ Log.d(AdsInsUtil.Placement.TAG,"广告 $placement 加载失败")
AnalyticsUtils.logAdEvent(
placement,
AnalyticsUtils.AdEvent.LOAD_FAIL,
adError.code,
adError.message
)
- adLoadListener?.loadFailed(adError.toString())
+ InstAdCacheManager.instance.notifyFail(placement, adError.toString())
}
},
)
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdInstShower.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdInstShower.kt
index 7262911..25701b2 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdInstShower.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdInstShower.kt
@@ -1,38 +1,52 @@
package com.all.pdfreader.pdf.reader.ad
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.google.android.gms.ads.AdError
import com.google.android.gms.ads.FullScreenContentCallback
class AdInstShower(
private val activity: Activity,
- private val placement: AdPlacement,
+ private val placement: AdsInsUtil.AdPlacement,
private val showListener: ShowListener?
) {
+ companion object {
+ private const val SHOW_INTERVAL = 60_000L // 60秒间隔
+ private var lastShowTimestamp = 0L
+ }
+
init {
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)
?: run {
- val errorMsg = "InterstitialAd cache is null for place = $placement"
- AnalyticsUtils.logAdEvent(
- placement,
- AnalyticsUtils.AdEvent.SHOW_FAIL,
- null,
- errorMsg
- )
+ val errorMsg = "插页式广告缓存为空 = $placement"
showListener?.onAdShowFailed(errorMsg)
+ Log.d(AdsInsUtil.Placement.TAG, "showAd->$errorMsg")
return
}
interstitialAd.fullScreenContentCallback = object : FullScreenContentCallback() {
override fun onAdShowedFullScreenContent() {
+ // ======= 新增:展示成功 记录时间 =======
+ lastShowTimestamp = System.currentTimeMillis()
+ Log.d(AdsInsUtil.Placement.TAG, "广告 $placement 展示成功")
AnalyticsUtils.logAdEvent(placement, AnalyticsUtils.AdEvent.SHOW_SUC)
showListener?.onAdShown()
}
@@ -52,6 +66,7 @@ class AdInstShower(
)
InstAdCacheManager.instance.remove(placement)
showListener?.onAdShowFailed(adError.toString())
+ Log.d(AdsInsUtil.Placement.TAG, "广告 $placement 展示失败->${adError}")
}
override fun onAdClicked() {
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdsInsUtil.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdsInsUtil.kt
index 774ad9c..a0a9019 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdsInsUtil.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/AdsInsUtil.kt
@@ -5,16 +5,17 @@ import android.app.Activity
object AdsInsUtil {
/** 广告位定义(可扩展) */
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(
- Placement.INT_AND_PDFTOHOME
- ),
- INT_AND_MERGE(Placement.INT_AND_MERGE), INT_AND_SPLIT(Placement.INT_AND_SPLIT), NATIVE_AND_EXIT(
- Placement.NATIVE_AND_EXIT
- ),
+ SPL_AND_INTO_HOME(Placement.SPL_AND_INTO_HOME),
+ 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(Placement.NATIVE_AND_EXIT),
BAN_AND_HOMEPAGE(Placement.BAN_AND_HOMEPAGE)
}
object Placement {
+ const val TAG = "ocean-ad"
/**
* 启动页插页
*/
@@ -53,7 +54,7 @@ object AdsInsUtil {
// 广告位对应的广告ID
val adUnitIdMap: Map = 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_PDFTOHOME to "ca-app-pub-5717753826607607/7085128570",
AdPlacement.INT_AND_MERGE to "ca-app-pub-5717753826607607/8928693282",
@@ -74,9 +75,33 @@ object AdsInsUtil {
return AdInstLoad(act, adPlacement, loadListener)
}
+ fun showAd(
+ act: Activity, adPlacement: AdPlacement
+ ): AdInstShower {
+ return AdInstShower(act, adPlacement, null)
+ }
+
fun showAd(
act: Activity, adPlacement: AdPlacement, listener: ShowListener?
): AdInstShower {
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()
+ }
+ }
+ )
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ad/BannerManager.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/BannerManager.kt
new file mode 100644
index 0000000..92598b1
--- /dev/null
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/BannerManager.kt
@@ -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()
+
+ //因为有多个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)
+ }
+}
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ad/InstAdCacheManager.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/InstAdCacheManager.kt
index 915dcbb..3a9ef9a 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/ad/InstAdCacheManager.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/InstAdCacheManager.kt
@@ -3,14 +3,56 @@ package com.all.pdfreader.pdf.reader.ad
import com.google.android.gms.ads.interstitial.InterstitialAd
class InstAdCacheManager {
+
private val mAdCacheDict: MutableMap = mutableMapOf()
+ // 是否正在加载
+ private val loadingSet = mutableSetOf()
+
+ // 等待回调的 listener(多个 load 时会加入这里)
+ private val pendingListeners =
+ mutableMapOf>()
+
companion object {
val instance: InstAdCacheManager by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
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) {
mAdCacheDict[place] = CachedAd(adCache)
}
@@ -19,7 +61,7 @@ class InstAdCacheManager {
val cached = mAdCacheDict[place]
return if (cached != null && cached.isValid()) cached.ad
else {
- mAdCacheDict.remove(place) // 过期广告清理
+ mAdCacheDict.remove(place) // 清理过期
null
}
}
@@ -28,12 +70,14 @@ class InstAdCacheManager {
mAdCacheDict.remove(place)
}
-
+ /**
+ * 包装广告 + 时间
+ */
data class CachedAd(
val ad: InterstitialAd,
val loadedAt: Long = System.currentTimeMillis()
) {
- // 广告有效期, 1 小时
+ // 广告有效期 1 小时
fun isValid(): Boolean = System.currentTimeMillis() - loadedAt < 3600_000
}
}
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ad/NativeAdCache.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/NativeAdCache.kt
new file mode 100644
index 0000000..dfb60ab
--- /dev/null
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ad/NativeAdCache.kt
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/BaseActivity.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/BaseActivity.kt
index 732bcf8..300f18e 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/BaseActivity.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/BaseActivity.kt
@@ -24,33 +24,27 @@ abstract class BaseActivity : AppCompatActivity() {
protected val appStore by lazy { AppStore(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- Log.d("ocean", "🚀 ${javaClass.simpleName} onCreate")
setupBackPressedCallback()//初始化back事件
}
override fun onStart() {
super.onStart()
- Log.d("ocean", "🔄 ${javaClass.simpleName} onStart")
}
override fun onResume() {
super.onResume()
- Log.d("ocean", "📱 ${javaClass.simpleName} onResume")
}
override fun onPause() {
super.onPause()
- Log.d("ocean", "⏸️ ${javaClass.simpleName} onPause")
}
override fun onStop() {
super.onStop()
- Log.d("ocean", "🛑 ${javaClass.simpleName} onStop")
}
override fun onDestroy() {
super.onDestroy()
- Log.d("ocean", "💀 ${javaClass.simpleName} onDestroy")
}
protected fun logDebug(message: String) {
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/MainActivity.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/MainActivity.kt
index 2399d19..0b4f277 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/MainActivity.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/MainActivity.kt
@@ -3,6 +3,8 @@ package com.all.pdfreader.pdf.reader.ui.act
import android.content.Intent
import android.os.Build
import android.os.Bundle
+import android.util.DisplayMetrics
+import android.util.Log
import android.view.View
import android.view.WindowManager
import androidx.activity.OnBackPressedCallback
@@ -12,10 +14,14 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.all.pdfreader.pdf.reader.PRApp
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.model.FileActionEvent
import com.all.pdfreader.pdf.reader.model.FragmentType
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.ProgressDialogFragment
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.viewmodel.PdfViewModel
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 java.io.File
@@ -60,6 +76,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
+ loadAd()
AnalyticsUtils.logEvent(AnalyticsUtils.Event.HOME_SHOW)
setupDoubleBackExit()
initObserve()
@@ -305,9 +322,9 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
}
binding.switchScreenOn.setOnCheckedChangeListener(object : OnCheckedChangeListener {
override fun onCheckedChanged(view: CustomSwitchButton?, isChecked: Boolean) {
- if(isChecked){
+ if (isChecked) {
AnalyticsUtils.logEvent(AnalyticsUtils.Event.KEEP_SCREEN_OPEN)
- }else{
+ } else {
AnalyticsUtils.logEvent(AnalyticsUtils.Event.KEEP_SCREEN_CLOSE)
}
view?.setChecked(isChecked)
@@ -414,25 +431,6 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
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() {
if (StoragePermissionHelper.hasBasicStoragePermission(this)) {
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() {
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
- val currentTime = System.currentTimeMillis()
- if (currentTime - lastBackPressedTime < EXIT_INTERVAL) {
- // 双击退出
+ ExitDialogFragment(onExitClick = {
isEnabled = false // 解除拦截
onBackPressedDispatcher.onBackPressed() // 调用系统默认返回逻辑
- } else {
- lastBackPressedTime = currentTime
- showToast(getString(R.string.press_again_to_exit))
- }
+ }).show(supportFragmentManager, TAG)
}
})
}
+
+ 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()
+ }
+
}
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/MergePdfActivity.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/MergePdfActivity.kt
index 2ba860d..8ce34e0 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/MergePdfActivity.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/MergePdfActivity.kt
@@ -11,6 +11,7 @@ import androidx.activity.result.contract.ActivityResultContract
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
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.model.PdfPickerSource
import com.all.pdfreader.pdf.reader.room.entity.PdfDocumentEntity
@@ -41,6 +42,7 @@ class MergePdfActivity : BaseActivity() {
binding = ActivityPdfMergeBinding.inflate(layoutInflater)
setContentView(binding.root)
AnalyticsUtils.logEvent(AnalyticsUtils.Event.MERGE_SHOW)
+ AdsInsUtil.loadAd(this, AdsInsUtil.AdPlacement.INT_AND_MERGE)
setupImmersionBar {
statusBarView(binding.view)
statusBarDarkFont(true)
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfResultActivity.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfResultActivity.kt
index 517f3d6..a31b679 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfResultActivity.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfResultActivity.kt
@@ -15,6 +15,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.all.pdfreader.pdf.reader.PRApp
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.model.PdfPageItem
import com.all.pdfreader.pdf.reader.model.PdfPickerSource
@@ -160,7 +161,11 @@ class PdfResultActivity : BaseActivity() {
}
if (source == PdfPickerSource.SPLIT) {
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.progress = 0
binding.progressBar.max = 100
@@ -210,7 +215,10 @@ class PdfResultActivity : BaseActivity() {
}
}
} 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.progress = 0
binding.progressBar.max = 100
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfViewActivity.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfViewActivity.kt
index dbfee17..4c16259 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfViewActivity.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfViewActivity.kt
@@ -11,13 +11,18 @@ import android.view.inputmethod.EditorInfo
import androidx.activity.OnBackPressedCallback
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
+import com.all.pdfreader.pdf.reader.PRApp
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.model.FileActionEvent
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.ListMoreDialogFragment
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.view.CustomScrollHandle
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.PDFHighlighter
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.observeEvent
import com.github.barteksc.pdfviewer.listener.OnErrorListener
@@ -48,6 +54,7 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
override val TAG: String = "PdfViewActivity"
override val rootBottomView: View?
get() = binding.rootBottomLayout
+
companion object {
const val FRAG_TAG = "PdfViewActivity"
private const val EXTRA_PDF_FILE_PATH = "extra_pdf_file_path"
@@ -77,6 +84,7 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
binding = ActivityPdfViewBinding.inflate(layoutInflater)
setContentView(binding.root)
AnalyticsUtils.logEvent(AnalyticsUtils.Event.PDF_SHOW)
+ BannerManager.loadBanner(this, binding.bannerAdFl, AdsInsUtil.AdPlacement.BAN_AND_HOMEPAGE)
setupImmersionBar {
statusBarView(binding.view)
statusBarDarkFont(true)
@@ -94,6 +102,13 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
//加载书签数据
viewModel.getBookmarks(filePath)
setupOnClick()
+
+ AdsInsUtil.showFinishAd(
+ this@PdfViewActivity,
+ AdsInsUtil.AdPlacement.INT_AND_TOPDF
+ ) {
+ AdsInsUtil.loadAd(this@PdfViewActivity, AdsInsUtil.AdPlacement.INT_AND_TOPDF)
+ }
}
private fun initObserve() {
@@ -451,6 +466,15 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
setSelection(0)
}
} else {
+ AdsInsUtil.showFinishAd(
+ this@PdfViewActivity,
+ AdsInsUtil.AdPlacement.INT_AND_PDFTOHOME
+ ) {
+ AdsInsUtil.loadAd(
+ this@PdfViewActivity,
+ AdsInsUtil.AdPlacement.INT_AND_PDFTOHOME
+ )
+ }
isEnabled = false // 解除拦截
onBackPressedDispatcher.onBackPressed() // 调用系统默认返回逻辑
}
@@ -558,10 +582,20 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
}
override fun onDestroy() {
+ BannerManager.onDestroy(this,AdsInsUtil.AdPlacement.BAN_AND_HOMEPAGE)
super.onDestroy()
if (::searchManager.isInitialized) {
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()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplashActivity.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplashActivity.kt
index 0df9e11..03a6d51 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplashActivity.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplashActivity.kt
@@ -5,11 +5,16 @@ import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
+import android.util.Log
import androidx.activity.OnBackPressedCallback
import androidx.core.view.doOnPreDraw
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.util.AnalyticsUtils
+import com.google.android.gms.ads.interstitial.InterstitialAd
import com.gyf.immersionbar.BarHide
import com.gyf.immersionbar.ImmersionBar
@@ -21,7 +26,7 @@ class SplashActivity : BaseActivity() {
private lateinit var binding: ActivitySplashBinding
companion object {
- private const val SPLASH_DELAY = 3000L // 启动页显示时长
+ private const val AD_TIMEOUT = 15000L // 广告加载超时时间 15 秒
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -35,15 +40,62 @@ class SplashActivity : BaseActivity() {
// 设置启动页布局
setContentView(binding.root)
AnalyticsUtils.logEvent(AnalyticsUtils.Event.SPLASH_VISIBLE)
- // 延迟跳转到权限检查
- Handler(Looper.getMainLooper()).postDelayed({
- navigateToNext()
- }, SPLASH_DELAY)
+ loadSplashAd()
+
}
-
+
+ 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() {
- val intent = Intent(this, MainActivity::class.java)
- startActivity(intent)
+ if (hasNavigated) return
+ hasNavigated = true
+ startActivity(Intent(this, MainActivity::class.java))
finish()
}
@@ -51,4 +103,9 @@ class SplashActivity : BaseActivity() {
override fun onInterceptBackPressed() {
}
+
+ override fun onDestroy() {
+ super.onDestroy()
+ adHandler.removeCallbacks(adTimeoutRunnable)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplitPdfActivity.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplitPdfActivity.kt
index 539cfc7..9da594e 100644
--- a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplitPdfActivity.kt
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplitPdfActivity.kt
@@ -9,6 +9,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
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.model.PdfPageItem
import com.all.pdfreader.pdf.reader.model.PdfPickerSource
@@ -66,6 +67,7 @@ class SplitPdfActivity : BaseActivity() {
super.onCreate(savedInstanceState)
binding = ActivityPdfSplitBinding.inflate(layoutInflater)
setContentView(binding.root)
+ AdsInsUtil.loadAd(this, AdsInsUtil.AdPlacement.INT_AND_SPLIT)
setupImmersionBar {
statusBarView(binding.view)
statusBarDarkFont(true)
diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/dialog/ExitDialogFragment.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/dialog/ExitDialogFragment.kt
new file mode 100644
index 0000000..08d05a7
--- /dev/null
+++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/dialog/ExitDialogFragment.kt
@@ -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(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()
+ }
+}
diff --git a/app/src/main/res/drawable/dr_rc_top_12_bg_color.xml b/app/src/main/res/drawable/dr_rc_top_12_bg_color.xml
new file mode 100644
index 0000000..7cf75ef
--- /dev/null
+++ b/app/src/main/res/drawable/dr_rc_top_12_bg_color.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/gnt_outline_shape.xml b/app/src/main/res/drawable/gnt_outline_shape.xml
new file mode 100644
index 0000000..8400e60
--- /dev/null
+++ b/app/src/main/res/drawable/gnt_outline_shape.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/gnt_rounded_corners_shape.xml b/app/src/main/res/drawable/gnt_rounded_corners_shape.xml
new file mode 100644
index 0000000..ddaf7a8
--- /dev/null
+++ b/app/src/main/res/drawable/gnt_rounded_corners_shape.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/gnt_top_round_12_outline_shape.xml b/app/src/main/res/drawable/gnt_top_round_12_outline_shape.xml
new file mode 100644
index 0000000..d371081
--- /dev/null
+++ b/app/src/main/res/drawable/gnt_top_round_12_outline_shape.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 397bab5..f4617ca 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -8,6 +8,7 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_exit.xml b/app/src/main/res/layout/dialog_exit.xml
new file mode 100644
index 0000000..2d44355
--- /dev/null
+++ b/app/src/main/res/layout/dialog_exit.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 829800a..2f79e3d 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -39,4 +39,14 @@
#FD4E1D
#7FE43521
#80FD4E1D
+ #00F4F4F4
+ #00A3A3A3
+ #000000
+ #FFFFFF
+ #FF0000
+ #00FF00
+ #4285f4
+ #3A6728
+ #808080
+ #E0E0E0
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index bda39f2..f242d90 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,4 +1,23 @@
16dp
+
+ - 0.4
+ - 0.25
+ - 1.0
+ - 0.2
+ 10dp
+ 5dp
+ 12sp
+ 15sp
+ 25dp
+ 20dp
+ 30dp
+ 0dp
+ 10dp
+ 10sp
+ 30dp
+ 50dp
+ 0dp
+ 0dp
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e6104ed..93690eb 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -195,4 +195,6 @@
%1$d imagesF converted
File not in PDF format or corrupted
Processing failed
+ Tap again to exit
+ Ad
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 5ec9b1f..fb771f3 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -45,4 +45,16 @@
- false
- @font/poppins_semibold
+
+
\ No newline at end of file