diff --git a/app/build.gradle b/app/build.gradle index 514e406611..3ccb2cbae3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -153,7 +153,7 @@ android { zipAlignEnabled false signingConfig signingConfigs.debug - buildConfigField "String", "EXPOSURE_REPO", "\"test\"" + buildConfigField "String", "EXPOSURE_REPO", "\"exposure\"" buildConfigField "String", "EXPOSURE_VERSION", "\"E4\"" multiDexKeepProguard file("tinker_multidexkeep.pro") diff --git a/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt b/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt index 84a3f47538..0553132192 100644 --- a/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt +++ b/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt @@ -16,12 +16,10 @@ import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.gamecenter.common.base.fragment.BaseDialogFragment -import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.FixLinearLayoutManager import com.gh.gamecenter.core.utils.DisplayUtils -import com.gh.gamecenter.core.utils.SPUtils.getBoolean import com.gh.gamecenter.databinding.DialogReserveBinding import com.gh.gamecenter.databinding.DialogReserveItemBinding import com.gh.gamecenter.feature.entity.GameEntity diff --git a/app/src/main/java/com/gh/common/exposure/DefaultExposureStateChangeListener.kt b/app/src/main/java/com/gh/common/exposure/DefaultExposureStateChangeListener.kt new file mode 100644 index 0000000000..095d764c8b --- /dev/null +++ b/app/src/main/java/com/gh/common/exposure/DefaultExposureStateChangeListener.kt @@ -0,0 +1,36 @@ +package com.gh.common.exposure + +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureStateChangeListener +import com.gh.gamecenter.feature.exposure.RecyclerViewExposureHelper +import com.lightgame.utils.Utils + +class DefaultExposureStateChangeListener : IExposureStateChangeListener { + + override fun onExposureStateChange( + exposureEvent: ExposureEvent, + position: Int, + inExposure: Boolean + ) { + val exposureStatus = if (inExposure) "曝光中" else "结束曝光" + + Utils.log( + RecyclerViewExposureHelper.TAG, + "onExposureStateChange: 名称 ${exposureEvent.payload.gameName} 位置 $position, $exposureStatus" + ) + + if (!inExposure + && System.currentTimeMillis() - exposureEvent.timeInMillisecond > RecyclerViewExposureHelper.VALID_EXPOSURE_THRESHOLD + ) { + Utils.log( + RecyclerViewExposureHelper.TAG, + "上报列表曝光 ${exposureEvent.payload.gameName} ${exposureEvent.id},曝光时长为 ${System.currentTimeMillis() - exposureEvent.timeInMillisecond}ms" + ) + + // 标记当前 ExposureEvent 已经被使用过 + exposureEvent.markIsUsed() + ExposureManager.log(exposureEvent) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/exposure/ExposureManager.kt b/app/src/main/java/com/gh/common/exposure/ExposureManager.kt index d88ec24083..e2c5a2ef18 100644 --- a/app/src/main/java/com/gh/common/exposure/ExposureManager.kt +++ b/app/src/main/java/com/gh/common/exposure/ExposureManager.kt @@ -4,10 +4,12 @@ import com.gh.gamecenter.BuildConfig import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.loghub.TLogHubHelper import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet -import com.gh.gamecenter.common.utils.toJson import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.minigame.wechat.WGameSubjectCPMListReportHelper +import com.google.gson.ExclusionStrategy +import com.google.gson.FieldAttributes +import com.google.gson.GsonBuilder import com.lightgame.utils.Utils import com.volcengine.model.tls.LogItem @@ -29,6 +31,20 @@ object ExposureManager { private val exposureSet by lazy { hashSetOf() } private val exposureCache by lazy { FixedSizeLinkedHashSet(300) } + private val gson by lazy { + GsonBuilder() + .addSerializationExclusionStrategy(object: ExclusionStrategy { + override fun shouldSkipClass(clazz: Class<*>): Boolean { + return false + } + + override fun shouldSkipField(f: FieldAttributes): Boolean { + return f.name == "additional" + } + }) + .create() + } + /** * Log a single exposure event. */ @@ -102,14 +118,14 @@ object ExposureManager { private fun buildLog(event: ExposureEvent) = LogItem(System.currentTimeMillis()).apply { addContent("__id", event.id) - addContent("payload", event.payload.toJson()) + addContent("payload", gson.toJson(event.payload)) addContent("event", event.event.toString()) - addContent("source", eliminateMultipleBrackets(event.source.toJson())) - addContent("meta", event.meta.toJson()) + addContent("source", eliminateMultipleBrackets(gson.toJson(event.source))) + addContent("meta", gson.toJson(event.meta)) addContent("real_millisecond", event.timeInMillisecond.toString()) addContent( "e-traces", if (event.eTrace != null) { - eliminateMultipleBrackets(event.eTrace?.toJson() ?: "") + eliminateMultipleBrackets(gson.toJson(event.eTrace)) } else "" ) } diff --git a/app/src/main/java/com/gh/common/exposure/ExposureTraceUtils.kt b/app/src/main/java/com/gh/common/exposure/ExposureTraceUtils.kt index 5849b1bc0f..dee73d495a 100644 --- a/app/src/main/java/com/gh/common/exposure/ExposureTraceUtils.kt +++ b/app/src/main/java/com/gh/common/exposure/ExposureTraceUtils.kt @@ -8,13 +8,13 @@ object ExposureTraceUtils { val traceList = arrayListOf() event?.let { - //这里使用deepCopy,是为了防止循环引用调用hashCode方法触发StackOverflowError错误 - val deepCopy = it.deepCopy() - if (deepCopy.eTrace == null) { - traceList.add(deepCopy) + //这里使用 copy,是为了防止循环引用调用hashCode方法触发StackOverflowError错误 + val exposureCopy = it.shallowCopy() + if (exposureCopy.eTrace == null) { + traceList.add(exposureCopy) } else { - traceList.addAll(deepCopy.eTrace!!) - traceList.add(flattenTrace(deepCopy)) + traceList.addAll(exposureCopy.eTrace!!) + traceList.add(flattenTrace(exposureCopy)) } } diff --git a/app/src/main/java/com/gh/common/prioritychain/LaunchRedirectHandler.kt b/app/src/main/java/com/gh/common/prioritychain/LaunchRedirectHandler.kt index 9c30994bb2..89f97a5d9a 100644 --- a/app/src/main/java/com/gh/common/prioritychain/LaunchRedirectHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/LaunchRedirectHandler.kt @@ -5,6 +5,7 @@ import com.gh.common.util.DirectUtils import com.gh.gamecenter.BuildConfig import com.gh.gamecenter.common.entity.LaunchRedirect import com.gh.gamecenter.common.entity.LaunchRedirectWrapper +import com.gh.gamecenter.common.pagelevel.PageLevelManager import com.gh.gamecenter.common.retrofit.BiResponse import com.gh.gamecenter.common.utils.singleToMain import com.gh.gamecenter.core.AppExecutor @@ -65,9 +66,11 @@ class LaunchRedirectHandler(priority: Int) : PriorityChainHandler(priority) { override fun onProcess(): Boolean { val currentActivity = CurrentActivityHolder.getCurrentActivity() - if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) { + if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity) && launchData?.type != "bottom_tab") { if (getStatus() == STATUS_VALID) { - // 当 type 为 "bottom_tab" 时上面 doPreProcess 中已经处理过了,但再选中一次好像也没有什么问题,先不特殊处理这个 case 了 + // 设置下一个页面为顶级页面 + PageLevelManager.setNextPageAsSupremePageLevel() + DirectUtils.directToLinkPage(currentActivity!!, launchData!!, "首次启动跳转", "") // 跳转页面不管回调,延迟 500ms 后执行下一个 handler AppExecutor.uiExecutor.executeWithDelay({ diff --git a/app/src/main/java/com/gh/common/provider/DownloadButtonClickedProviderImpl.kt b/app/src/main/java/com/gh/common/provider/DownloadButtonClickedProviderImpl.kt index de5040b6ba..d163820b15 100644 --- a/app/src/main/java/com/gh/common/provider/DownloadButtonClickedProviderImpl.kt +++ b/app/src/main/java/com/gh/common/provider/DownloadButtonClickedProviderImpl.kt @@ -27,6 +27,8 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider { var packageName = "" var exposureSourceList: List? = null var customPageTrackData: CustomPageTrackData? = null + var isAd = false + var adGroupId = "" val boundedObject = downloadButton.getObject() @@ -54,6 +56,8 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider { packageName = boundedObject.getUniquePackageName() ?: "" exposureSourceList = boundedObject.exposureEvent?.source customPageTrackData = boundedObject.customPageTrackData + isAd = boundedObject.adGroupId.isNotEmpty() + adGroupId = boundedObject.adGroupId } is GameUpdateEntity -> { @@ -136,6 +140,8 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider { "last_page_name", GlobalActivityManager.getLastPageEntity().pageName, "last_page_id", GlobalActivityManager.getLastPageEntity().pageId, "last_page_business_id", GlobalActivityManager.getLastPageEntity().pageBusinessId, + "is_ad", isAd.toString(), + "ad_group_id", adGroupId, *customPageKV ) } diff --git a/app/src/main/java/com/gh/common/provider/ExposureManagerProviderImpl.kt b/app/src/main/java/com/gh/common/provider/ExposureManagerProviderImpl.kt index 987c900732..00362c79c3 100644 --- a/app/src/main/java/com/gh/common/provider/ExposureManagerProviderImpl.kt +++ b/app/src/main/java/com/gh/common/provider/ExposureManagerProviderImpl.kt @@ -1,9 +1,6 @@ package com.gh.common.provider -import android.content.Context -import com.therouter.router.Route import com.gh.common.exposure.ExposureManager -import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.provider.IExposureManagerProvider @@ -12,4 +9,8 @@ class ExposureManagerProviderImpl: IExposureManagerProvider { override fun logExposure(exposureEvent: ExposureEvent) { ExposureManager.log(exposureEvent) } + + override fun logExposureList(exposureEventList: List) { + ExposureManager.log(exposureEventList) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/AdHelper.kt b/app/src/main/java/com/gh/common/util/AdHelper.kt index 2bec6856fc..fb9339e0ee 100644 --- a/app/src/main/java/com/gh/common/util/AdHelper.kt +++ b/app/src/main/java/com/gh/common/util/AdHelper.kt @@ -1,8 +1,19 @@ package com.gh.common.util +import android.annotation.SuppressLint +import android.text.TextUtils import com.gh.common.constant.Config +import com.gh.gamecenter.common.entity.IdfaData +import com.gh.gamecenter.common.pagelevel.IPageLevelProvider +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.tracker.IBusiness +import com.gh.gamecenter.core.utils.CurrentActivityHolder import com.gh.gamecenter.entity.StartupAdEntity +import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.SettingsEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.retrofit.RetrofitManager +import io.reactivex.schedulers.Schedulers object AdHelper { @@ -12,6 +23,74 @@ object AdHelper { const val LOCATION_SUGGESTION_FUNCTION = "suggestion_function" const val LOCATION_SIMULATOR_GAME = "simulator_game" + // 广告标识 https://jira.shanqu.cc/browse/GHZSCY-7041 + private var idfa = "" + + @SuppressLint("CheckResult") + @JvmStatic + fun getIdfa(versionName: String, channel: String) { + RetrofitManager.getInstance().newApi.getIdfa(versionName, channel) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: IdfaData) { + idfa = data.AD + } + }) + } + + fun getIdfaString() : String { + return idfa + } + + fun reportAdRequest(exposureEvent: ExposureEvent) { + val payload = exposureEvent.payload + NewFlatLogUtils.logAdRequest( + pageLevel = payload.pageLevel ?: "", + currentPageCode = payload.currentPageCode ?: "", + currentPageId = payload.currentPageId ?: "", + gameName = payload.gameName ?: "", + gameId = payload.gameId ?: "", + moduleType = payload.moduleType ?: "", + moduleStyle = payload.moduleStyle ?: "", + moduleName = payload.moduleName ?: "", + moduleId = payload.moduleId ?: "", + searchContent = payload.searchContent ?: "", + sequence = payload.sequence ?: -1, + outerSequence = payload.outerSequence ?: -1, + adGroupId = payload.adGroupId ?: "", + ) + } + + fun reportAdRequest(gameEntity: GameEntity) { + val currentActivity = CurrentActivityHolder.getCurrentActivity() + val currentActivitySimpleName = if (currentActivity != null) currentActivity::class.simpleName else "unknown" + val businessId = if (currentActivity is IBusiness) currentActivity.getBusinessId() else null + + var pageLevelString: String? = gameEntity.pageLevelString + + if (TextUtils.isEmpty(pageLevelString)) { + val pageLevelProvider = currentActivity as? IPageLevelProvider + pageLevelString = pageLevelProvider?.getPageLevel()?.toFormatedString() + } + + NewFlatLogUtils.logAdRequest( + pageLevel = gameEntity.pageLevelString, + currentPageCode = currentActivitySimpleName ?: "", + currentPageId = businessId?.first ?: "", + gameName = gameEntity.name ?: "", + gameId = gameEntity.id, + moduleStyle = gameEntity.customPageTrackData?.modulePattern ?: "", + moduleType = gameEntity.customPageTrackData?.moduleType ?: "", + moduleName = gameEntity.customPageTrackData?.linkContentName ?: "", + moduleId = gameEntity.customPageTrackData?.linkContentId ?: "", + searchContent = "", + sequence = gameEntity.sequence ?: -1, + outerSequence = gameEntity.outerSequence ?: -1, + adGroupId = gameEntity.adGroupId, + ) + } + + @JvmStatic fun getStartUp(): StartupAdEntity? { return Config.getNewApiSettingsEntity()?.startup diff --git a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt index 4cd3abc103..fb0959c239 100644 --- a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt +++ b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt @@ -821,6 +821,8 @@ object DownloadItemUtils { "last_page_id", getLastPageEntity().pageId, "last_page_business_id", getLastPageEntity().pageBusinessId, "source", gameEntity.exposureEvent?.source?.toString() ?: "", + "is_ad", if (gameEntity.adGroupId.isEmpty()) "false" else "true", + "ad_group_id", gameEntity.adGroupId, *gameEntity.customPageTrackData?.toKV() ?: arrayOf() ) allStateClickCallback?.onCallback() diff --git a/app/src/main/java/com/gh/common/util/DownloadObserver.kt b/app/src/main/java/com/gh/common/util/DownloadObserver.kt index 300a50a294..1a6dd17b07 100644 --- a/app/src/main/java/com/gh/common/util/DownloadObserver.kt +++ b/app/src/main/java/com/gh/common/util/DownloadObserver.kt @@ -497,6 +497,7 @@ object DownloadObserver { val isPlatformRecommend = java.lang.Boolean.parseBoolean(downloadEntity.getMetaExtra(Constants.IS_PLATFORM_RECOMMEND)) + val adGroupId = downloadEntity.getMetaExtra(Constants.AD_GROUP_ID) val exposureEvent = ExposureUtils.logADownloadCompleteExposureEvent( GameEntity( _id = downloadEntity.gameId, @@ -529,6 +530,8 @@ object DownloadObserver { "game_id", downloadEntity.gameId, "game_type", downloadEntity.categoryChinese, "game_schema_type", if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位", + "is_ad", if (adGroupId.isEmpty()) "false" else "true", + "ad_group_id", adGroupId, *kvs ) } else if (downloadEntity.gameId == Constants.HALO_FUN_GAME_ID) { @@ -572,6 +575,8 @@ object DownloadObserver { "last_page_business_id", getLastPageEntity().pageBusinessId, "download_status", downloadEntity.meta[Constants.DOWNLOAD_STATUS_IN_CHINESE] ?: "", "download_type", if (downloadEntity.asVGame()) "畅玩下载" else "本地下载", + "is_ad", if (adGroupId.isEmpty()) "false" else "true", + "ad_group_id", adGroupId, *kvs ) } diff --git a/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt b/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt index 8388b3ed03..2a4b76398a 100644 --- a/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt +++ b/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt @@ -2779,6 +2779,44 @@ object NewFlatLogUtils { }.let(::log) } + /** + * 广告游戏填充 + * 广告游戏填充到广告位置时,上报该广告游戏插入的位置信息(用于数据回收验证策略的效果,无须曝光,只要请求了广告,就上报) + */ + fun logAdRequest( + pageLevel: String, + currentPageCode: String, + currentPageId: String, + gameName: String, + gameId: String, + moduleType: String, + moduleStyle: String, + moduleName: String, + moduleId: String, + searchContent: String, + sequence: Int, + outerSequence: Int, + adGroupId: String, + ) { + json { + KEY_EVENT to "ad_request" + "page_level" to pageLevel + "current_page_code" to currentPageCode + "current_page_id" to currentPageId + KEY_GAME_NAME to gameName + KEY_GAME_ID to gameId + "module_type" to moduleType + "module_style" to moduleStyle + "module_name" to moduleName + "module_id" to moduleId + "search_content" to searchContent + "sequence" to sequence + "outer_sequence" to outerSequence + "ad_group_id" to adGroupId + parseAndPutMeta()(this) + }.let(::log) + } + // 自有开屏广告加载 fun logSplashAdLoad(id: String) { json { diff --git a/app/src/main/java/com/gh/common/util/ReservationHelper.kt b/app/src/main/java/com/gh/common/util/ReservationHelper.kt index 8deefc2e60..1abd0f5387 100644 --- a/app/src/main/java/com/gh/common/util/ReservationHelper.kt +++ b/app/src/main/java/com/gh/common/util/ReservationHelper.kt @@ -80,6 +80,8 @@ object ReservationHelper { "last_page_id", getLastPageEntity().pageId, "last_page_business_id", getLastPageEntity().pageBusinessId, "source", game?.exposureEvent?.source?.toString() ?: "", + "is_ad", if (game?.adGroupId.isNullOrEmpty() == true) "false" else "true", + "ad_group_id", game?.adGroupId ?: "", *game?.customPageTrackData?.toKV() ?: arrayOf() ) @@ -117,6 +119,8 @@ object ReservationHelper { "last_page_id", getLastPageEntity().pageId, "last_page_business_id", getLastPageEntity().pageBusinessId, "source", game?.exposureEvent?.source?.toString() ?: "", + "is_ad", if (game?.adGroupId.isNullOrEmpty() == true) "false" else "true", + "ad_group_id", game?.adGroupId ?: "", *game?.customPageTrackData?.toKV() ?: arrayOf() ) ToastUtils.showToast(exception.message ?: "") diff --git a/app/src/main/java/com/gh/download/DownloadManager.java b/app/src/main/java/com/gh/download/DownloadManager.java index b2123ad52c..4a1136da44 100644 --- a/app/src/main/java/com/gh/download/DownloadManager.java +++ b/app/src/main/java/com/gh/download/DownloadManager.java @@ -59,6 +59,7 @@ import com.gh.ndownload.NDownloadBridge; import com.gh.ndownload.NDownloadService; import com.gh.vspace.VHelper; import com.halo.assistant.HaloApp; +import com.lg.vspace.archive.common.Const; import com.lightgame.download.DataWatcher; import com.lightgame.download.DownloadConfig; import com.lightgame.download.DownloadDao; @@ -401,6 +402,9 @@ public class DownloadManager implements DownloadStatusListener { // 将下载事件放入 downloadEntity 中供下载完成时取出使用 downloadEntity.setExposureTrace(GsonUtils.toJson(downloadExposureEvent)); + // 记录广告组 id + ExtensionsKt.addMetaExtra(downloadEntity, Constants.AD_GROUP_ID, gameEntity.getAdGroupId()); + // 保存所有游戏标签 List tags = new ArrayList<>(); for (TagStyleEntity tag : gameEntity.getTagStyle()) { @@ -433,7 +437,9 @@ public class DownloadManager implements DownloadStatusListener { "game_name", gameEntity.getName(), "game_id", gameEntity.getId(), "game_type", gameEntity.getCategoryChinese(), - "game_schema_type", gameEntity.getGameBitChinese() + "game_schema_type", gameEntity.getGameBitChinese(), + "is_ad", TextUtils.isEmpty(gameEntity.getAdGroupId()) ? "false" : "true", + "ad_group_id", gameEntity.getAdGroupId(), }; List vaList = new ArrayList<>(Arrays.asList(vaKvs)); if (customPageTrackData != null) { @@ -451,11 +457,12 @@ public class DownloadManager implements DownloadStatusListener { trackDownloadType = "本地下载"; } - String[] arrayKv = { "game_id", gameEntity.getId(), "game_name", gameEntity.getName(), "game_type", gameEntity.getCategoryChinese(), + "is_ad", TextUtils.isEmpty(gameEntity.getAdGroupId()) ? "false" : "true", + "ad_group_id", gameEntity.getAdGroupId(), "game_label", String.join(",", tags), "game_schema_type", gameEntity.getGameBitChinese(), "page_name", GlobalActivityManager.getCurrentPageEntity().getPageName(), diff --git a/app/src/main/java/com/gh/gamecenter/MainActivity.java b/app/src/main/java/com/gh/gamecenter/MainActivity.java index 40555ff485..b505b766c6 100644 --- a/app/src/main/java/com/gh/gamecenter/MainActivity.java +++ b/app/src/main/java/com/gh/gamecenter/MainActivity.java @@ -76,6 +76,8 @@ import com.gh.gamecenter.common.entity.SuggestType; import com.gh.gamecenter.common.eventbus.EBNetworkState; import com.gh.gamecenter.common.eventbus.EBReuse; import com.gh.gamecenter.common.exposure.meta.MetaUtil; +import com.gh.gamecenter.common.pagelevel.PageLevel; +import com.gh.gamecenter.common.pagelevel.PageLevelManager; import com.gh.gamecenter.common.retrofit.BiResponse; import com.gh.gamecenter.common.retrofit.Response; import com.gh.gamecenter.common.utils.DialogHelper; @@ -88,7 +90,6 @@ import com.gh.gamecenter.core.AppExecutor; import com.gh.gamecenter.core.utils.ClassUtils; import com.gh.gamecenter.core.utils.DisplayUtils; import com.gh.gamecenter.core.utils.GsonUtils; -import com.gh.gamecenter.core.utils.MtaHelper; import com.gh.gamecenter.core.utils.SPUtils; import com.gh.gamecenter.core.utils.ToastUtils; import com.gh.gamecenter.core.utils.UrlFilterUtils; @@ -227,9 +228,6 @@ public class MainActivity extends BaseActivity { }, () -> { DirectUtils.directToSuggestion(MainActivity.this, SuggestType.APP, "APP闪退:", false, 100); - MtaHelper.onEventWithBasicDeviceInfo( - "闪退弹窗", - "玩家操作", "点击反馈"); return null; }); } else { @@ -237,17 +235,9 @@ public class MainActivity extends BaseActivity { , "暂不", "马上反馈", () -> { DirectUtils.directToSuggestion(MainActivity.this, SuggestType.APP, "APP闪退:", false, 100); - MtaHelper.onEventWithBasicDeviceInfo( - "闪退弹窗", - "玩家操作", "点击反馈"); return null; }, - () -> { - MtaHelper.onEventWithBasicDeviceInfo( - "闪退弹窗", - "玩家操作", "点击关闭"); - return null; - }); + () -> null); } } @@ -389,7 +379,6 @@ public class MainActivity extends BaseActivity { HistoryHelper.deleteAttentionVideoRecord(); } }); - } } @@ -1084,4 +1073,26 @@ public class MainActivity extends BaseActivity { } } } + + @Override + public void initPageLevel(@Nullable Bundle savedInstanceState) { + if (savedInstanceState != null) { + if (savedInstanceState.getParcelable(PageLevelManager.KEY_PAGE_LEVEL) != null) { + mPageLevel = savedInstanceState.getParcelable(PageLevelManager.KEY_PAGE_LEVEL); + } + } + + if (mPageLevel == null) { + mPageLevel = new PageLevel( + PageLevel.TYPE_T, + -1, + -1, + new HashMap<>(), + -1, + null); + } + + PageLevelManager.INSTANCE.setCurrentPageLevel(mPageLevel); + } + } diff --git a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt index 81c72a3567..14541ba103 100644 --- a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt @@ -316,6 +316,9 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen { } } + override fun initPageLevel(savedInstanceState: Bundle?) { + // do nothing + } companion object { private const val KEY_REGISTRATION_ID = "registration_id" diff --git a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt index 4076f5c8e0..f4325708ae 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt @@ -453,6 +453,8 @@ class DetailViewHolder( "last_page_id", getLastPageEntity().pageId, "last_page_business_id", getLastPageEntity().pageBusinessId, "source", mGameEntity.exposureEvent?.source?.toString() ?: "", + "is_ad", if (mGameEntity.adGroupId.isEmpty()) "false" else "true", + "ad_group_id", mGameEntity.adGroupId, *mGameEntity.customPageTrackData?.toKV() ?: arrayOf() ) CheckLoginUtils.checkLogin(mViewHolder.context, mEntrance) { diff --git a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameImageViewHolder.kt b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameImageViewHolder.kt index 0bb0953e1b..50aa9c1fd3 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameImageViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameImageViewHolder.kt @@ -8,14 +8,20 @@ import com.gh.gamecenter.common.utils.goneIf import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.databinding.GameImageItemBinding import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider /** * 游戏专题-大图-显示/只显示 */ -class GameImageViewHolder(var binding: GameImageItemBinding) : BaseRecyclerViewHolder(binding.root) { +class GameImageViewHolder(var binding: GameImageItemBinding) : BaseRecyclerViewHolder(binding.root), + IExposureProvider { + + private var boundedGameEntity: GameEntity? = null // 注意:专题详情的大图不能用此方法 fun bindImage(entity: GameEntity, applyRoundCorner: Boolean = false) { + boundedGameEntity = entity binding.run { gameContainer.goneIf(!(entity.type == "game" && entity.getApk().isNotEmpty())) gameIcon.displayGameIcon(entity) @@ -28,11 +34,17 @@ class GameImageViewHolder(var binding: GameImageItemBinding) : BaseRecyclerViewH if (applyRoundCorner) { val roundingParams = RoundingParams.fromCornersRadius( - binding.root.resources.getDimensionPixelSize(com.gh.gamecenter.common.R.dimen.home_large_image_radius).toFloat() + binding.root.resources.getDimensionPixelSize(com.gh.gamecenter.common.R.dimen.home_large_image_radius) + .toFloat() ) binding.gameImageIcon.hierarchy.roundingParams = roundingParams } ImageUtils.displayWithAdaptiveHeight(binding.gameImageIcon, entity.image, width) } + + override fun provideExposureData(): ExposureEvent? { + return boundedGameEntity?.exposureEvent?.getRefreshedExposureEvent() + } + } diff --git a/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogAdapter.kt b/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogAdapter.kt index b7e25d43d9..d19bb512b2 100644 --- a/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogAdapter.kt @@ -13,25 +13,26 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.gamecenter.common.constant.ItemViewType -import com.gh.common.exposure.IExposable import com.gh.common.util.* import com.gh.gamecenter.R import com.gh.gamecenter.common.viewholder.FooterViewHolder import com.gh.gamecenter.common.baselist.ListAdapter import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.core.utils.PageSwitchDataHelper import com.gh.gamecenter.databinding.* import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.entity.SpecialCatalogEntity import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.subject.SubjectActivity.Companion.startSubjectActivity class SpecialCatalogAdapter( context: Context, private val mCatalogViewModel: SpecialCatalogViewModel, private val mLastPageDataMap: HashMap? = null -) : ListAdapter(context), IExposable { +) : ListAdapter(context) { private val mExposureEventSparseArray: SparseArray = SparseArray() var isAutoScroll = true @@ -153,8 +154,8 @@ class SpecialCatalogAdapter( payload.sourcePageName = it[PageSwitchDataHelper.PAGE_BUSINESS_NAME] } } - mExposureEventSparseArray.append(position, exposureEvent) } + holder.exposureEvent = exposureEvent root.setOnClickListener { DirectUtils.directToLinkPage( mContext, @@ -208,28 +209,35 @@ class SpecialCatalogAdapter( is CatalogSubjectItemHolder -> { val subject = mEntityList[position].subject!! - val exposureList = arrayListOf() - for ((index, game) in subject.link.data.withIndex()) { - game.sequence = index - game.subjectName = subject.link.text - game.outerSequence = mEntityList[position].position + runOnIoThread(isLightWeightTask = true) { + val exposureList = arrayListOf() + for ((index, game) in subject.link.data.withIndex()) { + game.sequence = index + game.subjectName = subject.link.text + game.outerSequence = mEntityList[position].position - val exposureEvent = ExposureEvent.createEventWithSourceConcat( - game, - mCatalogViewModel.basicExposureSource, - listOf(ExposureSource("精选页专题", subject.link.text ?: "")) - ).apply { - mLastPageDataMap?.let { - payload.sourcePage = it[PageSwitchDataHelper.PAGE_BUSINESS_TYPE] - payload.sourcePageId = it[PageSwitchDataHelper.PAGE_BUSINESS_ID] - payload.sourcePageName = it[PageSwitchDataHelper.PAGE_BUSINESS_NAME] + val exposureEvent = ExposureEvent.createEventWithSourceConcat( + game, + mCatalogViewModel.basicExposureSource, + listOf(ExposureSource("精选页专题", subject.link.text ?: "")) + ).apply { + mLastPageDataMap?.let { + payload.sourcePage = it[PageSwitchDataHelper.PAGE_BUSINESS_TYPE] + payload.sourcePageId = it[PageSwitchDataHelper.PAGE_BUSINESS_ID] + payload.sourcePageName = it[PageSwitchDataHelper.PAGE_BUSINESS_NAME] + } + } + exposureList.add(exposureEvent) + + game.exposureEvent = exposureEvent + + if (!game.adGroupId.isEmpty() && !game.isAdRequestReported) { + AdHelper.reportAdRequest(exposureEvent) + game.isAdRequestReported = true } } - exposureList.add(exposureEvent) - - game.exposureEvent = exposureEvent + mEntityList[position].exposureEventList = exposureList } - mEntityList[position].exposureEventList = exposureList holder.bindSubject(subject.link.data, mEntityList[position].position) } @@ -265,10 +273,6 @@ class SpecialCatalogAdapter( } } - override fun getEventByPosition(pos: Int): ExposureEvent? = mExposureEventSparseArray.get(pos) - - override fun getEventListByPosition(pos: Int): List? = mEntityList[pos].exposureEventList - inner class CatalogBannerItemHolder(val binding: CatalogBannerItemBinding) : BaseRecyclerViewHolder(binding.root) { @@ -393,7 +397,14 @@ class SpecialCatalogAdapter( } } - inner class CatalogImageItemHolder(val binding: CatalogImageItemBinding) : BaseRecyclerViewHolder(binding.root) + inner class CatalogImageItemHolder(val binding: CatalogImageItemBinding) : + BaseRecyclerViewHolder(binding.root), IExposureProvider { + var exposureEvent: ExposureEvent? = null + + override fun provideExposureData(): ExposureEvent? { + return exposureEvent?.getRefreshedExposureEvent() + } + } inner class CatalogHeaderItemHolder(val binding: CatalogHeaderItemBinding) : BaseRecyclerViewHolder(binding.root) diff --git a/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogFragment.kt b/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogFragment.kt index 30beef682e..c19999c8c0 100644 --- a/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogFragment.kt @@ -3,14 +3,15 @@ package com.gh.gamecenter.catalog import android.os.Bundle import android.view.View import com.ethanhua.skeleton.Skeleton +import com.gh.common.exposure.DefaultExposureStateChangeListener import com.gh.gamecenter.common.constant.Constants -import com.gh.common.exposure.ExposureListener import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.R import com.gh.gamecenter.common.baselist.ListFragment import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.databinding.FragmentListBaseSkeletonBinding +import com.gh.gamecenter.feature.exposure.addExposureHelper class SpecialCatalogFragment : ListFragment() { @@ -21,8 +22,6 @@ class SpecialCatalogFragment : ListFragment? = null - private lateinit var mExposureListener: ExposureListener - override fun getLayoutId() = 0 override fun getInflatedLayout() = mBinding.root @@ -43,7 +42,6 @@ class SpecialCatalogFragment : ListFragment(binding.root) + BaseRecyclerViewHolder(binding.root), IExposureProvider { + private var boundedGameEntity: GameEntity? = null + + fun bindGameEntity(gameEntity: GameEntity) { + boundedGameEntity = gameEntity + } + + override fun provideExposureData(): ExposureEvent? { + return boundedGameEntity?.exposureEvent?.getRefreshedExposureEvent() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogSubjectCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogSubjectCollectionAdapter.kt index 2eebdcaf08..731783c9f4 100644 --- a/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogSubjectCollectionAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/catalog/SpecialCatalogSubjectCollectionAdapter.kt @@ -35,7 +35,6 @@ class SpecialCatalogSubjectCollectionAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = CatalogSubjectCollectionListItemViewHolder(parent.toBinding()) - override fun onBindViewHolder(holder: CatalogSubjectCollectionListItemViewHolder, position: Int) { holder.binding.run { root.layoutParams = (root.layoutParams as ViewGroup.MarginLayoutParams).apply { diff --git a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Activity.kt b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Activity.kt index bd1bd83cb2..5431719017 100644 --- a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Activity.kt +++ b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Activity.kt @@ -28,6 +28,12 @@ class CategoryV2Activity : DownloadToolbarActivity() { override fun isAutoResetViewBackgroundEnabled() = true + override fun getBusinessId(): Pair { + val categoryId = intent.extras?.getString(EntranceConsts.KEY_CATEGORY_ID, "") ?: "" + return Pair(categoryId, "") + return super.getBusinessId() + } + override fun onDarkModeChanged() { super.onDarkModeChanged() updateStatusBarColor(com.gh.gamecenter.common.R.color.ui_surface, com.gh.gamecenter.common.R.color.ui_surface) diff --git a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt index 05d58bcbee..0db748a7bb 100644 --- a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt @@ -1,12 +1,11 @@ package com.gh.gamecenter.category2 import android.content.Context -import android.util.SparseArray import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.gh.common.databind.BindingAdapters -import com.gh.common.exposure.IExposable +import com.gh.common.util.AdHelper import com.gh.common.util.DownloadItemUtils import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.adapter.viewholder.GameViewHolder @@ -17,6 +16,7 @@ import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.DrawableView import com.gh.gamecenter.common.viewholder.FooterViewHolder +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.core.utils.PageSwitchDataHelper import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.databinding.CategoryGameItemBinding @@ -24,6 +24,7 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.exposure.ExposureType +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.game.GameItemViewHolder import com.lightgame.download.DownloadEntity import org.json.JSONException @@ -35,9 +36,7 @@ class CategoryV2ListAdapter( private val mCategoryViewModel: CategoryV2ViewModel, private val mEntrance: String?, private var mLastPageDataMap: HashMap? = null -) : ListAdapter(context), IExposable { - - private val mExposureEventSparseArray: SparseArray = SparseArray() +) : ListAdapter(context) { val positionAndPackageMap = HashMap() @@ -76,8 +75,15 @@ class CategoryV2ListAdapter( ItemViewType.GAME_NORMAL -> { CategoryGameItemViewHolder(parent.toBinding()) } + else -> { - FooterViewHolder(mLayoutInflater.inflate(com.gh.gamecenter.common.R.layout.refresh_footerview, parent, false)) + FooterViewHolder( + mLayoutInflater.inflate( + com.gh.gamecenter.common.R.layout.refresh_footerview, + parent, + false + ) + ) } } } @@ -126,7 +132,13 @@ class CategoryV2ListAdapter( payload.sourcePageName = it[PageSwitchDataHelper.PAGE_BUSINESS_NAME] } } - mExposureEventSparseArray.put(position, event) + + runOnIoThread(isLightWeightTask = true) { + if (gameEntity.adGroupId.isEmpty() && !gameEntity.isAdRequestReported) { + AdHelper.reportAdRequest(event) + gameEntity.isAdRequestReported = true + } + } holder.itemView.setOnClickListener { GameDetailActivity.startGameDetailActivity( @@ -227,17 +239,13 @@ class CategoryV2ListAdapter( } } - override fun getEventByPosition(pos: Int): ExposureEvent? { - return mExposureEventSparseArray.get(pos) - } - - override fun getEventListByPosition(pos: Int): List? { - return null - } - inner class CategoryGameItemViewHolder(val binding: CategoryGameItemBinding) : - BaseRecyclerViewHolder(binding.root) { + BaseRecyclerViewHolder(binding.root), IExposureProvider { + + private var boundedGameEntity: GameEntity? = null + fun bindGameItem(gameEntity: GameEntity) { + boundedGameEntity = gameEntity binding.run { gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F @@ -266,24 +274,34 @@ class CategoryV2ListAdapter( binding.gameKaifuType.visibility = View.GONE binding.gameKaifuType.text = "" } + serverLabel != null -> { binding.gameKaifuType.visibility = View.VISIBLE binding.gameKaifuType.text = serverLabel.value if (gameEntity.isUseDefaultServerStyle()) { binding.gameKaifuType.background = com.gh.gamecenter.feature.R.drawable.server_label_default_bg.toDrawable(binding.root.context) - binding.gameKaifuType.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(binding.root.context)) + binding.gameKaifuType.setTextColor( + com.gh.gamecenter.common.R.color.text_secondary.toColor( + binding.root.context + ) + ) } else { binding.gameKaifuType.background = DrawableView.getServerDrawable(serverLabel.color) binding.gameKaifuType.setTextColor(com.gh.gamecenter.common.R.color.white.toColor(binding.root.context)) } } + else -> binding.gameKaifuType.visibility = View.GONE } // 由于RecyclerView的复用机制 需要每次测量gameName的宽 binding.gameName.requestLayout() } + + override fun provideExposureData(): ExposureEvent? { + return boundedGameEntity?.exposureEvent?.getRefreshedExposureEvent() + } } inner class CategoryGameViewHolder(val binding: CategoryGameItemBinding) : GameViewHolder(binding.root) { diff --git a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListFragment.kt b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListFragment.kt index 22f56f0a20..54f2ca1dfc 100644 --- a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListFragment.kt @@ -4,8 +4,8 @@ import android.os.Bundle import android.view.View import android.view.ViewGroup import com.ethanhua.skeleton.Skeleton +import com.gh.common.exposure.DefaultExposureStateChangeListener import com.gh.gamecenter.common.constant.Constants -import com.gh.common.exposure.ExposureListener import com.gh.common.util.DialogUtils import com.gh.common.view.CategoryFilterView import com.gh.common.xapk.XapkInstaller @@ -22,6 +22,7 @@ import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.entity.SubjectSettingEntity import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.exposure.addExposureHelper import com.google.android.flexbox.FlexboxLayout import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity @@ -119,7 +120,7 @@ class CategoryV2ListFragment : ListFragment } } - mListRv.addOnScrollListener(ExposureListener(this, provideListAdapter())) + mListRv.addExposureHelper(this, DefaultExposureStateChangeListener()) mSkeletonScreen = Skeleton.bind(mBinding?.listSkeleton) .shimmer(true) diff --git a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListViewModel.kt b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListViewModel.kt index a1fbcb2c61..eca30e9608 100644 --- a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.gh.gamecenter.common.entity.ExposureEntity import com.gh.common.exposure.ExposureUtils +import com.gh.common.util.AdHelper import com.gh.gamecenter.core.utils.UrlFilterUtils import com.gh.common.view.CategoryFilterView import com.gh.gamecenter.common.baselist.ListViewModel @@ -35,7 +36,7 @@ class CategoryV2ListViewModel( return RetrofitManager .getInstance() .api - .getCategoryV2Games(categoryId, getFilter(), getSortType(), page) + .getCategoryV2Games(categoryId, getFilter(), getSortType(), AdHelper.getIdfaString(), page) } override fun mergeResultLiveData() { @@ -79,7 +80,6 @@ class CategoryV2ListViewModel( "min_size", sortSize.min.toString(), "max_size", sortSize.max.toString() ) - } private fun getSortType(): String? { diff --git a/app/src/main/java/com/gh/gamecenter/discovery/DiscoveryAdapter.kt b/app/src/main/java/com/gh/gamecenter/discovery/DiscoveryAdapter.kt index 95616b3aa7..6091f5db9e 100644 --- a/app/src/main/java/com/gh/gamecenter/discovery/DiscoveryAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/discovery/DiscoveryAdapter.kt @@ -35,6 +35,7 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.exposure.ExposureType +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.game.GameItemViewHolder import com.lightgame.download.DownloadEntity @@ -346,7 +347,9 @@ class DiscoveryAdapter( return null } - class DiscoveryGameViewHolder(val binding: DiscoveryGameItemBinding) : GameViewHolder(binding.root) { + class DiscoveryGameViewHolder(val binding: DiscoveryGameItemBinding) : GameViewHolder(binding.root), IExposureProvider { + private var boundedGameEntity: GameEntity? = null + init { gameDownloadBtn = binding.downloadBtn gameDes = binding.gameDes @@ -358,6 +361,7 @@ class DiscoveryAdapter( } fun bindGameItem(gameEntity: GameEntity) { + boundedGameEntity = gameEntity binding.run { root.background = com.gh.gamecenter.common.R.drawable.reuse_listview_item_style.toDrawable(root.context) gameKaifuType.setBackgroundColor(com.gh.gamecenter.common.R.color.primary_theme.toColor(root.context)) @@ -415,8 +419,12 @@ class DiscoveryAdapter( ) } } - } + override fun provideExposureData(): ExposureEvent? { + boundedGameEntity?.updateBoundedExposureEventIfNeeded() + return boundedGameEntity?.exposureEvent + } + } class RecommendInterestViewHolder(val binding: ItemRecommendInterestBinding) : BaseRecyclerViewHolder(binding.root) diff --git a/app/src/main/java/com/gh/gamecenter/entity/AmwayCommentEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/AmwayCommentEntity.kt index 74a3c8c517..e9872c22ef 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/AmwayCommentEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/AmwayCommentEntity.kt @@ -4,6 +4,7 @@ import android.os.Parcelable import com.gh.gamecenter.common.entity.IconFloat import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.TagStyleEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent import com.google.gson.annotations.SerializedName import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize @@ -42,12 +43,16 @@ data class AmwayCommentEntity( // 曝光用的位置 var sequence: Int = 0, var outerSequence: Int = 0 + ) : Parcelable { @IgnoredOnParcel val name: String? get() = mName.removeSuffix(".") + @IgnoredOnParcel + var exposureEvent: ExposureEvent? = null + fun toGameEntity(): GameEntity { val gameEntity = GameEntity() gameEntity.id = id diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowCommonCollectionViewHolder.kt b/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowCommonCollectionViewHolder.kt index 989a727305..725ba4a959 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowCommonCollectionViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowCommonCollectionViewHolder.kt @@ -33,7 +33,6 @@ class FollowCommonCollectionViewHolder( override fun addExposureEvent(childPosition: Int, link: ExposureLinkEntity) = Unit override fun onChildItemClick(childPosition: Int, entity: CommonCollectionContentEntity) { - val linkEntity = entity.linkEntity NewLogUtils.logCommonCollectionClick( diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowHomeSlideListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowHomeSlideListViewHolder.kt index 69984b4061..e1627d7c6b 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowHomeSlideListViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowHomeSlideListViewHolder.kt @@ -50,9 +50,6 @@ class FollowHomeSlideListViewHolder( } override fun updateImmersiveColor(color: Int) = Unit - - override fun createExposureEvent(childPosition: Int, game: GameEntity?): ExposureEvent? = null - }) } diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowHomeSlideWithCardsViewHolder.kt b/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowHomeSlideWithCardsViewHolder.kt index eb00292f72..5d25923989 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowHomeSlideWithCardsViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/follow/viewholder/FollowHomeSlideWithCardsViewHolder.kt @@ -29,15 +29,12 @@ class FollowHomeSlideWithCardsViewHolder( useCase, lifecycleOwner, binding, + 0, + null, + "", object : CommonContentHomeSlideWithCardsUi.HomeSLideWithCardsEventListener { override fun updateImmersiveColor(color: Int) = Unit - override fun createEventWithSourceConcat(game: GameEntity, subSlideId: String) = Unit - - override fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent? = null - - override fun addGameExposureEvent(position: Int, game: GameEntity, subSlideId: String) = Unit - override fun navigateToGameDetailPage( childPosition: Int, gameEntity: GameEntity, diff --git a/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt index b165ebe64e..714024bc88 100644 --- a/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt @@ -399,7 +399,8 @@ class GameFragmentAdapter( position = prefixedPosition, gameColumnName = gameEntity.name ?: "", gameColumnId = gameEntity.link ?: "", - text = "游戏专题" + text = "游戏专题", + adGroupId = gameEntity.adGroupId ) } @@ -574,7 +575,7 @@ class GameFragmentAdapter( position = sequence, gameColumnName = subject.name ?: "", gameColumnId = subject.id ?: "", - text = "游戏专题" + text = "游戏专题", ) } diff --git a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt index b178608e65..c978e5b6f2 100644 --- a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt @@ -134,7 +134,8 @@ class GameHorizontalAdapter( gameColumnName = mSubjectEntity.name ?: "", gameColumnId = mSubjectEntity.id ?: "", text = "游戏", - columnPattern = subjectTypeToComponentStyle[mSubjectEntity.type] ?: "" + columnPattern = subjectTypeToComponentStyle[mSubjectEntity.type] ?: "", + adGroupId = gameEntity.adGroupId ) } } diff --git a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSimpleItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSimpleItemViewHolder.kt index 61c18a97cd..ad153e8d1a 100644 --- a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSimpleItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalSimpleItemViewHolder.kt @@ -6,9 +6,22 @@ import android.widget.TextView import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.gamecenter.databinding.GameHorizontalSimpleItemBinding import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider class GameHorizontalSimpleItemViewHolder(val binding: GameHorizontalSimpleItemBinding) : - BaseRecyclerViewHolder(binding.root) { + BaseRecyclerViewHolder(binding.root), IExposureProvider { + + private var boundedGameEntity: GameEntity? = null + + fun bindData(game: GameEntity) { + boundedGameEntity = game + } + + override fun provideExposureData(): ExposureEvent? { + return boundedGameEntity?.exposureEvent?.getRefreshedExposureEvent() + } + companion object { @JvmStatic fun setHorizontalNameAndGravity(view: TextView, name: String?) { diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/HotGameListViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/HotGameListViewModel.kt index 0182d09936..148f5bcff4 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/choose/HotGameListViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/choose/HotGameListViewModel.kt @@ -26,8 +26,9 @@ class HotGameListViewModel( subjectData.subjectId, subjectData.sort, subjectData.filter.ifEmpty { "type:全部" }, - page - ) + "", + "", + page) } override fun mergeResultLiveData() { diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt index bc38a53d9e..430e0ca979 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt @@ -819,6 +819,8 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable { "game_schema_type", mGameEntity?.gameBitChinese ?: "", "download_type", mGameEntity?.downloadType ?: "", "game_type", mGameEntity?.categoryChinese ?: "", + "is_ad", if (mGameEntity?.adGroupId.isNullOrEmpty()) "false" else "true", + "ad_group_id", mGameEntity?.adGroupId ?: "", *(mTraceEvent?.additional ?: emptyArray()) ) }, 120) diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt index eab10e699e..79178dda29 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt @@ -842,7 +842,8 @@ class DescAdapter( linkType = itemData.moreLink?.type ?: "", linkId = itemData.moreLink?.link ?: "", text = "右上角", - buttonType = itemData.displayHome + buttonType = itemData.displayHome, + adGroupId = "", ) if (itemData.displayHome == "换一批") { headPb.visibility = View.VISIBLE diff --git a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt index ac5b45c2a6..d01ca00b85 100644 --- a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt +++ b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt @@ -360,7 +360,8 @@ class LegacyHomeFragmentAdapterAssistant( position = prefixedPosition, gameColumnName = gameEntity.name ?: "", gameColumnId = gameEntity.link ?: "", - text = "游戏专题" + text = "游戏专题", + adGroupId = gameEntity.adGroupId ) } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageExposureExt.kt b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageExposureExt.kt index 4dd152dac4..882bd14556 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageExposureExt.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageExposureExt.kt @@ -1,5 +1,6 @@ package com.gh.gamecenter.home.custom +import com.gh.common.util.AdHelper import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.feature.entity.CustomPageTrackData @@ -18,6 +19,7 @@ fun createExposureEvent( game?.sequence = childPosition game?.outerSequence = position game?.customPageTrackData = customPageTrackData + game?.pageLevelString = customPageTrackData.pageLocation.pageLevelString val event = ExposureEvent.createEventWithSourceConcat( gameEntity = game, basicSource = base, @@ -32,9 +34,7 @@ fun fillExposureInSubjectCollection( base: List, customPageTrackData: CustomPageTrackData ) { - - val eventList = arrayListOf() - runOnIoThread(true) { + runOnIoThread(isLightWeightTask = true) { item.data.data.forEachIndexed { index, customSubject -> val source = if (item.isSubjectCollection) { listOf( @@ -51,8 +51,9 @@ fun fillExposureInSubjectCollection( ) } customSubject.games.forEach { game -> + game.pageLevelString = customPageTrackData.pageLocation.pageLevelString game.isAdData = customSubject.adIconActive - val event = createExposureEvent( + game.exposureEvent = createExposureEvent( game, source, base, @@ -60,10 +61,12 @@ fun fillExposureInSubjectCollection( item.componentPosition, customPageTrackData ) - eventList.add(event) + + if (!game.adGroupId.isEmpty() && !game.isAdRequestReported) { + AdHelper.reportAdRequest(game.exposureEvent!!) + game.isAdRequestReported = true + } } } } - item.exposureEventList = eventList - } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageFragment.kt b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageFragment.kt index 04fef31ff0..9970e98412 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageFragment.kt @@ -12,7 +12,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.Observer import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.OnScrollListener -import com.gh.common.exposure.ExposureListener +import com.gh.common.exposure.DefaultExposureStateChangeListener import com.gh.common.exposure.ExposureManager import com.gh.common.iinterface.ISearchToolbarTab import com.gh.common.iinterface.ISmartRefresh @@ -39,6 +39,7 @@ import com.gh.gamecenter.common.databinding.ReuseNoneDataBinding import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.common.eventbus.EBReuse import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.pagelevel.IPageLevelProvider import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.ScrollableLinearLayoutManager import com.gh.gamecenter.core.AppExecutor @@ -57,6 +58,7 @@ import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.PageLocation import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.exposure.ExposureType +import com.gh.gamecenter.feature.exposure.addExposureHelper import com.gh.gamecenter.feature.minigame.MiniGameItemHelper import com.gh.gamecenter.feature.utils.SentryHelper import com.gh.gamecenter.game.commoncollection.detail.CustomCommonCollectionDetailActivity @@ -108,6 +110,7 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable { private var customPageName = "" private var bottomTabId = "" private var bottomTabName = "" + private var bottomTabIndex = -1 private var tabIndex = -1 private lateinit var pageLocation: PageLocation @@ -138,6 +141,7 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable { customPageName = arguments?.getString(EntranceConsts.KEY_CUSTOM_PAGE_NAME, "") ?: "" bottomTabId = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_ID, "") ?: "" bottomTabName = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_NAME, "") ?: "" + bottomTabIndex = arguments?.getInt(EntranceConsts.KEY_BOTTOM_TAB_INDEX, -1) ?: -1 tabIndex = arguments?.getInt(EntranceConsts.KEY_TAB_INDEX, -1) ?: -1 val tabName = arguments?.getString(EntranceConsts.KEY_TAB_NAME, "") ?: "" val multiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: "" @@ -174,8 +178,16 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable { exposureSourceList, isInSearchToolbarTabWrapperPage ) + + val precisePageLevelString = (activity as? IPageLevelProvider)?.getPageLevel()?.geTempNewPageLevel( + topTabPosition = tabIndex, + bottomTabPosition = bottomTabIndex + )?.toFormatedString(ignoreBottomTabPositionMap = true) ?: "" + pageLocation = PageLocation( bottomTabName, + bottomTabIndex, + precisePageLevelString, multiTabNavName, multiTabNavId, tabIndex, @@ -328,7 +340,6 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable { traceEvent = game.exposureEvent, ) } - }) subjectDestination.observe(viewLifecycleOwner, EventObserver { subject -> @@ -529,6 +540,7 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable { binding.gameList.itemAnimator = null binding.gameList.layoutManager = layoutManager binding.gameList.adapter = adapter + binding.gameList.addExposureHelper(this, DefaultExposureStateChangeListener()) var listScrollHeight = 0 binding.gameList.addOnScrollListener(object : OnScrollListener() { @@ -546,9 +558,6 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable { } }) - val exposureListener = ExposureListener(this, adapter) - binding.gameList.addOnScrollListener(exposureListener) - binding.gameRefresh.setOnRefreshListener { onRefresh() } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageViewModel.kt b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageViewModel.kt index 58629c7b76..db5c9e88ad 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageViewModel.kt @@ -100,8 +100,6 @@ class CustomPageViewModel( */ private val cpmSubjectChangedPageMap: ArrayMap = ArrayMap() - var slideDiscoveryGamesPage = -1 - private lateinit var _pageTracker: CustomPageTracker val pageTracker: CustomPageTracker diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/AnnouncementBannerAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/AnnouncementBannerAdapter.kt index 07130de696..578cbd496d 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/AnnouncementBannerAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/AnnouncementBannerAdapter.kt @@ -7,6 +7,8 @@ import androidx.recyclerview.widget.RecyclerView import com.gh.gamecenter.common.utils.ImageUtils import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.databinding.GameCollectionBannerItemBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.home.custom.model.CustomPageData class AnnouncementBannerAdapter( @@ -44,7 +46,6 @@ class AnnouncementBannerAdapter( } else { notifyDataSetChanged() } - } } @@ -72,7 +73,6 @@ class AnnouncementBannerAdapter( override fun onBindViewHolder(holder: AnnouncementBannerChildViewHolder, position: Int) { val item = getItem(position) - listener.exposure(position, item) holder.bind(item) holder.binding.bannerIv.setOnClickListener { listener.onItemClick(getDataPosition(position), item) @@ -80,19 +80,23 @@ class AnnouncementBannerAdapter( } class AnnouncementBannerChildViewHolder(val binding: GameCollectionBannerItemBinding) : - RecyclerView.ViewHolder(binding.root) { + RecyclerView.ViewHolder(binding.root), IExposureProvider { + + var exposureEvent: ExposureEvent? = null fun bind(item: CustomPageData.Announcement) { + exposureEvent = item.exposureEvent ImageUtils.display(binding.bannerIv, item.image) } + + override fun provideExposureData(): ExposureEvent? { + return exposureEvent?.getRefreshedExposureEvent() + } } interface OnChildEventListener { - fun onItemClick(childPosition: Int, announcement: CustomPageData.Announcement) fun getCurrentPosition(): Int - - fun exposure(childPosition: Int, announcement: CustomPageData.Announcement) } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/ContentLabelLaneAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/ContentLabelLaneAdapter.kt index d494e1d5f3..cbbb9b8aac 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/ContentLabelLaneAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/ContentLabelLaneAdapter.kt @@ -9,12 +9,13 @@ import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.utils.toDrawable import com.gh.gamecenter.databinding.RecyclerContentLabelLaneItemBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.home.custom.model.CustomPageData class ContentLabelLaneAdapter( context: Context, private val clickInvoke: (Int, CustomPageData.CommonContentCollection.ContentTag) -> Unit, - private val exposureInvoke: (Int, CustomPageData.CommonContentCollection.ContentTag) -> Unit ) : CustomBaseChildAdapter( context ) { @@ -29,6 +30,9 @@ class ContentLabelLaneAdapter( override fun onBindViewHolder(holder: ContentLabelChildViewHolder, position: Int) { val item = getItem(position) + + holder.exposureEvent = item.exposureEvent + with(holder.binding) { vBackground.background = R.drawable.bg_shape_content_label_lane_item.toDrawable(context) tvTitle.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context)) @@ -47,7 +51,6 @@ class ContentLabelLaneAdapter( } } - exposureInvoke(position, item) holder.itemView.setOnClickListener { clickInvoke(position, item) } @@ -55,9 +58,12 @@ class ContentLabelLaneAdapter( class ContentLabelChildViewHolder( val binding: RecyclerContentLabelLaneItemBinding - ) : RecyclerView.ViewHolder(binding.root) { + ) : RecyclerView.ViewHolder(binding.root), IExposureProvider { + var exposureEvent : ExposureEvent? = null + override fun provideExposureData(): ExposureEvent? { + return exposureEvent?.getRefreshedExposureEvent() + } } - } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomCommonCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomCommonCollectionAdapter.kt index 8bac2a6547..e9d18170e3 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomCommonCollectionAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomCommonCollectionAdapter.kt @@ -9,6 +9,8 @@ import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.databinding.CommonCollectionItemBinding import com.gh.gamecenter.entity.CommonCollectionContentEntity import com.gh.gamecenter.entity.ExposureLinkEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.home.custom.model.CustomPageData import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER @@ -57,6 +59,7 @@ class CustomCommonCollectionAdapter( } } listener.addExposureEvent(position, item.linkEntity) + holder.boundedLinkEntity = item.linkEntity holder.binding.apply { ImageUtils.display(commonCollectionImage, item.image) @@ -109,10 +112,15 @@ class CustomCommonCollectionAdapter( } class CustomCommonCollectionItemViewHolder(val binding: CommonCollectionItemBinding) : - BaseRecyclerViewHolder(binding.root) + BaseRecyclerViewHolder(binding.root), IExposureProvider { + var boundedLinkEntity: ExposureLinkEntity? = null + + override fun provideExposureData(): ExposureEvent? { + return boundedLinkEntity?.exposureEvent?.getRefreshedExposureEvent() + } + } interface OnEventListener { - fun addExposureEvent(childPosition: Int, exposureLinkEntity: ExposureLinkEntity) fun onChildItemClick(childPosition: Int, item: CommonCollectionContentEntity) diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomDiscoverCardGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomDiscoverCardGameAdapter.kt index 51c0f2d0bb..0c74f6ff56 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomDiscoverCardGameAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomDiscoverCardGameAdapter.kt @@ -166,7 +166,6 @@ class CustomDiscoverCardGameAdapter( RetrofitManager.getInstance().api.discorveryFeedback(gameId, paramsMap.toRequestBody()) .compose(singleToMain()) .subscribe(object : BiResponse() { - override fun onSuccess(data: ResponseBody) { callback.invoke() } @@ -194,7 +193,6 @@ class CustomDiscoverCardGameAdapter( notifyChildItem(busFour.packageName) } - private fun notifyChildItem(packageName: String) { dataList.forEachIndexed { position, gameEntity -> gameEntity.getApk().forEach { apkEntity -> diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomFoldSlideLargeImageItemAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomFoldSlideLargeImageItemAdapter.kt index a4a8bb8c35..73c8ade2a3 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomFoldSlideLargeImageItemAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomFoldSlideLargeImageItemAdapter.kt @@ -25,8 +25,9 @@ import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.databinding.RecyclerFoldSlideLargeImageItemBinding import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage -import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.game.GameItemViewHolder import com.gh.gamecenter.feature.minigame.MiniGameItemHelper import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper @@ -53,7 +54,6 @@ class CustomFoldSlideLargeImageItemAdapter( } submitList(list, true) } - } override fun getKey(t: GameEntity): String { @@ -88,10 +88,8 @@ class CustomFoldSlideLargeImageItemAdapter( holder.itemView.setOnClickListener { eventHelper.navigateToGameDetailPage(realPosition, game) } - } - override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { _recyclerView = recyclerView } @@ -107,7 +105,6 @@ class CustomFoldSlideLargeImageItemAdapter( return@forEach } } - } override fun notifyDownloadDeleted(status: EBDownloadStatus) { @@ -139,7 +136,6 @@ class CustomFoldSlideLargeImageItemAdapter( action(position, game) } } - } fun getInitPosition() = @@ -153,7 +149,7 @@ class CustomFoldSlideLargeImageItemAdapter( private val eventHelper: SubjectEventHelper, val binding: RecyclerFoldSlideLargeImageItemBinding ) : - RecyclerView.ViewHolder(binding.root) { + RecyclerView.ViewHolder(binding.root), IExposureProvider { private lateinit var item: GameEntity @@ -200,7 +196,6 @@ class CustomFoldSlideLargeImageItemAdapter( } if (data.shouldShowDownloadButton) { - binding.btnDownload.goneIf(false) DownloadItemUtils.setOnClickListener( itemView.context, binding.btnDownload, game, bindingAdapterPosition, @@ -226,7 +221,6 @@ class CustomFoldSlideLargeImageItemAdapter( } else { binding.btnDownload.goneIf(true) } - } private fun getBottomBackground(oColor: String): Pair { @@ -303,6 +297,11 @@ class CustomFoldSlideLargeImageItemAdapter( } } + override fun provideExposureData(): ExposureEvent? { + item.updateBoundedExposureEventIfNeeded() + return item.exposureEvent + } + companion object { private const val BUBBLE_SHOW_DURATION = 4000L } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameHorizontalSlideAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameHorizontalSlideAdapter.kt index b2891e6676..ea139cc782 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameHorizontalSlideAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameHorizontalSlideAdapter.kt @@ -24,7 +24,6 @@ class CustomGameHorizontalSlideAdapter( var gameName = "" var entrance = "" - private var _exposureEventList: List? = null private var isShowFirstLine = false private var isShowSecondLine = false @@ -42,12 +41,11 @@ class CustomGameHorizontalSlideAdapter( get() = _data.data - fun setData(data: CustomSubjectItem, exposureEventList: List?) { + fun setData(data: CustomSubjectItem) { isShowFirstLine = false isShowSecondLine = false hasTwoLinesName = false _data = data - _exposureEventList = exposureEventList data.data.data?.forEach { if (!isShowFirstLine && it.assignRemark.firstLine.isNotEmpty()) { isShowFirstLine = true @@ -86,7 +84,6 @@ class CustomGameHorizontalSlideAdapter( } holder.bindGameHorizontalItem(gameEntity, subjectEntity, isShowFirstLine, isShowSecondLine) holder.itemView.setOnClickListener { - gameEntity.exposureEvent = _exposureEventList?.getOrNull(position) eventHelper.navigateToGameDetailPage(position, gameEntity) } @@ -99,7 +96,7 @@ class CustomGameHorizontalSlideAdapter( this, StringUtils.buildString("(游戏-专题:", subjectEntity.name, "-列表[", (position + 1).toString(), "])"), location = StringUtils.buildString("游戏-专题-", subjectEntity.name, ":", gameEntity.name), - traceEvent = _exposureEventList?.getOrNull(position) + traceEvent = gameEntity.exposureEvent ) { eventHelper.onDownloadButtonClick(position, gameEntity) } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameNavigationAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameNavigationAdapter.kt index 1f999d89d1..062d40298f 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameNavigationAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameNavigationAdapter.kt @@ -20,6 +20,7 @@ import com.gh.gamecenter.entity.GameNavigationEntity import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.exposure.ExposureType +import com.gh.gamecenter.feature.exposure.IExposureProvider class CustomGameNavigationAdapter( context: Context, @@ -46,6 +47,7 @@ class CustomGameNavigationAdapter( // 是否显示小红点 var isShowHint = false val entity = dataList[position] + holder.exposureEvent = exposureEventList?.getOrNull(position) ImageUtils.display(holder.binding.navigationView, entity.image) holder.binding.navigationNameTv.text = if (entity.isShowEntryName) { entity.entryName @@ -132,7 +134,6 @@ class CustomGameNavigationAdapter( listener.navigateToLinkPage(it, "导航栏", exposureEvent) } } - } private fun showGuide(entity: GameNavigationEntity, binding: ItemGameNavigationCustomBinding) { @@ -156,7 +157,6 @@ class CustomGameNavigationAdapter( gradientDrawable.setStroke(1F.dip2px(), entity.guide.borderColorInt) gradientDrawable.setColor(entity.guide.backgroundColorInt) binding.tvBubble.background = gradientDrawable - } companion object { @@ -164,7 +164,14 @@ class CustomGameNavigationAdapter( } class GameNavigationViewHolder(val binding: ItemGameNavigationCustomBinding) : - BaseRecyclerViewHolder(binding.root) + BaseRecyclerViewHolder(binding.root), IExposureProvider { + + var exposureEvent: ExposureEvent? = null + + override fun provideExposureData(): ExposureEvent? { + return exposureEvent?.getRefreshedExposureEvent() + } + } interface OnEventListener { fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGamePluginAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGamePluginAdapter.kt index 0574ff0dbc..a6373e3ef7 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGamePluginAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGamePluginAdapter.kt @@ -15,7 +15,6 @@ import com.gh.common.util.HomePluggableHelper import com.gh.gamecenter.common.constant.ItemViewType import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.DisplayUtils -import com.gh.gamecenter.core.utils.MtaHelper.onEvent import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.PluginLocation @@ -119,7 +118,6 @@ class CustomGamePluginAdapter( null ) holder.itemView.setOnClickListener { v: View? -> - onEvent("首页_新", "点击", "插件化" + (position + 1) + "_" + gameEntity.name) DataCollectionUtils.uploadClick( context, "插件化" + "-列表", diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameRefreshVerticalAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameRefreshVerticalAdapter.kt index aef57e6c19..eeb3740a5f 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameRefreshVerticalAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameRefreshVerticalAdapter.kt @@ -11,10 +11,10 @@ import com.gh.gamecenter.adapter.viewholder.GameViewHolder import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage -import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.game.GameItemViewHolder -import com.gh.gamecenter.feature.minigame.MiniGameItemHelper import com.gh.gamecenter.game.vertical.GameItemUi import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper import com.gh.gamecenter.home.custom.model.CustomPageData @@ -95,12 +95,13 @@ class CustomGameRefreshVerticalAdapter( return } } - } - inner class SimpleGameItemViewHolder(private val ui: GameItemUi) : ViewHolder(ui.root) { + inner class SimpleGameItemViewHolder(private val ui: GameItemUi) : ViewHolder(ui.root), IExposureProvider { var placeholderGameViewHolder: GameViewHolder? = null + private var exposureEvent: ExposureEvent? = null + fun bindSimpleGameItem( adapter: RecyclerView.Adapter, gameEntity: GameEntity, @@ -124,6 +125,8 @@ class CustomGameRefreshVerticalAdapter( var paddingEnd = if (isEndOfRow) 16F.dip2px() else 0F.dip2px() val height = 80F.dip2px() + exposureEvent = gameEntity.exposureEvent + itemView.layoutParams = if (!isEndOfRow) { paddingEnd += 1 ViewGroup.LayoutParams(maxWidth - 24F.dip2px(), height) @@ -209,6 +212,9 @@ class CustomGameRefreshVerticalAdapter( root.setPadding(paddingStart, 8F.dip2px(), paddingEnd, 8F.dip2px()) } } - } + override fun provideExposureData(): ExposureEvent? { + return exposureEvent?.getRefreshedExposureEvent() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameVerticalAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameVerticalAdapter.kt index 9536b4e87d..ae0391f4d0 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameVerticalAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameVerticalAdapter.kt @@ -13,8 +13,9 @@ import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.entity.SubjectEntity import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage -import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.game.GameItemViewHolder import com.gh.gamecenter.feature.minigame.MiniGameItemHelper import com.gh.gamecenter.game.vertical.GameItemUi @@ -102,9 +103,11 @@ class CustomGameVerticalAdapter( class SimpleGameItemViewHolder( private val ui: GameItemUi, private val eventHelper: SubjectEventHelper - ) : ViewHolder(ui.root) { + ) : ViewHolder(ui.root), IExposureProvider { var placeholderGameViewHolder: GameViewHolder? = null + private var boundedGameEntity: GameEntity? = null + fun bindSimpleGameItem( adapter: RecyclerView.Adapter, gameEntity: GameEntity, @@ -120,6 +123,8 @@ class CustomGameVerticalAdapter( ) { val context = itemView.context + boundedGameEntity = gameEntity + val paddingStart = 16F.dip2px() val isEndOfRow = position >= if (adapter.itemCount % spanCount == 0) { adapter.itemCount - spanCount @@ -211,6 +216,11 @@ class CustomGameVerticalAdapter( root.setPadding(paddingStart, root.paddingTop, paddingEnd, root.paddingBottom) } } + + override fun provideExposureData(): ExposureEvent? { + boundedGameEntity?.updateBoundedExposureEventIfNeeded() + return boundedGameEntity?.exposureEvent + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionCarouselAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionCarouselAdapter.kt index 7150f6175a..e653a484d0 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionCarouselAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionCarouselAdapter.kt @@ -12,6 +12,8 @@ import com.gh.gamecenter.common.utils.safelyGetInRelease import com.gh.gamecenter.common.view.AsyncCell import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.databinding.HomeGameCollectionCardItemCustomBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureListProvider import com.gh.gamecenter.home.PageConfigure import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper import com.gh.gamecenter.home.custom.model.CustomPageData @@ -21,10 +23,9 @@ class CustomHomeGameCollectionCarouselAdapter( context: Context, private val pageConfigure: PageConfigure, private val eventHelper: GameSubjectCollectionEventHelper -) : - CustomBaseChildAdapter( - context - ) { +) : CustomBaseChildAdapter< + CustomPageData.LinkColumnCollection.CustomSubjectEntity, + CustomHomeGameCollectionCarouselAdapter.HomeGameCollectionCardViewHolder>(context) { private val mPosterWidth = DisplayUtils.getScreenWidth() - 50F.dip2px() @@ -48,10 +49,8 @@ class CustomHomeGameCollectionCarouselAdapter( pageConfigure.entrance, position ) - } } - } override fun getItemViewType(position: Int) = position @@ -68,15 +67,18 @@ class CustomHomeGameCollectionCarouselAdapter( } inner class HomeGameCollectionCardViewHolder(cell: HomeGameCollectionCarouselItemCell) : - BaseRecyclerViewHolder(cell) { + BaseRecyclerViewHolder(cell), IExposureListProvider { + private var boundedItemData: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = null + fun bindGameCollectionCard( binding: HomeGameCollectionCardItemCustomBinding, itemData: CustomPageData.LinkColumnCollection.CustomSubjectEntity, entrance: String, itemPosition: Int ) { + boundedItemData = itemData + binding.run { - val context = root.context root.layoutParams.width = mPosterWidth if (itemData.user.isValid) { @@ -121,8 +123,16 @@ class CustomHomeGameCollectionCarouselAdapter( root.setOnClickListener { eventHelper.navigateSubjectCollectionPage(itemPosition, itemData) } - } } + + override fun provideExposureData(): ExposureEvent? = null + + override fun provideExposureDataList(): List? { + return boundedItemData?.games?.map { + it.updateBoundedExposureEventIfNeeded() + it.exposureEvent!! + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionSlideAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionSlideAdapter.kt index 53cfa4676e..6d9abe7f1a 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionSlideAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionSlideAdapter.kt @@ -18,8 +18,9 @@ import com.gh.gamecenter.databinding.ItemHomeGameCollectionBigSlideCardGameBindi import com.gh.gamecenter.databinding.ItemHomeGameCollectionSmallSlideCardCustomBinding import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage -import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureListProvider import com.gh.gamecenter.home.custom.IGameChangedNotifier import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper import com.gh.gamecenter.home.custom.model.CustomPageData @@ -106,6 +107,7 @@ class CustomHomeGameCollectionSlideAdapter( // 小卡片 if (holder is HomeGameCollectionSmallSlideCardViewHolder) { + holder.updateSubject(subject) holder.binding.run { val params = root.layoutParams as ViewGroup.MarginLayoutParams params.leftMargin = if (position == 0) 0 else (-24F).dip2px() @@ -184,8 +186,10 @@ class CustomHomeGameCollectionSlideAdapter( } } - inner class HomeGameCollectionBigSlideCardViewHolder(view: View) : BaseRecyclerViewHolder(view), - IGameChangedNotifier { + inner class HomeGameCollectionBigSlideCardViewHolder(view: View) : + BaseRecyclerViewHolder(view), + IGameChangedNotifier, + IExposureListProvider { private var subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = null @@ -294,8 +298,35 @@ class CustomHomeGameCollectionSlideAdapter( eventHelper.navigateToGameDetailPage(bindingAdapterPosition, gameEntity, subject) } } + + override fun provideExposureDataList(): List? { + return subject?.games?.map { + it.updateBoundedExposureEventIfNeeded() + it.exposureEvent!! + } + } + + override fun provideExposureData(): ExposureEvent? = null + } class HomeGameCollectionSmallSlideCardViewHolder(val binding: ItemHomeGameCollectionSmallSlideCardCustomBinding) : - RecyclerView.ViewHolder(binding.root) + RecyclerView.ViewHolder(binding.root), + IExposureListProvider { + + private var boundedSubject: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = null + + fun updateSubject(subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity) { + boundedSubject = subject + } + + override fun provideExposureDataList(): List? { + return boundedSubject?.games?.map { + it.updateBoundedExposureEventIfNeeded() + it.exposureEvent!! + } + } + + override fun provideExposureData(): ExposureEvent? = null + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2GameListRvAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2GameListRvAdapter.kt index 607a0eef61..172d37432a 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2GameListRvAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2GameListRvAdapter.kt @@ -5,20 +5,28 @@ import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.SensorsBridge import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.entity.GameDataWrapper import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.PageLocation import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.PageConfigure +import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.test_v2.HomeGameTestV2GameListPlaceHolderViewHolder import com.gh.gamecenter.home.test_v2.HomeGameTestV2GameListSpaceViewHolder import com.gh.gamecenter.home.test_v2.HomeGameTestV2GameListViewHolder import com.lightgame.download.DownloadEntity +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale /** * @author : liujiarui @@ -30,7 +38,9 @@ class CustomHomeGameTestV2GameListRvAdapter( private val childEventHelper: OtherItemEventHelper, private val linkText: String, private val pageLocation: PageLocation, - private val exposureInvoke: (Int, GameEntity) -> ExposureEvent + private val pageConfigure: PageConfigure, + private val componentPosition: Int, + private val trackData: CustomPageTrackData, ) : CustomBaseChildAdapter(context) { private val mEntrance: String = "新游开测" @@ -62,7 +72,6 @@ class CustomHomeGameTestV2GameListRvAdapter( && oldItem.gameData?.id == newItem.gameData?.id && oldItem.gameData?.name == newItem.gameData?.name } - }, false).dispatchUpdatesTo(this) } @@ -175,7 +184,6 @@ class CustomHomeGameTestV2GameListRvAdapter( notifyItemChanged(index) } } - } override fun notifyDownloadDeleted(status: EBDownloadStatus) { @@ -201,17 +209,21 @@ class CustomHomeGameTestV2GameListRvAdapter( gameEntity: GameEntity, position: Int ): ExposureEvent { - return exposureInvoke(position, gameEntity) + val time = gameEntity.time?.time ?: 0L + val date = Date(time * 1000L) + val sdf = SimpleDateFormat("MM.dd", Locale.CHINA) + val format = sdf.format(date) + + gameEntity.exposureEvent = createExposureEvent( + gameEntity, + listOf(ExposureSource("新游开测", "$linkText-$format")), + pageConfigure.exposureSourceList, + position, + componentPosition, + trackData + ) + + return gameEntity.exposureEvent!! } - /** - * 左右滑动曝光条目 - */ - fun exposureItem(firstItemPosition: Int) { - val gameList = dataList - for (position in firstItemPosition..firstItemPosition + 2) { - val gameEntity = gameList.getOrNull(position)?.gameData ?: continue - putExposureEvent(gameEntity, position) - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeHorizontalSlideVideoAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeHorizontalSlideVideoAdapter.kt index 393a421674..089104c18f 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeHorizontalSlideVideoAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeHorizontalSlideVideoAdapter.kt @@ -6,9 +6,7 @@ import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage -import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity -import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.home.PageConfigure import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper import com.gh.gamecenter.home.custom.model.CustomSubjectItem @@ -23,7 +21,6 @@ class CustomHomeHorizontalSlideVideoAdapter( context: Context, private val pageConfigure: PageConfigure, private val eventHelper: SubjectEventHelper, - private val exposureClosure: (GameEntity, Int) -> ExposureEvent, ) : CustomBaseChildAdapter(context) { private lateinit var item: CustomSubjectItem @@ -44,8 +41,6 @@ class CustomHomeHorizontalSlideVideoAdapter( override fun onBindViewHolder(holder: CustomHomeHorizontalSlideVideoItemViewHolder, position: Int) { val gameEntity = dataList[position] - val event = exposureClosure(gameEntity, position) - holder.binding.root.setPadding( 16F.dip2px(), holder.binding.root.topPadding, @@ -67,13 +62,11 @@ class CustomHomeHorizontalSlideVideoAdapter( gameEntity, this, pageConfigure.entrance, - event, item.data.showIndexIconSubscript, item.data.showIndexSubtitle ) } - override fun notifyDownload(download: DownloadEntity) { dataList.forEachIndexed { index, game -> if (game.id == download.gameId) { diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecentVGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecentVGameAdapter.kt index 93381ea920..d68aa43025 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecentVGameAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecentVGameAdapter.kt @@ -10,6 +10,8 @@ import com.gh.gamecenter.common.entity.IconFloat import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.databinding.ItemHomeVgameRefactorBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper import com.gh.gamecenter.manager.PackagesManager import com.gh.vspace.VGameItemData @@ -52,9 +54,13 @@ class CustomHomeRecentVGameAdapter( private var binding: ItemHomeVgameRefactorBinding, private val childEventHelper: OtherItemEventHelper ) : - RecyclerView.ViewHolder(binding.root) { + RecyclerView.ViewHolder(binding.root), IExposureProvider { + + private var boundedItem: VGameItemData? = null fun bindView(entity: VGameItemData) { + boundedItem = entity + if (binding.gameIconIv.getTag(R.string.app_name) != entity.downloadEntity.packageName) { val iconFloat = IconFloat( entity.downloadEntity.getMetaExtra(Constants.GAME_ICON_FLOAT_TOP_TEXT), @@ -176,5 +182,9 @@ class CustomHomeRecentVGameAdapter( binding.controlTv.text = itemData.controlText binding.progressBar.progress = downloadEntity.percent.toInt() } + + override fun provideExposureData(): ExposureEvent? { + return boundedItem?.exposureEvent?.getRefreshedExposureEvent() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecommendItemGridAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecommendItemGridAdapter.kt index 44add9d2c6..841ebfcc0f 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecommendItemGridAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecommendItemGridAdapter.kt @@ -130,7 +130,6 @@ class CustomHomeRecommendItemGridAdapter( } interface OnEventListener { - fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeSlideListAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeSlideListAdapter.kt index 9438541570..124dc50974 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeSlideListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeSlideListAdapter.kt @@ -15,8 +15,6 @@ import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.common.utils.DataLogUtils import com.gh.gamecenter.common.utils.goneIf import com.gh.gamecenter.common.utils.toBinding -import com.gh.gamecenter.core.runOnIoThread -import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.core.utils.PageSwitchDataHelper import com.gh.gamecenter.entity.HomeSlide import com.gh.gamecenter.feature.entity.GameEntity @@ -50,7 +48,6 @@ class CustomHomeSlideListAdapter( } } - val dataCount: Int get() = dataList.size @@ -77,21 +74,8 @@ class CustomHomeSlideListAdapter( return } - val homeSlide = dataList[actualPosition] - val game = homeSlide.linkGame - if (homeSlide.linkType == "game") { - game?.exposureEvent = listener.createExposureEvent(actualPosition, game) - } else { - game?.exposureEvent = listener.createExposureEvent(actualPosition, game) - ?.also { - it.payload.controlType = "轮播图" - it.payload.controlName = homeSlide.title - it.payload.controlLinkName = homeSlide.linkText - it.payload.controlLinkType = homeSlide.linkType - } - } if (holder is CustomHomeSlideListItemViewHolder) { holder.bindSlideListItem(homeSlide) @@ -129,11 +113,6 @@ class CustomHomeSlideListAdapter( ) { rootView.setOnClickListener { val actualPositionString = (actualPosition + 1).toString() - if (homeSlide.linkType == "video") { - MtaHelper.onEvent("首页_新", "轮播_点击", actualPositionString + "_视频详情") - } else if (homeSlide.linkType == "game") { - MtaHelper.onEvent("首页_新", "轮播_点击", actualPositionString + "_游戏详情") - } PageSwitchDataHelper.pushCurrentPageData( hashMapOf( @@ -240,11 +219,8 @@ class CustomHomeSlideListAdapter( fun getActualSize() = dataList.size interface OnEventListener { - fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent? - fun navigateToGameDetailPage(actualPosition: Int, game: GameEntity, text: String, link: LinkEntity?) fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) - } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomIconMatrixAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomIconMatrixAdapter.kt index b8ecb7064c..d7f78d084e 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomIconMatrixAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomIconMatrixAdapter.kt @@ -1,20 +1,18 @@ package com.gh.gamecenter.home.custom.adapter import android.content.Context -import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.gh.common.util.DownloadItemUtils -import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.GameViewHolder import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.databinding.RecyclerChildIconMatrixCustomBinding import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage -import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.home.custom.adapter.CustomPageAdapter.Companion.PAYLOAD_REFRESH_GAME_CHANGED import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem @@ -50,6 +48,9 @@ class CustomIconMatrixAdapter( val isRefreshDownloadStatus = payloads.any { PAYLOAD_REFRESH_GAME_CHANGED == it } + + holder.boundedGameEntity = getItem(position) + if (isRefreshDownloadStatus) { DownloadItemUtils.updateItem( context, @@ -77,7 +78,6 @@ class CustomIconMatrixAdapter( item.name ) gameName.maxLines = if (subject.showDownload) 1 else 2 - } val showStar = subject.showStar && subject.data?.any { it.commentCount > 3 } ?: false @@ -123,7 +123,6 @@ class CustomIconMatrixAdapter( notifyItemChanged(index, PAYLOAD_REFRESH_GAME_CHANGED) } } - } override fun notifyDownloadDeleted(status: EBDownloadStatus) { @@ -147,5 +146,12 @@ class CustomIconMatrixAdapter( class CustomChildIconMatrixViewHolder( val binding: RecyclerChildIconMatrixCustomBinding - ) : ViewHolder(binding.root) + ) : ViewHolder(binding.root), IExposureProvider { + var boundedGameEntity: GameEntity? = null + + override fun provideExposureData(): ExposureEvent? { + boundedGameEntity?.updateBoundedExposureEventIfNeeded() + return boundedGameEntity?.exposureEvent + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomPageAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomPageAdapter.kt index 94ae45d47c..6f22a65781 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomPageAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomPageAdapter.kt @@ -9,13 +9,11 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.OnScrollListener -import com.gh.common.exposure.IExposable import com.gh.gamecenter.R import com.gh.gamecenter.common.baselist.LoadStatus import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage -import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.game.GameAndPosition import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.IGameChangedNotifier @@ -66,7 +64,7 @@ class CustomPageAdapter( private val viewModel: CustomPageViewModel, private val lifecycleOwner: LifecycleOwner, private val scrollCalculatorHelper: ScrollCalculatorHelper, -) : ListAdapter(CALLBACK), IExposable, IGameChangedNotifier { +) : ListAdapter(CALLBACK), IGameChangedNotifier { private var loadStatus: LoadStatus? = null @@ -283,10 +281,10 @@ class CustomPageAdapter( val item = getItem(position) when (holder) { is CustomDoubleCardViewHolder -> holder.bindView(item) + is CustomHomeHorizontalSlideVideoListViewHolder -> holder.bindView(item) - is CustomGameItemViewHolder -> { - holder.bindView(item, this) - } + + is CustomGameItemViewHolder -> holder.bindView(item, this) is CustomGameVerticalSlideViewHolder -> holder.bindView(item) @@ -316,9 +314,7 @@ class CustomPageAdapter( is CustomHomeItemGameTestV2ViewHolder -> holder.bindView(item, position) - is CustomHomeDiscoverCardViewHolder -> { - holder.bindView(item, position) - } + is CustomHomeDiscoverCardViewHolder -> holder.bindView(item, position) is CustomBigImageRecommendViewHolder -> holder.bindView(item) @@ -392,14 +388,6 @@ class CustomPageAdapter( holder.onViewDetach(recyclerView) } - override fun getEventByPosition(pos: Int): ExposureEvent? { - return getItem(pos).exposureEvent - } - - override fun getEventListByPosition(pos: Int): List? { - return getItem(pos).exposureEventList - } - override fun notifyDownload(download: DownloadEntity) { val data = getGameEntityByPackage(download.packageName) data.forEach { (game, position, _) -> @@ -425,7 +413,6 @@ class CustomPageAdapter( it.notifyDownloadDeleted(status) } } - } override fun notifyInstalled(busFour: EBPackage) { @@ -488,7 +475,6 @@ class CustomPageAdapter( private val onLoadMoreListener = object : OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) val layoutManger = recyclerView.layoutManager @@ -499,7 +485,6 @@ class CustomPageAdapter( } } } - } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRankCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRankCollectionAdapter.kt index 00e14961b1..dcbb0a8113 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRankCollectionAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRankCollectionAdapter.kt @@ -10,8 +10,9 @@ import com.gh.gamecenter.common.view.AsyncCell import com.gh.gamecenter.databinding.RankCollectionItemCustomBinding import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage -import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureListProvider import com.gh.gamecenter.game.rank.CustomRankGameItem import com.gh.gamecenter.game.rank.RankGameItemUi import com.gh.gamecenter.home.custom.IGameChangedNotifier @@ -109,8 +110,6 @@ class CustomRankCollectionAdapter( holder.gameItemList[i].rankItemUi.root.visibility = View.GONE } } - - } } } @@ -138,7 +137,6 @@ class CustomRankCollectionAdapter( override fun onViewAttachedToWindow(holder: RankCollectionItemViewHolder) { super.onViewAttachedToWindow(holder) viewHolderList.add(holder) - } override fun onViewDetachedFromWindow(holder: RankCollectionItemViewHolder) { @@ -162,7 +160,7 @@ class CustomRankCollectionAdapter( } inner class RankCollectionItemViewHolder(view: View) : - BaseRecyclerViewHolder(view), IGameChangedNotifier { + BaseRecyclerViewHolder(view), IGameChangedNotifier, IExposureListProvider { val gameItemList = arrayListOf() @@ -235,5 +233,14 @@ class CustomRankCollectionAdapter( } } } + + override fun provideExposureData(): ExposureEvent? = null + + override fun provideExposureDataList(): List? { + return subjectEntity?.games?.map { + it.updateBoundedExposureEventIfNeeded() + it.exposureEvent!! + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRefreshIconAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRefreshIconAdapter.kt index 8d4e129596..248fd01c67 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRefreshIconAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRefreshIconAdapter.kt @@ -9,6 +9,8 @@ import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.databinding.RecyclerRefreshIconItemCustomBinding import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.game.horizontal.GameHorizontalSimpleItemViewHolder import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper import com.gh.gamecenter.home.custom.model.CustomPageData @@ -16,7 +18,6 @@ import com.gh.gamecenter.home.custom.model.CustomPageData class CustomRefreshIconAdapter( context: Context, private val eventHelper: GameSubjectCollectionEventHelper, - private val exposureInvoke: (Int, GameEntity) -> Unit ) : CustomBaseChildAdapter(context) { private var collectionName = "" @@ -45,8 +46,7 @@ class CustomRefreshIconAdapter( override fun onBindViewHolder(holder: RefreshIconChildViewHolder, position: Int) { val gameEntity = getItem(position) - exposureInvoke(position, gameEntity) - + holder.exposureEvent = gameEntity.exposureEvent holder.binding.run { val padR = if (position == itemCount - 1) 16F.dip2px() else 0 root.setPadding(16F.dip2px(), 0, padR, 0) @@ -65,10 +65,15 @@ class CustomRefreshIconAdapter( ) eventHelper.navigateToGameDetailPage(position, gameEntity, subject) } - } - } - class RefreshIconChildViewHolder(val binding: RecyclerRefreshIconItemCustomBinding) : ViewHolder(binding.root) + class RefreshIconChildViewHolder(val binding: RecyclerRefreshIconItemCustomBinding) : + ViewHolder(binding.root), IExposureProvider { + var exposureEvent: ExposureEvent? = null + + override fun provideExposureData(): ExposureEvent? { + return exposureEvent?.getRefreshedExposureEvent() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/NotificationColumnAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/NotificationColumnAdapter.kt index c200cdb12f..eb34f4241c 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/NotificationColumnAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/NotificationColumnAdapter.kt @@ -6,12 +6,11 @@ import androidx.recyclerview.widget.RecyclerView import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.common.utils.visibleIf import com.gh.gamecenter.databinding.RecyclerNotificationColumnItemBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.home.custom.model.CustomPageData -class NotificationColumnAdapter( - context: Context, - private val exposureInvoke: (Int, CustomPageData.Notify) -> Unit -) : +class NotificationColumnAdapter(context: Context, ) : CustomBaseChildAdapter(context) { private var toBeDeletedItemIds = arrayListOf() @@ -51,13 +50,14 @@ class NotificationColumnAdapter( override fun onBindViewHolder(holder: NotificationColumChildViewHolder, position: Int) { val item = getItem(position) + + holder.exposureEvent = item.exposureEvent with(holder.binding) { ivIcon.displayGameIcon(item.image, null, goneIfEmpty = true, null) tvTitle.text = item.title tvDesc.text = item.addedContent ivClose.visibleIf(item.isClosable) } - exposureInvoke(position, item) } private fun getValidItem(position: Int): CustomPageData.Notify { @@ -90,5 +90,13 @@ class NotificationColumnAdapter( class NotificationColumChildViewHolder( val binding: RecyclerNotificationColumnItemBinding - ) : RecyclerView.ViewHolder(binding.root) + ) : RecyclerView.ViewHolder(binding.root), IExposureProvider { + + var exposureEvent: ExposureEvent? = null + + override fun provideExposureData(): ExposureEvent? { + return exposureEvent?.getRefreshedExposureEvent() + } + + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/RecommenCardAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/RecommenCardAdapter.kt index 1c0863d6f6..7434d8e5d6 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/RecommenCardAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/RecommenCardAdapter.kt @@ -9,6 +9,8 @@ import androidx.recyclerview.widget.RecyclerView import com.gh.gamecenter.R import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.databinding.RecyclerRecommendCardItemBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.home.custom.model.CustomPageData import com.gh.gamecenter.home.custom.ui.RecommendCardUi @@ -23,7 +25,6 @@ class RecommendCardAdapter( override fun onBindViewHolder(holder: RecommendCardChildViewHolder, position: Int) { val item = getItem(position) - listener.onItemExposure(position, item) holder.bind(item, position) holder.itemView.setOnClickListener { listener.onItemClick(position, item) @@ -31,9 +32,13 @@ class RecommendCardAdapter( } class RecommendCardChildViewHolder(val binding: RecyclerRecommendCardItemBinding) : - RecyclerView.ViewHolder(binding.root) { + RecyclerView.ViewHolder(binding.root), IExposureProvider { + + private var exposureEvent: ExposureEvent? = null fun bind(item: CustomPageData.RecommendCard, position: Int) { + exposureEvent = item.exposureEvent + binding.tvTitle.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(itemView.context)) binding.tvOriginalPrice.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(itemView.context)) @@ -87,5 +92,9 @@ class RecommendCardAdapter( hideOutOfBoundViewsIfNeed(views) } } + + override fun provideExposureData(): ExposureEvent? { + return exposureEvent?.getRefreshedExposureEvent() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt index bd23d2f399..2561e7b767 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt @@ -7,6 +7,7 @@ import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.entity.* import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.FloatingWindowEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent import com.google.gson.annotations.SerializedName import kotlinx.parcelize.IgnoredOnParcel @@ -560,6 +561,8 @@ class CustomPageData( community = _linkCommunity ) + var exposureEvent: ExposureEvent? = null + data class Guide( @SerializedName("text") private val _text: String? = null @@ -627,6 +630,8 @@ class CustomPageData( text = linkText, community = _linkCommunity ) + + var exposureEvent: ExposureEvent? = null } data class Announcement( @@ -669,6 +674,8 @@ class CustomPageData( text = linkText, community = _linkCommunity ) + + var exposureEvent: ExposureEvent? = null } data class LinkQqGameRecentlyPlayed( @@ -749,6 +756,8 @@ class CustomPageData( community = _linkCommunity ) + var exposureEvent: ExposureEvent? = null + data class TextWithSymbol( @SerializedName("text") private val _text: String? = null, diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRemoteDataSource.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRemoteDataSource.kt index 1451e5997c..82ffd08627 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRemoteDataSource.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRemoteDataSource.kt @@ -1,5 +1,6 @@ package com.gh.gamecenter.home.custom.model +import com.gh.common.util.AdHelper import com.gh.gamecenter.BuildConfig import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.core.utils.SPUtils @@ -36,7 +37,7 @@ class CustomPageRemoteDataSource( it.onSuccess(mainWrapperRepository.customPageLiveData.value!!) } } else { - newApi.getCustomPageData(pageId, page) + newApi.getCustomPageData(pageId, page, AdHelper.getIdfaString()) } } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRepository.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRepository.kt index e6c5761933..d4cc4d70d7 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRepository.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRepository.kt @@ -635,14 +635,14 @@ class CustomPageRepository private constructor( } if (gameSubject != null && !gameSubject.data.isNullOrEmpty()) { + // 替换游戏 + substituteGameIfNeeded(gameSubject) + // 记录已显示的游戏 id for (game in gameSubject.data!!) { displayingGameIdSet.add(game.id) } - // 替换游戏 - substituteGameIfNeeded(gameSubject) - if (gameSubject.tag == "update") { // 优先显示更新标签 gameSubject.data?.forEach { diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/tracker/GameSubjectCollectionTracker.kt b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/GameSubjectCollectionTracker.kt index a7262899dc..ceb9f7113b 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/tracker/GameSubjectCollectionTracker.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/GameSubjectCollectionTracker.kt @@ -90,7 +90,8 @@ class GameSubjectCollectionTracker(private val pageLocation: PageLocation) { game?.name ?: "", link?.type ?: "", link?.link ?: "", - link?.text ?: "" + link?.text ?: "", + adGroupId = game?.adGroupId ?: "" ) } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/tracker/SubjectTracker.kt b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/SubjectTracker.kt index 61d1f9e27d..69e75c66f0 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/tracker/SubjectTracker.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/SubjectTracker.kt @@ -43,7 +43,8 @@ class SubjectTracker(private val pageLocation: PageLocation) { "自定义页面", item.componentStyle, text, - buttonType + buttonType, + adGroupId = game?.adGroupId ?: "", ) } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentCollection12Ui.kt b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentCollection12Ui.kt index 91bd6fc6fa..bbbddaee28 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentCollection12Ui.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentCollection12Ui.kt @@ -31,7 +31,6 @@ class CommonContentCollection12Ui( collection: CustomPageData.CommonContentCollection, leftPosition: Int ) { - val data = collection.data val isFirstLine = leftPosition == 0 @@ -43,7 +42,6 @@ class CommonContentCollection12Ui( listener.navigateToCommonCollectionDetailPage(collection) } - val width = (DisplayUtils.getScreenWidth() - 16F.dip2px() * 2 - 8F.dip2px()) / 2 val height = if (collection.layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD) { width * 4 / 3 @@ -54,7 +52,6 @@ class CommonContentCollection12Ui( val leftBinding = CommonCollectionItemBinding.bind(binding.leftView.root) val rightBinding = CommonCollectionItemBinding.bind(binding.rightView.root) - bindSubView( data[leftPosition], leftBinding, @@ -94,7 +91,6 @@ class CommonContentCollection12Ui( with(binding.root) { setPadding(paddingLeft, paddingTop, paddingRight, _paddingBottom) } - } private fun bindSubView( diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHomeSLideListUi.kt b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHomeSLideListUi.kt index 4e17282fd9..ed3057f323 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHomeSLideListUi.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHomeSLideListUi.kt @@ -62,10 +62,6 @@ class CommonContentHomeSLideListUi( layoutManager, false, object : CustomHomeSlideListAdapter.OnEventListener { - override fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent? { - return listener.createExposureEvent(actualPosition, game) - } - override fun navigateToGameDetailPage( actualPosition: Int, game: GameEntity, @@ -78,7 +74,6 @@ class CommonContentHomeSLideListUi( override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) { listener.navigateToLinkPage(link, text, exposureEvent) } - }) } @@ -277,13 +272,10 @@ class CommonContentHomeSLideListUi( } interface OnCommonHomeSlideListEventListener { - fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) fun navigateToGameDetailPage(position: Int, game: GameEntity, text: String, link: LinkEntity?) fun updateImmersiveColor(color: Int) - - fun createExposureEvent(childPosition: Int, game: GameEntity?): ExposureEvent? } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHomeSlideWithCardsUi.kt b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHomeSlideWithCardsUi.kt index 68d9640bea..7466af1523 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHomeSlideWithCardsUi.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHomeSlideWithCardsUi.kt @@ -17,6 +17,7 @@ import androidx.viewpager2.widget.ViewPager2 import com.gh.gamecenter.R import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.FixLinearLayoutManager import com.gh.gamecenter.common.view.ScrollEventListener @@ -43,9 +44,11 @@ class CommonContentHomeSlideWithCardsUi( private val useCase: CommonContentCollectionUseCase, private val lifecycleOwner: LifecycleOwner, val binding: HomeSlideWithCardsCustomBinding, - private val listener: HomeSLideWithCardsEventListener + private val refreshCount: Int, + private val basicExposureSource: List ? = null, + private val pageLevelString: String, + private val listener: HomeSLideWithCardsEventListener, ) { - private var collection: CustomPageData.CommonContentCollection? = null private val context: Context @@ -59,7 +62,6 @@ class CommonContentHomeSlideWithCardsUi( PagerSnapHelper() } - private val adapter by lazy { CustomHomeSlideListAdapter( context, @@ -67,10 +69,6 @@ class CommonContentHomeSlideWithCardsUi( layoutManager, true, object : CustomHomeSlideListAdapter.OnEventListener { - override fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent? { - return listener.createExposureEvent(actualPosition, game) - } - override fun navigateToGameDetailPage( actualPosition: Int, game: GameEntity, @@ -83,7 +81,6 @@ class CommonContentHomeSlideWithCardsUi( override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) { listener.navigateToLinkPage(link, text, exposureEvent) } - }) } @@ -115,13 +112,11 @@ class CommonContentHomeSlideWithCardsUi( if (collection.slides.subSlide.isNotEmpty()) { bindCards(collection, collection.slides.subSlide) } - } private fun bindSlide( slideList: List ) { - if (binding.recyclerView.adapter == null) { binding.recyclerView.layoutManager = layoutManager binding.recyclerView.adapter = adapter @@ -227,14 +222,12 @@ class CommonContentHomeSlideWithCardsUi( ?: com.gh.gamecenter.common.R.color.ui_surface.toColor(binding.root.context) ) - binding.bannerIndicator.run { pageSize = adapter.getActualSize() notifyDataChanged() } bannerController.start() - } private fun bindCards( @@ -337,8 +330,6 @@ class CommonContentHomeSlideWithCardsUi( } homeSubSlide.cardData.games.take(3).forEachIndexed { index, gameEntity -> - listener.addGameExposureEvent(position, gameEntity, homeSubSlide.id) - when (index) { 0 -> gameIconIv1.setGame(index, gameEntity) @@ -348,6 +339,31 @@ class CommonContentHomeSlideWithCardsUi( } } + + for ((index, game) in homeSubSlide.cardData.games.take(3).withIndex()) { + game.pageLevelString = pageLevelString + game.exposureEvent = ExposureEvent.createEventWithSourceConcat( + gameEntity = game.apply { + sequence = index + outerSequence = refreshCount + }, + basicSource = basicExposureSource ?: arrayListOf(), + source = listOf( + ExposureSource( + "通用内容合集", + "${data.name}+${data.layoutChinese}+${data.id}" + ), + ExposureSource("右侧卡片", homeSubSlide.id) + ) + ) + } + + cardCv.setExposureEventList( + homeSubSlide.cardData.games.mapNotNull { gameEntity -> + gameEntity.exposureEvent + } + ) + countTv.isVisible = homeSubSlide.cardData.gameTotal.game > 3 countTv.typeface = Typeface.createFromAsset(countTv.context.assets, Constants.DIN_FONT_PATH) countTv.text = "+${homeSubSlide.cardData.gameTotal.game - 3}" @@ -408,17 +424,10 @@ class CommonContentHomeSlideWithCardsUi( connect(textContainer.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) connect(textContainer.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM) }.applyTo(cardContainer) - listener.addGameExposureEvent( - position, - GameEntity(sequence = position, outerSequence = useCase.refreshCount), - homeSubSlide.id - ) - } } cardCv.setOnClickListener { - com.gh.common.util.NewFlatLogUtils.logRightSideCardClick(homeSubSlide, useCase.refreshCount, "卡片") listener.navigateToLinkPageInSubSlide( GameEntity(sequence = position, outerSequence = useCase.refreshCount), @@ -481,12 +490,6 @@ class CommonContentHomeSlideWithCardsUi( interface HomeSLideWithCardsEventListener { fun updateImmersiveColor(color: Int) - fun createEventWithSourceConcat(game: GameEntity, subSlideId: String) - - fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent? - - fun addGameExposureEvent(position: Int, game: GameEntity, subSlideId: String) - fun navigateToGameDetailPage(childPosition: Int, gameEntity: GameEntity, text: String, link: LinkEntity?) fun navigateToLinkPageInSubSlide(game: GameEntity, homeSubSlide: HomeSubSlide, text: String) diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHorizontalSlideListUi.kt b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHorizontalSlideListUi.kt index ac8e36788f..5ff3a2822e 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHorizontalSlideListUi.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentHorizontalSlideListUi.kt @@ -40,9 +40,7 @@ class CommonContentHorizontalSlideListUi( override fun onChildItemClick(childPosition: Int, item: CommonCollectionContentEntity) { listener.onChildItemClick(childPosition, item) } - }) - } fun bind(data: CustomPageData.CommonContentCollection) { @@ -99,7 +97,6 @@ class CommonContentHorizontalSlideListUi( } interface OnHorizontalSlideListListener { - fun addExposureEvent(childPosition: Int, link: ExposureLinkEntity) fun onChildItemClick(childPosition: Int, entity: CommonCollectionContentEntity) diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentNavigationUi.kt b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentNavigationUi.kt index 41a2f57a9e..4a98c727eb 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentNavigationUi.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentNavigationUi.kt @@ -24,16 +24,13 @@ class CommonContentNavigationUi( private val adapter by lazy { CustomGameNavigationAdapter(context, object : CustomGameNavigationAdapter.OnEventListener { - override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) { listener.navigateToLinkPage(link, text, exposureEvent) } - }) } fun bind(navigationList: List?, trackEventList: List?) { - if (binding.rvNavigation.adapter == null) { binding.rvNavigation.layoutManager = layoutManager binding.rvNavigation.addItemDecoration(GridSpacingItemDecoration(4, 8f.dip2px(), false, 16f.dip2px())) @@ -42,14 +39,11 @@ class CommonContentNavigationUi( } if (!navigationList.isNullOrEmpty()) { - adapter.setData(navigationList, trackEventList) } - } interface OnNavigationListener { - fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentRecommendUi.kt b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentRecommendUi.kt index b2cb92effb..daa2b6e58d 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentRecommendUi.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/ui/CommonContentRecommendUi.kt @@ -22,7 +22,6 @@ class CommonContentRecommendUi( override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) { listener.navigateToLinkPage(link, text, exposureEvent) } - }) } @@ -59,7 +58,6 @@ class CommonContentRecommendUi( } binding.recommendGv.adapter = adapter adapter.submitList(filteredList, spanCount, exposureList) - } companion object { diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/ui/RecommendCardUi.kt b/app/src/main/java/com/gh/gamecenter/home/custom/ui/RecommendCardUi.kt index 09c19075e6..fe1950e5f1 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/ui/RecommendCardUi.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/ui/RecommendCardUi.kt @@ -54,11 +54,8 @@ class RecommendCardUi( } interface OnRecommendCardEventListener { - fun onItemClick(childPosition: Int, card: CustomPageData.RecommendCard) - fun onItemExposure(childPosition: Int, card: CustomPageData.RecommendCard) - fun onAllClick() } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/BaseCustomViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/BaseCustomViewHolder.kt index 2b39ad07fc..cbe7f802b0 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/BaseCustomViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/BaseCustomViewHolder.kt @@ -4,14 +4,17 @@ import android.view.View import androidx.annotation.CallSuper import androidx.recyclerview.widget.RecyclerView import com.gh.common.exposure.IExposable +import com.gh.common.util.AdHelper import com.gh.common.util.NewFlatLogUtils import com.gh.gamecenter.R +import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.databinding.LayoutTitleCustomBinding import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.PageLocation import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.home.PageConfigure @@ -102,6 +105,28 @@ abstract class BaseCustomViewHolder( } } + fun fillExposureInfoAndReportAd(gameEntity: GameEntity, sequence: Int, item: CustomPageItem) { + gameEntity.pageLevelString = pageLocation.pageLevelString + gameEntity.sequence = sequence + gameEntity.outerSequence = item.componentPosition + if (item is CustomSplitSubjectItem || item is CustomSplitSubjectItem) { + gameEntity.isAdData = item.data.adIconActive + } + gameEntity.outerSequence = item.componentPosition + gameEntity.customPageTrackData = createTrackData(item) + gameEntity.exposureSource = ArrayList().apply { + addAll(pageConfigure.exposureSourceList) + if (item is CustomSplitSubjectItem || item is CustomSplitSubjectItem) { + addAll(item.exposureSource) + } + } + + if (!gameEntity.adGroupId.isEmpty() && gameEntity.isAdRequestReported == false) { + AdHelper.reportAdRequest(gameEntity) + gameEntity.isAdRequestReported = true + } + } + private val _title = TitleData() // 专题标题 @@ -320,7 +345,6 @@ abstract class BaseCustomViewHolder( lavRefresh.setAnimation(if (DarkModeUtils.isDarkModeOn(root.context)) "lottie/icon_title_change_dark.json" else "lottie/icon_title_change_light.json") lavRefresh.progress = 0F } - } } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/ContentLabelLaneViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/ContentLabelLaneViewHolder.kt index 9a3283777b..1db8147147 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/ContentLabelLaneViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/ContentLabelLaneViewHolder.kt @@ -3,6 +3,7 @@ package com.gh.gamecenter.home.custom.viewholder import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.RecyclerContentLabelLaneCustomBinding import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.home.custom.CustomPageViewModel @@ -10,6 +11,7 @@ import com.gh.gamecenter.home.custom.adapter.ContentLabelLaneAdapter import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem +import com.gh.gamecenter.home.custom.model.CustomPageData.CommonContentCollection.ContentTag import com.gh.gamecenter.home.custom.model.CustomPageItem /** @@ -25,59 +27,59 @@ class ContentLabelLaneViewHolder( } private val adapter by lazy { - ContentLabelLaneAdapter(itemView.context, - { index, contentTag -> - childEventHelper.navigateToLinkPage( - contentTag.link, - "内容标签泳道", - _item.exposureEventList.getOrNull(index) - ) - }, - { index, contentTag -> - - val item = _item as CustomCommonContentCollectionItem - val gameEntity = if (contentTag.linkType == "game") { - GameEntity(id = contentTag.linkId, name = contentTag.linkText) - } else { - GameEntity() - } - _item.exposureEventList.add( - createExposureEvent( - gameEntity.also { - it.sequence = index - it.outerSequence = _item.position - }, - listOf( - ExposureSource( - "通用内容合集", - "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" - ), - ExposureSource( - item.data.layoutChinese, - "${contentTag.title}+${contentTag.id}" - ) - ), - pageConfigure.exposureSourceList, - index, - _item.componentPosition, - createTrackData(item) - ) - ) - - }) + ContentLabelLaneAdapter(itemView.context) { index, contentTag -> + childEventHelper.navigateToLinkPage( + contentTag.link, + "内容标签泳道", + contentTag.exposureEvent + ) + } } override fun bindView(item: CustomPageItem) { super.bindView(item) if (item is CustomCommonContentCollectionItem) { - item.exposureEventList.clear() val contentTags = item.data.contentTags if (binding.rvGames.adapter == null) { binding.rvGames.layoutManager = LinearLayoutManager(itemView.context, RecyclerView.HORIZONTAL, false) binding.rvGames.adapter = adapter } + + runOnIoThread(isLightWeightTask = true) { + fillExposureInfo(item.data.contentTags) + } adapter.submitList(contentTags) } + } + private fun fillExposureInfo(contentTags: List) { + for ((index, contentTag) in contentTags.withIndex()) { + val item = _item as CustomCommonContentCollectionItem + val gameEntity = if (contentTag.linkType == "game") { + GameEntity(id = contentTag.linkId, name = contentTag.linkText) + } else { + GameEntity() + } + contentTag.exposureEvent = createExposureEvent( + gameEntity.also { + it.sequence = index + it.outerSequence = _item.position + }, + listOf( + ExposureSource( + "通用内容合集", + "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" + ), + ExposureSource( + item.data.layoutChinese, + "${contentTag.title}+${contentTag.id}" + ) + ), + pageConfigure.exposureSourceList, + index, + _item.componentPosition, + createTrackData(item) + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomAnnouncementBannerViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomAnnouncementBannerViewHolder.kt index 1892cd4f78..04ba8ebab5 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomAnnouncementBannerViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomAnnouncementBannerViewHolder.kt @@ -3,8 +3,8 @@ package com.gh.gamecenter.home.custom.viewholder import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback -import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.RecyclerAnnouncementBannerBinding import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.home.custom.BannerInRecyclerController @@ -31,44 +31,13 @@ class CustomAnnouncementBannerViewHolder( childEventHelper.navigateToLinkPage( announcement.link, "公告横幅", - _item.exposureEventList.getOrNull(childPosition) + announcement.exposureEvent ) } override fun getCurrentPosition(): Int { return binding.vpBanner.currentItem } - - override fun exposure(childPosition: Int, announcement: CustomPageData.Announcement) { - val item = _item as CustomCommonContentCollectionItem - val gameEntity = if (announcement.linkType == "game") { - GameEntity(id = announcement.linkId, name = announcement.linkText) - } else { - GameEntity() - } - _item.exposureEventList.add( - createExposureEvent( - gameEntity.also { - it.sequence = childPosition - it.outerSequence = _item.position - }, - listOf( - ExposureSource( - "通用内容合集", - "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" - ), - ExposureSource( - item.data.layoutChinese - ) - ), - pageConfigure.exposureSourceList, - childPosition, - _item.componentPosition, - createTrackData(item) - ) - ) - } - }) } @@ -78,7 +47,6 @@ class CustomAnnouncementBannerViewHolder( } } - override val childEventHelper by lazy { CommonContentCollectionEventHelper(viewModel) } @@ -89,12 +57,10 @@ class CustomAnnouncementBannerViewHolder( override fun bindView(item: CustomPageItem) { super.bindView(item) - _item.exposureEventList.clear() if (item is CustomCommonContentCollectionItem) { if (binding.vpBanner.adapter == null) { binding.vpBanner.adapter = adapter binding.vpBanner.registerOnPageChangeCallback(object : OnPageChangeCallback() { - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { binding.bannerIndicator.onPageScrolled(adapter.getDataPosition(position), positionOffset) } @@ -105,8 +71,12 @@ class CustomAnnouncementBannerViewHolder( bannerInRecyclerController.start() } }) - } + + runOnIoThread(isLightWeightTask = true) { + fillExposureInfo(item.data.announcements) + } + adapter.submitList(item.data.announcements) if (item.selectedPosition != 0) { binding.vpBanner.setCurrentItem(item.selectedPosition, false) @@ -118,6 +88,31 @@ class CustomAnnouncementBannerViewHolder( } } + private fun fillExposureInfo(dataList: List) { + for ((index, announcement) in dataList.withIndex()) { + val item = _item as CustomCommonContentCollectionItem + val gameEntity = if (announcement.linkType == "game") { + GameEntity(id = announcement.linkId, name = announcement.linkText) + } else { + GameEntity() + } + announcement.exposureEvent = createExposureEvent( + gameEntity.also { + it.sequence = index + it.outerSequence = _item.position + }, + listOf( + ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"), + ExposureSource(item.data.layoutChinese) + ), + pageConfigure.exposureSourceList, + index, + _item.componentPosition, + createTrackData(item) + ) + } + } + override fun onViewAttach(parent: RecyclerView?) { bannerInRecyclerController.onViewAttachedToWindow(parent) } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollection12ViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollection12ViewHolder.kt index b6b2ce364b..6eb8aafc35 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollection12ViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollection12ViewHolder.kt @@ -1,27 +1,20 @@ package com.gh.gamecenter.home.custom.viewholder -import androidx.constraintlayout.widget.ConstraintLayout import com.gh.common.exposure.ExposureManager import com.gh.common.exposure.ExposureTraceUtils import com.gh.common.util.NewLogUtils import com.gh.gamecenter.common.exposure.ExposureSource -import com.gh.gamecenter.common.utils.ImageUtils -import com.gh.gamecenter.common.utils.dip2px -import com.gh.gamecenter.common.utils.goneIf -import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.databinding.CommonCollection12ItemCustomBinding -import com.gh.gamecenter.databinding.CommonCollectionItemBinding import com.gh.gamecenter.entity.CommonCollectionContentEntity import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.exposure.ExposureType +import com.gh.gamecenter.feature.exposure.IExposureListProvider import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper import com.gh.gamecenter.home.custom.model.CustomPageData import com.gh.gamecenter.home.custom.model.CustomPageItem -import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD -import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT import com.gh.gamecenter.home.custom.model.CustomSplitCommonContentCollectionItem import com.gh.gamecenter.home.custom.ui.CommonContentCollection12Ui @@ -32,8 +25,9 @@ import com.gh.gamecenter.home.custom.ui.CommonContentCollection12Ui class CustomCommonCollection12ViewHolder( viewModel: CustomPageViewModel, val binding: CommonCollection12ItemCustomBinding -) : - BaseCustomViewHolder(viewModel, binding.root) { +) : BaseCustomViewHolder(viewModel, binding.root), IExposureListProvider { + + private var exposureEventList = arrayListOf() override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { CommonContentCollectionEventHelper(viewModel) @@ -69,7 +63,7 @@ class CustomCommonCollection12ViewHolder( "板块", "blockData?.name" ) - item.exposureEventList.getOrNull(childPosition)?.let { + linkEntity.exposureEvent?.let { val clickEvent = ExposureEvent( payload = it.payload, source = it.source, @@ -83,45 +77,45 @@ class CustomCommonCollection12ViewHolder( childEventHelper.navigateToLinkPage( linkEntity, "内容卡片", - item.exposureEventList.getOrNull(childPosition) + linkEntity.exposureEvent ) } override fun navigateToCommonCollectionDetailPage(collection: CustomPageData.CommonContentCollection) { childEventHelper.navigateToCommonCollectionDetailPage(_item.link) } - }) } override fun bindView(item: CustomPageItem) { super.bindView(item) if (item is CustomSplitCommonContentCollectionItem) { - - item.exposureEventList.clear() - val data = item.data.data val leftPosition = item.leftPosition - addExposureEvent(leftPosition, data[leftPosition]) + val firstExposureEvent = addExposureEvent(leftPosition, data[leftPosition]) + firstExposureEvent?.let { + exposureEventList.add(0, firstExposureEvent) + } val rightPosition = item.leftPosition + 1 if (rightPosition < data.size) { - addExposureEvent(rightPosition, data[rightPosition]) + val secondExposureEvent = addExposureEvent(rightPosition, data[rightPosition]) + secondExposureEvent?.let { + exposureEventList.add(1, secondExposureEvent) + } } - item.isFirstLine collection12Ui.bind(item.data, item.leftPosition) - } } - private fun addExposureEvent(childPosition: Int, contentEntity: CommonCollectionContentEntity) { + private fun addExposureEvent(childPosition: Int, contentEntity: CommonCollectionContentEntity): ExposureEvent? { (_item as? CustomSplitCommonContentCollectionItem)?.let { item -> val gameEntity = if (contentEntity.linkEntity.type == "game") { GameEntity(id = contentEntity.linkEntity.link, name = contentEntity.linkEntity.text) } else { GameEntity() } - val event = createExposureEvent( + return createExposureEvent( gameEntity.also { it.sequence = childPosition it.outerSequence = item.position @@ -136,58 +130,14 @@ class CustomCommonCollection12ViewHolder( childPosition, item.componentPosition, createTrackData(item) - ).also { contentEntity.linkEntity.exposureEvent = it } - item.exposureEventList.add(event) } + return null } - private fun bindSubView( - contentEntity: CommonCollectionContentEntity, - subBinding: CommonCollectionItemBinding, - layout: Int, - width: Int, - height: Int, - position: Int, - clickClosure: (position: Int, contentEntity: CommonCollectionContentEntity) -> Unit - ) { - subBinding.run { - ImageUtils.display(commonCollectionImage, contentEntity.image) - maskView.goneIf(layout == COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT || (contentEntity.title.isEmpty() && contentEntity.addedContent1.isNullOrEmpty())) - titleTv.goneIf(layout == COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) { - titleTv.text = contentEntity.title - } - desTv.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD) { - desTv.text = contentEntity.addedContent1 - } - linkTitleTv.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) { - linkTitleTv.text = contentEntity.title - } - linkDes1.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) { - linkDes1.text = contentEntity.addedContent1 - linkDes1.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(root.context)) - } - linkDes2.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) { - linkDes2.text = contentEntity.addedContent2 - linkDes2.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(root.context)) - } - - val maskHeight = if (layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD) { - 60f.dip2px() - } else { - 38f.dip2px() - } - val maskParams = maskView.layoutParams as ConstraintLayout.LayoutParams - maskParams.height = maskHeight - maskView.layoutParams = maskParams - - val imageParams = commonCollectionImage.layoutParams as ConstraintLayout.LayoutParams - imageParams.width = width - imageParams.height = height - commonCollectionImage.layoutParams = imageParams - - root.setOnClickListener { clickClosure.invoke(position, contentEntity) } - } + override fun provideExposureData(): ExposureEvent? = null + override fun provideExposureDataList(): List? { + return exposureEventList } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollectionViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollectionViewHolder.kt index 47e5e4b31b..07aa11175a 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollectionViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollectionViewHolder.kt @@ -31,7 +31,6 @@ class CustomCommonCollectionViewHolder( val binding: CommonCollectionListCustomBinding, ) : BaseCustomViewHolder(viewModel, binding.root) { - override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { CommonContentCollectionEventHelper(viewModel) } @@ -43,26 +42,23 @@ class CustomCommonCollectionViewHolder( (_item as? CustomCommonContentCollectionItem)?.let { item -> val gameEntity = if (link.type == "game") GameEntity(id = link.link, name = link.text) else GameEntity() - _item.exposureEventList.add( - createExposureEvent( - gameEntity.also { - it.sequence = childPosition - it.outerSequence = _item.position - }, - listOf( - ExposureSource( - "通用内容合集", - "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" - ) - ), - pageConfigure.exposureSourceList, - childPosition, - _item.componentPosition, - createTrackData(item) - ).also { link.exposureEvent = it } - ) + createExposureEvent( + gameEntity.also { + it.sequence = childPosition + it.outerSequence = _item.position + }, + listOf( + ExposureSource( + "通用内容合集", + "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" + ) + ), + pageConfigure.exposureSourceList, + childPosition, + _item.componentPosition, + createTrackData(item) + ).also { link.exposureEvent = it } } - } override fun onChildItemClick(childPosition: Int, entity: CommonCollectionContentEntity) { @@ -108,30 +104,25 @@ class CustomCommonCollectionViewHolder( childEventHelper.navigateToLinkPage( linkEntity, "内容卡片", - (_item as CustomCommonContentCollectionItem).exposureEventList.getOrNull(childPosition) + linkEntity.exposureEvent ) } - } override fun navigateToCommonCollectionDetailPage(data: CustomPageData.CommonContentCollection) { childEventHelper.navigateToCommonCollectionDetailPage(_item.link) } - }) } override fun bindView(item: CustomPageItem) { super.bindView(item) if (item is CustomCommonContentCollectionItem) { - item.exposureEventList.clear() horizontalSlideUi.bind(item.data) } - } companion object { - fun setTitle( data: CustomPageData.CommonContentCollection, layoutTitle: LayoutTitleCustomBinding, diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomDoubleCardViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomDoubleCardViewHolder.kt index 42298674ac..b50d0b8caa 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomDoubleCardViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomDoubleCardViewHolder.kt @@ -11,9 +11,9 @@ import com.gh.gamecenter.databinding.GameDoubleCardItemAlCustomBinding import com.gh.gamecenter.databinding.GameDoubleCardItemCustomBinding import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureListProvider import com.gh.gamecenter.feature.game.GameItemViewHolder import com.gh.gamecenter.home.custom.CustomPageViewModel -import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem @@ -25,10 +25,13 @@ import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem class CustomDoubleCardViewHolder( viewModel: CustomPageViewModel, val binding: GameDoubleCardItemAlCustomBinding -) : BaseCustomViewHolder(viewModel, binding.root) { +) : BaseCustomViewHolder(viewModel, binding.root), IExposureListProvider { private val mPosterWidth = (DisplayUtils.getScreenWidth() - 40F.dip2px()) / 2 + private var leftBoundedGameEntity: GameEntity? = null + private var rightBoundedGameEntity: GameEntity? = null + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { SubjectEventHelper(viewModel) } @@ -36,10 +39,11 @@ class CustomDoubleCardViewHolder( override fun bindView(item: CustomPageItem) { super.bindView(item) if (item is CustomSplitSubjectItem) { - val gameList = item.data.data ?: return - fillExposureList(item) + runOnIoThread(isLightWeightTask = true) { + fillExposureInfoAndReportAd(item) + } setSplitSubjectTitle(binding.layoutTitle, item, childEventHelper) @@ -59,16 +63,16 @@ class CustomDoubleCardViewHolder( itemView.layoutParams = it } - val leftPosition = item.startChildPosition val rightPosition = leftPosition + 1 val hasLeft = gameList.size > leftPosition if (hasLeft) { + leftBoundedGameEntity = gameList[leftPosition] bindSubView( item, - gameList[leftPosition], + leftBoundedGameEntity!!, binding.leftCardView, leftPosition ) @@ -76,15 +80,16 @@ class CustomDoubleCardViewHolder( val hasRight = gameList.size > rightPosition if (hasRight) { + rightBoundedGameEntity = gameList[rightPosition] binding.rightCardView.posterCardView.visibility = View.VISIBLE bindSubView( item, - gameList[rightPosition], + rightBoundedGameEntity!!, binding.rightCardView, rightPosition ) - } else { + rightBoundedGameEntity = null binding.rightCardView.posterCardView.visibility = View.INVISIBLE } } @@ -121,24 +126,27 @@ class CustomDoubleCardViewHolder( } } - private fun fillExposureList(item: CustomSplitSubjectItem) { - val eventList = arrayListOf() - runOnIoThread(true) { - for (i in item.startChildPosition..item.endChildPosition) { - val game = item.data.data?.getOrNull(i) ?: break - val event = createExposureEvent( - game, - item.exposureSource, - pageConfigure.exposureSourceList, - i, - item.componentPosition, - createTrackData(item) - ) - game.exposureEvent = event - eventList.add(event) - } + private fun fillExposureInfoAndReportAd(item: CustomSplitSubjectItem) { + for (i in item.startChildPosition..item.endChildPosition) { + val game = item.data.data?.getOrNull(i) ?: break + + fillExposureInfoAndReportAd(game, i, item) } - item.exposureEventList = eventList } + override fun provideExposureDataList(): List? { + val gameEntityList = arrayListOf().apply { + leftBoundedGameEntity?.let { add(leftBoundedGameEntity) } + rightBoundedGameEntity?.let { add(rightBoundedGameEntity) } + } + + for (gameEntity in gameEntityList) { + gameEntity?.updateBoundedExposureEventIfNeeded() + } + + return gameEntityList.mapNotNull { it?.exposureEvent } + } + + override fun provideExposureData(): ExposureEvent? = null + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomDoubleWelfareCardViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomDoubleWelfareCardViewHolder.kt index d5e3bbe8dc..a3c4099b59 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomDoubleWelfareCardViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomDoubleWelfareCardViewHolder.kt @@ -5,6 +5,7 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.core.view.marginStart +import com.gh.common.util.AdHelper import com.gh.gamecenter.R import com.gh.gamecenter.common.utils.ImageUtils import com.gh.gamecenter.common.utils.dip2px @@ -15,6 +16,7 @@ import com.gh.gamecenter.databinding.GameDoubleWelfareCardCustomBinding import com.gh.gamecenter.databinding.RecyclerDoubleWelfareCardCustomBinding import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureListProvider import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper @@ -27,7 +29,9 @@ import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem class CustomDoubleWelfareCardViewHolder( viewModel: CustomPageViewModel, val binding: RecyclerDoubleWelfareCardCustomBinding -) : BaseCustomViewHolder(viewModel, binding.root) { +) : BaseCustomViewHolder(viewModel, binding.root), IExposureListProvider { + + private var exposureEventList: ArrayList? = arrayListOf() private val posterWidth by lazy { (itemView.context.resources.displayMetrics.widthPixels - 40F.dip2px()) / 2 @@ -42,7 +46,9 @@ class CustomDoubleWelfareCardViewHolder( if (item is CustomSplitSubjectItem) { val gameList = item.data.data ?: return - fillExposureList(item) + runOnIoThread(true) { + fillExposureList(item) + } setSplitSubjectTitle(binding.layoutTitle, item, childEventHelper) @@ -62,9 +68,7 @@ class CustomDoubleWelfareCardViewHolder( itemView.layoutParams = it } - val leftPosition = item.startChildPosition - val rightPosition = leftPosition + 1 val hasLeft = gameList.size > leftPosition @@ -151,7 +155,6 @@ class CustomDoubleWelfareCardViewHolder( root.setOnClickListener { childEventHelper.navigateToGameDetailPage(position, gameEntity) } - } } @@ -170,22 +173,34 @@ class CustomDoubleWelfareCardViewHolder( } private fun fillExposureList(item: CustomSplitSubjectItem) { - val eventList = arrayListOf() - runOnIoThread(true) { - for (i in item.startChildPosition..item.endChildPosition) { - val game = item.data.data?.getOrNull(i) ?: break - val event = createExposureEvent( - game, - item.exposureSource, - pageConfigure.exposureSourceList, - i, - item.componentPosition, - createTrackData(item) - ) - game.exposureEvent = event - eventList.add(event) + exposureEventList = arrayListOf() + for (i in item.startChildPosition..item.endChildPosition) { + val game = item.data.data?.getOrNull(i) ?: break + val event = createExposureEvent( + game, + item.exposureSource, + pageConfigure.exposureSourceList, + i, + item.componentPosition, + createTrackData(item) + ) + game.exposureEvent = event + + if (!game.adGroupId.isEmpty() && !game.isAdRequestReported) { + AdHelper.reportAdRequest(game.exposureEvent!!) + game.isAdRequestReported = true } + + exposureEventList?.add(event) } - item.exposureEventList = eventList } + + override fun provideExposureDataList(): List? { + exposureEventList?.forEach { + it.getRefreshedExposureEvent() + } + return exposureEventList + } + + override fun provideExposureData(): ExposureEvent? = null } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomFoldSlideLargeImageViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomFoldSlideLargeImageViewHolder.kt index 473efef77b..bbf7f18fb9 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomFoldSlideLargeImageViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomFoldSlideLargeImageViewHolder.kt @@ -8,12 +8,11 @@ import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE import com.gh.gamecenter.R import com.gh.gamecenter.common.view.stacklayoutmanager2.StackAnimation2 import com.gh.gamecenter.common.view.stacklayoutmanager2.StackLayoutManagerV2 +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.RecyclerFoldSlideLargeImageCustomBinding -import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.IGameChangedNotifier import com.gh.gamecenter.home.custom.adapter.CustomFoldSlideLargeImageItemAdapter -import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.model.CustomSubjectItem @@ -46,14 +45,14 @@ class CustomFoldSlideLargeImageViewHolder( private val onScrollListener = object : OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { if (newState == SCROLL_STATE_IDLE) { - showBubble(layoutManager.selectedPosition) + updateTopViewHolder(layoutManager.selectedPosition) } } override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) if (dy == 0) { - showBubble(layoutManager.selectedPosition) + updateTopViewHolder(layoutManager.selectedPosition) } } } @@ -64,15 +63,14 @@ class CustomFoldSlideLargeImageViewHolder( binding.recyclerView.isNestedScrollingEnabled = false layoutManager.setOnItemSelectedListener(object : StackLayoutManagerV2.OnStackListener { override fun onSelectedPositionChanged(position: Int) { - showBubble(position) + updateTopViewHolder(position) } override fun onScrollStateChanged(isScrolling: Boolean) = Unit - }) } - private fun showBubble(position: Int) { + private fun updateTopViewHolder(position: Int) { val viewHolder = binding.recyclerView.findViewHolderForAdapterPosition(position) if (topViewHolder != viewHolder) { topViewHolder?.dismissBubble() @@ -89,13 +87,18 @@ class CustomFoldSlideLargeImageViewHolder( setSubjectTitle(binding.layoutTitle, item, childEventHelper) - fillExposureEventList(item) + runOnIoThread(isLightWeightTask = true) { + item.data.data?.forEachIndexed { index, game -> + fillExposureInfoAndReportAd(game, index, item) + } + } if (binding.recyclerView.adapter == null) { binding.recyclerView.layoutManager = layoutManager binding.recyclerView.adapter = adapter } adapter.setData(item) + if (isInit) { isInit = false binding.recyclerView.scrollToPosition(adapter.getInitPosition()) @@ -103,22 +106,6 @@ class CustomFoldSlideLargeImageViewHolder( } } - private fun fillExposureEventList(item: CustomSubjectItem) { - val eventList = arrayListOf() - item.data.data?.forEachIndexed { index, game -> - val event = createExposureEvent( - game, - item.exposureSource, - pageConfigure.exposureSourceList, - index, - item.componentPosition, - createTrackData(item) - ) - eventList.add(event) - } - item.exposureEventList = eventList - } - override fun onViewAttach(parent: RecyclerView?) { parent?.addOnScrollListener(onScrollListener) } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameCollectionRefreshVerticalSlideViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameCollectionRefreshVerticalSlideViewHolder.kt index 8b9b63ea62..13332d49b7 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameCollectionRefreshVerticalSlideViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameCollectionRefreshVerticalSlideViewHolder.kt @@ -2,12 +2,10 @@ package com.gh.gamecenter.home.custom.viewholder import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.AdHelper import com.gh.gamecenter.common.exposure.ExposureSource -import com.gh.gamecenter.common.utils.DarkModeUtils import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.GameVerticalSlideItemCustomBinding -import com.gh.gamecenter.feature.exposure.ExposureEvent -import com.gh.gamecenter.game.vertical.OnPagerSnapScrollListener import com.gh.gamecenter.game.vertical.SpanCountPagerSnapHelper import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.IGameChangedNotifier @@ -74,55 +72,9 @@ class CustomGameCollectionRefreshVerticalSlideViewHolder( val games = subjectEntity.games spanCount = if (games.size < 3) games.size else 3 - val listExposureEventList = arrayListOf() - val exposureEventList = arrayListOf() - val exposureClosure: (Int) -> Unit = { - runOnIoThread(true) { - listExposureEventList.clear() - - val source = if (item.isSubjectCollection) { - listOf( - ExposureSource("专题合集", "${item.data.name}+${item.componentStyle}+${item.data.id}"), - ExposureSource( - "专题", - "${subjectEntity.title}+${subjectEntity.styleChinese}+${subjectEntity.id}" - ) - ) - } else { - listOf( - ExposureSource("游戏单合集", "${item.data.name}+${item.componentStyle}+${item.data.id}"), - ExposureSource("游戏单", "${subjectEntity.title}+${subjectEntity.id}") - ) - } - - val startOffset = it * spanCount - val endOffset = if (startOffset + spanCount >= games.size) { - games.size - } else { - startOffset + spanCount - } - for (i in startOffset until endOffset) { - val game = games.getOrNull(i) ?: break - game.isAdData = subjectEntity.adIconActive - val event = createExposureEvent( - game, - source, - pageConfigure.exposureSourceList, - i, - item.componentPosition, - createTrackData(item) - ) - - listExposureEventList.add(event) - } - exposureEventList.addAll(listExposureEventList) - } - + runOnIoThread(isLightWeightTask = true) { + fillExposureInfo(item, subjectEntity) } - item.exposureEventList = exposureEventList - - exposureClosure(0) - if (binding.recyclerView.adapter == null) { binding.recyclerView.clearOnScrollListeners() @@ -132,9 +84,6 @@ class CustomGameCollectionRefreshVerticalSlideViewHolder( binding.recyclerView.adapter = adapter snapHelper?.let { it.attachToRecyclerView(binding.recyclerView) - binding.recyclerView.addOnScrollListener(OnPagerSnapScrollListener(it) { - exposureClosure(it) - }) } } changeSpanCountIfNeeded() @@ -152,6 +101,44 @@ class CustomGameCollectionRefreshVerticalSlideViewHolder( } } + private fun fillExposureInfo( + item: CustomSubjectCollectionItem, + subjectEntity: CustomPageData.LinkColumnCollection.CustomSubjectEntity + ) { + val games = subjectEntity.games + val source = if (item.isSubjectCollection) { + listOf( + ExposureSource("专题合集", "${item.data.name}+${item.componentStyle}+${item.data.id}"), + ExposureSource( + "专题", + "${subjectEntity.title}+${subjectEntity.styleChinese}+${subjectEntity.id}" + ) + ) + } else { + listOf( + ExposureSource("游戏单合集", "${item.data.name}+${item.componentStyle}+${item.data.id}"), + ExposureSource("游戏单", "${subjectEntity.title}+${subjectEntity.id}") + ) + } + + for ((i, game) in games.withIndex()) { + game.isAdData = subjectEntity.adIconActive + game.exposureEvent = createExposureEvent( + game, + source, + pageConfigure.exposureSourceList, + i, + item.componentPosition, + createTrackData(item) + ) + + if (!game.adGroupId.isEmpty() && !game.isAdRequestReported) { + AdHelper.reportAdRequest(game.exposureEvent!!) + game.isAdRequestReported = true + } + } + } + private fun changeSpanCountIfNeeded() { binding.recyclerView.run { if (spanCount != (layoutManager as GridLayoutManager).spanCount) { diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGallerySlideViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGallerySlideViewHolder.kt index b644ff38f5..679a12c19e 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGallerySlideViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGallerySlideViewHolder.kt @@ -14,8 +14,9 @@ import com.gh.gamecenter.databinding.GameGallerySlideItemCustomBinding import com.gh.gamecenter.databinding.ItemWithinGameGallerySlideCustomBinding import com.gh.gamecenter.entity.SubjectEntity import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.home.custom.CustomPageViewModel -import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.model.CustomSubjectItem @@ -49,37 +50,23 @@ class CustomGameGallerySlideViewHolder( val rvList = arrayListOf(binding.firstRecyclerView, binding.secondRecyclerView, binding.thirdRecyclerView) + runOnIoThread(isLightWeightTask = true) { + cachedSubject?.data?.forEachIndexed { index, gameEntity -> + fillExposureInfoAndReportAd(gameEntity, index, item) + } + } + for ((index, recyclerView) in rvList.withIndex()) { if (recyclerView.adapter == null) { recyclerView.layoutManager = LinearLayoutManager(binding.root.context, RecyclerView.HORIZONTAL, false) - recyclerView.adapter = - GameGallerySlideAdapter( - binding.root.context, - index - ) { childPosition, game -> - runOnIoThread(true) { - game.sequence = childPosition - item.exposureEventList.add( - createExposureEvent( - game, - item.exposureSource, - pageConfigure.exposureSourceList, - childPosition, - item.componentPosition, - createTrackData(item) - ) - ) - } - } + recyclerView.adapter = GameGallerySlideAdapter(binding.root.context, index) recyclerView.isNestedScrollingEnabled = false } val gameList = item.data.data ?: emptyList() (recyclerView.adapter as? GameGallerySlideAdapter)?.submitList(gameList) } } - - } override fun onResume(owner: LifecycleOwner) { @@ -105,7 +92,6 @@ class CustomGameGallerySlideViewHolder( inner class GameGallerySlideAdapter( context: Context, private val index: Int, - private val exposureInvoke: (Int, GameEntity) -> Unit ) : BaseRecyclerAdapter(context) { private val dataList = arrayListOf() @@ -128,13 +114,15 @@ class CustomGameGallerySlideViewHolder( val realPosition = dataPosition % dataList.size val gameEntity = dataList[realPosition] - exposureInvoke(realPosition, gameEntity) holder.bindView(realPosition, gameEntity) } inner class GameGallerySlideItemViewHolder(val binding: ItemWithinGameGallerySlideCustomBinding) : - BaseRecyclerViewHolder(binding.root) { + BaseRecyclerViewHolder(binding.root), IExposureProvider { + var boundedGameEntity: GameEntity? = null + fun bindView(realPosition: Int, gameEntity: GameEntity) { + boundedGameEntity = gameEntity binding.iconIv.displayGameIconWithIfShowSubscript( gameEntity, cachedSubject?.showIndexIconSubscript ?: false @@ -143,8 +131,12 @@ class CustomGameGallerySlideViewHolder( childEventHelper.navigateToGameDetailPage(realPosition, gameEntity) } } - } + override fun provideExposureData(): ExposureEvent? { + boundedGameEntity?.updateBoundedExposureEventIfNeeded() + return boundedGameEntity?.exposureEvent + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGalleryViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGalleryViewHolder.kt index 2b39af8743..c475962751 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGalleryViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGalleryViewHolder.kt @@ -2,15 +2,17 @@ package com.gh.gamecenter.home.custom.viewholder import android.content.Context import android.view.View +import com.gh.common.util.AdHelper import com.gh.gamecenter.R import com.gh.gamecenter.common.utils.safelyGetInRelease import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.view.AsyncCell import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.GameGalleryItemCustomBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureListProvider import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.createExposureEvent -import com.gh.gamecenter.home.custom.eventlistener.CustomPageItemChildEventHelper import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.model.CustomSubjectItem @@ -21,7 +23,9 @@ import com.gh.gamecenter.home.custom.model.CustomSubjectItem class CustomGameGalleryViewHolder( viewModel: CustomPageViewModel, cell: GameGalleryItemCell -) : BaseCustomViewHolder(viewModel, cell.rootView) { +) : BaseCustomViewHolder(viewModel, cell.rootView), IExposureListProvider { + + private var boundedItem: CustomSubjectItem? = null override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { SubjectEventHelper(viewModel) @@ -32,6 +36,7 @@ class CustomGameGalleryViewHolder( if (item is CustomSubjectItem) { val subjectEntity = item.data item.exposureEventList.clear() + boundedItem = item (itemView as? GameGalleryItemCell)?.let { it.bindWhenInflated { val binding = it.binding ?: return@bindWhenInflated @@ -57,6 +62,7 @@ class CustomGameGalleryViewHolder( val gameEntity = subjectEntity.data?.safelyGetInRelease(index) ?: continue gameEntity.subjectId = subjectEntity.id + gameEntity.pageLevelString = pageLocation.pageLevelString gameIcon.visibility = View.VISIBLE gameIcon.rotation = 35F @@ -70,16 +76,20 @@ class CustomGameGalleryViewHolder( } runOnIoThread(true) { - item.exposureEventList.add( - createExposureEvent( - gameEntity, - item.exposureSource, - pageConfigure.exposureSourceList, - index, - item.componentPosition, - createTrackData(item) - ) + val exposureEvent = createExposureEvent( + gameEntity, + item.exposureSource, + pageConfigure.exposureSourceList, + index, + item.componentPosition, + createTrackData(item) ) + + if (!gameEntity.adGroupId.isEmpty() && !gameEntity.isAdRequestReported) { + AdHelper.reportAdRequest(exposureEvent) + gameEntity.isAdRequestReported = true + } + item.exposureEventList.add(exposureEvent) } } } @@ -95,6 +105,18 @@ class CustomGameGalleryViewHolder( } } + override fun provideExposureData(): ExposureEvent? = null + + override fun provideExposureDataList(): List? { + if (boundedItem?.exposureEventList == null) return null + + for (exposure in boundedItem!!.exposureEventList) { + exposure.getRefreshedExposureEvent() + } + + return boundedItem?.exposureEventList + } + class GameGalleryItemCell(context: Context) : AsyncCell(context) { var binding: GameGalleryItemCustomBinding? = null diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalItemViewHolder.kt index af7de90550..43c7e8d9ed 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalItemViewHolder.kt @@ -8,10 +8,14 @@ import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.databinding.GameHorizontalItemCustomBinding import com.gh.gamecenter.entity.SubjectEntity import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.home.custom.adapter.CustomViewExt -class CustomGameHorizontalItemViewHolder(val binding: GameHorizontalItemCustomBinding) : - BaseRecyclerViewHolder(binding.root) { +class CustomGameHorizontalItemViewHolder(val binding: GameHorizontalItemCustomBinding) + : BaseRecyclerViewHolder(binding.root), IExposureProvider +{ + private var boundedGameEntity: GameEntity? = null fun bindGameHorizontalItem( gameEntity: GameEntity, @@ -23,6 +27,8 @@ class CustomGameHorizontalItemViewHolder(val binding: GameHorizontalItemCustomBi && subjectEntity.data?.any { it.commentCount > 3 } ?: false CustomViewExt.setGameRattingWithSubject(binding.gameRating, showStar, gameEntity) + boundedGameEntity = gameEntity + binding.firstRemark.text = gameEntity.assignRemark.firstLine binding.firstRemark.setTextColor( if (gameEntity.assignRemark.markedRed) Color.parseColor("#F10000") else com.gh.gamecenter.common.R.color.text_primary.toColor( @@ -105,4 +111,9 @@ class CustomGameHorizontalItemViewHolder(val binding: GameHorizontalItemCustomBi binding.lineContainer.visibility = View.GONE } } + + override fun provideExposureData(): ExposureEvent? { + boundedGameEntity?.updateBoundedExposureEventIfNeeded() + return boundedGameEntity?.exposureEvent + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalSlideListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalSlideListViewHolder.kt index 67a919d2f0..9c6ef68b22 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalSlideListViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalSlideListViewHolder.kt @@ -3,19 +3,14 @@ package com.gh.gamecenter.home.custom.viewholder import android.view.View import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout -import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.RecyclerView import com.gh.gamecenter.R import com.gh.gamecenter.common.view.FixLinearLayoutManager import com.gh.gamecenter.core.runOnIoThread -import com.gh.gamecenter.core.utils.TimeUtils import com.gh.gamecenter.databinding.RecyclerHorizontalSlideListCustomBinding -import com.gh.gamecenter.entity.SubjectEntity -import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.IGameChangedNotifier import com.gh.gamecenter.home.custom.adapter.CustomGameHorizontalSlideAdapter -import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.model.CustomSubjectItem @@ -48,7 +43,6 @@ class CustomGameHorizontalSlideListViewHolder( setSubjectTitle(binding.layoutTitle, item, childEventHelper) val subjectEntity = item.data if (binding.recyclerView.adapter == null) { - binding.recyclerView.layoutManager = FixLinearLayoutManager(itemView.context, RecyclerView.HORIZONTAL, false) binding.recyclerView.itemAnimator = null @@ -56,7 +50,6 @@ class CustomGameHorizontalSlideListViewHolder( binding.recyclerView.isNestedScrollingEnabled = false binding.recyclerView.adapter = adapter - binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) @@ -66,7 +59,6 @@ class CustomGameHorizontalSlideListViewHolder( } } }) - } if (item.scrolledOffset == 0 && item.firstPositionInHorizontalTimeline != -1) { @@ -77,36 +69,21 @@ class CustomGameHorizontalSlideListViewHolder( } } - val exposureEventList = arrayListOf() - runOnIoThread(true) { + runOnIoThread(isLightWeightTask = true) { subjectEntity.data?.let { it.forEachIndexed { index, game -> - game.isAdData = subjectEntity.adIconActive - game.sequence = index - val event = createExposureEvent( - game, - item.exposureSource, - pageConfigure.exposureSourceList, - index, - item.componentPosition, - createTrackData(item) - ) - exposureEventList.add(event) + fillExposureInfoAndReportAd(game, index, item) } } } - item.exposureEventList = exposureEventList - - adapter.setData(item, item.exposureEventList) + adapter.setData(item) } } /** * 获取游戏专题中 "今天/最靠近今天的一天(未来优先于过去)" 游戏的位置 */ - - private fun getTopOfLineContainer(vg: ViewGroup): Int { var height = 0 for (i in 0 until vg.childCount) { diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameItemViewHolder.kt index 42aac9e2ce..10325b52d7 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameItemViewHolder.kt @@ -11,7 +11,6 @@ import androidx.recyclerview.widget.RecyclerView import com.therouter.TheRouter import com.gh.common.util.DownloadItemUtils import com.gh.gamecenter.adapter.viewholder.GameViewHolder -import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.DrawableView import com.gh.gamecenter.core.runOnIoThread @@ -19,11 +18,12 @@ import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.databinding.GameItemCustomBinding import com.gh.gamecenter.feature.R import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.minigame.MiniGameItemHelper import com.gh.gamecenter.feature.provider.IBindingAdaptersProvider import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.adapter.CustomViewExt -import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem @@ -35,7 +35,9 @@ import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem class CustomGameItemViewHolder( viewModel: CustomPageViewModel, val binding: GameItemCustomBinding -) : BaseCustomViewHolder(viewModel, binding.root) { +) : BaseCustomViewHolder(viewModel, binding.root), IExposureProvider { + + private var boundedGameEntity: GameEntity? = null override val onlyNotifyItemChanged: Boolean get() = true @@ -50,12 +52,17 @@ class CustomGameItemViewHolder( ) { super.bindView(item) if (item is CustomSplitSubjectItem) { - fillExposureEventList(item) - setSplitSubjectTitle(binding.layoutTitle, item, childEventHelper, TITLE_TYPE_SMALL) val subject = item.data - val entity = subject.data?.getOrNull(item.startChildPosition) ?: return + boundedGameEntity = subject.data?.getOrNull(item.startChildPosition) ?: return + + runOnIoThread(isLightWeightTask = true) { + fillExposureInfoAndReportAd(boundedGameEntity!!, item.startChildPosition, item) + } + + val entity = boundedGameEntity ?: return + binding.run { val paddingBottom = if (item.isLastLine) { 8f.dip2px() @@ -64,7 +71,6 @@ class CustomGameItemViewHolder( } root.setPadding(root.paddingLeft, root.paddingTop, root.paddingRight, paddingBottom) - clContainer.background = com.gh.gamecenter.common.R.drawable.reuse_listview_item_style.toDrawable(root.context) selectIv.setImageDrawable(DrawableView.getCheckSelectorDrawable(root.context)) gameKaifuType.setBackgroundColor(com.gh.gamecenter.common.R.color.primary_theme.toColor(root.context)) @@ -134,20 +140,6 @@ class CustomGameItemViewHolder( } } - private fun fillExposureEventList(item: CustomSplitSubjectItem) { - val game = item.data.data?.getOrNull(item.startChildPosition) ?: return - runOnIoThread(true) { - item.exposureEvent = createExposureEvent( - game, - item.exposureSource, - pageConfigure.exposureSourceList, - item.startChildPosition, - item.componentPosition, - createTrackData(item) - ) - } - } - companion object { private const val ADVANCED_DOWNLOAD_WIDTH = 34F private const val SUBTITLE_MIN_WIDTH = 24F @@ -283,4 +275,9 @@ class CustomGameItemViewHolder( } } + override fun provideExposureData(): ExposureEvent? { + boundedGameEntity?.updateBoundedExposureEventIfNeeded() + return boundedGameEntity?.exposureEvent + } + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluggableViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluggableViewHolder.kt index e847275584..56a28c006b 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluggableViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluggableViewHolder.kt @@ -7,9 +7,16 @@ import com.gh.gamecenter.common.utils.toDrawable import com.gh.gamecenter.common.view.DrawableView import com.gh.gamecenter.databinding.GameItemCustomBinding import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider + +class CustomGamePluggableViewHolder(val binding: GameItemCustomBinding) + : BaseRecyclerViewHolder(binding.root), IExposureProvider { + + private var boundedGameEntity: GameEntity? = null -class CustomGamePluggableViewHolder(val binding: GameItemCustomBinding) : BaseRecyclerViewHolder(binding.root) { fun initServerType(gameEntity: GameEntity) { + boundedGameEntity = gameEntity val serverLabel = gameEntity.serverLabel when { gameEntity.test != null -> { @@ -35,4 +42,8 @@ class CustomGamePluggableViewHolder(val binding: GameItemCustomBinding) : BaseRe // 由于RecyclerView的复用机制 需要每次测量gameName的宽 binding.gameName.requestLayout() } + + override fun provideExposureData(): ExposureEvent? { + return boundedGameEntity?.exposureEvent?.getRefreshedExposureEvent() + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluginViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluginViewHolder.kt index 9907f403a3..493b1145f3 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluginViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluginViewHolder.kt @@ -151,25 +151,21 @@ class CustomGamePluginViewHolder( binding.pluginHead.setOnClickListener(View.OnClickListener(function = expandClick)) } } - } private fun fillExposureEvent(item: CustomPluginItem) { - val exposureList = arrayListOf() - runOnIoThread(true) { + runOnIoThread(isLightWeightTask = true) { item.data.forEachIndexed { index, game -> - exposureList.add( - createExposureEvent( - game, - listOf(ExposureSource("插件化", "")), - pageConfigure.exposureSourceList, - index, - item.componentPosition, - createTrackData(item) - ) + game.pageLevelString = pageLocation.pageLevelString + game.exposureEvent = createExposureEvent( + game, + listOf(ExposureSource("插件化", "")), + pageConfigure.exposureSourceList, + index, + item.componentPosition, + createTrackData(item) ) } } - item.exposureEventList = exposureList } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameVerticalSlideViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameVerticalSlideViewHolder.kt index f22f69676e..e27ce34013 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameVerticalSlideViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameVerticalSlideViewHolder.kt @@ -3,15 +3,11 @@ package com.gh.gamecenter.home.custom.viewholder import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.gh.gamecenter.core.runOnIoThread -import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.databinding.GameVerticalSlideItemCustomBinding -import com.gh.gamecenter.feature.exposure.ExposureEvent -import com.gh.gamecenter.game.vertical.OnPagerSnapScrollListener import com.gh.gamecenter.game.vertical.SpanCountPagerSnapHelper import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.IGameChangedNotifier import com.gh.gamecenter.home.custom.adapter.CustomGameVerticalAdapter -import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.model.CustomSubjectItem @@ -66,49 +62,16 @@ class CustomGameVerticalSlideViewHolder( gridLayoutManager?.spanCount = spanCount } } - adapter.setData(item.data) - snapHelper.attachToRecyclerView(binding.recyclerView) - - val listExposureEventList = arrayListOf() - val exposureEventList = arrayListOf() - item.exposureEventList = exposureEventList - val exposureClosure: (Int) -> Unit = { - runOnIoThread(true) { - listExposureEventList.clear() - - val startOffset = it * subjectEntity.list - val endOffset = if (startOffset + subjectEntity.list >= games.size) { - games.size - } else { - startOffset + subjectEntity.list - } - for (i in startOffset until endOffset) { - val game = games[i].apply { - isAdData = subjectEntity.adIconActive - } - - val event = createExposureEvent( - game, - item.exposureSource, - pageConfigure.exposureSourceList, - i, - item.componentPosition, - createTrackData(item) - ) - game.exposureEvent = event - listExposureEventList.add(event) - } - exposureEventList.addAll(listExposureEventList) + runOnIoThread(isLightWeightTask = true) { + games.forEachIndexed { index, gameEntity -> + fillExposureInfoAndReportAd(gameEntity, index, item) } } - exposureClosure(0) + adapter.setData(item.data) - binding.recyclerView.addOnScrollListener(OnPagerSnapScrollListener(snapHelper) { - MtaHelper.onEvent("游戏专题", "滑动", subjectEntity.name) - exposureClosure(it) - }) + snapHelper.attachToRecyclerView(binding.recyclerView) } } } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayItemViewHolder.kt index 4d9d657ce6..9f1b2b4fa6 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayItemViewHolder.kt @@ -10,14 +10,20 @@ import com.gh.gamecenter.common.view.AsyncCell import com.gh.gamecenter.databinding.HomeAmwayItemBinding import com.gh.gamecenter.databinding.HomeAmwayItemCustomBinding import com.gh.gamecenter.entity.AmwayCommentEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.game.GameItemViewHolder import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity import java.util.regex.Pattern -class CustomHomeAmwayItemViewHolder(val binding: HomeAmwayItemCustomBinding) : BaseRecyclerViewHolder(binding.root) { +class CustomHomeAmwayItemViewHolder(val binding: HomeAmwayItemCustomBinding) : + BaseRecyclerViewHolder(binding.root), IExposureProvider { + + private var boundedGame: AmwayCommentEntity.Game? = null fun bindAmway(amway: AmwayCommentEntity) { val gameEntity = amway.game.toGameEntity() + boundedGame = amway.game binding.gameIcon.displayGameIcon(gameEntity) binding.gameName.text = amway.game.name @@ -37,7 +43,10 @@ class CustomHomeAmwayItemViewHolder(val binding: HomeAmwayItemCustomBinding) : B val m = Pattern.compile(RatingEditActivity.LABEL_REGEX).matcher(amway.comment.content) if (m.find()) { - val contents = TextHelper.getCommentLabelSpannableStringBuilder(amway.comment.content, com.gh.gamecenter.common.R.color.text_theme) + val contents = TextHelper.getCommentLabelSpannableStringBuilder( + amway.comment.content, + com.gh.gamecenter.common.R.color.text_theme + ) binding.content.setTextWithHighlightedTextWrappedInsideWrapper( contents, highlightedTextClickListener = TextHelper.DirectToWebViewHighlightedTextClick( @@ -66,6 +75,10 @@ class CustomHomeAmwayItemViewHolder(val binding: HomeAmwayItemCustomBinding) : B } GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, binding.gameSubtitleTv) } + + override fun provideExposureData(): ExposureEvent? { + return boundedGame?.exposureEvent?.getRefreshedExposureEvent() + } } class HomeAmwayAsyncCell(context: Context) : AsyncCell(context) { diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayListViewHolder.kt index 900e741cb0..0b998f600b 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayListViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayListViewHolder.kt @@ -56,7 +56,9 @@ class CustomHomeAmwayListViewHolder( super.bindView(item) if (item is CustomAmwayItem) { - fillExposureEventList(item) + runOnIoThread(isLightWeightTask = true) { + fillExposureEventList(item) + } setTitle() @@ -67,7 +69,6 @@ class CustomHomeAmwayListViewHolder( snapHelper.attachToRecyclerView(binding.recyclerView) } adapter.submitList(item.data) - } } @@ -80,26 +81,20 @@ class CustomHomeAmwayListViewHolder( binding.layoutTitle.tvRight.setOnClickListener { NewLogUtils.logHomeShareWallButtonClick("右上角") childEventHelper.navigateToAmway() - } } private fun fillExposureEventList(item: CustomAmwayItem) { - val exposureList = arrayListOf() - runOnIoThread(true) { - item.data.forEachIndexed { index, amway -> - val event = createExposureEvent( - amway.game.toGameEntity(), - listOf(ExposureSource("安利墙", "")), - pageConfigure.exposureSourceList, - index, - item.componentPosition, - createTrackData(item) - ) - exposureList.add(event) - } + item.data.forEachIndexed { index, amway -> + amway.game.exposureEvent = createExposureEvent( + amway.game.toGameEntity(), + listOf(ExposureSource("安利墙", "")), + pageConfigure.exposureSourceList, + index, + item.componentPosition, + createTrackData(item) + ) } - item.exposureEventList = exposureList } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeDiscoverCardViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeDiscoverCardViewHolder.kt index 8c2617d25f..20c372fc7a 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeDiscoverCardViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeDiscoverCardViewHolder.kt @@ -7,15 +7,13 @@ import android.widget.TextView import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.Config +import com.gh.common.util.AdHelper import com.gh.common.util.NewFlatLogUtils import com.gh.gamecenter.R import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.HomeDiscoverCardItemCustomBinding import com.gh.gamecenter.entity.DiscoveryGameCardLabel -import com.gh.gamecenter.feature.exposure.ExposureEvent -import com.gh.gamecenter.game.vertical.OnPagerSnapScrollListener import com.gh.gamecenter.game.vertical.SpanCountPagerSnapHelper import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.IGameChangedNotifier @@ -84,7 +82,6 @@ class CustomHomeDiscoverCardViewHolder( } adapter.submitList(entity.games) - val childCount = labelContainer.childCount entity.label.forEachIndexed { index, label -> if (index < childCount) { @@ -114,12 +111,7 @@ class CustomHomeDiscoverCardViewHolder( } } - fillExposureEventList(0, item) - - binding.recyclerView.addOnScrollListener(OnPagerSnapScrollListener(snapHelper) { - fillExposureEventList(it, item) - }) - + fillExposureEventList(item) } } @@ -164,31 +156,22 @@ class CustomHomeDiscoverCardViewHolder( } } - fun fillExposureEventList(position: Int, item: CustomDiscoverCardItem) { - val exposureEventList = arrayListOf() + fun fillExposureEventList(item: CustomDiscoverCardItem) { val gameList = item.data?.games ?: return - runOnIoThread(true) { - val startOffset = position * item.spanCount - val endOffset = if (startOffset + item.spanCount >= gameList.size) { - gameList.size - } else { - startOffset + item.spanCount - } - for (i in startOffset until endOffset) { - val game = gameList.getOrNull(i) ?: return@runOnIoThread - val event = createExposureEvent( - game, - listOf(ExposureSource("发现", game.getTypeName())), - pageConfigure.exposureSourceList, - i, - item.componentPosition, - createTrackData(item) - ) - game.exposureEvent = event - exposureEventList.add(event) + for ((i, gameEntity) in gameList.withIndex()) { + gameEntity.exposureEvent = createExposureEvent( + gameEntity, + listOf(ExposureSource("发现", gameEntity.getTypeName())), + pageConfigure.exposureSourceList, + i, + item.componentPosition, + createTrackData(item) + ) + + if (!gameEntity.adGroupId.isEmpty() && !gameEntity.isAdRequestReported) { + AdHelper.reportAdRequest(gameEntity.exposureEvent!!) + gameEntity.isAdRequestReported = true } } - item.exposureEventList = exposureEventList - } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionCarouselViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionCarouselViewHolder.kt index 859cc00290..d3cfc4e2ca 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionCarouselViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionCarouselViewHolder.kt @@ -3,7 +3,6 @@ package com.gh.gamecenter.home.custom.viewholder import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.OnScrollListener -import com.gh.gamecenter.common.utils.DarkModeUtils import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.view.stacklayoutmanager.StackLayoutManager import com.gh.gamecenter.core.utils.DisplayUtils @@ -26,14 +25,12 @@ class CustomHomeGameCollectionCarouselViewHolder( viewModel: CustomPageViewModel, licOwner: LifecycleOwner, val binding: HomeGameCollectionItemCustomBinding -) : - BaseCustomViewHolder(viewModel, binding.root) { +) : BaseCustomViewHolder(viewModel, binding.root) { private val bannerController = BannerInRecyclerController { if (adapter.itemCount > 0) { binding.recyclerView.smoothScrollToPosition(layoutManager.getFirstVisibleItemPosition() + 1) } - } override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { @@ -65,7 +62,6 @@ class CustomHomeGameCollectionCarouselViewHolder( override fun bindView(item: CustomPageItem) { super.bindView(item) if (item is CustomSubjectCollectionItem) { - fillExposureInSubjectCollection(item, pageConfigure.exposureSourceList, createTrackData(item)) setSubjectCollectionTitle(item, binding.layoutTitle, childEventHelper) @@ -84,7 +80,6 @@ class CustomHomeGameCollectionCarouselViewHolder( } } }) - } adapter.setData(item) bannerController.start() diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionSlideViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionSlideViewHolder.kt index a9ed613793..ca4cca9bb7 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionSlideViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionSlideViewHolder.kt @@ -34,11 +34,9 @@ class CustomHomeGameCollectionSlideViewHolder( ) } - override val gameChangedNotifier: IGameChangedNotifier? get() = if (isBigSlide) _adapter else null - override fun bindView(item: CustomPageItem) { if (item is CustomSubjectCollectionItem) { super.bindView(item) @@ -56,5 +54,4 @@ class CustomHomeGameCollectionSlideViewHolder( } } - } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameItemViewHolder.kt index ee498130c3..722255fb96 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameItemViewHolder.kt @@ -6,6 +6,7 @@ import android.view.ViewGroup.MarginLayoutParams import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.gh.common.databind.BindingAdapters +import com.gh.common.util.AdHelper import com.gh.common.util.DownloadItemUtils import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.GameViewHolder @@ -16,6 +17,7 @@ import com.gh.gamecenter.databinding.HomeGameItemCustomBinding import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.game.GameItemViewHolder import com.gh.gamecenter.feature.minigame.MiniGameItemHelper import com.gh.gamecenter.home.custom.CustomPageViewModel @@ -33,7 +35,9 @@ import com.shuyu.gsyvideoplayer.video.base.GSYVideoView class CustomHomeGameItemViewHolder( viewModel: CustomPageViewModel, val binding: HomeGameItemCustomBinding -) : BaseCustomViewHolder(viewModel, binding.root) { +) : BaseCustomViewHolder(viewModel, binding.root), IExposureProvider { + + private var boundedGameEntity: GameEntity? = null override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { SubjectEventHelper(viewModel) @@ -49,9 +53,6 @@ class CustomHomeGameItemViewHolder( ) { super.bindView(item) if (item is CustomGameItem) { - - _item.exposureEventList.clear() - val topMargin = if (item.isFirstLine) { 0 } else { @@ -74,9 +75,11 @@ class CustomHomeGameItemViewHolder( val game = item.data val topVideo = game.topVideo val homeSetting = game.homeSetting - game.outerSequence = item.componentPosition - val event = createExposureEvent( + boundedGameEntity = game + game.pageLevelString = pageLocation.pageLevelString + game.outerSequence = item.componentPosition + game.exposureEvent = createExposureEvent( item.data, listOf( ExposureSource( @@ -89,8 +92,11 @@ class CustomHomeGameItemViewHolder( item.componentPosition, createTrackData(item) ) - item.exposureEventList.add(event) - item.exposureEvent = event + + if (!game.adGroupId.isEmpty() && !game.isAdRequestReported) { + AdHelper.reportAdRequest(game.exposureEvent!!) + game.isAdRequestReported = true + } bindGameInfo( position, @@ -230,4 +236,10 @@ class CustomHomeGameItemViewHolder( binding.gameRating.goneIf(!(game.commentCount > 3 && game.star >= 7 && game.homeSetting.downloadBtnSwitch == "on")) binding.gameRating2.goneIf(!(game.commentCount > 3 && game.star >= 7 && game.homeSetting.downloadBtnSwitch != "on")) } + + override fun provideExposureData(): ExposureEvent? { + boundedGameEntity?.updateBoundedExposureEventIfNeeded() + return boundedGameEntity?.exposureEvent + } + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoItemViewHolder.kt index 802e2f59d3..bdf96d582a 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoItemViewHolder.kt @@ -12,9 +12,9 @@ import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.RandomUtils import com.gh.gamecenter.databinding.ItemHomeHorizontalSlideVideoCustomBinding -import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.game.GameItemViewHolder import com.gh.gamecenter.feature.minigame.MiniGameItemHelper import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper @@ -25,14 +25,15 @@ class CustomHomeHorizontalSlideVideoItemViewHolder( private val eventHelper: SubjectEventHelper, val binding: ItemHomeHorizontalSlideVideoCustomBinding ) : - BaseRecyclerViewHolder(binding.root) { + BaseRecyclerViewHolder(binding.root), IExposureProvider { + + private val mBoundedGameEntity: GameEntity? = null fun bindAttachGame( childPosition: Int, gameEntity: GameEntity, adapter: RecyclerView.Adapter, entrance: String, - exposureEvent: ExposureEvent, showSubscript: Boolean, showSubTitle: Boolean ) { @@ -41,7 +42,6 @@ class CustomHomeHorizontalSlideVideoItemViewHolder( childPosition, gameEntity, adapter, - exposureEvent, entrance, showSubscript, showSubTitle @@ -85,7 +85,6 @@ class CustomHomeHorizontalSlideVideoItemViewHolder( childPosition: Int, gameEntity: GameEntity, adapter: RecyclerView.Adapter, - exposureEvent: ExposureEvent?, entrance: String, showSubscript: Boolean, showSubTitle: Boolean @@ -110,7 +109,7 @@ class CustomHomeHorizontalSlideVideoItemViewHolder( adapter, entrance, location = "", - traceEvent = exposureEvent + traceEvent = null ) { eventHelper.onDownloadButtonClick(childPosition, gameEntity) } @@ -138,4 +137,9 @@ class CustomHomeHorizontalSlideVideoItemViewHolder( } } + + override fun provideExposureData(): ExposureEvent? { + mBoundedGameEntity?.updateBoundedExposureEventIfNeeded() + return mBoundedGameEntity?.exposureEvent + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoListViewHolder.kt index 29eae25d99..88db6c1468 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoListViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoListViewHolder.kt @@ -3,12 +3,11 @@ package com.gh.gamecenter.home.custom.viewholder import androidx.recyclerview.widget.RecyclerView import com.gh.gamecenter.common.utils.LeftPagerSnapHelper import com.gh.gamecenter.common.view.FixLinearLayoutManager +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.HomeHorizontalSlideVideoListCustomBinding -import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.IGameChangedNotifier import com.gh.gamecenter.home.custom.adapter.CustomHomeHorizontalSlideVideoAdapter -import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.model.CustomSubjectItem @@ -34,19 +33,7 @@ class CustomHomeHorizontalSlideVideoListViewHolder( itemView.context, pageConfigure, childEventHelper, - ) { game, childPosition -> - val item = _item as CustomSubjectItem - val event = createExposureEvent( - game, - item.exposureSource, - viewModel.pageConfigure.exposureSourceList, - childPosition, _item.componentPosition, - createTrackData(item) - ) - game.exposureEvent = event - _item.exposureEventList.add(event) - return@CustomHomeHorizontalSlideVideoAdapter event - } + ) } override val gameChangedNotifier: IGameChangedNotifier @@ -80,6 +67,10 @@ class CustomHomeHorizontalSlideVideoListViewHolder( }) } + runOnIoThread(isLightWeightTask = true) { + fillExposureInfoAndReportAd(item) + } + adapter.setNewData(item) fun isParentScrolling() = scrollCalculatorHelper.mListRv.scrollState != RecyclerView.SCROLL_STATE_IDLE @@ -96,7 +87,14 @@ class CustomHomeHorizontalSlideVideoListViewHolder( } } } + } + fun fillExposureInfoAndReportAd(item: CustomSubjectItem) { + if (item.data.data == null) return + + for ((index, game) in item.data.data!!.withIndex()) { + fillExposureInfoAndReportAd(game, index, item) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeItemGameTestV2ViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeItemGameTestV2ViewHolder.kt index edd9f90443..ae3a5a0502 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeItemGameTestV2ViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeItemGameTestV2ViewHolder.kt @@ -16,7 +16,6 @@ import com.gh.common.util.NewFlatLogUtils import com.gh.gamecenter.R import com.gh.gamecenter.common.baselist.LoadStatus import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.divider.PaddingDecoration import com.gh.gamecenter.databinding.ItemHomeGameTestV2CustomBinding @@ -29,15 +28,12 @@ import com.gh.gamecenter.home.custom.IGameChangedNotifier import com.gh.gamecenter.home.custom.adapter.CustomHomeGameTestV2GameListRvAdapter import com.gh.gamecenter.home.custom.adapter.CustomHomeGameTestV2RecommendRvAdapter import com.gh.gamecenter.home.custom.adapter.CustomHomeGameTestV2TimePointRvAdapter -import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper import com.gh.gamecenter.home.custom.model.CustomGameTestV2Item import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.test_v2.HomeGameTestV2Decoration import com.gh.gamecenter.servers.gametest2.GameServerTestV2Activity import com.lightgame.utils.Utils -import java.text.SimpleDateFormat -import java.util.* import kotlin.math.abs /** @@ -81,24 +77,11 @@ class CustomHomeItemGameTestV2ViewHolder( itemView.context, childEventHelper, linkText, - pageLocation - ) { childPosition, game -> - val time = game.time?.time ?: 0L - val date = Date(time * 1000L) - val sdf = SimpleDateFormat("MM.dd", Locale.CHINA) - val format = sdf.format(date) - - val exposureEvent = createExposureEvent( - game, - listOf(ExposureSource("新游开测", "$linkText-$format")), - pageConfigure.exposureSourceList, - childPosition, - _item.componentPosition, - createTrackData(_item) - ) - _item.exposureEventList.add(exposureEvent) - return@CustomHomeGameTestV2GameListRvAdapter exposureEvent - } + pageLocation, + pageConfigure, + _item.componentPosition, + createTrackData(_item) + ) } private val pointAdapter by lazy { @@ -143,8 +126,6 @@ class CustomHomeItemGameTestV2ViewHolder( } } - item.exposureEventList.clear() - val data = item.data // 设置标题 bindTitle(data) @@ -157,7 +138,6 @@ class CustomHomeItemGameTestV2ViewHolder( //绑定游戏列表 bindGameList(testUseCase, data) } - } private fun bindTitle(data: HomeItemTestV2Entity) { @@ -220,7 +200,6 @@ class CustomHomeItemGameTestV2ViewHolder( } private fun bindGameList(viewModel: CustomNewGameTestUseCase, data: HomeItemTestV2Entity) { - val isDarkModeOn = DarkModeUtils.isDarkModeOn(context) if (isDarkModeOn) { binding.container.background = null @@ -422,8 +401,6 @@ class CustomHomeItemGameTestV2ViewHolder( mLastParseIndex = parsePosition Utils.log("onScrollStateChanged: parseActionWithPosition = $parsePosition") } - //曝光条目 - adapter.exposureItem(parsePosition) } } @@ -502,7 +479,6 @@ class CustomHomeItemGameTestV2ViewHolder( } val timePointList = data.startPoint.map { it.timeType } pointAdapter.submitList(timePointList) - } /** @@ -522,7 +498,6 @@ class CustomHomeItemGameTestV2ViewHolder( } companion object { - private const val ALL = "all" private const val RIGHT_TEXT_ALL = "全部" private const val RIGHT_TEXT_MORE = "更多" diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecentMiniGameViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecentMiniGameViewHolder.kt index ae776bc4c5..98ef03f14d 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecentMiniGameViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecentMiniGameViewHolder.kt @@ -64,7 +64,10 @@ class CustomHomeRecentMiniGameViewHolder( else -> listOf() } - fillExposureEventList(item, entities) + + runOnIoThread(isLightWeightTask = true) { + fillExposureEventList(item, entities) + } if (entities.isEmpty()) { binding.root.goneIf(true) @@ -145,7 +148,6 @@ class CustomHomeRecentMiniGameViewHolder( gameListAdapter.submitList(entities) - val bindingAdapter = bindingAdapter if (bindingAdapter is CustomPageAdapter) { bindingAdapter.updateFirstItemId() @@ -153,23 +155,15 @@ class CustomHomeRecentMiniGameViewHolder( } private fun fillExposureEventList(item: CustomPageItem, entities: List) { - val exposureEventList = arrayListOf() - runOnIoThread(true) { - entities.forEachIndexed { index, game -> - - val event = createExposureEvent( - game, - listOf( - ExposureSource(item.componentName, "") - ), - pageConfigure.exposureSourceList, - index, - item.componentPosition, - createTrackData(item) - ) - exposureEventList.add(event) - } + entities.forEachIndexed { index, game -> + game.exposureEvent = createExposureEvent( + game, + listOf(ExposureSource(item.componentName, "")), + pageConfigure.exposureSourceList, + index, + item.componentPosition, + createTrackData(item) + ) } - item.exposureEventList = exposureEventList } } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecentVGameViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecentVGameViewHolder.kt index 0df5b203e4..4327672b6b 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecentVGameViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecentVGameViewHolder.kt @@ -126,7 +126,6 @@ class CustomHomeRecentVGameViewHolder( if (binding.recyclerView.adapter == null) { binding.recyclerView.adapter = adapter binding.moreTv.visibility = View.VISIBLE - } adapter.submitList(entities) } @@ -139,10 +138,9 @@ class CustomHomeRecentVGameViewHolder( } private fun fillExposureEventList(item: CustomRecentGamesItem) { - val exposureEventList = arrayListOf() - runOnIoThread(true) { + runOnIoThread(isLightWeightTask = true) { item.data.forEachIndexed { index, vGame -> - val event = createExposureEvent( + vGame.exposureEvent = createExposureEvent( VHelper.toGameEntity(vGame.downloadEntity), listOf(ExposureSource("最近在玩", "")), pageConfigure.exposureSourceList, @@ -150,10 +148,8 @@ class CustomHomeRecentVGameViewHolder( item.componentPosition, createTrackData(item) ) - exposureEventList.add(event) } } - item.exposureEventList = exposureEventList } } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecommendItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecommendItemViewHolder.kt index 715bc218f2..fe1851b6f4 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecommendItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecommendItemViewHolder.kt @@ -6,6 +6,7 @@ import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.ItemHomeRecommendListCustomBinding import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureListProvider import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper @@ -17,11 +18,12 @@ import com.gh.gamecenter.home.custom.ui.CommonContentRecommendUi * 通用内容合集-金刚区 * 首页推荐入口 */ - class CustomHomeRecommendItemViewHolder( viewModel: CustomPageViewModel, val binding: ItemHomeRecommendListCustomBinding -) : BaseCustomViewHolder(viewModel, binding.root) { +) : BaseCustomViewHolder(viewModel, binding.root), IExposureListProvider { + + private var exposureEventList: ArrayList? = null override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { CommonContentCollectionEventHelper(viewModel) @@ -32,7 +34,6 @@ class CustomHomeRecommendItemViewHolder( override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) { childEventHelper.navigateToLinkPage(link, text, exposureEvent) } - }) } @@ -40,8 +41,8 @@ class CustomHomeRecommendItemViewHolder( super.bindView(item) if (item is CustomCommonContentCollectionItem) { val recommends = item.data.recommends - _item.exposureEventList.clear() runOnIoThread(true) { + exposureEventList = arrayListOf() item.data.recommends.forEachIndexed { index, homeRecommend -> val exposureEvent = createExposureEvent( GameEntity().also { it.sequence = index }, @@ -62,13 +63,23 @@ class CustomHomeRecommendItemViewHolder( exposureEvent.payload.controlLinkName = homeRecommend.linkText exposureEvent.payload.controlLinkType = homeRecommend.linkType homeRecommend.exposureEvent = exposureEvent - _item.exposureEventList.add(exposureEvent) + + exposureEventList?.add(exposureEvent) } } - recommendUi.bind(recommends, _item.exposureEventList) + recommendUi.bind(recommends, exposureEventList) } } + override fun provideExposureData(): ExposureEvent? = null + + override fun provideExposureDataList(): List? { + exposureEventList?.forEach { + it.getRefreshedExposureEvent() + } + return exposureEventList + } + companion object { //图标最多列数 const val MAX_SPAN_COUNT = 5 diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListItemViewHolder.kt index 4814d659ca..8d85627eba 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListItemViewHolder.kt @@ -14,11 +14,16 @@ import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.core.utils.RandomUtils import com.gh.gamecenter.databinding.HomeSlideListItemCustomBinding import com.gh.gamecenter.entity.HomeSlide +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.game.GameItemViewHolder import com.halo.assistant.HaloApp -class CustomHomeSlideListItemViewHolder(val binding: HomeSlideListItemCustomBinding) : BaseRecyclerViewHolder(binding.root) { +class CustomHomeSlideListItemViewHolder(val binding: HomeSlideListItemCustomBinding) : + BaseRecyclerViewHolder(binding.root), IExposureProvider { + private var boundedGameEntity: GameEntity? = null private val mImageWith = HaloApp.getInstance().application.resources.displayMetrics.widthPixels - 40F.dip2px() fun bindSlideListItem(homeSlide: HomeSlide) { @@ -70,10 +75,16 @@ class CustomHomeSlideListItemViewHolder(val binding: HomeSlideListItemCustomBind val linkGame = homeSlide.linkGame ?: return + boundedGameEntity = linkGame + // 强制更改标签颜色 val homeTags = linkGame.tagStyle if (!homeTags.isNullOrEmpty()) { for (homeTag in homeTags) homeTag.color = "cccccc" } } + + override fun provideExposureData(): ExposureEvent? { + return boundedGameEntity?.exposureEvent?.getRefreshedExposureEvent() + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListViewHolder.kt index 072e287b08..54394618ef 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListViewHolder.kt @@ -4,10 +4,12 @@ import android.graphics.drawable.GradientDrawable import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.Observer import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.AdHelper import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.view.DrawableView +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.HomeSlideListCustomBinding import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent @@ -27,8 +29,7 @@ class CustomHomeSlideListViewHolder( viewModel: CustomPageViewModel, private val lifecycleOwner: LifecycleOwner, val binding: HomeSlideListCustomBinding, -) : - BaseCustomViewHolder(viewModel, binding.root), OnCustomPageRefreshStateListener { +) : BaseCustomViewHolder(viewModel, binding.root), OnCustomPageRefreshStateListener { // 是否是首位 private var isFirst = false @@ -63,30 +64,6 @@ class CustomHomeSlideListViewHolder( updateBackground(color) } } - - override fun createExposureEvent(childPosition: Int, game: GameEntity?): ExposureEvent? { - val item = _item - return if (item is CustomCommonContentCollectionItem) { - createExposureEvent( - game, - listOf( - ExposureSource( - "通用内容合集", - "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" - ), - ExposureSource("轮播图", "") - ), - pageConfigure.exposureSourceList, - childPosition, - _item.componentPosition, - createTrackData(item) - ) - } else { - return null - } - - } - }) } @@ -98,10 +75,44 @@ class CustomHomeSlideListViewHolder( override fun bindView(item: CustomPageItem) { super.bindView(item) if (item is CustomCommonContentCollectionItem) { - val slideList = item.data.slides.slide - item.exposureEventList.clear() + runOnIoThread(isLightWeightTask = true) { + for ((index, homeSlide) in slideList.withIndex()) { + val game = homeSlide.linkGame + + game?.exposureEvent = createExposureEvent( + game, + listOf( + ExposureSource( + "通用内容合集", + "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" + ), + ExposureSource("轮播图", "") + ), + pageConfigure.exposureSourceList, + index, + _item.componentPosition, + createTrackData(item) + ) + + game?.let { + if (!it.adGroupId.isEmpty() && !it.isAdRequestReported) { + AdHelper.reportAdRequest(it.exposureEvent!!) + it.isAdRequestReported = true + } + } + + if (homeSlide.linkType != "game") { + game?.exposureEvent?.let { + it.payload.controlType = "轮播图" + it.payload.controlName = homeSlide.title + it.payload.controlLinkName = homeSlide.linkText + it.payload.controlLinkType = homeSlide.linkType + } + } + } + } slideListUi.bind(slideList, bindingAdapterPosition) } @@ -137,6 +148,5 @@ class CustomHomeSlideListViewHolder( } else { binding.backgroundView.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(itemView.context)) } - } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideWithCardsViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideWithCardsViewHolder.kt index 7d69882764..a0aa912e1a 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideWithCardsViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideWithCardsViewHolder.kt @@ -5,12 +5,14 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.Observer import androidx.recyclerview.widget.RecyclerView import com.gh.common.exposure.ExposureManager +import com.gh.common.util.AdHelper import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.json.json import com.gh.gamecenter.common.utils.SensorsBridge import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.view.DrawableView +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.HomeSlideWithCardsCustomBinding import com.gh.gamecenter.entity.HomeSubSlide import com.gh.gamecenter.feature.entity.GameEntity @@ -22,7 +24,6 @@ import com.gh.gamecenter.home.custom.adapter.CustomPageAdapter import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem -import com.gh.gamecenter.home.custom.model.CustomPageData import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.ui.CommonContentHomeSlideWithCardsUi @@ -48,65 +49,14 @@ class CustomHomeSlideWithCardsViewHolder( viewModel.commonContentCollectionUseCase, lifecycleOwner, binding, + viewModel.refreshCount, + viewModel.pageConfigure.exposureSourceList, + pageLocation.pageLevelString, object : CommonContentHomeSlideWithCardsUi.HomeSLideWithCardsEventListener { override fun updateImmersiveColor(color: Int) { this@CustomHomeSlideWithCardsViewHolder.updateImmersiveColor(color) } - override fun createEventWithSourceConcat(game: GameEntity, subSlideId: String) { - (_item as? CustomCommonContentCollectionItem)?.let { - ExposureEvent.createEventWithSourceConcat( - game, - basicSource = pageConfigure.exposureSourceList, - source = listOf( - ExposureSource( - "通用内容合集", - "${it.data.name}+${it.data.layoutChinese}+${it.data.id}" - ), - ExposureSource("右侧卡片", subSlideId) - ) - ) - } - - } - - override fun createExposureEvent(actualPosition: Int, game: GameEntity?): ExposureEvent? { - val item = _item - return if (item is CustomCommonContentCollectionItem) { - createExposureEvent( - game, - listOf( - ExposureSource( - "通用内容合集", - "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" - ), - ExposureSource("轮播图", "") - ), - pageConfigure.exposureSourceList, - actualPosition, - _item.componentPosition, - createTrackData(item) - ) - } else { - return null - } - } - - override fun addGameExposureEvent(position: Int, game: GameEntity, subSlideId: String) { - (_item as? CustomCommonContentCollectionItem)?.let { - _item.exposureEventList.add( - getGameExposureEvent( - it.data, - subSlideId, - game, - position, - pageConfigure.exposureSourceList - ) - ) - } - - } - override fun navigateToGameDetailPage( childPosition: Int, gameEntity: GameEntity, @@ -145,13 +95,11 @@ class CustomHomeSlideWithCardsViewHolder( }) childEventHelper.navigateToLinkPage(homeSubLink, "右侧卡片-游戏", clickEvent) } - } override fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) { childEventHelper.navigateToLinkPage(link, text, exposureEvent) } - }) } @@ -162,32 +110,48 @@ class CustomHomeSlideWithCardsViewHolder( override fun bindView(item: CustomPageItem) { super.bindView(item) + if (item is CustomCommonContentCollectionItem) { + runOnIoThread(isLightWeightTask = true) { + fillExposureEvent(item) + } slideWithCardsUi.bind(item.data, bindingAdapterPosition) } } + private fun fillExposureEvent(item: CustomCommonContentCollectionItem) { + // 处理轮播图 + for ((index, homeSlide) in item.data.slides.slide.withIndex()) { + val game = homeSlide.linkGame + game?.exposureEvent = createExposureEvent( + game, + listOf( + ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"), + ExposureSource("轮播图", "") + ), + pageConfigure.exposureSourceList, + index, + _item.componentPosition, + createTrackData(item) + ) - private fun getGameExposureEvent( - collectionData: CustomPageData.CommonContentCollection, - subSlideId: String, - gameEntity: GameEntity, - position: Int, - basicExposureSource: List - ) = ExposureEvent.createEventWithSourceConcat( - gameEntity = gameEntity.apply { - sequence = position - outerSequence = viewModel.refreshCount - }, - basicSource = basicExposureSource, - source = listOf( - ExposureSource( - "通用内容合集", - "${collectionData.name}+${collectionData.layoutChinese}+${collectionData.id}" - ), - ExposureSource("右侧卡片", subSlideId) - ) - ) + game?.let { + if (!it.adGroupId.isEmpty() && !it.isAdRequestReported) { + AdHelper.reportAdRequest(it.exposureEvent!!) + it.isAdRequestReported = true + } + } + + if (homeSlide.linkType != "game") { + game?.exposureEvent?.let { + it.payload.controlType = "轮播图" + it.payload.controlName = homeSlide.title + it.payload.controlLinkName = homeSlide.linkText + it.payload.controlLinkType = homeSlide.linkType + } + } + } + } override fun onViewAttach(parent: RecyclerView?) { (bindingAdapter as? CustomPageAdapter)?.firstShowPosition?.observe( diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSubSlideListItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSubSlideListItemViewHolder.kt index 6cde4a5080..decc7b7287 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSubSlideListItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSubSlideListItemViewHolder.kt @@ -16,15 +16,20 @@ import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.core.utils.RandomUtils import com.gh.gamecenter.databinding.HomeSubSlideListItemCustomBinding import com.gh.gamecenter.entity.HomeSlide +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.game.GameItemViewHolder class CustomHomeSubSlideListItemViewHolder(val binding: HomeSubSlideListItemCustomBinding) : - BaseRecyclerViewHolder(binding.root) { + BaseRecyclerViewHolder(binding.root), IExposureProvider { + private var boundedGameEntity: GameEntity? = null private val mImageWith = ((DisplayUtils.getScreenWidth() / 2F) - 20F.dip2px()).toInt() fun bindSubSlideListItem(homeSlide: HomeSlide) { if (homeSlide.linkGame != null) { + boundedGameEntity = homeSlide.linkGame binding.gameGroup.visibility = View.VISIBLE binding.gameName.text = homeSlide.linkGame.name binding.gameIconIv.displayGameIcon(homeSlide.linkGame) @@ -84,4 +89,8 @@ class CustomHomeSubSlideListItemViewHolder(val binding: HomeSubSlideListItemCust hierarchy.setPlaceholderImage(RandomUtils.getRandomPlaceholderColor()) } } + + override fun provideExposureData(): ExposureEvent? { + return boundedGameEntity?.exposureEvent?.getRefreshedExposureEvent() + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomIconMatrixViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomIconMatrixViewHolder.kt index 6bd8c46f83..98e404bcf3 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomIconMatrixViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomIconMatrixViewHolder.kt @@ -4,11 +4,9 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.RecyclerIconMatrixCustomBinding -import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.IGameChangedNotifier import com.gh.gamecenter.home.custom.adapter.CustomIconMatrixAdapter -import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem @@ -35,10 +33,14 @@ class CustomIconMatrixViewHolder( override fun bindView(item: CustomPageItem) { super.bindView(item) if (item is CustomSplitSubjectItem) { - setSplitSubjectTitle(binding.layoutTitle, item, childEventHelper) - fillExposureEventList(item) + runOnIoThread(isLightWeightTask = true) { + (item.startChildPosition..item.endChildPosition).forEach { + val game = item.data.data?.getOrNull(it) ?: return@forEach + fillExposureInfoAndReportAd(game, it, item) + } + } if (binding.recyclerView.adapter == null) { binding.recyclerView.layoutManager = @@ -54,24 +56,4 @@ class CustomIconMatrixViewHolder( } } - private fun fillExposureEventList(item: CustomSplitSubjectItem) { - val eventList = arrayListOf() - runOnIoThread(true) { - (item.startChildPosition..item.endChildPosition).forEach { - val game = item.data.data?.getOrNull(it) ?: return@forEach - val event = createExposureEvent( - game, - item.exposureSource, - pageConfigure.exposureSourceList, - it, - item.componentPosition, - createTrackData(item) - ) - eventList.add(event) - } - } - item.exposureEventList = eventList - - } - } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomNavigationViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomNavigationViewHolder.kt index 3b3d667f3b..29974e455e 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomNavigationViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomNavigationViewHolder.kt @@ -37,7 +37,6 @@ class CustomNavigationViewHolder( override fun bindView(item: CustomPageItem) { super.bindView(item) if (item is CustomCommonContentCollectionItem) { - val navigationList = item.data.navigations if (!navigationList.isNullOrEmpty()) { @@ -67,9 +66,7 @@ class CustomNavigationViewHolder( exposureEventList.add(event) createTrackData(item) } - } - item.exposureEventList = exposureEventList navigationUi.bind(navigationList, exposureEventList) } } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRankCollectionViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRankCollectionViewHolder.kt index d4be1f3e38..c51e99b86a 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRankCollectionViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRankCollectionViewHolder.kt @@ -1,6 +1,7 @@ package com.gh.gamecenter.home.custom.viewholder import androidx.recyclerview.widget.LinearLayoutManager +import com.gh.common.util.AdHelper import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.RankCollectionListCustomBinding @@ -42,11 +43,8 @@ class CustomRankCollectionViewHolder( binding.recyclerView.adapter = _adapter } - _adapter.setData(item) - - val collection = item.data - runOnIoThread(true) { - val exposureEventList = arrayListOf() + runOnIoThread(isLightWeightTask = true) { + val collection = item.data collection.data.forEach { customSubject -> customSubject.games.forEachIndexed { index, gameEntity -> val source = listOf( @@ -56,21 +54,25 @@ class CustomRankCollectionViewHolder( "${customSubject.title}+${customSubject.styleChinese}+${customSubject.id}" ) ) - exposureEventList.add( - createExposureEvent( - gameEntity, - source, - pageConfigure.exposureSourceList, - index, - item.componentPosition, - createTrackData(item) - ) + gameEntity.pageLevelString = pageLocation.pageLevelString + gameEntity.exposureEvent = createExposureEvent( + gameEntity, + source, + pageConfigure.exposureSourceList, + index, + item.componentPosition, + createTrackData(item) ) + + if (!gameEntity.adGroupId.isEmpty() && !gameEntity.isAdRequestReported) { + AdHelper.reportAdRequest(gameEntity.exposureEvent!!) + gameEntity.isAdRequestReported = true + } } } - _item.exposureEventList = exposureEventList } - } + _adapter.setData(item) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRecommendCardViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRecommendCardViewHolder.kt index b29391a3aa..98d699d8d5 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRecommendCardViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRecommendCardViewHolder.kt @@ -1,13 +1,14 @@ package com.gh.gamecenter.home.custom.viewholder import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.RecyclerRecommendCardCustomBinding import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem -import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomPageData.RecommendCard import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.ui.RecommendCardUi @@ -20,44 +21,15 @@ class CustomRecommendCardViewHolder( ) : BaseCustomViewHolder(viewModel, binding.root) { private val recommendCardUi = RecommendCardUi(binding, object : RecommendCardUi.OnRecommendCardEventListener { - override fun onItemClick(childPosition: Int, card: CustomPageData.RecommendCard) { - childEventHelper.navigateToLinkPage(card.link, "内容卡片", _item.exposureEventList.getOrNull(childPosition)) - } - - override fun onItemExposure(childPosition: Int, card: CustomPageData.RecommendCard) { - val item = _item as CustomCommonContentCollectionItem - val gameEntity = if (card.linkType == "game") { - GameEntity(id = card.linkId, name = card.linkText) - } else { - GameEntity() - } - _item.exposureEventList.add( - createExposureEvent( - gameEntity.also { - it.sequence = childPosition - it.outerSequence = _item.position - }, - listOf( - ExposureSource( - "通用内容合集", - "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" - ) - ), - pageConfigure.exposureSourceList, - childPosition, - _item.componentPosition, - createTrackData(item) - ) - ) + override fun onItemClick(childPosition: Int, card: RecommendCard) { + childEventHelper.navigateToLinkPage(card.link, "内容卡片", card.exposureEvent) } override fun onAllClick() { childEventHelper.navigateToCommonCollectionDetailPage(_item.link) } - }) - override val childEventHelper by lazy { CommonContentCollectionEventHelper(viewModel) } @@ -65,8 +37,37 @@ class CustomRecommendCardViewHolder( override fun bindView(item: CustomPageItem) { super.bindView(item) if (item is CustomCommonContentCollectionItem) { - item.exposureEventList.clear() + runOnIoThread(isLightWeightTask = true) { + fillExposureInfo(item.data.recommendCards) + } recommendCardUi.bind(item.data.topRightText, item.data.name, item.data.subtitle, item.data.recommendCards) } } + + private fun fillExposureInfo(dataList: List) { + for ((index, card) in dataList.withIndex()) { + val item = _item as CustomCommonContentCollectionItem + val gameEntity = if (card.linkType == "game") { + GameEntity(id = card.linkId, name = card.linkText) + } else { + GameEntity() + } + card.exposureEvent = createExposureEvent( + gameEntity.also { + it.sequence = index + it.outerSequence = _item.position + }, + listOf( + ExposureSource( + "通用内容合集", + "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" + ) + ), + pageConfigure.exposureSourceList, + index, + _item.componentPosition, + createTrackData(item) + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRefreshIconViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRefreshIconViewHolder.kt index 78f6b3805c..d6aa3f4164 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRefreshIconViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRefreshIconViewHolder.kt @@ -6,9 +6,12 @@ import android.view.View import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.AdHelper import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.HomeGameCollectionRefreshItemCustomBinding +import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.adapter.CustomRefreshIconAdapter import com.gh.gamecenter.home.custom.createExposureEvent @@ -38,34 +41,8 @@ class CustomRefreshIconViewHolder( GameSubjectCollectionEventHelper(viewModel) } - private - val adapter by lazy { - CustomRefreshIconAdapter(context, childEventHelper) { childPosition, game -> - val subject = showSubject ?: return@CustomRefreshIconAdapter - (_item as? CustomSubjectCollectionItem)?.let { - val source = if (it.isSubjectCollection) { - listOf( - ExposureSource("专题合集", "${it.data.name}+${it.componentStyle}+${it.data.id}"), - ExposureSource("专题", "${subject.title}+${subject.styleChinese}+${subject.id}") - ) - } else { - listOf( - ExposureSource("游戏单合集", "${it.data.name}+${it.componentStyle}+${it.data.id}"), - ExposureSource("游戏单", "${subject.title}+${subject.id}") - ) - } - game.isAdData = subject.adIconActive - val event = createExposureEvent( - game, - source, - pageConfigure.exposureSourceList, - childPosition, - _item.componentPosition, - createTrackData(_item) - ) - _item.exposureEventList.add(event) - } - } + private val adapter by lazy { + CustomRefreshIconAdapter(context, childEventHelper) } private var _subjectEntity: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = null @@ -78,17 +55,9 @@ class CustomRefreshIconViewHolder( override fun bindView(item: CustomPageItem) { super.bindView(item) if (item is CustomSubjectCollectionItem) { - - _item.exposureEventList.clear() - val collection = item.data - setSubjectCollectionTitle( - item, - binding.layoutTitle, - childEventHelper, - true - ) + setSubjectCollectionTitle(item, binding.layoutTitle, childEventHelper, true) if (binding.recyclerView.adapter == null) { val layoutManager = if (isMatrix) { @@ -106,6 +75,13 @@ class CustomRefreshIconViewHolder( } val subjectEntity = item.showSubject ?: return + + runOnIoThread(isLightWeightTask = true) { + for ((index, game) in subjectEntity.games.withIndex()) { + fillExposureInfo(subjectEntity, game, index) + } + } + if (subjectEntity.id == _subjectEntity?.id) { adapter.notifyItemRangeChanged(0, adapter.itemCount) return @@ -133,8 +109,40 @@ class CustomRefreshIconViewHolder( } } - class MyItemDecoration : RecyclerView.ItemDecoration() { + private fun fillExposureInfo(subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity, + game: GameEntity, + position: Int + ) { + (_item as? CustomSubjectCollectionItem)?.let { + val source = if (it.isSubjectCollection) { + listOf( + ExposureSource("专题合集", "${it.data.name}+${it.componentStyle}+${it.data.id}"), + ExposureSource("专题", "${subject.title}+${subject.styleChinese}+${subject.id}") + ) + } else { + listOf( + ExposureSource("游戏单合集", "${it.data.name}+${it.componentStyle}+${it.data.id}"), + ExposureSource("游戏单", "${subject.title}+${subject.id}") + ) + } + game.isAdData = subject.adIconActive + game.exposureEvent = createExposureEvent( + game, + source, + pageConfigure.exposureSourceList, + position, + _item.componentPosition, + createTrackData(_item) + ) + if (!game.adGroupId.isEmpty() && !game.isAdRequestReported) { + AdHelper.reportAdRequest(game.exposureEvent!!) + game.isAdRequestReported = true + } + } + } + + class MyItemDecoration : RecyclerView.ItemDecoration() { override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { val position = parent.getChildAdapterPosition(view) val isFirstLine = position <= 3 diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomSingleGameCardViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomSingleGameCardViewHolder.kt index c734dc2520..04fbcdf1d7 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomSingleGameCardViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomSingleGameCardViewHolder.kt @@ -3,6 +3,7 @@ package com.gh.gamecenter.home.custom.viewholder import android.graphics.Paint import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.Adapter +import com.gh.common.util.AdHelper import com.gh.common.util.DownloadItemUtils import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.GameViewHolder @@ -11,6 +12,8 @@ import com.gh.gamecenter.common.utils.goneIf import com.gh.gamecenter.databinding.RecyclerSingleGameCardCustomBinding import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.TagStyleEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.home.custom.CustomPageViewModel import com.gh.gamecenter.home.custom.adapter.CustomViewExt.setGameRattingWithSubject import com.gh.gamecenter.home.custom.createExposureEvent @@ -24,7 +27,9 @@ import com.gh.gamecenter.home.custom.model.CustomSubjectItem class CustomSingleGameCardViewHolder( viewModel: CustomPageViewModel, val binding: RecyclerSingleGameCardCustomBinding, -) : BaseCustomViewHolder(viewModel, binding.root) { +) : BaseCustomViewHolder(viewModel, binding.root), IExposureProvider { + + private var exposureEvent: ExposureEvent? = null override val onlyNotifyItemChanged: Boolean get() = true @@ -36,12 +41,11 @@ class CustomSingleGameCardViewHolder( fun bindView(item: CustomPageItem, adapter: Adapter) { super.bindView(item) if (item is CustomSubjectItem) { - _item.exposureEventList.clear() val data = item.data.data ?: return val showPosition = viewModel.refreshCount % data.size val game = data[showPosition] - val event = createExposureEvent( + exposureEvent = createExposureEvent( game, listOf( ExposureSource( @@ -54,8 +58,11 @@ class CustomSingleGameCardViewHolder( item.componentPosition, createTrackData(item) ) - item.exposureEventList.add(event) - item.exposureEvent = event + + if (!game.adGroupId.isEmpty() && !game.isAdRequestReported) { + AdHelper.reportAdRequest(game.exposureEvent!!) + game.isAdRequestReported = true + } with(binding) { tvTitle.text = game.abbreviation.ifBlank { game.name } @@ -130,6 +137,9 @@ class CustomSingleGameCardViewHolder( ) } } + } + override fun provideExposureData(): ExposureEvent? { + return exposureEvent?.getRefreshedExposureEvent() } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/NotificationColumnViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/NotificationColumnViewHolder.kt index a671246f6f..2ce1d5e6e3 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/NotificationColumnViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/NotificationColumnViewHolder.kt @@ -15,6 +15,7 @@ import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.utils.goneIf import com.gh.gamecenter.common.view.ScrollEventListener +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.databinding.RecyclerNotificationColumnBinding import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.home.custom.BannerInRecyclerController @@ -23,6 +24,7 @@ import com.gh.gamecenter.home.custom.adapter.NotificationColumnAdapter import com.gh.gamecenter.home.custom.createExposureEvent import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem +import com.gh.gamecenter.home.custom.model.CustomPageData import com.gh.gamecenter.home.custom.model.CustomPageItem import com.gh.gamecenter.home.custom.tracker.CommonContentCollectionTracker @@ -46,41 +48,11 @@ class NotificationColumnViewHolder( } private val adapter by lazy(LazyThreadSafetyMode.NONE) { - NotificationColumnAdapter(itemView.context) { index, notify -> - val item = _item as CustomCommonContentCollectionItem - val gameEntity = if (notify.linkType == "game") { - GameEntity(id = notify.linkId, name = notify.linkText) - } else { - GameEntity() - } - _item.exposureEventList.add( - createExposureEvent( - gameEntity.also { - it.sequence = index - it.outerSequence = _item.position - }, - listOf( - ExposureSource( - "通用内容合集", - "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" - ), - ExposureSource( - item.data.layoutChinese, - "${notify.id}" - ) - ), - pageConfigure.exposureSourceList, - index, - _item.componentPosition, - createTrackData(item) - ) - ) - } + NotificationColumnAdapter(itemView.context) } private val layoutManager by lazy(LazyThreadSafetyMode.NONE) { object : LinearLayoutManager(itemView.context) { - override fun smoothScrollToPosition( recyclerView: RecyclerView?, state: RecyclerView.State?, @@ -90,12 +62,10 @@ class NotificationColumnViewHolder( override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float { return MILLISECONDS_PER_INCH / displayMetrics.densityDpi } - } linearSmoothScroller.targetPosition = position startSmoothScroll(linearSmoothScroller) } - } } @@ -131,11 +101,14 @@ class NotificationColumnViewHolder( hiddenIds?.contains(notify.id) == true } if (dataList.isNotEmpty()) { + runOnIoThread(isLightWeightTask = true) { + fillExposureInfo(dataList) + } + adapter.submitList(dataList) } else { setVisible(false) } - } init { @@ -150,7 +123,7 @@ class NotificationColumnViewHolder( childEventHelper.navigateToLinkPage( notify.link, "通知栏目", - item.exposureEventList.getOrNull(childPosition) + notify.exposureEvent ) } } @@ -165,7 +138,6 @@ class NotificationColumnViewHolder( override fun bindView(item: CustomPageItem) { super.bindView(item) if (item is CustomCommonContentCollectionItem) { - item.exposureEventList.clear() if (item.data.notifies.isEmpty()) { setVisible(false) return @@ -184,7 +156,6 @@ class NotificationColumnViewHolder( }) } } - } override fun onViewAttach(parent: RecyclerView?) { @@ -209,6 +180,37 @@ class NotificationColumnViewHolder( } } + private fun fillExposureInfo(dataList: List) { + for ((index, notify) in dataList.withIndex()) { + val item = _item as CustomCommonContentCollectionItem + val gameEntity = if (notify.linkType == "game") { + GameEntity(id = notify.linkId, name = notify.linkText) + } else { + GameEntity() + } + notify.exposureEvent = createExposureEvent( + gameEntity.also { + it.sequence = index + it.outerSequence = _item.position + }, + listOf( + ExposureSource( + "通用内容合集", + "${item.data.name}+${item.data.layoutChinese}+${item.data.id}" + ), + ExposureSource( + item.data.layoutChinese, + "${notify.id}" + ) + ), + pageConfigure.exposureSourceList, + index, + _item.componentPosition, + createTrackData(item) + ) + } + } + private fun closeItem() { val selectedPosition = layoutManager.findFirstCompletelyVisibleItemPosition() if (selectedPosition != -1) { @@ -221,7 +223,6 @@ class NotificationColumnViewHolder( CommonContentCollectionTracker(pageLocation).trackLinkContentCollectionClick(_item, link, "关闭通知") viewModel.hideNotificationItem(it.data.id, item.id) } - } } @@ -245,5 +246,4 @@ class NotificationColumnViewHolder( private const val MILLISECONDS_PER_INCH = 1000f } - } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2GameListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2GameListViewHolder.kt index a05e8787fa..cc875707a1 100644 --- a/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2GameListViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2GameListViewHolder.kt @@ -10,6 +10,8 @@ import com.gh.gamecenter.common.utils.toDrawable import com.gh.gamecenter.core.utils.TimeUtils import com.gh.gamecenter.databinding.ItemHomeGameTestV2ItemBinding import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.game.GameItemViewHolder import java.text.SimpleDateFormat import java.util.* @@ -21,7 +23,9 @@ import java.util.* */ class HomeGameTestV2GameListViewHolder( private val mBinding: ItemHomeGameTestV2ItemBinding -) : GameViewHolder(mBinding.root) { +) : GameViewHolder(mBinding.root), IExposureProvider { + + private var boundedGameEntity: GameEntity? = null companion object { private val mDayList = TimeUtils.getTodayYesterdayAndTomorrow() @@ -40,6 +44,8 @@ class HomeGameTestV2GameListViewHolder( } fun bindGameItem(gameEntity: GameEntity) { + boundedGameEntity = gameEntity + mBinding.run { gameIconView.displayGameIcon(gameEntity) BindingAdapters.setGameName(gameName, gameEntity, false) @@ -127,4 +133,9 @@ class HomeGameTestV2GameListViewHolder( TimeUtils.getFormatDate(time) } } + + override fun provideExposureData(): ExposureEvent? { + return boundedGameEntity?.exposureEvent?.getRefreshedExposureEvent() + } + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/minigame/MiniGameRecentlyPlayListAdapter.kt b/app/src/main/java/com/gh/gamecenter/minigame/MiniGameRecentlyPlayListAdapter.kt index 35f8833175..be1c6fe5e4 100644 --- a/app/src/main/java/com/gh/gamecenter/minigame/MiniGameRecentlyPlayListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/minigame/MiniGameRecentlyPlayListAdapter.kt @@ -8,6 +8,8 @@ import com.gh.gamecenter.common.baselist.DiffUtilAdapter import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.databinding.ItemHomeVgameBinding import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.minigame.MiniGameItemHelper /** @@ -15,7 +17,7 @@ import com.gh.gamecenter.feature.minigame.MiniGameItemHelper */ class MiniGameRecentlyPlayListAdapter( context: Context, - private val onItemClick:((GameEntity, Int) -> Unit)? = null + private val onItemClick: ((GameEntity, Int) -> Unit)? = null ) : DiffUtilAdapter(context) { override fun onCreateViewHolder( @@ -42,9 +44,13 @@ class MiniGameRecentlyPlayListAdapter( } class MiniGameRecentlyPlayItemViewHolder(private var mBinding: ItemHomeVgameBinding) : - RecyclerView.ViewHolder(mBinding.root) { + RecyclerView.ViewHolder(mBinding.root), IExposureProvider { + + private var exposureEvent: ExposureEvent? = null fun bindView(entity: GameEntity, position: Int, onItemClick: ((GameEntity, Int) -> Unit)? = null) { + exposureEvent = entity.exposureEvent + mBinding.gameIconIv.displayGameIcon(entity) mBinding.maskView.visibility = View.GONE @@ -63,5 +69,9 @@ class MiniGameRecentlyPlayListAdapter( } } } + + override fun provideExposureData(): ExposureEvent? { + return exposureEvent?.getRefreshedExposureEvent() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/minigame/qq/QGameSubjectListRepository.kt b/app/src/main/java/com/gh/gamecenter/minigame/qq/QGameSubjectListRepository.kt index 89ae631019..fed8d33538 100644 --- a/app/src/main/java/com/gh/gamecenter/minigame/qq/QGameSubjectListRepository.kt +++ b/app/src/main/java/com/gh/gamecenter/minigame/qq/QGameSubjectListRepository.kt @@ -12,7 +12,12 @@ class QGameSubjectListRepository( private val api: ApiService = RetrofitManager.getInstance().newApi ) : ISubjectListRepository { - override fun getColumn(column_id: String?, page: Int, sort: String?, order: String?): Single> { + override fun getColumn(column_id: String?, + page: Int, + sort: String?, + order: String?, + ad: String?, + columnCollectionId: String?): Single> { return api.getQGameColumn(column_id, order, page, 20) } diff --git a/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectCPMListRepository.kt b/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectCPMListRepository.kt index dc23bb2d83..1ea3bf990b 100644 --- a/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectCPMListRepository.kt +++ b/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectCPMListRepository.kt @@ -10,7 +10,12 @@ class WGameSubjectCPMListRepository( private val dataSource: WGameSubjectCPMRemoteDataSource = WGameSubjectCPMRemoteDataSource() ) : ISubjectListRepository { - override fun getColumn(column_id: String?, page: Int, sort: String?, order: String?): Single> { + override fun getColumn(column_id: String?, + page: Int, + sort: String?, + order: String?, + ad: String?, + columnCollectionId: String?): Single> { return dataSource.getEditorRecommendCPMList(page) } diff --git a/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectCPMListUseCase.kt b/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectCPMListUseCase.kt index cc07947422..2658e80296 100644 --- a/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectCPMListUseCase.kt +++ b/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectCPMListUseCase.kt @@ -33,7 +33,7 @@ class WGameSubjectCPMListUseCase( } requestKeyList.add(subjectId) - repository.getColumn(null, 1, null, null) + repository.getColumn(null, 1, null, null, null, null) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : BiResponse>() { diff --git a/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectListRepository.kt b/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectListRepository.kt index 8a7362d5f0..9a8b22724e 100644 --- a/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectListRepository.kt +++ b/app/src/main/java/com/gh/gamecenter/minigame/wechat/WGameSubjectListRepository.kt @@ -12,7 +12,12 @@ class WGameSubjectListRepository( private val api: ApiService = RetrofitManager.getInstance().newApi ) : ISubjectListRepository { - override fun getColumn(column_id: String?, page: Int, sort: String?, order: String?): Single> { + override fun getColumn(column_id: String?, + page: Int, + sort: String?, + order: String?, + ad: String?, + columnCollectionId: String?): Single> { return api.getWGameColumn(column_id, order, page, 20) } diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index 96d5b17f29..ee568f572b 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -1,6 +1,7 @@ package com.gh.gamecenter.retrofit.service; import com.gh.common.filter.RegionSetting; +import com.gh.gamecenter.common.entity.IdfaData; import com.gh.gamecenter.common.entity.LaunchRedirectWrapper; import com.gh.gamecenter.common.entity.LinkEntity; import com.gh.gamecenter.common.entity.OssEntity; @@ -339,7 +340,14 @@ public interface ApiService { * 获取专题数据 */ @GET("columns/{column_id}/games") - Single> getColumn(@Path("column_id") String column_id, @Query("sort") String sort, @Query("filter") String order, @Query("page") int page); + Single> getColumn( + @Path("column_id") String column_id, + @Query("sort") String sort, + @Query("filter") String order, + @Query("AD") String ad, + @Query("column_collection_id") String columnCollectionId, + @Query("page") int page + ); /** * 获取专题数据标题 @@ -2348,6 +2356,7 @@ public interface ApiService { Single> getCategoryV2Games(@Path("category_id") String categoryId, @Query("filter") String filter, @Query("sort") String sort, + @Query("AD") String ad, @Query("page") int page); /** @@ -3338,7 +3347,8 @@ public interface ApiService { @GET("app/custom_page/{page_id}") Single getCustomPageData( @Path("page_id") String pageId, - @Query("page") int page); + @Query("page") int page, + @Query("AD") String ad); @GET("app/custom_page/{page_id}/suspended_window") Single> loadSuspendedWindow( @@ -3435,4 +3445,10 @@ public interface ApiService { */ @GET("app/column_test_v2/{link_id}/top_games") Observable> getServerTestV2TopGames(@Path("link_id") String linkId); + /** + * 获取广告标识 + */ + @POST("idfa") + Single getIdfa(@Query("version") String version, @Query("channel") String channel); + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt index 3622c08553..9b214914f6 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt @@ -25,6 +25,7 @@ import com.gh.gamecenter.feature.exposure.ExposureType import com.gh.gamecenter.feature.game.GameItemViewHolder import com.gh.gamecenter.SearchType import com.gh.gamecenter.db.ISearchHistoryDao +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.search.SearchGameResultAdapter.Companion.setItemCLick import com.gh.gamecenter.search.SearchGameResultAdapter.Companion.showContentTag import com.lzf.easyfloat.utils.DisplayUtils @@ -37,7 +38,7 @@ class SearchGameFirstItemViewHolder( fragment: Fragment, val binding: SearchGameFirstItemBinding, private val dao: ISearchHistoryDao -) : RecyclerView.ViewHolder(binding.root) { +) : RecyclerView.ViewHolder(binding.root), IExposureProvider { private var contentTag: GameEntity.ContentTag? = null private var game: GameEntity? = null @@ -120,7 +121,6 @@ class SearchGameFirstItemViewHolder( fun bind( key: String, item: SearchItemData?, - exposureEventArray: SparseArray?, downloadListener: (ExposureEvent) -> Unit ) { item ?: return @@ -169,7 +169,7 @@ class SearchGameFirstItemViewHolder( exposureSources, null, ExposureType.EXPOSURE ) - exposureEventArray?.put(bindingAdapterPosition, exposureEvent) + exposureEvent.payload.searchContent = key binding.run { val isShowTag = gameEntity.contentTag != null @@ -255,6 +255,10 @@ class SearchGameFirstItemViewHolder( bannerController.onViewDetachedFromWindow(recyclerView) } + override fun provideExposureData(): ExposureEvent? { + return game?.exposureEvent?.getRefreshedExposureEvent() + } + class CardItemDecoration(context: Context) : RecyclerView.ItemDecoration() { private val dp8 = DisplayUtils.dp2px(context, 8f) diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt index 79d1971074..d752c0b6f0 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt @@ -52,10 +52,8 @@ class SearchGameIndexAdapter( val entrance: String, val type: String, val sourceEntrance: String -) : ListAdapter(context), IExposable { +) : ListAdapter(context) { - - private var exposureEventArray: SparseArray? = null private var searchMap: ArrayMap = ArrayMap() var key: String = "" @@ -63,7 +61,6 @@ class SearchGameIndexAdapter( val positionAndPackageMap = HashMap() override fun setListData(updateData: MutableList?) { - exposureEventArray = SparseArray(updateData?.size ?: 0) // 记录游戏位置 if (updateData != null) { positionAndPackageMap.clear() @@ -133,7 +130,7 @@ class SearchGameIndexAdapter( null, ExposureType.EXPOSURE ) - exposureEventArray!!.put(position, exposureEvent) + exposureEvent.payload.searchContent = key if (holder is SearchGameIndexItemViewHolder) { val binding = holder.binding @@ -569,14 +566,6 @@ class SearchGameIndexAdapter( return mEntityList.size } - override fun getEventByPosition(pos: Int): ExposureEvent? { - return exposureEventArray!!.get(pos) - } - - override fun getEventListByPosition(pos: Int): List? { - return mEntityList[pos].exposureEventList - } - fun notifyItemByDownload(download: DownloadEntity) { for (key in positionAndPackageMap.keys) { if (key.contains(download.packageName) && key.contains(download.gameId)) { diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt index 60a616e0a2..7d12bf176d 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt @@ -8,7 +8,7 @@ import android.text.method.LinkMovementMethod import android.text.style.ClickableSpan import android.view.View import androidx.core.content.ContextCompat -import com.gh.common.exposure.ExposureListener +import com.gh.common.exposure.DefaultExposureStateChangeListener import com.gh.common.util.* import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus @@ -30,6 +30,7 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.SettingsEntity +import com.gh.gamecenter.feature.exposure.addExposureHelper import com.gh.gamecenter.help.HelpAndFeedbackBridge import com.halo.assistant.HaloApp import com.lightgame.download.DataWatcher @@ -41,7 +42,6 @@ import org.greenrobot.eventbus.ThreadMode class SearchGameIndexFragment : ListFragment() { private var mAdapter: SearchGameIndexAdapter? = null - private var mExposureListener: ExposureListener? = null private val mBinding by lazy { FragmentSearchResultBinding.inflate(layoutInflater) } private var mKey: String = "" @@ -112,9 +112,10 @@ class SearchGameIndexFragment : ListFragment() - for ((index, game) in entity.getFilterGame().withIndex()) { - game.sequence = index - game.outerSequence = adapterPosition - game.isAdData = entity.adIconActive + runOnIoThread(isLightWeightTask = true) { + for ((index, game) in entity.getFilterGame().withIndex()) { + game.sequence = index + game.outerSequence = adapterPosition + game.isAdData = entity.adIconActive - val exposureSources = arrayListOf().apply { - add(ExposureSource("首页搜索")) - add(ExposureSource(type, key)) - add(ExposureSource("专题", "${entity.name}+${entity.columnId}")) + val exposureSources = arrayListOf().apply { + add(ExposureSource("首页搜索")) + add(ExposureSource(type, key)) + add(ExposureSource("专题", "${entity.name}+${entity.columnId}")) + } + val exposureEvent = ExposureEvent.createEvent(game, exposureSources) + exposureEvent.payload.searchContent = key + game.exposureEvent = exposureEvent + + if (!game.adGroupId.isEmpty() && !game.isAdRequestReported) { + AdHelper.reportAdRequest(exposureEvent) + game.isAdRequestReported = true + } } - val exposureEvent = ExposureEvent.createEvent(game, exposureSources) - exposureList.add(exposureEvent) - - game.exposureEvent = exposureEvent } - itemData.exposureEventList = exposureList binding.run { subjectRv.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) @@ -108,7 +127,8 @@ class SearchSubjectItemViewHolder(var binding: SearchSubjectItemBinding) : Recyc gameColumnName = entity.name, gameId = it.id, gameName = it.name ?: "", - text = "游戏" + text = "游戏", + adGroupId = it.adGroupId ) } } @@ -147,7 +167,8 @@ class SearchSubjectItemViewHolder(var binding: SearchSubjectItemBinding) : Recyc gameColumnId = entity.columnId, gameColumnName = entity.name, text = "右上角", - buttonType = "全部" + buttonType = "全部", + adGroupId = "" ) } } diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt index 9c5e771c9b..a90a4187fd 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt @@ -10,7 +10,6 @@ import androidx.collection.ArrayMap import androidx.recyclerview.widget.RecyclerView import com.gh.ad.AdDelegateHelper import com.gh.common.databind.BindingAdapters -import com.gh.common.exposure.IExposable import com.gh.common.filter.RegionSettingHelper import com.gh.common.util.* import com.gh.common.util.LogUtils @@ -33,6 +32,7 @@ import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.json.json import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.viewholder.FooterViewHolder +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.core.utils.StringUtils @@ -46,7 +46,6 @@ import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.exposure.ExposureType import com.gh.gamecenter.feature.game.GameItemViewHolder import com.gh.gamecenter.feature.minigame.MiniGameItemHelper -import com.gh.gamecenter.gamedetail.GameDetailFragment import com.gh.gamecenter.help.HelpAndFeedbackBridge import com.lightgame.download.DownloadEntity import com.lightgame.utils.Util_System_Keyboard @@ -61,10 +60,9 @@ class SearchGameResultAdapter( val entrance: String, var type: String, val sourceEntrance: String -) : ListAdapter(context), IExposable { +) : ListAdapter(context) { private var searchMap: ArrayMap = ArrayMap() - private var exposureEventArray: SparseArray? = null private var _recyclerView: RecyclerView? = null var key: String = "" @@ -78,7 +76,6 @@ class SearchGameResultAdapter( } override fun setListData(updateData: MutableList?) { - exposureEventArray = SparseArray(updateData?.size ?: 0) // 记录游戏位置 if (updateData != null) { positionAndPackageMap.clear() @@ -222,7 +219,7 @@ class SearchGameResultAdapter( val item = mEntityList[position] val gameEntity = item.game gameEntity ?: return - holder.bind(key, item, exposureEventArray) { exposureEvent -> + holder.bind(key, item) { exposureEvent -> setDownloadBtn( entrance, type, @@ -349,6 +346,8 @@ class SearchGameResultAdapter( val binding = holder.binding val item = mEntityList[holder.bindingAdapterPosition] val gameEntity = item.game ?: return + + holder.bindData(gameEntity) binding.gameItemIncluded.run { gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F @@ -392,7 +391,14 @@ class SearchGameResultAdapter( exposureSources, null, ExposureType.EXPOSURE ) - exposureEventArray!!.put(holder.adapterPosition, exposureEvent) + exposureEvent.payload.searchContent = key + + runOnIoThread(isLightWeightTask = true) { + if (!gameEntity.adGroupId.isEmpty() && !gameEntity.isAdRequestReported) { + AdHelper.reportAdRequest(exposureEvent) + gameEntity.isAdRequestReported = true + } + } binding.run { val isShowTag = gameEntity.contentTag != null @@ -451,7 +457,6 @@ class SearchGameResultAdapter( mContext, this ) - } override fun getItemViewType(position: Int): Int { @@ -476,14 +481,6 @@ class SearchGameResultAdapter( return if (mEntityList.isNotEmpty()) mEntityList.size + FOOTER_ITEM_COUNT else 0 } - override fun getEventByPosition(pos: Int): ExposureEvent? { - return exposureEventArray!!.get(pos) - } - - override fun getEventListByPosition(pos: Int): List? { - return mEntityList[pos].exposureEventList - } - fun notifyItemByDownload(download: DownloadEntity) { for (key in positionAndPackageMap.keys) { if (key.contains(download.packageName) && key.contains(download.gameId)) { @@ -783,7 +780,8 @@ class SearchGameResultAdapter( gameEntity.id, gameEntity.name ?: "", gameEntity.categoryChinese, - position + position, + gameEntity.adGroupId ) GameDetailActivity.startGameDetailActivity( context, gameEntity, diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt index 5055f13d8f..270fd98631 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt @@ -13,7 +13,7 @@ import android.widget.TextView import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels import androidx.recyclerview.widget.RecyclerView -import com.gh.common.exposure.ExposureListener +import com.gh.common.exposure.DefaultExposureStateChangeListener import com.gh.common.util.* import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus @@ -34,6 +34,7 @@ import com.gh.gamecenter.db.SearchHistoryDao import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.addExposureHelper import com.gh.gamecenter.help.HelpAndFeedbackBridge import com.gh.gamecenter.search.viewmodel.SearchTabViewModel import com.halo.assistant.HaloApp @@ -50,7 +51,6 @@ open class SearchGameResultFragment : ListFragment? = null ) : - SubjectListViewModel(application, mSubjectData, pageLocation, exposureSourceList) { + SubjectListViewModel(application, mSubjectData, pageLocation, exposureSourceList, null) { override fun provideDataSingle(page: Int): Single> = RetrofitManager.getInstance().newApi.getAdGames( mSubjectData.adId, diff --git a/app/src/main/java/com/gh/gamecenter/subject/ISubjectListRepository.kt b/app/src/main/java/com/gh/gamecenter/subject/ISubjectListRepository.kt index 3543b097fc..81b5b7767c 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/ISubjectListRepository.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/ISubjectListRepository.kt @@ -12,6 +12,8 @@ interface ISubjectListRepository { page: Int, sort: String?, order: String?, + ad: String?, + columnCollectionId: String? ): Single> fun getColumnSettings(column_id: String?): Observable diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectActivity.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectActivity.kt index 25ceccd255..a16ae154eb 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectActivity.kt @@ -23,6 +23,7 @@ open class SubjectActivity : DownloadToolbarActivity() { super.onCreate(savedInstanceState) updateStatusBarColor(com.gh.gamecenter.common.R.color.ui_surface, com.gh.gamecenter.common.R.color.ui_surface) subjectData = intent.getBundleExtra(NORMAL_FRAGMENT_BUNDLE)?.getParcelable(EntranceConsts.KEY_SUBJECT_DATA) + ?: intent.getParcelableExtra(EntranceConsts.KEY_SUBJECT_DATA) ?: SubjectData("", "", false) } diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt index 72be7dc82a..3b1b8d7bfd 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt @@ -8,7 +8,7 @@ import android.view.ViewGroup import androidx.core.view.setPadding import androidx.recyclerview.widget.RecyclerView import com.gh.common.databind.BindingAdapters -import com.gh.common.exposure.IExposable +import com.gh.common.util.AdHelper import com.gh.common.util.DataCollectionUtils import com.gh.common.util.DirectUtils import com.gh.common.util.DownloadItemUtils @@ -23,7 +23,7 @@ import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.viewholder.FooterViewHolder -import com.gh.gamecenter.core.utils.MtaHelper +import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.core.utils.PageSwitchDataHelper import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.databinding.GameImageItemBinding @@ -45,7 +45,7 @@ class SubjectAdapter( private val mCollectionId: String, private val mCollectionName: String, private val mCollectionStyle: String -) : ListAdapter(context), IExposable { +) : ListAdapter(context) { private val mExposureEventSparseArray: SparseArray = SparseArray() private val subjectData = mViewModel.subjectData @@ -224,6 +224,13 @@ class SubjectAdapter( val humanReadablePosition = position + 1 mExposureEventSparseArray.put(position, exposureEvent) + runOnIoThread(isLightWeightTask = true) { + if (!gameEntity.adGroupId.isEmpty() && !gameEntity.isAdRequestReported) { + AdHelper.reportAdRequest(exposureEvent) + gameEntity.isAdRequestReported = true + } + } + holder.itemView.setOnClickListener { DataCollectionUtils.uploadClick(mContext, "列表", subjectData.subjectName, gameEntity.name) @@ -266,7 +273,8 @@ class SubjectAdapter( gameColumnName = subjectData.subjectName ?: "", gameId = gameEntity.id, gameName = gameEntity.name ?: "", - text = "游戏" + text = "游戏", + adGroupId = gameEntity.adGroupId ) } } @@ -282,7 +290,8 @@ class SubjectAdapter( gameId = gameEntity.id, gameName = gameEntity.name ?: "", columnCollectionPattern = CustomPageItem.collectionTypeToComponentName[mCollectionStyle] - ?: mCollectionStyle + ?: mCollectionStyle, + adGroupId = gameEntity.adGroupId ) } } @@ -317,7 +326,8 @@ class SubjectAdapter( gameName = gameEntity.name ?: "", text = "按钮", columnCollectionPattern = CustomPageItem.collectionTypeToComponentName[mCollectionStyle] - ?: mCollectionStyle + ?: mCollectionStyle, + adGroupId = gameEntity.adGroupId ) } else { SensorsBridge.trackColumnClick( @@ -326,7 +336,8 @@ class SubjectAdapter( gameColumnName = subjectData.subjectName ?: "", gameId = gameEntity.id, gameName = gameEntity.name ?: "", - text = "按钮" + text = "按钮", + adGroupId = gameEntity.adGroupId ) } @@ -398,7 +409,6 @@ class SubjectAdapter( holder.itemView.setOnClickListener { DataCollectionUtils.uploadClick(mContext, "头图", subjectData.subjectName) - MtaHelper.onEvent("游戏专题", "头图", subjectData.subjectName + ":" + gameEntity.name) val linkEntity = LinkEntity() linkEntity.link = gameEntity.link linkEntity.type = gameEntity?.type @@ -434,7 +444,8 @@ class SubjectAdapter( gameName = gameEntity.name ?: "", text = "头图", columnCollectionPattern = CustomPageItem.collectionTypeToComponentName[mCollectionStyle] - ?: mCollectionStyle + ?: mCollectionStyle, + adGroupId = gameEntity.adGroupId ) } else { SensorsBridge.trackColumnClick( @@ -446,7 +457,8 @@ class SubjectAdapter( linkId = linkEntity.link ?: "", linkText = linkEntity.text ?: "", linkType = linkEntity.type ?: "", - text = "头图" + text = "头图", + adGroupId = gameEntity.adGroupId ) } } @@ -558,13 +570,6 @@ class SubjectAdapter( } } - override fun getEventByPosition(pos: Int): ExposureEvent? { - return mExposureEventSparseArray.get(pos) - } - - override fun getEventListByPosition(pos: Int): List? { - return null - } fun notifyItemByDownload(download: DownloadEntity) { for (key in positionAndPackageMap.keys) { diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt index c71fe3514a..d99b318d46 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt @@ -4,7 +4,7 @@ import android.view.View import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.RecyclerView import com.ethanhua.skeleton.Skeleton -import com.gh.common.exposure.ExposureListener +import com.gh.common.exposure.DefaultExposureStateChangeListener import com.gh.common.util.DialogUtils import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus @@ -14,7 +14,7 @@ import com.gh.gamecenter.common.baselist.LazyListFragment import com.gh.gamecenter.common.baselist.LoadType import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts -import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.pagelevel.IPageLevelProvider import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.utils.viewModelProviderFromParent import com.gh.gamecenter.common.view.SpacingItemDecoration @@ -25,6 +25,7 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.feature.exposure.addExposureHelper import com.halo.assistant.HaloApp import com.lightgame.OnTitleClickListener import com.lightgame.download.DataWatcher @@ -34,8 +35,6 @@ import org.greenrobot.eventbus.ThreadMode open class SubjectListFragment : LazyListFragment(), OnTitleClickListener { - private lateinit var mExposureListener: ExposureListener - private var mAdapter: SubjectAdapter? = null private var mSubjectViewModel: SubjectViewModel? = null @@ -89,21 +88,30 @@ open class SubjectListFragment : LazyListFragment(EntranceConsts.KEY_SUBJECT_DATA)?.let { @@ -146,19 +154,19 @@ open class SubjectListFragment : LazyListFragment> { - return api.getColumn(column_id, sort, order, page) + override fun getColumn(column_id: String?, page: Int, sort: String?, order: String?, ad: String?, columnCollectionId: String?): Single> { + return api.getColumn(column_id, sort, order, ad, columnCollectionId, page) } override fun getColumnSettings(column_id: String?): Observable { diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt index cce8963ff6..5bb191d8e8 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.gh.common.exposure.ExposureUtils +import com.gh.common.util.AdHelper import com.gh.gamecenter.common.baselist.ListViewModel import com.gh.gamecenter.common.baselist.LoadStatus import com.gh.gamecenter.common.baselist.LoadType @@ -26,7 +27,8 @@ open class SubjectListViewModel( application: Application, var subjectData: SubjectData, var pageLocation: PageLocation, - var exposureSourceList: List? + var exposureSourceList: List?, + var columnCollectionId: String?, ) : ListViewModel(application) { // 供专题类型为 rows 时统计用 @@ -45,6 +47,8 @@ open class SubjectListViewModel( page, subjectData.sort, subjectData.filter.ifEmpty { "type:全部" }, + AdHelper.getIdfaString(), + columnCollectionId ) } @@ -117,10 +121,11 @@ open class SubjectListViewModel( private val mApplication: Application, private val subjectData: SubjectData, private val pageLocation: PageLocation, - private val exposureSourceList: List? = null + private val exposureSourceList: List? = null, + private val columnCollectionId: String? = null ) : ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { - return SubjectListViewModel(mApplication, subjectData, pageLocation, exposureSourceList) as T + return SubjectListViewModel(mApplication, subjectData, pageLocation, exposureSourceList, columnCollectionId) as T } } diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/BaseTabWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/wrapper/BaseTabWrapperFragment.kt index f8cb7bd06d..6912f89e39 100644 --- a/app/src/main/java/com/gh/gamecenter/wrapper/BaseTabWrapperFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/wrapper/BaseTabWrapperFragment.kt @@ -25,6 +25,7 @@ import com.gh.gamecenter.common.databinding.ReuseNoConnectionBinding import com.gh.gamecenter.common.databinding.ReuseNoneDataBinding import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.json.json +import com.gh.gamecenter.common.pagelevel.IPageLevelProvider import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.TabIndicatorView import com.gh.gamecenter.common.view.WrapContentDraweeView @@ -59,6 +60,7 @@ abstract class BaseTabWrapperFragment : BaseLazyFragment(), IMultiTab { protected var multiTabNavName = "" protected var bottomTabId = "" protected var bottomTabName = "" + protected var bottomTabIndex = -1 protected var defaultSelectedTab = -1 protected var lastSelectedPosition = 0 @@ -90,6 +92,7 @@ abstract class BaseTabWrapperFragment : BaseLazyFragment(), IMultiTab { multiTabNavName = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, "") ?: "" bottomTabName = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_NAME, "") ?: "" bottomTabId = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_ID, "") ?: "" + bottomTabIndex = arguments?.getInt(EntranceConsts.KEY_BOTTOM_TAB_INDEX, -1) ?: -1 } override fun onSaveInstanceState(outState: Bundle) { @@ -149,6 +152,13 @@ abstract class BaseTabWrapperFragment : BaseLazyFragment(), IMultiTab { } } + override fun onResume() { + super.onResume() + if (activity is IPageLevelProvider) { + (activity as IPageLevelProvider).getPageLevel().updateTopTabPosition(bottomTabIndex, lastSelectedPosition) + } + } + private fun showNoConnection(show: Boolean) { if (show) { if (noConnectionBinding == null) { @@ -405,6 +415,9 @@ abstract class BaseTabWrapperFragment : BaseLazyFragment(), IMultiTab { protected open fun onPageSelected(position: Int) { notifyChildFragmentLifecycle(position) + if (activity is IPageLevelProvider) { + (activity as IPageLevelProvider).getPageLevel().updateTopTabPosition(bottomTabIndex, position) + } } abstract fun onPageScrolled(position: Int, positionOffset: Float, tabEntityList: ArrayList) diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/MainFragmentStateAdapter.kt b/app/src/main/java/com/gh/gamecenter/wrapper/MainFragmentStateAdapter.kt index 1de9c57e6f..50d004b608 100644 --- a/app/src/main/java/com/gh/gamecenter/wrapper/MainFragmentStateAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/wrapper/MainFragmentStateAdapter.kt @@ -22,6 +22,7 @@ class MainFragmentStateAdapter(private val mFragment: Fragment) : BaseDiffFragme bundle.putBoolean(EntranceConsts.KEY_IS_HOME, true) bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_ID, data.id) bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_NAME, data.name) + bundle.putInt(EntranceConsts.KEY_BOTTOM_TAB_INDEX, position) bundle.putInt(EntranceConsts.KEY_POSITION, position) val exposureSourceList = arrayListOf(ExposureSource("底部tab", data.name)) diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperFragment.kt index 7db9e2ab24..c7ec805e57 100644 --- a/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperFragment.kt @@ -27,6 +27,7 @@ import com.gh.gamecenter.common.callback.OnDoubleTapListener import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.eventbus.EBReuse import com.gh.gamecenter.common.json.json +import com.gh.gamecenter.common.pagelevel.IPageLevelProvider import com.gh.gamecenter.common.syncpage.SyncPageRepository import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.DisplayUtils @@ -334,6 +335,18 @@ class MainWrapperFragment : BaseBottomTabFragment(), OnBa .into(mBottomTabBindingList[index].tabIv) } } + + // 更新当前页面的 PageLevel tab position 信息 + if (activity is IPageLevelProvider) { + // 更新底部 tab 的 position + (activity as IPageLevelProvider).getPageLevel().bottomTabPosition = position + + // 若选中的底部 tab 对应的 fragment 不为 BaseTabWrapperFragment,则清空顶部 tab 的 position 信息 + val currentFragment = childFragmentManager.findFragmentByTag(mAdapter.getFragmentTag(position)) + if (currentFragment !is BaseTabWrapperFragment) { + (activity as IPageLevelProvider).getPageLevel().updateTopTabPosition(position, -1) + } + } } private fun updateRealNameErrorContainer() { diff --git a/app/src/main/java/com/gh/vspace/VGameItemData.kt b/app/src/main/java/com/gh/vspace/VGameItemData.kt index fd92d4f310..e3e6f661ea 100644 --- a/app/src/main/java/com/gh/vspace/VGameItemData.kt +++ b/app/src/main/java/com/gh/vspace/VGameItemData.kt @@ -1,5 +1,6 @@ package com.gh.vspace +import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.manager.PackagesManager import com.lightgame.download.DownloadEntity import com.lightgame.download.DownloadStatus @@ -14,6 +15,10 @@ data class VGameItemData( var shouldShowDot: Boolean = false, var controlText: String = "继续" ) { + + // 绑定的曝光事件 + var exposureEvent: ExposureEvent? = null + fun refresh() { if (downloadEntity.status == DownloadStatus.done && VHelper.getLastPlayedTime(downloadEntity) == 0L diff --git a/app/src/main/java/com/gh/vspace/VHelper.kt b/app/src/main/java/com/gh/vspace/VHelper.kt index 348abab274..f260f68300 100644 --- a/app/src/main/java/com/gh/vspace/VHelper.kt +++ b/app/src/main/java/com/gh/vspace/VHelper.kt @@ -1883,10 +1883,7 @@ object VHelper { // 保存所有游戏标签 originDownloadEntity.tags = updateEntity.tagStyle.map { it.name } originDownloadEntity.addMetaExtra(Constants.RAW_GAME_ICON, updateEntity.rawIcon) - originDownloadEntity.addMetaExtra( - Constants.GAME_ICON_SUBSCRIPT, - updateEntity.iconSubscript - ) + originDownloadEntity.addMetaExtra(Constants.GAME_ICON_SUBSCRIPT, updateEntity.iconSubscript) originDownloadEntity.addMetaExtra(Constants.APK_MD5, updateEntity.md5) if (updateEntity.iconFloat != null) { originDownloadEntity.addMetaExtra( @@ -1915,6 +1912,7 @@ object VHelper { if (updateEntity == null) ExposureUtils.DownloadType.FUN_DOWNLOAD else ExposureUtils.DownloadType.FUN_UPDATE val gameEntity = GameEntity(originDownloadEntity.gameId, originDownloadEntity.name) gameEntity.gameVersion = originDownloadEntity.versionName ?: "" + gameEntity.adGroupId = originDownloadEntity.getMetaExtra(Constants.AD_GROUP_ID) val event = ExposureUtils.logADownloadExposureEvent( gameEntity, @@ -1953,6 +1951,8 @@ object VHelper { "last_page_name", GlobalActivityManager.getLastPageEntity().pageName, "last_page_id", GlobalActivityManager.getLastPageEntity().pageId, "last_page_business_id", GlobalActivityManager.getLastPageEntity().pageBusinessId, + "is_ad", if (TextUtils.isEmpty(gameEntity.adGroupId)) "false" else "true", + "ad_group_id", gameEntity.adGroupId, "download_type", "畅玩下载", ) } diff --git a/app/src/main/java/com/halo/assistant/HaloApp.java b/app/src/main/java/com/halo/assistant/HaloApp.java index 7acb50d164..26ffd4ffc9 100644 --- a/app/src/main/java/com/halo/assistant/HaloApp.java +++ b/app/src/main/java/com/halo/assistant/HaloApp.java @@ -17,6 +17,9 @@ import androidx.lifecycle.ProcessLifecycleOwner; import androidx.multidex.MultiDexApplication; import androidx.webkit.WebViewCompat; +import com.gh.common.util.AdHelper; +import com.gh.gamecenter.login.interceptor.LoginInterceptor; +import com.therouter.TheRouter; import com.facebook.imageformat.DefaultImageFormats; import com.facebook.imagepipeline.core.ImagePipelineConfig; import com.facebook.imagepipeline.core.ImagePipelineFactory; @@ -362,6 +365,8 @@ public class HaloApp extends MultiDexApplication { // 获取 settings 配置 com.gh.common.constant.Config.getGhzsSettings(); + AdHelper.getIdfa(BuildConfig.VERSION_NAME, getChannel()); + // 5 秒获取后台配置的获取应用列表限制接口结果超时后,回落到关闭状态 PackageHelper.INSTANCE.ignoreInstalledPackageApiSwitchAfterTimeout(5000L); diff --git a/app/src/main/res/layout/home_slide_card_item_custom.xml b/app/src/main/res/layout/home_slide_card_item_custom.xml index 84142bd2c4..5ecb41379a 100644 --- a/app/src/main/res/layout/home_slide_card_item_custom.xml +++ b/app/src/main/res/layout/home_slide_card_item_custom.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/GlobalActivityLifecycleObserver.kt b/module_common/src/main/java/com/gh/gamecenter/common/base/GlobalActivityLifecycleObserver.kt index 976abf959e..d27232e648 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/GlobalActivityLifecycleObserver.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/GlobalActivityLifecycleObserver.kt @@ -15,8 +15,8 @@ import org.greenrobot.eventbus.EventBus class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks { override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { - if (GlobalActivityManager.currentActivity?.javaClass?.simpleName == "MainActivity" || - GlobalActivityManager.currentActivity?.javaClass?.simpleName == "ToolbarWrapperActivity") { + if (GlobalActivityManager.currentActivity?.javaClass?.simpleName == "MainActivity" + || GlobalActivityManager.currentActivity?.javaClass?.simpleName == "ToolbarWrapperActivity") { EventBus.getDefault().post(EBReuse(Constants.FINISH_PULL_DOWN_PUSH)) } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java b/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java index 6b14939447..a26b7e6f96 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java @@ -36,6 +36,9 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.Lifecycle; +import com.gh.gamecenter.common.pagelevel.IPageLevelProvider; +import com.gh.gamecenter.common.pagelevel.PageLevel; +import com.gh.gamecenter.common.pagelevel.PageLevelManager; import com.gh.gamecenter.core.iinterface.ISplashScreen; import com.therouter.TheRouter; import com.blankj.utilcode.util.LanguageUtils; @@ -66,7 +69,6 @@ import com.gh.gamecenter.core.provider.IPackageInstallerProvider; import com.gh.gamecenter.core.provider.IQGameProvider; import com.gh.gamecenter.core.provider.IQuickLoginProvider; import com.gh.gamecenter.core.utils.DisplayUtils; -import com.gh.gamecenter.core.utils.MtaHelper; import com.gh.gamecenter.core.utils.SPUtils; import com.gh.gamecenter.core.utils.StringUtils; import com.lightgame.BaseAppCompatActivity; @@ -77,10 +79,12 @@ import com.tencent.tauth.Tencent; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import org.jetbrains.annotations.NotNull; import org.json.JSONObject; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -90,9 +94,9 @@ import kotlin.Pair; * 只提供基础的服务(EventBus/Share/GlobalDialog) *

* 需要工具栏的页面请继承{@link ToolBarActivity} + * */ - -public abstract class BaseActivity extends BaseAppCompatActivity implements IBusiness { +public abstract class BaseActivity extends BaseAppCompatActivity implements IBusiness, IPageLevelProvider { // global dialog key public final static String DOWNLOAD_HIJACK = "hijack"; @@ -103,6 +107,8 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements IBus public final static int ID_NIGHT_INDICATOR = 998; public final int MAX_BUNDLE_SIZE = 300; + protected PageLevel mPageLevel; + @NonNull protected String mEntrance; @@ -182,6 +188,8 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements IBus Utils.log("ACTIVITY_ENTRANCE -> " + mEntrance); } + initPageLevel(savedInstanceState); + // disableAutofill(); if (savedInstanceState != null) { @@ -238,6 +246,10 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements IBus super.onResume(); startPageTime = System.currentTimeMillis(); + if (mPageLevel != null) { + PageLevelManager.INSTANCE.setCurrentPageLevel(mPageLevel); + } + if (mIsDarkModeOn != DarkModeUtils.INSTANCE.isDarkModeOn(this)) { if (mShouldApplyNewUiModeOnNextResume) { // 返回时应用新的 config,否则后续 activity 更新时会无效 @@ -298,11 +310,6 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements IBus shareEntrance, id, callBack); - if (shareEntrance == ShareUtils.ShareEntrance.game || shareEntrance == ShareUtils.ShareEntrance.plugin) { - MtaHelper.onEvent("内容分享", "内容分享", shareTitle + shareSummary); - } else { - MtaHelper.onEvent("内容分享", "内容分享", shareTitle); - } } /** @@ -546,6 +553,8 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements IBus outState = discardFragmentFromSaveInstanceState(outState); } + outState.putParcelable(PageLevelManager.KEY_PAGE_LEVEL, mPageLevel); + long bundleSize = getBundleSize(outState); if (bundleSize > MAX_BUNDLE_SIZE * 1024) { outState.clear(); @@ -821,4 +830,82 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements IBus appProvider.initImageLoaderIfNeeded(); } } + + public void initPageLevel(@Nullable Bundle savedInstanceState) { + Bundle intentExtras = getIntent().getExtras(); + + // 是否作为顶级页面 (PageLevel.TYPE_F) + boolean asSupremePageLevel = false; + + if (intentExtras != null) { + asSupremePageLevel = PageLevelManager.INSTANCE.isCurrentActivityAsSupremePageLevel(); + if (asSupremePageLevel) { + PageLevelManager.INSTANCE.markSupremePageLevelIsUsed(); + } + } + + String currentPageLevelType = PageLevel.TYPE_P; + if (asSupremePageLevel) { + currentPageLevelType = PageLevel.TYPE_F; + } + + if (savedInstanceState != null) { + if (savedInstanceState.getParcelable(PageLevelManager.KEY_PAGE_LEVEL) != null) { + mPageLevel = savedInstanceState.getParcelable(PageLevelManager.KEY_PAGE_LEVEL); + } + } + + if (mPageLevel == null) { + if (asSupremePageLevel) { + mPageLevel = new PageLevel( + currentPageLevelType, + -1, + -1, + new HashMap<>(), + -1, + null); + } else { + PageLevel previousPageLevel = PageLevelManager.INSTANCE.getCurrentPageLevel(); + if (previousPageLevel != null) { + if (previousPageLevel.getInferiorPageCount() == -1) { + mPageLevel = new PageLevel( + currentPageLevelType, + -1, + -1, + previousPageLevel.getBottomTopTabPositionMap(), + 2, + previousPageLevel); + } else { + mPageLevel = new PageLevel( + currentPageLevelType, + -1, + -1, + previousPageLevel.getBottomTopTabPositionMap(), + previousPageLevel.getInferiorPageCount() + 1, + previousPageLevel.getSupremePageLevel()); + } + } else { + mPageLevel = new PageLevel( + currentPageLevelType, + -1, + -1, + new HashMap<>(), + 2, + null + ); + } + } + } + + Utils.log(PageLevelManager.TAG, mPageLevel); + Utils.log(PageLevelManager.TAG, mPageLevel.toFormatedString(false)); + + PageLevelManager.INSTANCE.setCurrentPageLevel(mPageLevel); + } + + @Override + public @NotNull PageLevel getPageLevel() { + return mPageLevel; + } + } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java index 10a57f3cf1..ff4ab47563 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java @@ -74,6 +74,8 @@ public class Constants { public static final String SIMULATOR = "simulator"; public static final String VGAME = "smooth_game"; // 畅玩类型的游戏 + public static final String AD_GROUP_ID = "ad_group_id"; + public static final String QQ_MINI_GAME = "qq"; public static final String WECHAT_MINI_GAME = "wechat"; public static final String WECHAT_MINI_GAME_CPM = "wechat_cpm"; diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java index 4bef37a44f..b968f51d59 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java @@ -337,6 +337,7 @@ public class EntranceConsts { public static final String KEY_TAB_NAME = "tab_name"; public static final String KEY_BOTTOM_TAB_ID = "bottom_tab_id"; public static final String KEY_BOTTOM_TAB_NAME = "bottom_tab_name"; + public static final String KEY_BOTTOM_TAB_INDEX = "bottom_tab_index"; public static final String KEY_SEARCH_BOX_PATTERN = "search_box_pattern"; public static final String KEY_GAME_COLUMN_NAME = "game_column_name"; public static final String KEY_GAME_COLUMN_ID = "game_column_id"; diff --git a/module_common/src/main/java/com/gh/gamecenter/common/entity/ExposureEntity.kt b/module_common/src/main/java/com/gh/gamecenter/common/entity/ExposureEntity.kt index 0ffb0aa1c1..5022e52f79 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/entity/ExposureEntity.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/entity/ExposureEntity.kt @@ -106,7 +106,44 @@ data class ExposureEntity( var redirectedUrlList: String? = null, // 由于有一些埋点数据来源于上层,上层定义的实体这里无法引用,这里将上层额外的字段存在 additional 数组中 - var additional: Array? = null + var additional: Array? = null, + + // 广告计划曝光字段补充 https://jira.shanqu.cc/browse/GHZSCY-7041 + + // 页面层级 + @SerializedName("page_level") + var pageLevel: String? = null, + // 当前页面代码名称 + @SerializedName("current_page_code") + val currentPageCode: String? = null, + // 当前页面ID + @SerializedName("current_page_id") + val currentPageId: String? = null, + // 组件类型 + @SerializedName("module_type") + val moduleType: String? = null, + // 组件样式 + @SerializedName("module_style") + val moduleStyle: String? = null, + // 组件名称 + @SerializedName("module_name") + val moduleName: String? = null, + // 组件ID + @SerializedName("module_id") + val moduleId: String? = null, + // 搜索内容 + @SerializedName("search_content") + var searchContent: String? = null, + // 是否广告推荐位 + @SerializedName("is_ad") + val isAd: Boolean = false, + // 广告组ID + @SerializedName("ad_group_id") + val adGroupId: String? = null, + // 后台设置的位置序号 + @SerializedName("backend_sequence") + val backendSequence: Int = -1, + ) : Parcelable { fun setContainerInfo(id: String?, type: String?) { diff --git a/module_common/src/main/java/com/gh/gamecenter/common/entity/IdfaData.kt b/module_common/src/main/java/com/gh/gamecenter/common/entity/IdfaData.kt new file mode 100644 index 0000000000..bf2ea0195e --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/entity/IdfaData.kt @@ -0,0 +1,5 @@ +package com.gh.gamecenter.common.entity + +class IdfaData { + var AD: String = "" +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/pagelevel/IPageLevelProvider.kt b/module_common/src/main/java/com/gh/gamecenter/common/pagelevel/IPageLevelProvider.kt new file mode 100644 index 0000000000..e4bb203656 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/pagelevel/IPageLevelProvider.kt @@ -0,0 +1,5 @@ +package com.gh.gamecenter.common.pagelevel + +interface IPageLevelProvider { + fun getPageLevel(): PageLevel +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/pagelevel/PageLevel.kt b/module_common/src/main/java/com/gh/gamecenter/common/pagelevel/PageLevel.kt new file mode 100644 index 0000000000..84dadcb062 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/pagelevel/PageLevel.kt @@ -0,0 +1,133 @@ +package com.gh.gamecenter.common.pagelevel + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * 页面级别 + * @param type 页面级别类型 (T: 顶级, F: 首次跳转的页面, P: 次级) + * @param bottomTabPosition 底部 tab 位置 (仅首页有值) + * @param topTabPosition 顶部 tab 位置 (仅包含多 tab 导航页时有值) + * @param bottomTopTabPositionMap 顶部 tab 与底部 tab 的映射关系 (当 bottomTabPosition 有值时,topTabPosition 的值应从这个 map 里取) + * @param inferiorPageCount 当前页面是第几个次级页面 (仅次级页面有值) + * @param supremePageLevel 顶级页面的 PageLevel (仅次级页面有值) + */ +@Parcelize +class PageLevel( + var type: String, + var bottomTabPosition: Int = -1, + private var topTabPosition: Int = -1, + var bottomTopTabPositionMap: HashMap = hashMapOf(), + var inferiorPageCount: Int = -1, + var supremePageLevel: PageLevel? = null +) : Parcelable { + + override fun toString(): String { + return toFormatedString() + } + + /** + * 获取与当前页面组合后的 PageLevel 字符串 + * 示例: T1(M1)-P2(M5) or F(M1)-P2(M5) + */ + fun toFormatedString(ignoreBottomTabPositionMap: Boolean = false): String { + val thisIsSupremePage = type == TYPE_T || type == TYPE_F + + var supremePageLevelString = "" + var inferiorPageLevelString = "" + + if (thisIsSupremePage) { + supremePageLevelString = getFormatedString(this, ignoreBottomTabPositionMap) + } else { + inferiorPageLevelString = getFormatedString(this) + if (this.supremePageLevel != null) { + supremePageLevelString = getFormatedString(this.supremePageLevel!!, ignoreBottomTabPositionMap) + } + } + + return if (inferiorPageLevelString.isNotEmpty()) { + "$supremePageLevelString-$inferiorPageLevelString" + } else { + supremePageLevelString + } + } + + /** + * 更新底部 tab 对应的顶部 tab 位置 + * @param bottomTabPosition 底部 tab 位置 (并不会更新实际的 bottomTabPosition!只是辅助更新 topTabPosition!) + * @param topTabPosition 顶部 tab 位置 + */ + fun updateTopTabPosition(bottomTabPosition: Int, topTabPosition: Int) { + if (bottomTabPosition == -1) { + this.topTabPosition = topTabPosition + } else { + this.bottomTopTabPositionMap.put(bottomTabPosition, topTabPosition) + } + } + + fun geTempNewPageLevel(bottomTabPosition: Int, topTabPosition: Int): PageLevel { + return PageLevel(type, bottomTabPosition, topTabPosition, bottomTopTabPositionMap, inferiorPageCount, null) + } + + companion object { + const val TYPE_T = "T" // 顶级 (MainActivity) + const val TYPE_F = "F" // 顶级 (后台配置首次跳转的页面) + const val TYPE_P = "P" // 次级 (所有非顶级的页面) + + private fun getFormatedString( + pageLevel: PageLevel, + ignoreBottomTabPositionMap: Boolean = false + ): String { + val thisHasBottomTab = pageLevel.bottomTabPosition != -1 + + if (!thisHasBottomTab) { + return getSinglePageLevelFormatedString( + pageLevel.type, + -1, + pageLevel.topTabPosition, + pageLevel.inferiorPageCount + ) + } else { + val topTabPosition = if (ignoreBottomTabPositionMap) { + pageLevel.topTabPosition + } else { + pageLevel.bottomTopTabPositionMap[pageLevel.bottomTabPosition] ?: -1 + } + + return getSinglePageLevelFormatedString( + pageLevel.type, + pageLevel.bottomTabPosition, + topTabPosition, + pageLevel.inferiorPageCount + ) + } + } + + private fun getSinglePageLevelFormatedString( + type: String, + bottomTabPosition: Int, + topTabPosition: Int, + inferiorPageCount: Int + ): String { + var formatedString = type + + if (type == TYPE_T || type == TYPE_F) { + if (bottomTabPosition != -1) { + formatedString = formatedString + "${getHumanReadablePosition(bottomTabPosition)}" + } + } else { + formatedString = formatedString + "$inferiorPageCount" + } + + if (topTabPosition != -1) { + formatedString = formatedString + "(M${getHumanReadablePosition(topTabPosition)})" + } + return formatedString + } + + private fun getHumanReadablePosition(position: Int): Int { + return position + 1 + } + } + +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/pagelevel/PageLevelManager.kt b/module_common/src/main/java/com/gh/gamecenter/common/pagelevel/PageLevelManager.kt new file mode 100644 index 0000000000..24d36d6f54 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/pagelevel/PageLevelManager.kt @@ -0,0 +1,33 @@ +package com.gh.gamecenter.common.pagelevel + +object PageLevelManager { + + const val TAG = "PageLevelManager" + const val KEY_PAGE_LEVEL = "page_level" + + // 当前页面的 PageLevel + private var currentPageLevel: PageLevel? = null + // 下一个 Activity 是否作为顶级页面 (F) + private var nextActivityAsSupremePageLevel = false + + fun isCurrentActivityAsSupremePageLevel(): Boolean { + return nextActivityAsSupremePageLevel + } + + fun markSupremePageLevelIsUsed() { + nextActivityAsSupremePageLevel = false + } + + fun setNextPageAsSupremePageLevel() { + nextActivityAsSupremePageLevel = true + } + + fun getCurrentPageLevel(): PageLevel? { + return currentPageLevel + } + + fun setCurrentPageLevel(pageLevel: PageLevel) { + currentPageLevel = pageLevel + } + +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt index 614e9d3b10..429d66daba 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt @@ -272,10 +272,21 @@ fun View.goneIf(predicate: Boolean, visibleCallback: (() -> Unit)? = null) { } } +/** + * view 在屏幕上的可见比例 + * @param percentage 取值范围 0~0.99 + */ +fun View.isVisibleOnScreenLargerThanPercentage(percentage: Float): Boolean { + val visibleRect = Rect() + getGlobalVisibleRect(visibleRect) + + return ((visibleRect.height() * visibleRect.width().toFloat()) / (height * width)) > percentage +} + fun View.addSelectableItemBackground() { val outValue = TypedValue() context.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true) - setBackgroundResource(outValue.resourceId); + setBackgroundResource(outValue.resourceId) } fun View.removeSelectableItemBackground() { diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt index f201707430..def1a4de9c 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt @@ -123,6 +123,8 @@ object SensorsBridge { private const val KEY_WECHAT_REMIND = "wechat_remind" private const val KEY_MESSAGE_REMIND = "message_remind" private const val KEY_AUTOMATIC_DOWNLOAD = "automatic_download" + private const val KEY_IS_AD = "is_ad" + private const val KEY_AD_GROUP_ID = "ad_group_id" private const val EVENT_GAME_DETAIL_PAGE_TAB_SELECT = "GameDetailPageTabSelect" private const val EVENT_GAME_DETAIL_PAGE_TAG_CLICK = "GameDetailPageGameTagClick" @@ -1985,6 +1987,8 @@ object SensorsBridge { * @param gameColumnName 游戏专题名称 * @param gameColumnId 游戏专题ID * @param text 点击位置 1.全部 2.游戏专题 + * @param adGroupId 广告组广告 id + * @param * @see EVENT_COLUMN_COLLECTION_CLICK */ @JvmStatic @@ -2011,6 +2015,7 @@ object SensorsBridge { linkType: String = "", linkId: String = "", linkText: String = "", + adGroupId: String = "" ) { val json = json { KEY_LOCATION to location @@ -2037,6 +2042,8 @@ object SensorsBridge { KEY_LINK_ID to linkId KEY_LINK_TYPE to linkType KEY_LINK_TEXT to linkText + KEY_IS_AD to adGroupId.isNotEmpty() + KEY_AD_GROUP_ID to adGroupId } trackEvent(EVENT_COLUMN_COLLECTION_CLICK, json) @@ -3272,7 +3279,8 @@ object SensorsBridge { location: String = "", columnPattern: String = "", text: String = "", - buttonType: String = "" + buttonType: String = "", + adGroupId: String, ) { val json = json { KEY_BOTTOM_TAB to bottomTab @@ -3295,6 +3303,8 @@ object SensorsBridge { KEY_COLUMN_PATTERN to columnPattern KEY_TEXT to text KEY_BUTTON_TYPE to buttonType + KEY_IS_AD to adGroupId.isNotEmpty() + KEY_AD_GROUP_ID to adGroupId } trackEvent(EVENT_COLUMN_CLICK, json) } @@ -4510,7 +4520,8 @@ object SensorsBridge { gameId: String, gameName: String, gameType: String, - position: Int + position: Int, + adGroupId: String ) { val json = json { KEY_PAGE_ID to pageId @@ -4522,6 +4533,8 @@ object SensorsBridge { KEY_GAME_NAME to gameName KEY_GAME_TYPE to gameType KEY_POSITION to position + KEY_IS_AD to adGroupId.isNotEmpty() + KEY_AD_GROUP_ID to adGroupId } trackEvent(EVENT_GAME_SEARCH_RESULT_CLICK, json) } diff --git a/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt b/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt index 5071e1d0b2..face06a797 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/AppExecutor.kt @@ -54,7 +54,7 @@ object AppExecutor { CORE_POOL_SIZE, MAX_POOL_SIZE, 20L, TimeUnit.SECONDS, - LinkedBlockingQueue(256), + LinkedBlockingQueue(12), GHThreadFactory("GH_IO_THREAD") ) } diff --git a/module_core/src/main/java/com/gh/gamecenter/core/utils/GsonUtils.kt b/module_core/src/main/java/com/gh/gamecenter/core/utils/GsonUtils.kt index aa5de7f22a..372bb7b81f 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/utils/GsonUtils.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/utils/GsonUtils.kt @@ -14,7 +14,11 @@ object GsonUtils { @JvmStatic val gson by lazy { Gson() } - val gsonThatIgnoreNull by lazy { GsonBuilder().serializeNulls().create() } + val gsonThatIgnoreNull by lazy { + GsonBuilder() + .serializeNulls() + .create() + } @JvmStatic fun fromJson(json: String, t: Class): T { diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/CustomPageTrackData.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/CustomPageTrackData.kt index 9b73ce6615..6be628c8dd 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/CustomPageTrackData.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/CustomPageTrackData.kt @@ -7,6 +7,8 @@ import kotlinx.parcelize.Parcelize @Parcelize data class PageLocation( val bottomTab: String = "", + val bottomTabIndex: Int = -1, + val pageLevelString: String = "", val severalTabPageName: String = "", val severalTabPageId: String = "", val tabPosition: Int = 0, diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt index 45e9c0fc86..c94cba6e03 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt @@ -7,6 +7,7 @@ import com.gh.gamecenter.common.entity.CommunityEntity import com.gh.gamecenter.common.entity.Display import com.gh.gamecenter.common.entity.IconFloat import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.core.provider.IAppProvider import com.gh.gamecenter.feature.HaloApp import com.gh.gamecenter.feature.exposure.ExposureEvent @@ -391,7 +392,13 @@ data class GameEntity( //游戏详情大家都在玩推荐类型:标签推荐/下载推荐/安装推荐,用于曝光使用 var recommendType: String = "", @SerializedName("open_test") - var openTest: OpenTest? = null + var openTest: OpenTest? = null, + // 广告组 id (存在且不为空表示该游戏为广告游戏) + @SerializedName("ad_group_id") + var adGroupId: String = "", + // 后台设置的位置序号 + @SerializedName("backend_sequence") + var backendSequence: Int = -1, ) : Parcelable { constructor(id: String?) : this() { @@ -407,6 +414,10 @@ data class GameEntity( @IgnoredOnParcel var customPageTrackData: CustomPageTrackData? = null + // 曝光来源,用于埋点 + @IgnoredOnParcel + var exposureSource: List? = null + // 临时变量 @IgnoredOnParcel var hasBubbleShowed = false @@ -674,6 +685,13 @@ data class GameEntity( val wifiAutoDownload get() = _wifiAutoDownload ?: false + // 广告游戏填充日志是否已经上报 + @IgnoredOnParcel + var isAdRequestReported = false + + @IgnoredOnParcel + var pageLevelString: String = "" + // 游戏是否有 wifi自动下载功能 val wifiAutoDownloadEnable: Boolean get() = _wifiAutoDownload != null @@ -996,6 +1014,12 @@ data class GameEntity( return provider?.shouldThisGameShowSpecialDownload(_id) == true } + fun updateBoundedExposureEventIfNeeded() { + if (exposureEvent == null || exposureEvent?.isUsed() == true) { + exposureEvent = ExposureEvent.createEvent(this, exposureSource ?: arrayListOf()) + } + } + /** * 是否为海外地址下载游戏 */ diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/ExposurableCardView.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/ExposurableCardView.kt new file mode 100644 index 0000000000..5302672973 --- /dev/null +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/ExposurableCardView.kt @@ -0,0 +1,28 @@ +package com.gh.gamecenter.feature.exposure + +import android.content.Context +import android.util.AttributeSet +import androidx.cardview.widget.CardView + +class ExposurableCardView: CardView, IExposureListProvider { + + private var boundedExposureEventList: List? = null + + constructor(context: Context) : this(context, null) + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + fun setExposureEventList(exposureEventList: List) { + boundedExposureEventList = exposureEventList + } + + override fun provideExposureData(): ExposureEvent? = null + + override fun provideExposureDataList(): List? { + boundedExposureEventList?.forEach { + it.getRefreshedExposureEvent() + } + return boundedExposureEventList + } + +} \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/ExposureEvent.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/ExposureEvent.kt index 256cbac77d..46e626c64f 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/ExposureEvent.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/ExposureEvent.kt @@ -1,22 +1,23 @@ package com.gh.gamecenter.feature.exposure import android.os.Parcelable +import android.text.TextUtils import androidx.annotation.Keep -import androidx.room.PrimaryKey import com.therouter.TheRouter import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.entity.ExposureEntity import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.exposure.meta.Meta import com.gh.gamecenter.common.exposure.meta.MetaUtil +import com.gh.gamecenter.common.pagelevel.IPageLevelProvider +import com.gh.gamecenter.common.tracker.IBusiness import com.gh.gamecenter.common.utils.getFirstElementDividedByDivider import com.gh.gamecenter.core.provider.IBrowserInstallHelperProvider +import com.gh.gamecenter.core.utils.CurrentActivityHolder import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.time.TimeUtil import com.gh.gamecenter.feature.provider.IRegionSettingHelperProvider import com.lightgame.download.DownloadEntity -import com.lightgame.utils.Utils import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize import java.util.* @@ -31,28 +32,34 @@ data class ExposureEvent( val meta: Meta = MetaUtil.getMeta(), var time: Int = TimeUtil.currentTime(), var timeInMillisecond: Long = System.currentTimeMillis(), - @PrimaryKey var id: String = UUID.randomUUID().toString(), ) : Parcelable, Cloneable { - @Transient - @IgnoredOnParcel - var next: ExposureEvent? = null - - @Transient - @IgnoredOnParcel - var flags = 0 - // 一些未在 ExposureEntity 定义的字段 val additional: Array get() = eTrace?.firstOrNull()?.payload?.additional ?: payload.additional ?: emptyArray() + // 是否已经被使用过 + @IgnoredOnParcel + private var isUsed: Boolean = false + private fun initExposureEventData( gameEntity: GameEntity?, source: List, eTrace: List? = null, event: ExposureType = ExposureType.EXPOSURE ) { + val currentActivity = CurrentActivityHolder.getCurrentActivity() + val currentActivitySimpleName = if (currentActivity != null) currentActivity::class.simpleName else "unknown" + val businessId = if (currentActivity is IBusiness) currentActivity.getBusinessId() else null + + var pageLevelString = gameEntity?.pageLevelString + + if (TextUtils.isEmpty(pageLevelString)) { + val pageLevelProvider = currentActivity as? IPageLevelProvider + pageLevelString = pageLevelProvider?.getPageLevel()?.toFormatedString() + } + if (gameEntity?.adIconActive == true) { gameEntity.isAdData = true } @@ -114,7 +121,20 @@ data class ExposureEvent( miniGameId = gameEntity?.miniGameAppId ?: "", miniGameType = gameEntity?.miniGameType ?: "", miniGameRecommendId = gameEntity?.miniGameRecommendId, - additional = gameEntity?.customPageTrackData?.toKV() + + additional = gameEntity?.customPageTrackData?.toKV(), + + pageLevel = pageLevelString ?: firstTracePayload?.pageLevel ?: "", + currentPageCode = currentActivitySimpleName, + currentPageId = businessId?.first ?: firstTracePayload?.currentPageId ?: "", + moduleStyle = gameEntity?.customPageTrackData?.modulePattern ?: firstTracePayload?.moduleStyle ?: "", + moduleType = gameEntity?.customPageTrackData?.moduleType ?: firstTracePayload?.moduleType ?: "", + moduleName = gameEntity?.customPageTrackData?.linkContentName ?: firstTracePayload?.moduleName ?: "", + moduleId = gameEntity?.customPageTrackData?.linkContentId ?: firstTracePayload?.moduleId ?: "", + searchContent = firstTracePayload?.searchContent ?: "", + isAd = !TextUtils.isEmpty(gameEntity?.adGroupId ?: firstTracePayload?.adGroupId), + adGroupId = gameEntity?.adGroupId ?: firstTracePayload?.adGroupId, + backendSequence = gameEntity?.backendSequence ?: firstTracePayload?.backendSequence ?: -1, ) this.id = UUID.randomUUID().toString() this.timeInMillisecond = System.currentTimeMillis() @@ -140,45 +160,59 @@ data class ExposureEvent( } } - fun recycle() { - if (inUse()) { - Utils.log( - EXPOSURE_EVENT_LOG_TAG, - "This ExposureEvent $this cannot be recycled because it is still in use." - ) - return - } - recycleUnchecked() - } - - private fun recycleUnchecked() { - this.flags = FLAG_IN_USE - synchronized(sPoolSync) { - if (sPoolSize < EXPOSURE_EVENT_MAX_POOL_SIZE) { - next = sPool - sPool = this - sPoolSize++ - Utils.log(EXPOSURE_EVENT_LOG_TAG, "This ExposureEvent $this Recycled") - } - } - } - - private fun inUse(): Boolean { - return flags == FLAG_IN_USE - } - - fun deepCopy(): ExposureEvent { + fun shallowCopy(): ExposureEvent { return super.clone() as ExposureEvent } - companion object { - private val sPoolSync = Any() - private var sPool: ExposureEvent? = null - private var sPoolSize: Int = 0 + /** + * 刷新曝光 ID 和曝光时间 + */ + private fun refresh() { + this.id = UUID.randomUUID().toString() + this.timeInMillisecond = System.currentTimeMillis() + this.time = TimeUtil.currentTime() + this.isUsed = false + } - private const val EXPOSURE_EVENT_MAX_POOL_SIZE: Int = 300 - private const val FLAG_IN_USE = 1 - private const val EXPOSURE_EVENT_LOG_TAG = "ExposureEvent" + /** + * 获取一个刷新曝光 ID 和曝光时间后的曝光事件 + */ + fun getRefreshedExposureEvent(): ExposureEvent { + if (isUsed) { + refresh() + } + return this + } + + /** + * 曝光时间 + */ + fun refreshTime() { + this.timeInMillisecond = System.currentTimeMillis() + this.time = TimeUtil.currentTime() + } + + fun markIsUsed() { + this.isUsed = true + } + + fun isUsed(): Boolean { + return isUsed + } + + override fun equals(other: Any?): Boolean { + return if (other is ExposureEvent) { + this.id == other.id + } else { + false + } + } + + override fun hashCode(): Int { + return super.hashCode() + } + + companion object { @JvmStatic fun createEvent( @@ -187,25 +221,6 @@ data class ExposureEvent( eTrace: List? = null, event: ExposureType = ExposureType.EXPOSURE ): ExposureEvent { -// synchronized(sPoolSync) { -// if (sPool != null) { -// val exposureEvent = sPool -// sPool = exposureEvent?.next -// exposureEvent?.next = null -// exposureEvent?.flags = 0 -// sPoolSize-- -// exposureEvent?.initExposureEventData(gameEntity, source, eTrace, event) -// Utils.log( -// EXPOSURE_EVENT_LOG_TAG, -// "ExposureEvent 对象 $this 正在被循环使用, 当前对象池数量还有 $sPoolSize" -// ) -// return exposureEvent!! -// } -// } -// Utils.log( -// EXPOSURE_EVENT_LOG_TAG, -// "new ExposureEvent 对象" -// ) val exposureEvent = ExposureEvent( payload = ExposureEntity(), source = source, @@ -230,6 +245,5 @@ data class ExposureEvent( } return createEvent(gameEntity, concatSourceList, eTrace, event) } - } } \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/IExposureListProvider.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/IExposureListProvider.kt new file mode 100644 index 0000000000..2aa574615f --- /dev/null +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/IExposureListProvider.kt @@ -0,0 +1,5 @@ +package com.gh.gamecenter.feature.exposure + +interface IExposureListProvider: IExposureProvider { + fun provideExposureDataList(): List? +} \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/IExposureProvider.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/IExposureProvider.kt new file mode 100644 index 0000000000..4b6f941a94 --- /dev/null +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/IExposureProvider.kt @@ -0,0 +1,5 @@ +package com.gh.gamecenter.feature.exposure + +interface IExposureProvider { + fun provideExposureData(): ExposureEvent? +} \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/IExposureStateChangeListener.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/IExposureStateChangeListener.kt new file mode 100644 index 0000000000..537a4c3a47 --- /dev/null +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/IExposureStateChangeListener.kt @@ -0,0 +1,18 @@ +package com.gh.gamecenter.feature.exposure + +/** + * item曝光状态变更监听器 + */ +interface IExposureStateChangeListener { + /** + * 曝光状态变更 + * @param exposureEvent 当前位置绑定的曝光数据 + * @param position 位置 + * @param inExposure true从非曝光状态转为曝光状态,false从曝光状态转为非曝光状态 + */ + fun onExposureStateChange( + exposureEvent: ExposureEvent, + position: Int, + inExposure: Boolean + ) +} \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/RecyclerViewExposureHelper.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/RecyclerViewExposureHelper.kt new file mode 100644 index 0000000000..90d6f81dcf --- /dev/null +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/RecyclerViewExposureHelper.kt @@ -0,0 +1,441 @@ +package com.gh.gamecenter.feature.exposure + +import android.view.View +import android.view.ViewGroup +import android.view.ViewTreeObserver +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.StaggeredGridLayoutManager +import com.gh.gamecenter.common.utils.isVisibleOnScreenLargerThanPercentage +import com.gh.gamecenter.common.view.stacklayoutmanager.StackLayoutManager +import com.gh.gamecenter.common.view.stacklayoutmanager2.StackLayoutManagerV2 +import com.gh.gamecenter.feature.exposure.model.InExposureData +import com.gh.gamecenter.feature.exposure.model.VisibleItemPositionRange +import com.lightgame.utils.Utils +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.subjects.BehaviorSubject +import java.util.concurrent.TimeUnit + +/** + * ViewHolder (或者 View) 需实现 IExposureProvider 接口来提供曝光实体,曝光实体的曝光时间会在实际成为有效曝光的时候进行刷新 + * @see com.gh.gamecenter.feature.exposure.IExposureProvider + * + * 曝光实体的收集: + * 1. 通过 RecyclerView.layoutManager 获取当前可见的首个和最后一个 item 的 position + * 2. 计算首个和最后一个 item 在屏幕上显示的比例,若比例小于指定的比例可不进行曝光收集,并且修正首个和最后一个 item 的 position + * 3. 遍历首个和最后一个 item 的 position,对 View 层级递归查找,尝试找到每个 position 对应的 item 里首个满足一定可见比例且实现了 IExposureProvider 的实体 + * 4. 记录有效的曝光实体 + * + * 曝光实体的收集时机: + * 1. lifecycleOwner 生命周期感知,当 lifecycleOwner 生命周期变为可见时,自动触发曝光收集;当 lifecycleOwner 生命周期变为不可见时,自动触发结束曝光 + * 2. RecyclerView 滚动时,触发曝光收集的信号 + * 3. 内嵌 RecyclerView 滚动时调用 onScroll 方法,触发曝光收集的信号 + * + * 曝光实体的收集频率: + * 通过 BehaviorSubject.create().throttleLatest(SAMPLE_RATE, TimeUnit.MILLISECONDS) 控制曝光收集的频率 + * + * 曝光实体的比较: + * 存在一个处于曝光中的 item 集合 + * 当新的曝光实体被收集时,会与处于曝光中的 item 集合进行比较,若新的曝光实体不在处于曝光中的 item 集合中,则代表是新的曝光实体,需要触发开始曝光 + * 当处于曝光中的 item 集合与新的曝光实体集合进行比较,若处于曝光中的 item 集合中的 item 不在新的曝光实体集合中,则代表是结束曝光 + * + * 曝光实体的处理: + * 当结束曝光时间 - 开始曝光时间 > 指定的曝光时长,更新曝光实体的状态为已使用,并上报曝光事件 + * + * @constructor 创建一个 RecyclerView 曝光收集实例,必传三个参数 + * @param recyclerView 需要收集曝光的 RecyclerView + * @param lifecycleOwner RecyclerView 感知此生命周期组件,根据生命周期感知RV可见性,以便自动处理开始曝光和结束曝光 + * @param exposureStateChangeListener 曝光状态改变监听器 + * + * @author 原始作者 wp529 https://github.com/wp529/Exposure + */ +class RecyclerViewExposureHelper( + private val recyclerView: RecyclerView, + private val lifecycleOwner: LifecycleOwner?, + private val exposureStateChangeListener: IExposureStateChangeListener +) : RecyclerView.OnScrollListener() { + // 处于曝光中的Item数据集合 + private val inExposureDataList = ArrayList>() + + // 是否可见,不可见的状态就不触发收集了。主要是为了避免RV处于滚动惯性中然后退出后台导致收集异常 + private var visible = true + + private val collectExposureSubject = BehaviorSubject.create() + private val compositeDisposable = CompositeDisposable() + + init { + val disposable = collectExposureSubject + .throttleLatest(SAMPLE_RATE, TimeUnit.MILLISECONDS) + .subscribeOn(AndroidSchedulers.mainThread()) + .subscribe({ + collectExposureData() + }, { + Utils.log(TAG, "曝光收集异常: $it") + }) + compositeDisposable.add(disposable) + + recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (visible) { + recyclerView.post { + sendCollectExposureDataSignal() + } + } + } + }) + val adapter = recyclerView.adapter + ?: throw IllegalStateException("在初始化RecyclerViewExposureHelper之前,RecyclerView必须已经设置好了adapter") + adapter.registerAdapterDataObserver( + object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + Utils.log(TAG, "adapter的item有改变") + if (adapter.itemCount == 0) { + endExposure() + return + } + // item改变,触发重新收集 + recyclerView.viewTreeObserver.addOnGlobalLayoutListener( + object : ViewTreeObserver.OnGlobalLayoutListener { + override fun onGlobalLayout() { + // 绑定的曝光数据刷新完毕后才重新收集 + recyclerView.viewTreeObserver.removeOnGlobalLayoutListener(this) + val itemAnimator = recyclerView.itemAnimator + if (itemAnimator == null) { + recyclerView.post { + sendCollectExposureDataSignal() + } + } else { + itemAnimator.isRunning { + recyclerView.post { + sendCollectExposureDataSignal() + } + } + } + } + } + ) + } + + override fun onItemRangeChanged(positionStart: Int, itemCount: Int) { + onChanged() + } + + override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { + onChanged() + } + + override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) { + onChanged() + } + + override fun onItemRangeMoved( + fromPosition: Int, + toPosition: Int, + itemCount: Int + ) { + onChanged() + } + } + ) + + // 感知生命周期 + lifecycleOwner?.lifecycle?.addObserver(object : DefaultLifecycleObserver { + override fun onResume(owner: LifecycleOwner) { + onVisible() + } + + override fun onPause(owner: LifecycleOwner) { + onInvisible() + } + + override fun onDestroy(owner: LifecycleOwner) { + compositeDisposable.clear() + } + }) + } + + /** + * 若传递了生命周期组件,那么常规场景下RecyclerViewExposureHelper会自动感知RV变更为可见状态并调用此方法。 + * 但可能实际的业务场景会出现非常规情况,所以仍可让外部告知RV处于可见状态以便触发曝光 + */ + fun onVisible() { + Utils.log(TAG, "外部告知RecyclerView可见了") + visible = true + recyclerView.post { + collectExposureData() + } + } + + /** + * 若传递了生命周期组件,那么常规场景下RecyclerViewExposureHelper会自动感知RV变更为可见状态并调用此方法。 + * 但可能实际的业务场景会出现非常规情况,所以仍可让外部告知RV处于不可见状态以便触发结束曝光 + */ + fun onInvisible() { + Utils.log(TAG, "外部告知RecyclerView不可见了") + visible = false + endExposure() + } + + /** + * 一般用于RecyclerView被嵌套在可滚动布局中(eg:ScrollView,NestedScrollView,RecyclerView等),导致RecyclerViewExposureHelper持有的RV不能响应滑动的情况,就必须由外部告知RV被滚动了触发曝光收集 + * 虽然Google强烈建议不能把RecyclerView嵌套在滚动布局中,但在实际开发中仍然存在复杂的业务逻辑导致难以避免这样的用法,故提供此方法由外部调用 + */ + fun onScroll() { + if (visible) { + recyclerView.post { + sendCollectExposureDataSignal() + } + } + } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + + onScroll() + } + + private fun sendCollectExposureDataSignal() { + collectExposureSubject.onNext(Any()) + } + + // 收集开始曝光和结束曝光的数据 + private fun collectExposureData() { + val startTime = System.currentTimeMillis() + val layoutManager = recyclerView.layoutManager ?: return + val visibleItemPositionRange = getVisibleItemPositionRange(layoutManager) ?: return + val visiblePositions = IntRange( + visibleItemPositionRange.firstVisibleItemPosition, + visibleItemPositionRange.lastVisibleItemPosition + ) + Utils.log(TAG, "当前可见的position范围: $visiblePositions") + // 当前所有可见的曝光中的数据 + val currentVisibleExposureEventList = ArrayList>() + for (visiblePosition in visiblePositions) { + val visiblePositionExposureEventList = getExposureDataListByPosition(visiblePosition) ?: continue + currentVisibleExposureEventList.addAll(visiblePositionExposureEventList) + visiblePositionExposureEventList.forEach { visiblePositionExposureEvent -> + if (visiblePositionExposureEvent !in inExposureDataList) { + // 刷新曝光时间 + visiblePositionExposureEvent.data.refreshTime() + // 当前可见位置不在曝光中集合,代表是从不可见变为可见,那么此 position 开始曝光 + inExposureDataList.add(visiblePositionExposureEvent) + invokeExposureStateChange( + visiblePositionExposureEvent.data, + visiblePosition, + true + ) + } + } + } + inExposureDataList.filter { inExposureData -> + // 过滤出处于曝光中的 position 在当前扫描的时候不再处于可见位了 + inExposureData !in currentVisibleExposureEventList + }.also { + // 更改为结束曝光状态 + it.forEach { inExposureData -> + invokeExposureStateChange(inExposureData.data, inExposureData.position, false) + } + // 移除出正在曝光中的集合 + inExposureDataList.removeAll(it) + } + + Utils.log(TAG, "曝光收集耗时: ${System.currentTimeMillis() - startTime}毫秒") + } + + // 第一个可见 position 和最后一个可见 position + private fun getVisibleItemPositionRange(layoutManager: RecyclerView.LayoutManager): VisibleItemPositionRange? { + val visibleItemPositionRange = when (layoutManager) { + is LinearLayoutManager -> { + var firstVisiblePosition = layoutManager.findFirstVisibleItemPosition() + var lastVisiblePosition = layoutManager.findLastVisibleItemPosition() + + // 校验首个和最后一个可见的 item 是否真的可见 (大于 20 % 的区域显示在屏幕上 ),不可见则修正 + if (lastVisiblePosition - firstVisiblePosition > 1) { + val firstVisibleItemView = recyclerView.getChildAt(firstVisiblePosition) + if (firstVisibleItemView != null + && !firstVisibleItemView.isVisibleOnScreenLargerThanPercentage(0.2F) + ) { + firstVisiblePosition = firstVisiblePosition + 1 + } + + val lastVisibleItemView = recyclerView.getChildAt(lastVisiblePosition) + if (lastVisibleItemView != null + && !lastVisibleItemView.isVisibleOnScreenLargerThanPercentage(0.2F) + ) { + lastVisiblePosition = lastVisiblePosition - 1 + } + } + VisibleItemPositionRange(firstVisiblePosition, lastVisiblePosition) + } + + is StaggeredGridLayoutManager -> { + val firstVisibleItemPosition = layoutManager.let { + val visiblePositionArray = IntArray(it.spanCount) + it.findFirstVisibleItemPositions(visiblePositionArray) + visiblePositionArray.min() + } + val lastVisibleItemPosition = layoutManager.let { + val visiblePositionArray = IntArray(it.spanCount) + it.findLastVisibleItemPositions(visiblePositionArray) + visiblePositionArray.max() + } + VisibleItemPositionRange(firstVisibleItemPosition, lastVisibleItemPosition) + } + + else -> null + } + return if (visibleItemPositionRange == null + || visibleItemPositionRange.firstVisibleItemPosition < 0 + || visibleItemPositionRange.lastVisibleItemPosition < 0 + ) { + null + } else { + visibleItemPositionRange + } + } + + // 获取 position 绑定的曝光数据 + private fun getExposureDataListByPosition(position: Int): List>? { +// // 获取 PageLevel 信息 (覆写在 ExposureEvent.create 时填充的 PageLevel,避免因为线程不一样导致的数据异常) +// val pageLevelString = if ((recyclerView.context) is IPageLevelProvider) { +// (recyclerView.context as IPageLevelProvider).getPageLevel().toFormatedString() +// } else { +// "" +// } + val provideExposureDataViewList = + findAllExposureProviderAndAddOnScrollListener(recyclerView.layoutManager?.findViewByPosition(position)) + if (provideExposureDataViewList.isNullOrEmpty()) { + Utils.log( + TAG, + "position 为 $position 的 ItemView 没有实现 IExposureProvider 接口,无法处理曝光" + ) + return null + } + val inExposureDataListResult = ArrayList>() + @Suppress("UNCHECKED_CAST") + provideExposureDataViewList.forEach { + // 若实现了 IExposureListProvider 接口,则调用 provideExposureDataList 方法获取曝光数据集合 + if (it is IExposureListProvider) { + val exposureEventList = it.provideExposureDataList() + if (exposureEventList != null) { + for (exposureEvent in exposureEventList) { +// // 附加 PageLevel 信息 +// exposureEvent.payload.pageLevel = pageLevelString + // 添加到队列里 + inExposureDataListResult.add(InExposureData(exposureEvent, position)) + } + } + } else { + // 若实现了 IExposureProvider 接口,则调用 provideExposureData 方法获取曝光数据 + val exposureEvent = it.provideExposureData() + if (exposureEvent != null) { +// // 附加 PageLevel 信息 +// exposureEvent.payload.pageLevel = pageLevelString + // 添加到队列里 + inExposureDataListResult.add(InExposureData(exposureEvent, position)) + } + } + } + return inExposureDataListResult + } + + /** + * 获取当前 ViewGroup 节点下所有绑定了曝光数据的集合,当该层 View 有支持曝光的实现,会停止继续递归往下查找 + **/ + private fun findAllExposureProviderAndAddOnScrollListener(rootView: View?): List { + val currentVisibleExposureProviderList = ArrayList() + rootView ?: return currentVisibleExposureProviderList + + val parentRecyclerView = rootView.parent as? RecyclerView + val isUsingStackLayoutManager = if (parentRecyclerView != null) { + parentRecyclerView.layoutManager is StackLayoutManagerV2 + || parentRecyclerView.layoutManager is StackLayoutManager + } else { + false + } + val containingViewHolder = if (parentRecyclerView != null) { + // 为子 recyclerView 添加 scrollListener + if (rootView.parent != recyclerView) { + parentRecyclerView.removeOnScrollListener(this) + parentRecyclerView.addOnScrollListener(this) + } + parentRecyclerView.findContainingViewHolder(rootView) + } else { + null + } + + if (containingViewHolder is IExposureProvider + && rootView.isVisibleOnScreenLargerThanPercentage(VALID_VISIBLE_RECT_PERCENTAGE) + ) { + // 当 layoutManager 为 StackLayoutManager 的时候,仅 scale 大于 0.95 的 item 才曝光 + if (isUsingStackLayoutManager && rootView.scaleX < 0.95) { + return currentVisibleExposureProviderList + } + + currentVisibleExposureProviderList.add(containingViewHolder) + // 当前 ViewGroup 节点已经支持统计曝光了,那么就不需要再向下找了 + return currentVisibleExposureProviderList + } + + if (rootView is IExposureProvider + && rootView.isVisibleOnScreenLargerThanPercentage(VALID_VISIBLE_RECT_PERCENTAGE) + ) { + currentVisibleExposureProviderList.add(rootView) + // 当前 ViewGroup 节点已经支持统计曝光了,那么就不需要再向下找了 + return currentVisibleExposureProviderList + } + + if (rootView !is ViewGroup) { + return currentVisibleExposureProviderList + } + + repeat(rootView.childCount) { + currentVisibleExposureProviderList.addAll( + findAllExposureProviderAndAddOnScrollListener( + rootView.getChildAt(it) + ) + ) + } + return currentVisibleExposureProviderList + } + + // 将处于曝光的 item 全部结束曝光 + private fun endExposure() { + inExposureDataList.onEach { inExposureData -> + // 当前 position 绑定了曝光数据,回调结束曝光 + invokeExposureStateChange(inExposureData.data, inExposureData.position, false) + }.also { + // 清空曝光中 position 集合 + it.clear() + } + } + + // 回调曝光状态改变 + private fun invokeExposureStateChange( + data: ExposureEvent, + position: Int, + inExposure: Boolean + ) { + exposureStateChangeListener.onExposureStateChange( + exposureEvent = data, + position = position, + inExposure = inExposure + ) + } + + companion object { + const val TAG = "RecyclerViewExposureHelper" + + const val VALID_VISIBLE_RECT_PERCENTAGE = 0.7F + const val VALID_EXPOSURE_THRESHOLD = 1000L + const val SAMPLE_RATE = 100L + } +} + +fun RecyclerView.addExposureHelper(lifecycleOwner: LifecycleOwner?, exposureStateChangeListener: IExposureStateChangeListener) { + RecyclerViewExposureHelper(this, lifecycleOwner, exposureStateChangeListener) +} \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/model/InExposureData.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/model/InExposureData.kt new file mode 100644 index 0000000000..e401c2dfed --- /dev/null +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/model/InExposureData.kt @@ -0,0 +1,22 @@ +package com.gh.gamecenter.feature.exposure.model + +import androidx.annotation.Keep + +/** + * 处于曝光中的数据 + */ +@Keep +data class InExposureData( + val data: T, + val position: Int +) { + override fun equals(other: Any?): Boolean { + return (other is InExposureData<*>) && (data == other.data) + } + + override fun hashCode(): Int { + var result = data?.hashCode() ?: 0 + result = 31 * result + position + return result + } +} \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/model/VisibleItemPositionRange.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/model/VisibleItemPositionRange.kt new file mode 100644 index 0000000000..3af03fe513 --- /dev/null +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/exposure/model/VisibleItemPositionRange.kt @@ -0,0 +1,9 @@ +package com.gh.gamecenter.feature.exposure.model + +/** + * 可见item的position区间 + */ +data class VisibleItemPositionRange( + val firstVisibleItemPosition: Int, + val lastVisibleItemPosition: Int +) \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/game/GameItemViewHolder.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/game/GameItemViewHolder.kt index 5385f122c2..cf3b905584 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/game/GameItemViewHolder.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/game/GameItemViewHolder.kt @@ -15,13 +15,18 @@ import com.gh.gamecenter.common.view.DrawableView import com.gh.gamecenter.feature.R import com.gh.gamecenter.feature.databinding.GameItemBinding import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.IExposureProvider import com.gh.gamecenter.feature.minigame.MiniGameItemHelper import com.gh.gamecenter.feature.provider.IBindingAdaptersProvider /** * 正常游戏 */ -class GameItemViewHolder(var binding: GameItemBinding) : BaseRecyclerViewHolder(binding.root) { +class GameItemViewHolder(var binding: GameItemBinding) : BaseRecyclerViewHolder(binding.root), IExposureProvider { + + private var boundedGameEntity: GameEntity? = null + fun initServerType(gameEntity: GameEntity) { initServerType(binding.gameName, binding.gameKaifuType, gameEntity) } @@ -32,6 +37,7 @@ class GameItemViewHolder(var binding: GameItemBinding) : BaseRecyclerViewHolder< isShowPlatform: Boolean = false, forceShowSubtitle: Boolean = false ) { + boundedGameEntity = entity binding.run { bindCommonItem(entity, isShowPlatform) gameIconView.displayGameIcon(entity) @@ -57,6 +63,7 @@ class GameItemViewHolder(var binding: GameItemBinding) : BaseRecyclerViewHolder< showSubTitle: Boolean, showIconSubScript: Boolean ) { + boundedGameEntity = entity binding.run { bindCommonItem(entity, false) gameIconView.displayGameIconWithIfShowSubscript(entity, showIconSubScript) @@ -73,6 +80,7 @@ class GameItemViewHolder(var binding: GameItemBinding) : BaseRecyclerViewHolder< } private fun bindCommonItem(entity: GameEntity, isShowPlatform: Boolean) { + boundedGameEntity = entity binding.run { root.background = com.gh.gamecenter.common.R.drawable.reuse_listview_item_style.toDrawable(root.context) selectIv.setImageDrawable(DrawableView.getCheckSelectorDrawable(root.context)) @@ -320,5 +328,9 @@ class GameItemViewHolder(var binding: GameItemBinding) : BaseRecyclerViewHolder< } } + override fun provideExposureData(): ExposureEvent? { + return boundedGameEntity?.exposureEvent?.getRefreshedExposureEvent() + } + } \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/provider/IExposureManagerProvider.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/provider/IExposureManagerProvider.kt index 921433ce1e..eabcfff242 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/provider/IExposureManagerProvider.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/provider/IExposureManagerProvider.kt @@ -8,4 +8,6 @@ interface IExposureManagerProvider { fun logExposure(exposureEvent: ExposureEvent) + fun logExposureList(exposureEventList: List) + } \ No newline at end of file