From 28436d93cbb3e7ca2daeb1a2b690080e5cc90f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=99=A8?= Date: Mon, 14 Apr 2025 17:06:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9Edsp=E5=B9=BF=E5=91=8A?= =?UTF-8?q?=E5=90=88=E4=BD=9C=E6=A8=A1=E5=BC=8F=E2=80=94=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=20https://jira.shanqu.cc/browse/GHZSCY-7820?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 1 + .../java/com/gh/common/constant/Config.java | 2 + .../com/gh/common/provider/BuildConfigImpl.kt | 2 + .../com/gh/gamecenter/entity/SubjectEntity.kt | 24 ++- .../dialog/DspGameDetailDialogFragment.kt | 142 ++++++++++++++ .../home/custom/CustomPageFragment.kt | 41 ++-- .../home/custom/CustomPageViewModel.kt | 38 ++++ .../home/custom/DspSubjectUseCase.kt | 34 ++++ .../home/custom/model/CustomPageData.kt | 2 + .../home/custom/model/CustomPageItem.kt | 19 ++ .../home/custom/model/CustomPageRepository.kt | 37 ++-- .../home/custom/model/DspSubjectRepository.kt | 34 ++++ .../gamecenter/retrofit/RetrofitManager.java | 8 + .../retrofit/service/DspApiService.kt | 13 ++ .../dialog_fragment_dsp_game_detail.xml | 171 ++++++++++++++++ app/src/main/res/values-zh-rTW/strings.xml | 3 + app/src/main/res/values/strings.xml | 3 + gradle.properties | 1 + .../gh/gamecenter/common/utils/EnvHelper.kt | 6 + .../core/provider/IBuildConfigProvider.kt | 2 + .../gh/gamecenter/feature/entity/DspEntity.kt | 185 ++++++++++++++++++ .../gamecenter/feature/entity/GameEntity.kt | 8 + 22 files changed, 753 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/com/gh/gamecenter/gamedetail/dialog/DspGameDetailDialogFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/home/custom/DspSubjectUseCase.kt create mode 100644 app/src/main/java/com/gh/gamecenter/home/custom/model/DspSubjectRepository.kt create mode 100644 app/src/main/java/com/gh/gamecenter/retrofit/service/DspApiService.kt create mode 100644 app/src/main/res/layout/dialog_fragment_dsp_game_detail.xml create mode 100644 module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/DspEntity.kt diff --git a/app/build.gradle b/app/build.gradle index 679a91d464..c166ea605e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,6 +113,7 @@ android { buildConfigField "String", "WECHAT_SECRET", "\"${WECHAT_SECRET}\"" buildConfigField "String", "TENCENT_APPID", "\"${TENCENT_APPID}\"" buildConfigField "String", "WEIBO_APPKEY", "\"${WEIBO_APPKEY}\"" + buildConfigField "String", "DSP_API_HOST","\"${DSP_API_HOST}\"" // 一体包的32位畅玩游戏助手包名 buildConfigField "String", "EXT_PACKAGE_NAME", "\"${rootProject.ext.EXT_PACKAGE_NAME}\"" } diff --git a/app/src/main/java/com/gh/common/constant/Config.java b/app/src/main/java/com/gh/common/constant/Config.java index 5f58dd1b5a..3b98d77fd1 100644 --- a/app/src/main/java/com/gh/common/constant/Config.java +++ b/app/src/main/java/com/gh/common/constant/Config.java @@ -66,6 +66,8 @@ public class Config { public static final String WGAME_CPM_BUSIAPPID = BuildConfig.WGAME_CPM_BUSIAPPID; public static final String WGAME_CPM_API_HOST = EnvHelper.getWGameCPMHost(); + public static final String DSP_API_HOST = EnvHelper.getDspHost(); + // Third-Party confs public static final String WECHAT_APPID = BuildConfig.WECHAT_APPID; public static final String WECHAT_SECRET = BuildConfig.WECHAT_SECRET; diff --git a/app/src/main/java/com/gh/common/provider/BuildConfigImpl.kt b/app/src/main/java/com/gh/common/provider/BuildConfigImpl.kt index 6ebcd5fe24..84046bda82 100644 --- a/app/src/main/java/com/gh/common/provider/BuildConfigImpl.kt +++ b/app/src/main/java/com/gh/common/provider/BuildConfigImpl.kt @@ -31,4 +31,6 @@ class BuildConfigImpl : IBuildConfigProvider { override fun getWGameCPMBusiAppId(): String = BuildConfig.WGAME_CPM_BUSIAPPID override fun getLogProducerProject(): String = BuildConfig.LOG_HUB_PROJECT + + override fun getDspApiHost(): String = BuildConfig.DSP_API_HOST } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt index aa25c22005..e2686413b1 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt @@ -117,7 +117,12 @@ data class SubjectEntity( @SerializedName("show_index_icon_subscript") val showIndexIconSubscript: Boolean = false, @SerializedName("welfare_info") - val welfareInfo: WelfareInfo? = null + val welfareInfo: WelfareInfo? = null, + + @SerializedName("column_type") + private val _columnType: String? = null, + @SerializedName("size") + private val _size: Size? = null ) : Parcelable { @IgnoredOnParcel @@ -150,10 +155,27 @@ data class SubjectEntity( val list: Int get() = max(min(_list ?: 0, data?.size ?: 0), 1) + val columnType: String + get() = _columnType ?: "" + + val size: Size + get() = _size ?: Size() + + var isDspSubject: Boolean = false + companion object { const val SUBJECT_TAG_UPDATE = "update" // 更新时间 const val SUBJECT_TAG_TYPE = "type" // 游戏标签 const val SUBJECT_TAG_TEST = "test" // 开测时间 const val SUBJECT_TAG_SELLING_POINT = "selling_points&type" // 卖点文案+游戏标签 } + + @Parcelize + data class Size( + @SerializedName("index") + private val _index: Int? = null + ) : Parcelable { + val index: Int + get() = _index ?: 0 + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/dialog/DspGameDetailDialogFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/dialog/DspGameDetailDialogFragment.kt new file mode 100644 index 0000000000..58f2f6b9ed --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/dialog/DspGameDetailDialogFragment.kt @@ -0,0 +1,142 @@ +package com.gh.gamecenter.gamedetail.dialog + +import android.content.Context +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import com.gh.common.util.DetailDownloadUtils +import com.gh.common.util.DialogUtils +import com.gh.common.util.DownloadItemUtils +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus +import com.gh.download.DownloadManager +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.DetailViewHolder +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.base.fragment.BaseBottomDialogFragment +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.core.utils.CurrentActivityHolder +import com.gh.gamecenter.databinding.DialogFragmentDspGameDetailBinding +import com.gh.gamecenter.feature.entity.GameEntity +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity + +class DspGameDetailDialogFragment : BaseBottomDialogFragment() { + + private var gameEntity: GameEntity? = null + + private val dataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + updateDownloadStatus() + + if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { + DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) + } + } + + + override fun onDataInit(downloadEntity: DownloadEntity) { + updateDownloadStatus() + } + } + + private fun updateDownloadStatus() { + gameEntity?.let { + DetailDownloadUtils.updateViewHolder( + DetailViewHolder( + mBinding.detailLlBottom, + null, + it, + false, + "", + "", + "", + null, + false, + null, + null + ) + ) + } + + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + gameEntity = arguments?.getParcelable(EntranceConsts.KEY_GAME_ENTITY) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + gameEntity?.let { + val apk = it.getApk().firstOrNull() + + mBinding.ivIcon.displayGameIcon(it) + mBinding.tvName.text = it.name + mBinding.tvVersion.text = apk?.version ?: "" + mBinding.tvDeveloper.text = getString(R.string.developer_name, it.developer ?: "") + + + mBinding.detailProgressbar.goneIf(apk == null) + if (apk != null) { + DownloadItemUtils.setOnClickListener( + requireContext(), + mBinding.detailProgressbar, + it, + 0, + null, + "", + "", + "", + null, + null + ) + + DetailDownloadUtils.updateViewHolder( + DetailViewHolder( + mBinding.detailLlBottom, + null, + it, + false, + "", + "", + "", + null, + false, + null, + null + ) + ) + } + } + } + + override fun onResume() { + super.onResume() + DownloadManager.getInstance().addObserver(dataWatcher) + } + + override fun onPause() { + super.onPause() + DownloadManager.getInstance().removeObserver(dataWatcher) + } + + companion object { + fun show(context: Context, gameEntity: GameEntity) { + + if (context is AppCompatActivity) { + context.supportFragmentManager + } else { + (CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager + }?.let { + val fragment = DspGameDetailDialogFragment().apply { + arguments = Bundle().apply { + putParcelable(EntranceConsts.KEY_GAME_ENTITY, gameEntity) + } + } + fragment.show(it, fragment::class.java.simpleName) + } + } + } +} \ No newline at end of file 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 994992b1b4..f612111fdc 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 @@ -62,6 +62,7 @@ import com.gh.gamecenter.feature.exposure.ExposureType import com.gh.gamecenter.feature.minigame.MiniGameItemHelper import com.gh.gamecenter.feature.utils.SentryHelper import com.gh.gamecenter.game.commoncollection.detail.CustomCommonCollectionDetailActivity +import com.gh.gamecenter.gamedetail.dialog.DspGameDetailDialogFragment import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity import com.gh.gamecenter.home.PageConfigure import com.gh.gamecenter.home.custom.adapter.CustomPageAdapter @@ -130,6 +131,7 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB } } + override fun onDataInit(downloadEntity: DownloadEntity) { adapter.notifyDownload(downloadEntity) } @@ -266,7 +268,11 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB if (status == LoadStatus.INIT_LOADING) { binding.reuseLoading.root.goneIf(false) - binding.root.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext())) + binding.root.setBackgroundColor( + com.gh.gamecenter.common.R.color.ui_surface.toColor( + requireContext() + ) + ) } else { binding.reuseLoading.root.goneIf(true) binding.root.setBackgroundColor(Color.TRANSPARENT) @@ -324,15 +330,23 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB } gameDetailDestination.observe(viewLifecycleOwner, EventObserver { (entrance, game) -> - if (game.isMiniGame()) { - MiniGameItemHelper.launchMiniGame(game) - } else { - GameDetailActivity.startGameDetailActivity( - requireContext(), - game.id, - entrance, - traceEvent = game.exposureEvent, - ) + when { + game.isDspGame -> { + DspGameDetailDialogFragment.show(requireContext(), game) + } + + game.isMiniGame() -> { + MiniGameItemHelper.launchMiniGame(game) + } + + else -> { + GameDetailActivity.startGameDetailActivity( + requireContext(), + game.id, + entrance, + traceEvent = game.exposureEvent, + ) + } } }) @@ -779,7 +793,11 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB adapter.notifyItemRangeChanged(0, adapter.itemCount) binding.gameList.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext())) - searchBarBinding?.searchTv?.setHintTextColor(com.gh.gamecenter.common.R.color.text_instance.toColor(requireContext())) + searchBarBinding?.searchTv?.setHintTextColor( + com.gh.gamecenter.common.R.color.text_instance.toColor( + requireContext() + ) + ) onScrollChanged(true) } } @@ -816,6 +834,7 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB RefreshState.TwoLevel -> { setScrollEnabled(false) } + RefreshState.None -> { setScrollEnabled(true) } 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 2650e393d7..a1d4fa603d 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 @@ -86,6 +86,7 @@ class CustomPageViewModel( addSource(repository.wechatMiniGameCPMItemLiveData, ::notifyWechatMiniGameCPMSubjectItemChanged) addSource(repository.customRecentAcceleratorItemLiveData, ::notifyItemChanged) addSource(repository.pkDataListLiveData, ::notifyPKDataChanged) + addSource(repository.dspSubjectLiveData, ::notifyDspSubjectItemChanged) } val dataList: LiveData> = _dataList @@ -580,6 +581,43 @@ class CustomPageViewModel( _dataList.value = newData } + /** + * 通过 dsp 接口异步获取数据插入 + */ + private fun notifyDspSubjectItemChanged(result: Pair>) { + val (subjectId, gameList) = result + val oldData = dataList.value ?: return + val index = oldData.indexOfFirst { + it is CustomDspPlaceholderItem && subjectId == it.data.id + } + if (index == -1) { + return + } + val subjectItem = (oldData[index] as CustomDspPlaceholderItem).apply { + data.data = gameList.toMutableList() + data.isDspSubject = true + } + val position = subjectItem.position + val componentPosition = subjectItem.componentPosition + val newData = oldData.toMutableList() + val customPageItemList = repository.convertColumnDetailSubjectItems( + position, + componentPosition, + subjectItem.link, + subjectItem.data + ) + if (customPageItemList.isEmpty()) return + newData[index] = customPageItemList[0] + newData.addAll(index + 1, customPageItemList.subList(1, customPageItemList.size)) + newData.forEachIndexed { pos, customPageItem -> + customPageItem.position = pos + } + repository.notifyPositionChanged(newData.size) + gamePositionAndPackageHelper.clear() + gamePositionAndPackageHelper.putAll(getPositionAndPackageMap(newData)) + _dataList.value = newData + } + private fun notifyPKDataChanged(pkList: List) { if (pkList.isEmpty()) return diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/DspSubjectUseCase.kt b/app/src/main/java/com/gh/gamecenter/home/custom/DspSubjectUseCase.kt new file mode 100644 index 0000000000..728d97dff9 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/DspSubjectUseCase.kt @@ -0,0 +1,34 @@ +package com.gh.gamecenter.home.custom + +import android.annotation.SuppressLint +import androidx.lifecycle.LiveData +import com.gh.gamecenter.common.livedata.NonStickyMutableLiveData +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.utils.singleToMain +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.custom.model.DspSubjectRepository + +class DspSubjectUseCase( + private val repository: DspSubjectRepository +) { + private val _dspGameList = NonStickyMutableLiveData>>() + val dspGameList: LiveData>> = _dspGameList + + @SuppressLint("CheckResult") + fun fetchDspData(subjectId: String?) { + if (subjectId.isNullOrBlank()) { + return + } + repository.fetchDspData() + .compose(singleToMain()) + .subscribe(object : BiResponse>() { + override fun onSuccess(data: List) { + _dspGameList.value = subjectId to data + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + } + }) + } +} \ 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 d689f85151..125c8a7fab 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 @@ -82,6 +82,8 @@ class CustomPageData( val linkQqGameRecentlyPlayed: LinkRecentlyPlayed? = null, @SerializedName("link_accelerator_recently_played") val linkAcceleratorRecentlyPlayed: LinkRecentlyPlayed? = null, + @SerializedName("link_dsp_game_column") + val linkDspGameColumn: SubjectEntity? = null, var linkPK: PKEntity? = null, ) { // 游戏专题 diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageItem.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageItem.kt index a623eb6c18..fbb1f0407f 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageItem.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageItem.kt @@ -96,6 +96,7 @@ abstract class CustomPageItem( const val CUSTOM_LINK_TYPE_COMMON_COLLECTION = "common_collection" const val CUSTOM_LINK_TYPE_TOP_GAME_COMMENT = "top_game_comment" const val CUSTOM_LINK_TYPE_PK = "pk" + const val CUSTOM_LINK_TYPE_DSP_GAME_COLUMN = "dsp_game_column" // 游戏专题 const val COMPONENTS_GAME_SUBJECT_TYPE_DOUBLE_CARD = "game_double_card" @@ -906,6 +907,24 @@ data class CustomPKItem( } } +data class CustomDspPlaceholderItem( + private val _link: LinkEntity, + val data: SubjectEntity, + private val _position: Int, + private val _componentPosition: Int +) : CustomPageItem(_link, _position, _componentPosition) { + + override val itemType: Int + get() = CUSTOM_PAGE_ITEM_TYPE_MINI_GAME_RECENT_PLAY + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomDspPlaceholderItem + && data == other.data + && position == other.position + && componentPosition == other.componentPosition + } +} + object CustomFooterItem : CustomPageItem(LinkEntity(), -1, -1) { override val itemType: Int get() = CUSTOM_PAGE_ITEM_TYPE_FOOTER 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 9646050134..ece9f064e1 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 @@ -17,20 +17,14 @@ import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.utils.singleToMain import com.gh.gamecenter.common.utils.sp2px import com.gh.gamecenter.core.utils.DisplayUtils -import com.gh.gamecenter.core.utils.TimeUtils import com.gh.gamecenter.entity.DiscoveryCardEntity import com.gh.gamecenter.entity.GameUpdateEntity import com.gh.gamecenter.entity.PullDownPush import com.gh.gamecenter.entity.SubjectEntity import com.gh.gamecenter.feature.entity.* -import com.gh.gamecenter.entity.SubjectEntity.Companion.SUBJECT_TAG_SELLING_POINT -import com.gh.gamecenter.entity.SubjectEntity.Companion.SUBJECT_TAG_UPDATE -import com.gh.gamecenter.feature.entity.FloatingWindowEntity -import com.gh.gamecenter.feature.entity.GameEntity -import com.gh.gamecenter.feature.entity.PluginLocation -import com.gh.gamecenter.feature.entity.TagStyleEntity import com.gh.gamecenter.feature.utils.ApkActiveUtils import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity +import com.gh.gamecenter.home.custom.DspSubjectUseCase import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_BANNER import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_BANNER import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD @@ -55,13 +49,13 @@ import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_S import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_SUBJECT_TYPE_GAME_TIMELINE_HORIZONTAL_SLIDE import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_SUBJECT_TYPE_GAME_VERTICAL_IMAGE_TEXT import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_SUBJECT_TYPE_GAME_VERTICAL_SLIDE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_ACCELERATOR_RECENTLY_PLAYED import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_COLUMN import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_GAME import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_HALO_RECOMMEND import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_PK import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_QQ_GAME_RECENTLY_PLAYED import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_QQ_MINI_GAME_COLUMN_DETAIL -import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_ACCELERATOR_RECENTLY_PLAYED import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_WECHAT_GAME_RECENTLY_PLAYED import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_WECHAT_MINI_GAME_COLUMN_DETAIL import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_WECHAT_WECHAT_GAME_CPM_COLUMN_DETAIL @@ -85,7 +79,7 @@ import java.util.regex.Pattern class CustomPageRepository private constructor( private val remoteDataSource: CustomPageRemoteDataSource, private val localDataSource: CustomPageLocalDataSource, - private val wGameSubjectCPMRemoteDataSource: WGameSubjectCPMRemoteDataSource + private val wGameSubjectCPMRemoteDataSource: WGameSubjectCPMRemoteDataSource, ) { private val pageInfo = PageInfo() @@ -98,6 +92,8 @@ class CustomPageRepository private constructor( WGameSubjectCPMListRepository(wGameSubjectCPMRemoteDataSource) ) + private val dspSubjectUseCase = DspSubjectUseCase(DspSubjectRepository.newInstance()) + private var recentItem: CustomRecentGamesItem? = null private var recentQQItem: CustomRecentQqMiniGamesItem? = null @@ -168,6 +164,9 @@ class CustomPageRepository private constructor( val pkDataListLiveData = MutableLiveData>() + val dspSubjectLiveData: LiveData>> = + dspSubjectUseCase.dspGameList + private var lastComponentType = "" private var lastSubjectId = "" private var gameChildPosition = 0 @@ -360,14 +359,15 @@ class CustomPageRepository private constructor( item.link.type == CUSTOM_LINK_TYPE_WECHAT_WECHAT_GAME_CPM_COLUMN_DETAIL -> { item.gameSubjectEntity?.let { subject -> list.add( - CustomWeChatMiniGamesCPMSubjectItem( + CustomDspPlaceholderItem( item.link, subject, pageInfo.position, pageInfo.componentPosition ) ) - wGameSubjectCPMListUseCase.getWechatMiniGameCPMList(subject.id) +// wGameSubjectCPMListUseCase.getWechatMiniGameCPMList(subject.id) + dspSubjectUseCase.fetchDspData(subject.id) pageInfo.positionIncrement() pageInfo.componentPositionIncrement() } @@ -673,6 +673,21 @@ class CustomPageRepository private constructor( } } + item.linkDspGameColumn != null -> { // dsp 游戏专题 + // 这里只是暂时使用 CustomDspPlaceholderItem 占位,后续 dsp数据下来之后,会覆盖掉当前 item + list.add( + CustomDspPlaceholderItem( + item.link, + item.linkDspGameColumn, + pageInfo.position, + pageInfo.componentPosition + ) + ) +// wGameSubjectCPMListUseCase.getWechatMiniGameCPMList(item.li.id) + pageInfo.positionIncrement() + pageInfo.componentPositionIncrement() + } + else -> { CustomInvalidItem } diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/DspSubjectRepository.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/DspSubjectRepository.kt new file mode 100644 index 0000000000..b9dcf7e88d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/DspSubjectRepository.kt @@ -0,0 +1,34 @@ +package com.gh.gamecenter.home.custom.model + +import com.gh.gamecenter.common.utils.toRequestBody +import com.gh.gamecenter.feature.entity.DspEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.gamecenter.retrofit.service.DspApiService +import io.reactivex.Single + +class DspSubjectRepository( + private val apiService: DspApiService +) { + + fun fetchDspData(): Single> { + val body = hashMapOf( + "busid" to "chongchong1205", + "scene" to 3, + "count" to 5, + "device" to hashMapOf( + "oaid" to "94f001f5-bd25-4a7a-b094-16d52c1be852", + "brand" to "Mi", + "model" to "Redmi Note 8 Pro", + "osv" to "12" + ) + ).toRequestBody() + return apiService.fetch(body) + .map(DspEntity::toGameList) + } + + companion object { + + fun newInstance() = DspSubjectRepository(RetrofitManager.getInstance().dspApiService) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java b/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java index d9d6c37ce6..c2b3fa110c 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java @@ -6,6 +6,7 @@ import com.gh.common.constant.Config; import com.gh.gamecenter.common.retrofit.BaseRetrofitManager; import com.gh.gamecenter.feature.retrofit.WGameCPMApiService; import com.gh.gamecenter.retrofit.service.ApiService; +import com.gh.gamecenter.retrofit.service.DspApiService; import com.gh.gamecenter.retrofit.service.VApiService; import com.halo.assistant.HaloApp; @@ -23,6 +24,8 @@ public class RetrofitManager extends BaseRetrofitManager { private final VApiService mVApiService; private final WGameCPMApiService mWGameCPMApiService; + private final DspApiService mDspApiService; + private RetrofitManager() { Context context = HaloApp.getInstance().getApplicationContext(); OkHttpClient okHttpNormalConfig = getOkHttpConfig(context, 0, 2); @@ -31,6 +34,7 @@ public class RetrofitManager extends BaseRetrofitManager { mUploadApiService = provideService(getOkHttpConfig(context, UPLOAD_CALL_TIME_OUT, 1), Config.API_HOST, ApiService.class); mVApiService = provideService(okHttpNormalConfig, Config.VAPI_HOST, VApiService.class); mWGameCPMApiService = provideService(okHttpNormalConfig, Config.WGAME_CPM_API_HOST, WGameCPMApiService.class); + mDspApiService = provideService(okHttpNormalConfig, Config.DSP_API_HOST, DspApiService.class); } public static RetrofitManager getInstance() { @@ -57,6 +61,10 @@ public class RetrofitManager extends BaseRetrofitManager { return mWGameCPMApiService; } + public DspApiService getDspApiService() { + return mDspApiService; + } + private static class SingletonHolder { private static final RetrofitManager INSTANCE = new RetrofitManager(); } diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/DspApiService.kt b/app/src/main/java/com/gh/gamecenter/retrofit/service/DspApiService.kt new file mode 100644 index 0000000000..0f252659fe --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/DspApiService.kt @@ -0,0 +1,13 @@ +package com.gh.gamecenter.retrofit.service + +import com.gh.gamecenter.feature.entity.DspEntity +import io.reactivex.Single +import okhttp3.RequestBody +import retrofit2.http.Body +import retrofit2.http.POST + +interface DspApiService { + + @POST("bid_test/fetch") + fun fetch(@Body body: RequestBody):Single +} \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_fragment_dsp_game_detail.xml b/app/src/main/res/layout/dialog_fragment_dsp_game_detail.xml new file mode 100644 index 0000000000..f5a1344814 --- /dev/null +++ b/app/src/main/res/layout/dialog_fragment_dsp_game_detail.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index dea3c8003b..94ae469c66 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -689,5 +689,8 @@ %1$s人預約 請檢查是否安裝微信客戶端 刪測 + 應用權限 + 開發者: %1$s + 遊戲詳情 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4d865bb9d9..903d6d6366 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -689,4 +689,7 @@ %1$s人预约 请检查是否安装微信客户端 删测 + 应用权限 + 开发者: %1$s + 游戏详情 diff --git a/gradle.properties b/gradle.properties index eaa82ee54a..bed95ec195 100644 --- a/gradle.properties +++ b/gradle.properties @@ -71,6 +71,7 @@ NEW_API_HOST=https\://app-api.ghzs6.com/ NEW_API_HOST_GAT=https\://app-api.junrui66.com/ DEV_VAPI_HOST=https://dev-app-api.796697.com VAPI_HOST=https://app-api.796697.com +DSP_API_HOST=https://zx.20130123.com/ DEV_SA_SERVER_URL=https://sensors-data-api.ghzs.com/sa?project=default SA_SERVER_URL=https://sensors-data-api.ghzs.com/sa?project=produciton_2 diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt index 7a4c934a13..ff54384b54 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt @@ -88,6 +88,12 @@ object EnvHelper { return buildConfig?.getWGameCPMApiHost() ?: "" } + @JvmStatic + fun getDspHost(): String { + val buildConfig = TheRouter.get(IBuildConfigProvider::class.java) + return buildConfig?.getDspApiHost() ?: "" + } + @JvmStatic fun getWGameCPMBusiAppId(): String { val buildConfig = TheRouter.get(IBuildConfigProvider::class.java) diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IBuildConfigProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IBuildConfigProvider.kt index e26b060f63..1346e25f7c 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/provider/IBuildConfigProvider.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IBuildConfigProvider.kt @@ -31,4 +31,6 @@ interface IBuildConfigProvider { fun getLogProducerProject(): String fun getWGameCPMBusiAppId(): String + + fun getDspApiHost(): String } \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/DspEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/DspEntity.kt new file mode 100644 index 0000000000..935630b77b --- /dev/null +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/DspEntity.kt @@ -0,0 +1,185 @@ +package com.gh.gamecenter.feature.entity + +import com.google.gson.annotations.SerializedName + +data class DspEntity( + @SerializedName("ret") + private val _ret: Int? = null, + @SerializedName("bidList") + private val _bidList: List? = null +) { + + val bidList: List + get() = _bidList ?: emptyList() + + data class BidInfo( + @SerializedName("ad_id") + private val _adId: String? = null, + @SerializedName("interaction_type") + private val _interactionType: Int? = null, + @SerializedName("nurl") + private val _nUrl: String? = null, + @SerializedName("lurl") + private val _lUrl: String? = null, + @SerializedName("price") + private val _price: Int? = null, + @SerializedName("show_url") + private val _showUrl: String? = null, + @SerializedName("click_url") + private val _clickUrl: String? = null, + @SerializedName("download_url") + private val _downloadUrl: String? = null, + @SerializedName("install_url") + private val _installUrl: String? = null, + @SerializedName("creative_type") + private val _creativeType: Int? = null, + @SerializedName("icon") + private val _icon: String? = null, + @SerializedName("title") + private val _title: String? = null, + @SerializedName("description") + private val _description: String? = null, + @SerializedName("image_list") + private val _imageList: List? = null, + @SerializedName("video") + private val _video: Video? = null, + @SerializedName("target_url") + private val _targetUrl: String? = null, + @SerializedName("deeplink_url") + private val _deeplinkUrl: String? = null, + @SerializedName("wechat_path") + private val _wechatPath: String? = null, + @SerializedName("wechat_appid") + private val _wechatAppId: String? = null, + @SerializedName("wechat_ext_data") + private val _wechatExtData: String? = null, + @SerializedName("apk_url") + private val _apkUrl: String? = null, + @SerializedName("app_info") + private val _appInfo: AppInfo? = null + ) { + + val appInfo: AppInfo + get() = _appInfo ?: AppInfo() + + fun toGameEntity() = + GameEntity( + mIcon = _icon, + mName = _appInfo?.name, + mBrief = _description, + apk = arrayListOf(toGhApk()), + mTagStyle = arrayListOf( + TagStyleEntity(name = appInfo.categoryName), + ), + developer = _appInfo?.developer, + permissionsUrl = _appInfo?.permissions, + privacyUrl = _appInfo?.privacy + ).apply { + isDspGame = true + } + + private fun toGhApk(): ApkEntity { + val appInfo = _appInfo ?: AppInfo() + return ApkEntity( + packageName = appInfo.packageName, + url = _apkUrl ?: "", + version = appInfo.version, + isActive = true, + md5 = appInfo.apkMd5, + versionCode = appInfo.versionCode.toIntOrNull() ?: 0 + ) + } + + + data class Image( + @SerializedName("url") + private val _url: String? = null, + @SerializedName("width") + private val _width: Int? = null, + @SerializedName("height") + private val _height: Int? = null + ) { + + val url: String + get() = _url ?: "" + + val width: Int + get() = _width ?: 0 + + val height: Int + get() = _height ?: 0 + } + + data class Video( + @SerializedName("url") + private val _url: String? = null, + @SerializedName("width") + private val _width: Int? = null, + @SerializedName("height") + private val _height: Int? = null, + @SerializedName("duration") + private val _duration: String? = null, + @SerializedName("cover_url") + private val _coverUrl: String? = null, + @SerializedName("cover_width") + private val _coverWidth: Int? = null, + @SerializedName("cover_height") + private val _coverHeight: Int? = null + ) + + data class AppInfo( + @SerializedName("name") + private val _name: String? = null, + @SerializedName("version") + private val _version: String? = null, + @SerializedName("version_code") + private val _versionCode: String? = null, + @SerializedName("package_name") + private val _packageName: String? = null, + @SerializedName("apk_md5") + private val _apkMd5: String? = null, + @SerializedName("developer") + private val _developer: String? = null, + @SerializedName("permissions") + private val _permissions: String? = null, + @SerializedName("privacy") + private val _privacy: String? = null, + @SerializedName("icp_entity") + private val _icpEntity: String? = null, + @SerializedName("category_name") + private val _categoryName: String? = null + ) { + val name: String + get() = _name ?: "" + + val version: String + get() = _version ?: "" + + val versionCode: String + get() = _versionCode ?: "" + + val packageName: String + get() = _packageName ?: "" + + val apkMd5: String + get() = _apkMd5 ?: "" + + val developer: String + get() = _developer ?: "" + + val permissions: String + get() = _permissions ?: "" + + val privacy: String + get() = _privacy ?: "" + + val icpEntity: String + get() = _icpEntity ?: "" + + val categoryName: String + get() = _categoryName ?: "" + } + } + + fun toGameList() = bidList.map(BidInfo::toGameEntity) +} \ No newline at end of file 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 1556fdf93d..ffc27de897 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 @@ -417,6 +417,11 @@ data class GameEntity( var event: List? = null, // 游戏大事件(如果事件时间为今日,且今日有多条大事件,则返回今日全部大事件) @SerializedName("official_entry") var officialEntry: Boolean = false, // 官方入驻标识 + + // 临时变量:dsp游戏专用 + val developer: String? = null, + val permissionsUrl: String? = null, + val privacyUrl: String? = null ) : Parcelable { constructor(id: String?) : this() { @@ -736,6 +741,9 @@ data class GameEntity( val appointmentCount: Int get() = _appointmentCount ?: 0 + @IgnoredOnParcel + var isDspGame: Boolean = false + //是否需要弹出试玩弹窗 fun isShowVersionNumber(): Boolean { return versionNumber == "无版号无内购有弹窗"