Files
assistant-android/app/src/main/java/com/gh/gamecenter/home/HomeFragmentAdapter.kt

562 lines
25 KiB
Kotlin

package com.gh.gamecenter.home
import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.exposure.ExposureEvent
import com.gh.common.exposure.ExposureSource
import com.gh.common.exposure.IExposable
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewLogUtils
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.ReuseViewHolder
import com.gh.gamecenter.common.baselist.DiffUtilAdapter
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.constant.ItemViewType
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.safelyGetInRelease
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.viewholder.FooterViewHolder
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.entity.AmwayCommentEntity
import com.gh.gamecenter.entity.SubjectEntity
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.game.GameAndPosition
import com.gh.gamecenter.game.horizontal.GameHorizontalAdapter
import com.gh.gamecenter.game.horizontal.GameHorizontalSlideAdapter
import com.gh.gamecenter.game.rank.RankCollectionAdapter
import com.gh.gamecenter.game.vertical.GameVerticalAdapter
import com.gh.gamecenter.game.vertical.OnPagerSnapScrollListener
import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity
import com.gh.gamecenter.home.amway.HomeAmwayListViewHolder
import com.gh.gamecenter.home.discovercard.DiscoverCardGameAdapter
import com.gh.gamecenter.home.discovercard.HomeDiscoverCardViewHolder
import com.gh.gamecenter.home.gamecollection.HomeGameCollectionViewHolder
import com.gh.gamecenter.home.slide.HomeSlideListAdapter
import com.gh.gamecenter.home.slide.HomeSlideListViewHolder
import com.gh.gamecenter.setting.SettingBridge
import com.gh.vspace.HomeRecentVGameAdapter
import com.gh.vspace.HomeRecentVGameViewHolder
import com.gh.vspace.VHelper
import com.halo.assistant.fragment.game.GamePluginAdapter
import com.lightgame.download.DownloadEntity
class HomeFragmentAdapter(
context: Context,
val viewModel: HomeViewModel,
private val mLayoutManager: LinearLayoutManager,
private val mCallback: (colorStr: Int) -> Unit
) : DiffUtilAdapter<HomeItemData>(context), IExposable {
private var mLoadStatus: LoadStatus? = null
private val mLegacyHomeFragmentAdapterAssistant by lazy {
LegacyHomeFragmentAdapterAssistant(mContext, this, mBasicExposureSource)
}
private val mBasicExposureSource by lazy { listOf(ExposureSource("新首页", "")) }
override fun areItemsTheSame(oldItem: HomeItemData?, newItem: HomeItemData?): Boolean {
// recyclerView 仅只会存在一个 item 的,只要 item 对应的值不为空即为 true
if (oldItem?.slides != null && newItem?.slides != null) return true
if (oldItem?.recommends != null && newItem?.recommends != null) return true
if (oldItem?.pluginList != null && newItem?.pluginList != null) return true
if (oldItem?.recentVGame != null && newItem?.recentVGame != null) return true
// recyclerView 可存在多个 item 的,需要需要比较它们的 id 或特征值
if (oldItem?.amway != null && newItem?.amway != null) {
return oldItem.amway?.firstOrNull()?.id == newItem.amway?.firstOrNull()?.id
}
if (oldItem?.gameCollection != null && newItem?.gameCollection != null) {
return oldItem.gameCollection?.firstOrNull()?.gameCollectionItem?.id == newItem.gameCollection?.firstOrNull()?.gameCollectionItem?.id
}
if (oldItem?.attachGame != null && newItem?.attachGame != null) {
return oldItem.attachGame?.linkId == newItem.attachGame?.linkId
}
if (oldItem?.lineDivider != null && newItem?.lineDivider != null) {
return oldItem.lineDivider == newItem.lineDivider
}
if (oldItem?.game != null && newItem?.game != null) {
return oldItem.game?.id == newItem.game?.id
}
if (oldItem?.columnHead != null && newItem?.columnHead != null) {
return oldItem.columnHead?.name == newItem.columnHead?.name
}
if (oldItem?.horizontalColumn != null && newItem?.horizontalColumn != null) {
return oldItem.horizontalColumn?.id == newItem.horizontalColumn?.id
}
if (oldItem?.image != null && newItem?.image != null) {
return oldItem.image?.id == newItem.image?.id
}
if (oldItem?.verticalSlide != null && newItem?.verticalSlide != null) {
return oldItem.verticalSlide?.id == newItem.verticalSlide?.id
}
if (oldItem?.horizontalSlide != null && newItem?.horizontalSlide != null) {
return oldItem.horizontalSlide?.id == newItem.horizontalSlide?.id
}
if (oldItem?.gallery != null && newItem?.gallery != null) {
return oldItem.gallery?.id == newItem.gallery?.id
}
if (oldItem?.gallerySlide != null && newItem?.gallerySlide != null) {
return oldItem.gallerySlide?.id == newItem.gallerySlide?.id
}
if (oldItem?.imageSlide != null && newItem?.imageSlide != null) {
return oldItem.imageSlide?.id == newItem.imageSlide?.id
}
if (oldItem?.columnCollection != null && newItem?.columnCollection != null) {
return oldItem.columnCollection?.id == newItem.columnCollection?.id
}
if (oldItem?.commonCollection != null && newItem?.commonCollection != null) {
return oldItem.commonCollection?.id == newItem.commonCollection?.id
}
if (oldItem?.commonCollection12 != null && newItem?.commonCollection12 != null) {
return oldItem.commonCollection12?.id == newItem.commonCollection12?.id && oldItem.offset == newItem.offset
}
if (oldItem?.rankCollection != null && newItem?.rankCollection != null) {
return oldItem.rankCollection?.id == newItem.rankCollection?.id
}
if (oldItem?.doubleCardColumn != null && newItem?.doubleCardColumn != null) {
return oldItem.doubleCardColumn?.id == newItem.doubleCardColumn?.id && oldItem.offset == newItem.offset
}
if (oldItem?.bigImageRecommend != null && newItem?.bigImageRecommend != null) {
return oldItem.bigImageRecommend?.id == newItem.bigImageRecommend?.id
}
if (oldItem?.blankDivider != null && newItem?.blankDivider != null) {
return oldItem.blankDivider == newItem.blankDivider
}
if (oldItem?.discoverCard != null && newItem?.discoverCard != null) {
return oldItem.discoverCard == newItem.discoverCard
}
return false
}
fun setLoadStatus(status: LoadStatus) {
// 重置偏移
if (status == LoadStatus.INIT_LOADED) {
mLegacyHomeFragmentAdapterAssistant.resetOffset()
}
this.mLoadStatus = status
notifyItemChanged(itemCount - 1)
}
override fun getItemViewType(position: Int): Int {
if (itemCount == position + 1) return FOOTER_ITEM
val itemData = mDataList[position]
if (itemData.slides != null) return SLIDE_ITEM
if (itemData.recommends != null) return RECOMMENDS_ITEM
if (itemData.attachGame != null) return GAME_ITEM
if (itemData.amway != null) return AMWAY_ITEM
if (itemData.gameCollection != null) return GAME_COLLECTION_ITEM
if (itemData.recentVGame != null) return RECENT_V_GAME
if (itemData.discoverCard != null) return DISCOVER_CARD
if (itemData.lineDivider != null) return DIVIDER_ITEM
if (itemData.unknownData != null) return UNKNOWN_ITEM
return mLegacyHomeFragmentAdapterAssistant.getLegacyItemType(itemData)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
SLIDE_ITEM -> HomeSlideListViewHolder(parent.toBinding(), mCallback)
RECOMMENDS_ITEM -> HomeRecommendItemViewHolder(parent.toBinding())
GAME_ITEM -> HomeGameItemViewHolder(parent.toBinding())
AMWAY_ITEM -> HomeAmwayListViewHolder(parent.toBinding())
GAME_COLLECTION_ITEM -> HomeGameCollectionViewHolder(parent.toBinding())
DIVIDER_ITEM -> HomeDividerViewHolder(parent.toBinding())
RECENT_V_GAME -> HomeRecentVGameViewHolder(parent.toBinding())
DISCOVER_CARD -> HomeDiscoverCardViewHolder(parent.toBinding())
FOOTER_ITEM -> FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false))
UNKNOWN_ITEM -> ReuseViewHolder(mLayoutInflater.inflate(R.layout.home_unknown_item, parent, false))
// 原游戏板块样式
else -> return mLegacyHomeFragmentAdapterAssistant.createLegacyViewHolder(parent, viewType)
}
}
override fun getItemCount(): Int {
return if (mDataList.isEmpty()) 0 else mDataList.size + 1 // 1: footer
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is HomeSlideListViewHolder -> {
// 根据首页是否有其它 tab 设置不同的 marginTop
val marginTop = if (viewModel.homeOnlyWithoutOtherTab) 4F.dip2px() else 0
(holder.binding.recyclerView.layoutParams as ConstraintLayout.LayoutParams)
.setMargins(0, marginTop, 0, 0)
holder.bindSlideList(mDataList[position], mBasicExposureSource)
}
is HomeRecommendItemViewHolder -> holder.bindRecommend(mDataList[position], mBasicExposureSource)
is HomeAmwayListViewHolder -> bindAmway(holder, position)
is HomeGameItemViewHolder -> bindAttachGame(holder, position)
is FooterViewHolder -> bindFooterView(holder)
is ReuseViewHolder -> bindUnknown(holder)
is HomeDividerViewHolder -> holder.bindView(mDataList[position].lineDivider ?: 1F)
is HomeGameCollectionViewHolder -> bindGameCollection(holder, position)
is HomeRecentVGameViewHolder -> bindRecentVGame(holder, position)
is HomeDiscoverCardViewHolder -> bindDiscoverCard(holder, position)
else -> mLegacyHomeFragmentAdapterAssistant.bindLegacyViewHolder(holder, mDataList[position], position)
}
}
private fun bindUnknown(holder: ReuseViewHolder) {
holder.itemView.setOnClickListener {
mContext.startActivity(SettingBridge.getAboutIntent(mContext, true))
}
}
private fun bindAmway(holder: HomeAmwayListViewHolder, position: Int) {
val homeItemData = mDataList[position]
val amwayList = homeItemData.amway!!
val exposureList = arrayListOf<ExposureEvent>()
for (amway in amwayList) {
runOnIoThread(true) {
val event = ExposureEvent.createEventWithSourceConcat(
gameEntity = amway.game.toGameEntity(),
basicSource = mBasicExposureSource,
source = listOf(ExposureSource("安利墙", ""))
)
exposureList.add(event)
}
}
homeItemData.exposureEventList = exposureList
val clickClosure: (View, Int, AmwayCommentEntity) -> Unit = { v, itemPosition, amway ->
val path = "(首页安利墙)"
when (v.id) {
R.id.rating_block -> {
val intent = RatingReplyActivity.getIntent(
mContext,
amway.game.id,
amway.comment.id,
false,
path,
""
)
mContext.startActivity(intent)
NewLogUtils.logHomeShareWallCardClick(amway.game.name ?: "", amway.game.id, "评论内容")
}
R.id.user_icon_container -> {
DirectUtils.directToHomeActivity(mContext, amway.comment.user.id, path, "")
NewLogUtils.logHomeShareWallCardClick(amway.game.name ?: "", amway.game.id, "用户信息")
}
else -> {
GameDetailActivity.startGameDetailActivity(
mContext,
amway.game.id,
path,
homeItemData.exposureEventList?.safelyGetInRelease(itemPosition)
)
NewLogUtils.logHomeShareWallCardClick(amway.game.name ?: "", amway.game.id, "游戏信息")
}
}
}
holder.bindAmwayList(amwayList, clickClosure)
}
private fun bindGameCollection(holder: HomeGameCollectionViewHolder, position: Int) {
val homeItemData = mDataList[position]
val gameCollectionItemDataList = homeItemData.gameCollection ?: listOf()
val exposureList = arrayListOf<ExposureEvent>()
for (gameCollectionItemData in gameCollectionItemDataList) {
runOnIoThread(true) {
val gameCollection = gameCollectionItemData.gameCollectionItem
val gameCollectionSource =
listOf(ExposureSource("游戏单", "${gameCollection?.title} + ${gameCollection?.id}"))
val gameExposureList = arrayListOf<ExposureEvent>()
gameCollection?.games?.take(3)?.forEachIndexed { index, game ->
gameExposureList.add(
ExposureEvent.createEventWithSourceConcat(
gameEntity = game.toGameEntity().apply {
isAdData = gameCollection.adIconActive
outerSequence = homeItemData.blockPosition
sequence = gameCollectionItemData.gameStartPosition + index + 1
},
basicSource = mBasicExposureSource,
source = gameCollectionSource
)
)
}
gameCollectionItemData.exposureEventList = gameExposureList
exposureList.addAll(gameExposureList)
}
}
homeItemData.exposureEventList = exposureList
holder.bindGameCollectionList(gameCollectionItemDataList, "首页内容列表")
}
private fun bindRecentVGame(holder: HomeRecentVGameViewHolder, position: Int) {
val homeItemData = mDataList[position]
val exposureEventList = arrayListOf<ExposureEvent>()
runOnIoThread(true) {
homeItemData.recentVGame?.forEach {
val event = ExposureEvent.createEventWithSourceConcat(
gameEntity = VHelper.toGameEntity(it.downloadEntity),
basicSource = mBasicExposureSource,
source = listOf(ExposureSource("最近在玩", ""))
)
exposureEventList.add(event)
}
}
homeItemData.exposureEventList = exposureEventList
if (!homeItemData.recentVGame.isNullOrEmpty()) {
holder.bindView(homeItemData.recentVGame!!)
}
}
private fun bindDiscoverCard(holder: HomeDiscoverCardViewHolder, position: Int) {
val homeItemData = mDataList[position]
val discoverCard = homeItemData.discoverCard!!
val binding = holder.binding
val exposureEventList = arrayListOf<ExposureEvent>()
val listExposureEventList = arrayListOf<ExposureEvent>()
val spanCount = 3
val snapHelper = holder.bindView(discoverCard, "首页")
val exposureClosure: (Int) -> Unit = {
val gameList = discoverCard.games
runOnIoThread(true) {
listExposureEventList.clear()
val startOffset = it * spanCount
val endOffset = if (startOffset + spanCount >= gameList.size) {
gameList.size
} else {
startOffset + spanCount
}
for (i in startOffset until endOffset) {
val event = ExposureEvent.createEventWithSourceConcat(
gameEntity = gameList[i].apply {
outerSequence = homeItemData.blockPosition
sequence = i
},
basicSource = mBasicExposureSource,
source = listOf(ExposureSource("发现", gameList[i].getTypeName()))
)
gameList[i].exposureEvent = event
listExposureEventList.add(event)
}
exposureEventList.addAll(listExposureEventList)
}
}
exposureClosure(0)
binding.recyclerView.addOnScrollListener(OnPagerSnapScrollListener(snapHelper) {
exposureClosure(it)
})
homeItemData.exposureEventList = exposureEventList
}
private fun bindAttachGame(holder: HomeGameItemViewHolder, position: Int) {
val homeItemData = mDataList[position]
val attachGame = homeItemData.attachGame
val subjectEntity = SubjectEntity().apply {
id = attachGame?.linkId
type = attachGame?.linkType
linkText = attachGame?.linkText ?: ""
displayContent = attachGame?.displayContent ?: ""
recommendText = attachGame?.recommendText ?: ""
linkGame = attachGame?.linkGame
}
holder.bindAttachGame(holder, subjectEntity, this, mBasicExposureSource, position, "新首页") {
homeItemData.exposureEvent = it
}
}
private fun bindFooterView(holder: FooterViewHolder) {
holder.initFooterViewHolder(
mLoadStatus == LoadStatus.LIST_LOADING,
mLoadStatus == LoadStatus.LIST_FAILED,
mLoadStatus == LoadStatus.LIST_OVER,
R.string.load_over_with_click_hint
) {
if (mLoadStatus == LoadStatus.LIST_OVER) {
mLayoutManager.scrollToPosition(0)
} else if (mLoadStatus == LoadStatus.LIST_FAILED) {
viewModel.getHomeContent(false)
notifyItemChanged(itemCount - 1)
}
}
}
fun notifyItemByDownload(download: DownloadEntity) {
val data = getGameEntityByPackage(download.packageName)
for (gameAndPosition in data) {
if (gameAndPosition.entity != null && gameAndPosition.entity.name == download.name) {
val entryMap = gameAndPosition.entity.getEntryMap()
entryMap[download.platform] = download
}
if (getItemViewType(gameAndPosition.position) == ItemViewType.VERTICAL_SLIDE_ITEM
|| getItemViewType(gameAndPosition.position) == ItemViewType.GAME_PLUGIN
|| getItemViewType(gameAndPosition.position) == SLIDE_ITEM
|| getItemViewType(gameAndPosition.position) == ItemViewType.RANK_COLLECTION
|| getItemViewType(gameAndPosition.position) == RECENT_V_GAME
|| getItemViewType(gameAndPosition.position) == ItemViewType.GAME_SUBJECT
|| getItemViewType(gameAndPosition.position) == ItemViewType.GAME_SUBJECT_SLIDE
|| getItemViewType(gameAndPosition.position) == DISCOVER_CARD
) {
val view = mLayoutManager.findViewByPosition(gameAndPosition.position)
val recyclerView = view?.findViewById<RecyclerView>(R.id.recycler_view)
when (val adapter = recyclerView?.adapter) {
is GameVerticalAdapter -> adapter.notifyItemByDownload(download)
is GamePluginAdapter -> adapter.notifyItemByDownload(download)
is HomeSlideListAdapter -> adapter.notifyItemByDownload(download)
is RankCollectionAdapter -> adapter.notifyItemByDownload(download)
is GameHorizontalAdapter -> adapter.notifyItemByDownload(download)
is GameHorizontalSlideAdapter -> adapter.notifyItemByDownload(download)
is HomeRecentVGameAdapter -> adapter.notifyItemByDownload(download)
is DiscoverCardGameAdapter -> adapter.notifyItemByDownload(download)
}
} else {
notifyItemChanged(gameAndPosition.position)
}
}
}
fun notifyItemAndRemoveDownload(status: EBDownloadStatus) {
val data = getGameEntityByPackage(status.packageName)
for (gameAndPosition in data) {
if (gameAndPosition.entity != null && gameAndPosition.entity.name == status.name) {
gameAndPosition.entity.getEntryMap().remove(status.platform)
}
notifyChildItem(gameAndPosition.position, status.packageName)
}
}
fun notifyChildItem(position: Int, packageName: String) {
if (getItemViewType(position) == ItemViewType.VERTICAL_SLIDE_ITEM
|| getItemViewType(position) == ItemViewType.GAME_PLUGIN
|| getItemViewType(position) == SLIDE_ITEM
|| getItemViewType(position) == ItemViewType.RANK_COLLECTION
|| getItemViewType(position) == ItemViewType.GAME_SUBJECT
|| getItemViewType(position) == ItemViewType.GAME_SUBJECT_SLIDE
|| getItemViewType(position) == DISCOVER_CARD
) {
val view = mLayoutManager.findViewByPosition(position)
val recyclerView = view?.findViewById<RecyclerView>(R.id.recycler_view)
when (val adapter = recyclerView?.adapter) {
is RankCollectionAdapter -> adapter.notifyChildItem()
is GameVerticalAdapter -> adapter.notifyChildItem(packageName)
is GameHorizontalAdapter -> adapter.notifyChildItem(packageName)
is GameHorizontalSlideAdapter -> adapter.notifyChildItem(packageName)
is DiscoverCardGameAdapter -> adapter.notifyChildItem(packageName)
else -> recyclerView?.adapter?.notifyDataSetChanged()
}
} else {
notifyItemChanged(position)
}
}
fun getGameEntityByPackage(packageName: String): List<GameAndPosition> {
val positionList = ArrayList<GameAndPosition>()
val positionMap = viewModel.positionAndPackageMap.value ?: return positionList
for (key in positionMap.keys) {
if (key.contains(packageName)) {
val position = positionMap[key]!!
if (position >= mDataList.size) return arrayListOf()
val itemData = mDataList[position]
val slides = itemData.slides
if (slides != null) {
main@
for (slide in slides) {
val linkGame = slide.linkGame
if (linkGame != null) {
for (apkEntity in linkGame.getApk()) {
if (apkEntity.packageName == packageName) {
positionList.add(GameAndPosition(linkGame, position))
break@main
}
}
}
}
continue
}
val attachGame = itemData.attachGame?.linkGame
val recentVGame = itemData.recentVGame
if (attachGame != null || recentVGame != null) {
positionList.add(GameAndPosition(attachGame, position))
continue
}
val discoverCard = itemData.discoverCard
if (discoverCard != null) {
discoverCard.games.forEach {
for (apkEntity in it.getApk()) {
if (apkEntity.packageName == packageName) {
positionList.add(GameAndPosition(it, position))
}
}
}
continue
}
// 原游戏板块样式
mLegacyHomeFragmentAdapterAssistant.getLegacyGameEntityByPackage(
positionList,
itemData,
packageName,
position
)
}
}
return positionList
}
override fun getEventByPosition(pos: Int): ExposureEvent? {
return mDataList[pos].exposureEvent
}
override fun getEventListByPosition(pos: Int): List<ExposureEvent>? {
return mDataList[pos].exposureEventList
}
companion object {
const val SLIDE_ITEM: Int = 100
const val RECOMMENDS_ITEM: Int = 101
const val GAME_ITEM: Int = 102
const val AMWAY_ITEM: Int = 103
const val DIVIDER_ITEM: Int = 114
const val FOOTER_ITEM: Int = 110
const val UNKNOWN_ITEM: Int = 111
const val GAME_COLLECTION_ITEM: Int = 116
const val RECENT_V_GAME: Int = 117
const val DISCOVER_CARD: Int = 118
}
}