Compare commits

...

1 Commits

22 changed files with 753 additions and 23 deletions

View File

@ -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}\""
}

View File

@ -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;

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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<DialogFragmentDspGameDetailBinding>() {
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)
}
}
}
}

View File

@ -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)
}

View File

@ -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<List<CustomPageItem>> = _dataList
@ -580,6 +581,43 @@ class CustomPageViewModel(
_dataList.value = newData
}
/**
* 通过 dsp 接口异步获取数据插入
*/
private fun notifyDspSubjectItemChanged(result: Pair<String, List<GameEntity>>) {
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<PKEntity>) {
if (pkList.isEmpty()) return

View File

@ -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<Pair<String, List<GameEntity>>>()
val dspGameList: LiveData<Pair<String, List<GameEntity>>> = _dspGameList
@SuppressLint("CheckResult")
fun fetchDspData(subjectId: String?) {
if (subjectId.isNullOrBlank()) {
return
}
repository.fetchDspData()
.compose(singleToMain())
.subscribe(object : BiResponse<List<GameEntity>>() {
override fun onSuccess(data: List<GameEntity>) {
_dspGameList.value = subjectId to data
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
}
})
}
}

View File

@ -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,
) {
// 游戏专题

View File

@ -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

View File

@ -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<List<PKEntity>>()
val dspSubjectLiveData: LiveData<Pair<String, List<GameEntity>>> =
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
}

View File

@ -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<List<GameEntity>> {
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)
}
}

View File

@ -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();
}

View File

@ -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<DspEntity>
}

View File

@ -0,0 +1,171 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="24dp"
android:background="@color/ui_surface">
<com.gh.gamecenter.common.view.titlebar.BottomSheetTitleView
android:id="@+id/title_view"
android:layout_width="match_parent"
android:layout_height="48dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:left_style="title"
app:right_icon_color="@color/text_tertiary"
app:right_icon_resource="@drawable/ic_basic_x_18"
app:right_style="icon"
app:title="@string/select_the_acceleration_zone" />
<com.gh.gamecenter.feature.view.GameIconView
android:id="@+id/iv_icon"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title_view" />
<TextView
android:id="@+id/tv_name"
style="@style/TextHeadline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/text_primary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_icon"
tools:text="原神" />
<TextView
android:id="@+id/tv_version"
style="@style/TextCaption1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:textColor="@color/text_secondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_name"
tools:text="版本: 11.45.2.0" />
<TextView
android:id="@+id/tv_developer"
style="@style/TextCaption1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="8dp"
android:gravity="center"
android:textColor="@color/text_secondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_version"
tools:text="开发者:杭州阿里巴巴广告有限公司杭州阿里巴巴广告有限公司杭州阿里巴巴广告有限公司" />
<Space
android:id="@+id/space"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_developer" />
<TextView
android:id="@+id/tv_permissions"
style="@style/TextCaption1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_permissions"
android:textColor="@color/text_theme"
app:layout_constraintBottom_toBottomOf="@id/space"
app:layout_constraintEnd_toStartOf="@id/v_line"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/space" />
<View
android:id="@+id/v_line"
android:layout_width="1dp"
android:layout_height="8dp"
android:layout_marginStart="8dp"
android:background="@color/text_theme"
app:layout_constraintBottom_toBottomOf="@id/space"
app:layout_constraintEnd_toStartOf="@id/tv_privacy_policy"
app:layout_constraintStart_toEndOf="@id/tv_permissions"
app:layout_constraintTop_toTopOf="@id/space" />
<TextView
android:id="@+id/tv_privacy_policy"
style="@style/TextCaption1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/setting_privacy_policy"
android:textColor="@color/text_theme"
app:layout_constraintBottom_toBottomOf="@id/space"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/v_line"
app:layout_constraintTop_toTopOf="@id/space"
app:layout_goneMarginStart="0dp" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/detail_ll_bottom"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_privacy_policy">
<com.gh.gamecenter.feature.view.DownloadButton
android:id="@+id/detail_progressbar"
android:layout_width="match_parent"
android:layout_height="40dp"
app:download_button_show_progress="true"
app:download_button_text_size="@dimen/primary_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/multiVersionDownloadTv"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:drawablePadding="4dp"
android:ellipsize="end"
android:gravity="center"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="@dimen/primary_text_size"
android:visibility="gone"
app:drawableEndCompat="@drawable/ic_jump_universal"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="@id/detail_progressbar"
app:layout_constraintEnd_toEndOf="@id/detail_progressbar"
app:layout_constraintStart_toStartOf="@id/detail_progressbar"
app:layout_constraintTop_toTopOf="@id/detail_progressbar"
tools:text="选择下载你的版本" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/downloadTipsLottie"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginTop="-8dp"
android:layout_marginRight="8dp"
android:visibility="gone"
app:layout_constraintRight_toRightOf="@+id/detail_progressbar"
app:layout_constraintTop_toTopOf="@+id/detail_progressbar"
app:lottie_autoPlay="false"
app:lottie_loop="true"
app:lottie_repeatMode="restart" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -689,5 +689,8 @@
<string name="number_of_reservations">%1$s人預約</string>
<string name="wechat_app_not_install_tips">請檢查是否安裝微信客戶端</string>
<string name="delete_test">刪測</string>
<string name="app_permissions">應用權限</string>
<string name="developer_name">開發者: %1$s</string>
<string name="game_detail">遊戲詳情</string>
</resources>

View File

@ -689,4 +689,7 @@
<string name="number_of_reservations">%1$s人预约</string>
<string name="wechat_app_not_install_tips">请检查是否安装微信客户端</string>
<string name="delete_test">删测</string>
<string name="app_permissions">应用权限</string>
<string name="developer_name">开发者: %1$s</string>
<string name="game_detail">游戏详情</string>
</resources>

View File

@ -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

View File

@ -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)

View File

@ -31,4 +31,6 @@ interface IBuildConfigProvider {
fun getLogProducerProject(): String
fun getWGameCPMBusiAppId(): String
fun getDspApiHost(): String
}

View File

@ -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<BidInfo>? = null
) {
val bidList: List<BidInfo>
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<Image>? = 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)
}

View File

@ -417,6 +417,11 @@ data class GameEntity(
var event: List<BigEvent>? = 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 == "无版号无内购有弹窗"