|
@@ -1,271 +0,0 @@
|
|
|
-package com.kdanmobile.reader.adpage
|
|
|
-
|
|
|
-import android.util.SparseBooleanArray
|
|
|
-import com.kdanmobile.kmpdfkit.globaldata.Config
|
|
|
-import com.kdanmobile.kmpdfkit.manager.controller.KMPDFDocumentController
|
|
|
-
|
|
|
-/**
|
|
|
- * 廣告頁面小幫手
|
|
|
- * 判斷哪些頁面是廣告、是否該顯示廣告
|
|
|
- *
|
|
|
- * <名詞解釋>
|
|
|
- * 廣告預留頁:在原始文件之間插入的空頁面,只有該頁面可以插入廣告
|
|
|
- *
|
|
|
- * 預設在原始文件的每頁之間都插入廣告預留頁,因此每隔一頁都可以插入一個廣告
|
|
|
- */
|
|
|
-class AdPageHelper {
|
|
|
-
|
|
|
- /**
|
|
|
- * 一些常數定義
|
|
|
- * 未完全了解的情況下,強烈不建議修改這些數值
|
|
|
- */
|
|
|
- companion object {
|
|
|
- // 原始文件每隔幾頁是「廣告預留頁」
|
|
|
- const val DEFAULT_AD_INTERVAL = 1
|
|
|
- // 第一個廣告頁至少是從原始文件第幾頁開始
|
|
|
- const val DEFAULT_INIT_AD_INDEX = 1
|
|
|
- // 至少每隔幾頁(包含預留頁)才可以顯示廣告,預設值4即表示每隔至少原始文件2頁才可以顯示廣告
|
|
|
- const val DEFAULT_PAGE_INTERVAL = 4
|
|
|
- // 至少每隔幾秒才可以顯示廣告
|
|
|
- const val DEFAULT_TIME_INTERVAL = 5
|
|
|
- // 當前頁面改變後,判斷前後第N頁是否該顯示廣告
|
|
|
- const val DEFAULT_NEXT_INTERVAL = 3
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 頁間廣告顯示策略
|
|
|
- * 廣告請求成功後,且符合當前策略時才會顯示廣告
|
|
|
- *
|
|
|
- * HIDE:不顯示頁間廣告,行為表現與未使用客製化的ReaderView前完全相同
|
|
|
- * PAGE_INTERVAL:每隔至少幾頁顯示一個廣告
|
|
|
- * TIME_INTERVAL:每隔至少幾秒顯示一個廣告
|
|
|
- * PAGE_OR_TIME_INTERVAL:每隔幾頁或每隔幾秒顯示一個廣告
|
|
|
- * PAGE_AND_TIME_INTERVAL:每隔幾頁且每隔幾秒顯示一個廣告
|
|
|
- */
|
|
|
- enum class DisplayStrategy {
|
|
|
- HIDE,
|
|
|
- PAGE_INTERVAL,
|
|
|
- TIME_INTERVAL,
|
|
|
- PAGE_OR_TIME_INTERVAL,
|
|
|
- PAGE_AND_TIME_INTERVAL
|
|
|
- }
|
|
|
-
|
|
|
- // 廣告顯示策略
|
|
|
- var displayStrategy = DisplayStrategy.PAGE_INTERVAL
|
|
|
-
|
|
|
- // 原始文件每隔幾頁是「廣告預留頁」,該空間可以用來塞入廣告
|
|
|
- var adInterval = DEFAULT_AD_INTERVAL
|
|
|
- set(value) {
|
|
|
- if (field != value) {
|
|
|
- field = if (value <= 0) 1 else value
|
|
|
- adVisibleSet.clear()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 第一個廣告頁至少是從原始文件第幾頁開始
|
|
|
- var initAdIndex = DEFAULT_INIT_AD_INDEX
|
|
|
- set(value) {
|
|
|
- if (field != value) {
|
|
|
- field = if (value <= 0) 1 else value
|
|
|
- adVisibleSet.clear()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 至少每隔幾頁(包含預留頁)才可以顯示廣告
|
|
|
- var pageInterval = DEFAULT_PAGE_INTERVAL
|
|
|
- set(value) {
|
|
|
- if (field != value) {
|
|
|
- field = if (value <= 0) 1 else value
|
|
|
- adVisibleSet.clear()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 至少每隔幾秒才可以顯示廣告
|
|
|
- var timeInterval = DEFAULT_TIME_INTERVAL
|
|
|
-
|
|
|
- // 當前頁面改變後,判斷前後第N頁是否該顯示廣告
|
|
|
- var nextInterval = DEFAULT_NEXT_INTERVAL
|
|
|
- set(value) {
|
|
|
- field = Math.max(DEFAULT_NEXT_INTERVAL, value)
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 廣告最後顯示時間,與「TIME_INTERVAL」相關的顯示策略會使用到此變數
|
|
|
- */
|
|
|
- private var lastDisplayTime = System.currentTimeMillis()
|
|
|
-
|
|
|
- private val adVisibleSet = SparseBooleanArray()
|
|
|
-
|
|
|
- var kmpdfDocumentController: KMPDFDocumentController? = null
|
|
|
-
|
|
|
- /**
|
|
|
- * 請求廣告
|
|
|
- */
|
|
|
- var requestAd: (position: Int) -> Unit = {}
|
|
|
-
|
|
|
- /**
|
|
|
- * 是否該顯示廣告(例:已訂閱則不顯示廣告)
|
|
|
- */
|
|
|
- var shouldDisplayAd: () -> Boolean = {
|
|
|
- defaultShouldDisplayAd()
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判斷廣告是否讀取完畢
|
|
|
- */
|
|
|
- var isAdLoaded: (position: Int) -> Boolean = {
|
|
|
- false
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 預設的是否該顯示廣告判斷
|
|
|
- * 如果顯示策略為HIDE或當前為水平翻頁則不顯示,反之則顯示廣告
|
|
|
- */
|
|
|
- fun defaultShouldDisplayAd(): Boolean {
|
|
|
- if (displayStrategy == DisplayStrategy.HIDE) return false
|
|
|
- return isVerticalContinuesViewMode()
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判斷第pageIndex頁是否是廣告預留頁,並且該廣告是否可見
|
|
|
- */
|
|
|
- fun isAdVisible(pageIndex: Int): Boolean {
|
|
|
- return isAdPage(pageIndex) && adVisibleSet[pageIndex]
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判斷第pageIndex頁是否可顯示廣告
|
|
|
- * 若是滿足以下任意條件則不可顯示廣告:
|
|
|
- * .該頁小於起始廣告頁
|
|
|
- * .該頁不是廣告預留頁
|
|
|
- * .現在不應顯示廣告
|
|
|
- * .該頁在可視範圍內(與當前頁差距小於nextInterval頁)
|
|
|
- * .頁碼超過文件範圍
|
|
|
- */
|
|
|
- private fun couldAdDisplayAt(pageIndex: Int): Boolean {
|
|
|
- return when {
|
|
|
- pageIndex < initAdIndex -> false
|
|
|
- !isAdPage(pageIndex) -> false
|
|
|
- !shouldDisplayAd.invoke() -> false
|
|
|
- Math.abs(pageIndex - getCurrentPage()) < nextInterval -> false
|
|
|
- pageIndex < 0 || pageIndex >= getPageCount() -> false
|
|
|
- else -> true
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 當前頁面更新時呼叫此方法
|
|
|
- * 判斷是否哪幾頁應請求/顯示廣告
|
|
|
- */
|
|
|
- fun onPageChanged(pageIndex: Int) {
|
|
|
- // 若不該顯示廣告則停止執行
|
|
|
- if (!shouldDisplayAd.invoke()) return
|
|
|
-
|
|
|
- for (dir in -1 .. 1 step 2) {
|
|
|
- // 判斷pageIndex的前後第nextInterval頁
|
|
|
- val targetIndex = pageIndex + nextInterval * dir
|
|
|
- // 是否應顯示廣告
|
|
|
- val displayAd = when (displayStrategy) {
|
|
|
- DisplayStrategy.HIDE -> false
|
|
|
- DisplayStrategy.PAGE_INTERVAL -> isPageStrategyValid(targetIndex)
|
|
|
- DisplayStrategy.TIME_INTERVAL -> isTimeStrategyValid()
|
|
|
- DisplayStrategy.PAGE_OR_TIME_INTERVAL -> isPageStrategyValid(targetIndex) || isTimeStrategyValid()
|
|
|
- DisplayStrategy.PAGE_AND_TIME_INTERVAL -> isPageStrategyValid(targetIndex) && isTimeStrategyValid()
|
|
|
- }
|
|
|
- if (displayAd) {
|
|
|
- // 該頁廣告是否請求成功
|
|
|
- if (isAdLoaded.invoke(targetIndex)) {
|
|
|
- // 若可顯示廣告則直接顯示,並重設最後顯示時間
|
|
|
- if (couldAdDisplayAt(targetIndex)) {
|
|
|
- adVisibleSet.put(targetIndex, true)
|
|
|
- lastDisplayTime = System.currentTimeMillis()
|
|
|
- }
|
|
|
- } else {
|
|
|
- requestAd.invoke(targetIndex)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判斷是否符合「PAGE_INTERVAL」顯示策略
|
|
|
- */
|
|
|
- private fun isPageStrategyValid(pageIndex: Int): Boolean {
|
|
|
- for (index in 0 until adVisibleSet.size()) {
|
|
|
- if (Math.abs(pageIndex - adVisibleSet.keyAt(index)) < pageInterval) {
|
|
|
- return false
|
|
|
- }
|
|
|
- }
|
|
|
- return true
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判斷是否符合「TIME_INTERVAL」顯示策略
|
|
|
- */
|
|
|
- private fun isTimeStrategyValid(): Boolean {
|
|
|
- return System.currentTimeMillis() - lastDisplayTime >= timeInterval * 1000L
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判斷文件第pageIndex頁是否是廣告預留頁
|
|
|
- */
|
|
|
- fun isAdPage(pageIndex: Int): Boolean {
|
|
|
- if (!shouldDisplayAd.invoke()) return false
|
|
|
- if (pageIndex < initAdIndex) return false
|
|
|
- return (pageIndex - initAdIndex) % (adInterval + 1) == 0
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 頁碼轉換,計算原始文件的第rawPageIndex頁在插入廣告預留頁後是第幾頁
|
|
|
- */
|
|
|
- fun convertToPageIndex(rawPageIndex: Int): Int {
|
|
|
- if (!shouldDisplayAd.invoke()) return rawPageIndex
|
|
|
- if (rawPageIndex < initAdIndex) return rawPageIndex
|
|
|
- return rawPageIndex + 1 + (rawPageIndex - initAdIndex) / adInterval
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 頁碼轉換,計算第pageIndex頁刪除廣告預留頁後是原始文件的第幾頁
|
|
|
- */
|
|
|
- fun convertToRawPageIndex(pageIndex: Int): Int {
|
|
|
- if (!shouldDisplayAd.invoke()) return pageIndex
|
|
|
- if (pageIndex < initAdIndex) return pageIndex
|
|
|
- return (pageIndex * adInterval + initAdIndex) / (adInterval + 1)
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 取得包含廣告預留頁的總頁數
|
|
|
- */
|
|
|
- fun getPageCount(isNativeRefresh: Boolean = false): Int {
|
|
|
- val rawPageCount = getRawPageCount(isNativeRefresh)
|
|
|
- if (!shouldDisplayAd.invoke()) return rawPageCount
|
|
|
- val pageCount = convertToPageIndex(rawPageCount)
|
|
|
- val lastPage = pageCount - 1
|
|
|
- // 最後一頁不能是廣告預留頁
|
|
|
- return when (isAdPage(lastPage)) {
|
|
|
- true -> pageCount - 1
|
|
|
- false -> pageCount
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 取得原始文件的總頁數
|
|
|
- */
|
|
|
- fun getRawPageCount(isNativeRefresh: Boolean = false): Int {
|
|
|
- return kmpdfDocumentController?.getDocumentPageCount(isNativeRefresh) ?: 0
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 取得當前顯示頁面的頁碼
|
|
|
- */
|
|
|
- fun getCurrentPage(): Int {
|
|
|
- return kmpdfDocumentController?.currentPageNum ?: 0
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 是否是垂直閱覽模式
|
|
|
- */
|
|
|
- private fun isVerticalContinuesViewMode(): Boolean {
|
|
|
- return kmpdfDocumentController?.pdfViewMode == Config.PDFViewMode.VERTICAL_SINGLE_PAGE_CONTINUES
|
|
|
- }
|
|
|
-}
|