Compare commits

..

4 Commits

Author SHA1 Message Date
f875fa1b14 Merge branch 'feat/GHZSCY-6828' into 'release'
feat: CPM微信小游戏一期优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-6828

See merge request halo/android/assistant-android!1948
2024-10-28 14:54:49 +08:00
8076c3a70a feat: CPM微信小游戏一期优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-6828 2024-10-28 14:54:49 +08:00
c450ca570d Merge branch 'hotfix/v5.38.2-1112/GHZSCY-6854' into 'release'
fix:【光环助手】切换深色/普通模式tab字体颜色变化问题 https://jira.shanqu.cc/browse/GHZSCY-6854

See merge request halo/android/assistant-android!1950
2024-10-28 10:23:56 +08:00
d239b0755f fix:【光环助手】切换深色/普通模式tab字体颜色变化问题 https://jira.shanqu.cc/browse/GHZSCY-6854 2024-10-28 10:20:53 +08:00
12 changed files with 176 additions and 118 deletions

View File

@ -72,7 +72,6 @@ android_build:
only:
- dev
- release
- pack/update_sentry_plugin_cache
# 代码检查
sonarqube_analysis:
@ -158,4 +157,3 @@ oss-upload&send-email:
only:
- dev
- release
- pack/update_sentry_plugin_cache

View File

@ -1045,6 +1045,7 @@ public class MainActivity extends BaseActivity {
blackList.add(R.id.historyTv);
blackList.add(R.id.myCollectionTv);
blackList.add(R.id.searchTv);
blackList.add(R.id.subject_tab);
updateStaticView(view, blackList);
View communityHomeWrapper = view.findViewById(R.id.communityHomeContainer);

View File

@ -18,7 +18,13 @@ data class SearchSubjectEntity(
@SerializedName("ad_icon_active")
val adIconActive: Boolean = false,
// 本地字段标记是否为微信小游戏CPM专题
var isWGameSubjectCPM: Boolean = false
var isWGameSubjectCPM: Boolean = false,
val type: String = ""
) : Parcelable {
companion object {
const val TYPE_WECHAT_GAME_CPM_COLUMN = "wechat_game_cpm_column"
}
fun getFilterGame() = RegionSettingHelper.filterGame(games)
}

View File

@ -806,7 +806,7 @@ class CustomPageRepository private constructor(
fun loadChangeSubjectGame(subjectEntity: SubjectEntity): Observable<List<GameEntity>> =
if (subjectEntity.isWechatColumnCPM) {// 微信小游戏CPM专题的“换一批”接口
wGameSubjectCPMRemoteDataSource.getRecommendCPMList(2, 10).toObservable()
wGameSubjectCPMRemoteDataSource.getEditorRecommendCPMList(2, 10).toObservable()
} else {
remoteDataSource.loadChangeSubjectGame(subjectEntity)
}

View File

@ -26,6 +26,6 @@ class MiniGameSearchResultRepository(
}
override fun getWGameCPMGameList(): Single<MutableList<GameEntity>> {
return mWGameSubjectCPMDataSource.getRecommendCPMList(1)
return mWGameSubjectCPMDataSource.getUserRecommendCPMList()
}
}

View File

@ -11,7 +11,7 @@ class WGameSubjectCPMListRepository(
) : ISubjectListRepository {
override fun getColumn(column_id: String?, page: Int, sort: String?, order: String?): Single<MutableList<GameEntity>> {
return dataSource.getRecommendCPMList(page)
return dataSource.getEditorRecommendCPMList(page)
}
override fun getColumnSettings(column_id: String?): Observable<SubjectSettingEntity> {

View File

@ -14,7 +14,50 @@ class WGameSubjectCPMRemoteDataSource(
private val api: WGameCPMApiService = RetrofitManager.getInstance().wGameCPMApi
) {
fun getRecommendCPMList(page: Int, pageSize: Int = 10): Single<MutableList<GameEntity>> {
fun getEditorRecommendCPMList(page: Int, pageSize: Int = 10): Single<MutableList<GameEntity>> {
val meta = MetaUtil.getMeta()
val request = mapOf(
"head" to mapOf(
"busiAppid" to Config.WGAME_CPM_BUSIAPPID,
"oaid" to (meta.oaid ?: ""),
"manufacturer" to (meta.manufacturer ?: ""),
"mode" to (meta.model ?: ""),
"androidId" to (MetaUtil.getAndroidId()),
"imei" to (MetaUtil.getIMEI())
),
"body" to mapOf(
"page" to page - 1,
"pageSize" to pageSize,
)
)
return api.getEditorRecommendList(request.toRequestBody())
.map {
if (it.ret == 0) {
it.appInfoList.map { info ->
GameEntity(
mName = info.appName,
mIcon = info.logo,
mBrief = info.briefIntro,
miniGameUid = info.appID,
miniGameAppId = info.userName,
miniGameType = Constants.WECHAT_MINI_GAME_CPM,
miniGameAppStatus = 2,
miniGameAppPath = info.wechatAppPath,
miniGameExtData = info.extData,
miniGameRecommendId = info.recommendID,
mTagStyle = arrayListOf(
TagStyleEntity(name = info.categoryName),
TagStyleEntity(name = info.subcategoryName)
)
)
}.toMutableList()
} else {
mutableListOf()
}
}
}
fun getUserRecommendCPMList(page: Int = 1, pageSize: Int = 10): Single<MutableList<GameEntity>> {
val meta = MetaUtil.getMeta()
val request = mapOf(
"head" to mapOf(

View File

@ -107,6 +107,7 @@ class SearchGameResultAdapter(
return when {
oldItem?.subject != null && newItem?.subject != null -> {
oldItem.subject.columnId == newItem.subject.columnId
&& oldItem.subject.games == newItem.subject.games
}
oldItem?.game != null && newItem?.game != null -> {
@ -121,6 +122,7 @@ class SearchGameResultAdapter(
return when {
oldItem?.subject != null && newItem?.subject != null -> {
oldItem.subject.columnId == newItem.subject.columnId
&& oldItem.subject.games == newItem.subject.games
}
oldItem?.game != null && newItem?.game != null -> {
@ -169,39 +171,50 @@ class SearchGameResultAdapter(
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is SearchSubjectItemViewHolder -> {
holder.binding.run {
when {
position == 0 -> topDivider.visibility = View.GONE
position > 0 -> {
val gameEntity = mEntityList[position - 1].game
if (gameEntity != null) {
val isShowTag = gameEntity.contentTag != null
&& (gameEntity.contentTag!!.custom.isNotEmpty()
|| gameEntity.contentTag!!.zone.link.isNotEmpty()
|| gameEntity.contentTag!!.isLibaoExists
|| gameEntity.contentTag!!.server)
val isShowTagByMirror =
if (gameEntity.shouldUseMirrorInfo()) isShowTag && gameEntity.obtainMirrorData()?.contentTagStatus == "on" else isShowTag
if (isShowTagByMirror) {
topDivider.visibility = View.GONE
val itemData = mEntityList[position]
if (itemData.subject == null || itemData.subject.games.isEmpty()) {
holder.binding.topDivider.visibility = View.GONE
holder.binding.bottomDivider.visibility = View.GONE
holder.binding.headContainer.root.visibility = View.GONE
holder.binding.subjectRv.visibility = View.GONE
} else {
holder.binding.headContainer.root.visibility = View.VISIBLE
holder.binding.subjectRv.visibility = View.VISIBLE
holder.binding.run {
when {
position == 0 -> topDivider.visibility = View.GONE
position > 0 -> {
val gameEntity = mEntityList[position - 1].game
if (gameEntity != null) {
val isShowTag = gameEntity.contentTag != null
&& (gameEntity.contentTag!!.custom.isNotEmpty()
|| gameEntity.contentTag!!.zone.link.isNotEmpty()
|| gameEntity.contentTag!!.isLibaoExists
|| gameEntity.contentTag!!.server)
val isShowTagByMirror =
if (gameEntity.shouldUseMirrorInfo()) isShowTag && gameEntity.obtainMirrorData()?.contentTagStatus == "on" else isShowTag
if (isShowTagByMirror) {
topDivider.visibility = View.GONE
} else {
topDivider.visibility = View.VISIBLE
}
} else {
topDivider.visibility = View.VISIBLE
topDivider.visibility = View.GONE
}
} else {
topDivider.visibility = View.GONE
}
}
bottomDivider.visibility = View.VISIBLE
}
bottomDivider.visibility = View.VISIBLE
holder.bindSubjectItem(
mContext,
itemData,
SearchType.fromString(type).toChinese(),
key,
dao,
sourceEntrance = sourceEntrance
)
}
holder.bindSubjectItem(
mContext,
mEntityList[position],
SearchType.fromString(type).toChinese(),
key,
dao,
sourceEntrance = sourceEntrance
)
}
is SearchGameFirstItemViewHolder -> {

View File

@ -36,6 +36,6 @@ class SearchGameResultRepository(
}
override fun getWGameCPMGameList(): Single<MutableList<GameEntity>> {
return mWGameSubjectCPMDataSource.getRecommendCPMList(1)
return mWGameSubjectCPMDataSource.getUserRecommendCPMList()
}
}

View File

@ -76,40 +76,39 @@ class SearchGameResultViewModel(
refreshWrongInstallStatus()
repository.getSearchSubject(mSearchKey, mPage)
.map { dataList ->
mSearchSubjects.addAll(dataList)
var cpmSearchSubject: SearchSubjectEntity? = null
mSearchSubjects.forEach {
// 微信小游戏CPM专题需要等搜索广告位插入完成后再插入
if (it.location == WGAME_CPM_SUBJECT_POSITION) {
cpmSearchSubject = it.apply { isWGameSubjectCPM = true }
} else {
val item = SearchItemData(subject = it)
if (it.location <= 0 || it.location > itemDataList.size) {
itemDataList.add(item)
} else {
itemDataList.add(it.location - 1, item)
}
}
}
cpmSearchSubject to dataList
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ result ->
.subscribe({ mutableList ->
val cpmSearchSubjects = mutableListOf<SearchSubjectEntity>()
mSearchSubjects.addAll(mutableList)
mSearchSubjects.forEach {
if (it.type == SearchSubjectEntity.TYPE_WECHAT_GAME_CPM_COLUMN) {
cpmSearchSubjects.add(it.apply { isWGameSubjectCPM = true })
}
val item = SearchItemData(subject = it)
if (it.location <= 0 || it.location > itemDataList.size) {
itemDataList.add(item)
} else {
itemDataList.add(it.location - 1, item)
}
}
// 处理初始化列表且游戏列表size为0的情况
handleLoadStatusWhenGameListIsEmpty(list, itemDataList)
if (mIsManuallySearch) {
if (mSearchKey == AdDelegateHelper.gameSearchKeyword) {
updateAdConfigAndDecorateList(itemDataList, list, result.first)
updateAdConfigAndDecorateList(itemDataList, list)
} else {
AdDelegateHelper.requestAdConfig(false, mSearchKey ?: "") {
updateAdConfigAndDecorateList(itemDataList, list, result.first)
updateAdConfigAndDecorateList(itemDataList, list)
}
}
} else {
decorateWithWGameSubjectCPM(itemDataList, list, result.first)
postResultList(itemDataList, list)
}
if (cpmSearchSubjects.isNotEmpty()) {
decorateWithWGameCPMList(cpmSearchSubjects, itemDataList, list)
}
}, {
it.printStackTrace()
@ -118,12 +117,51 @@ class SearchGameResultViewModel(
})
}
/**
* 请求微信小游戏CPM接口获取专题游戏数据并插入对应的专题项中相关需求如下
* @see <a href="https://jira.shanqu.cc/browse/GHZSCY-6710">【光环助手】CPM微信小游戏API接入工作</a>
* @see <a href="https://jira.shanqu.cc/browse/GHZSCY-6827">【光环助手】CPM微信小游戏一期优化</a>
*/
@SuppressLint("CheckResult")
private fun updateAdConfigAndDecorateList(
private fun decorateWithWGameCPMList(
subjects: List<SearchSubjectEntity>,
itemDataList: ArrayList<SearchItemData>,
list: MutableList<GameEntity>,
cpmSubjectEntity: SearchSubjectEntity?
list: MutableList<GameEntity>
) {
val subjectList = subjects.filterNot {
it.games.isNotEmpty()
}
if (subjectList.isEmpty()) return
val subjectSingleList = subjectList.map { subject ->
repository.getWGameCPMGameList()
.onErrorReturnItem(mutableListOf())
.map { subject.columnId to it }
}
Single.zip(subjectSingleList) { it.map { item -> item as Pair<String, MutableList<GameEntity>> } }
.map { dataList ->
val decoratedItemDataList = mResultLiveData.value?.toArrayList() ?: itemDataList
for (index in decoratedItemDataList.indices) {
val itemData = decoratedItemDataList[index]
val subject = itemData.subject ?: continue
if (subject.games.isNotEmpty()) continue
val pair = dataList.firstOrNull { data ->
data.first == subject.columnId
} ?: continue
val newItemData = SearchItemData(
subject = subject.copy(games = pair.second)
)
decoratedItemDataList[index] = newItemData
}
decoratedItemDataList
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ dataList -> postResultList(dataList, list) }, {})
}
@SuppressLint("CheckResult")
private fun updateAdConfigAndDecorateList(itemDataList: ArrayList<SearchItemData>, list: MutableList<GameEntity>) {
mGameSearchAdList =
AdDelegateHelper.getGameSearchAdList().filter { AdDelegateHelper.shouldShowGameSearchAd(it) }.toArrayList()
.apply { sortBy { it.position } }
@ -137,7 +175,6 @@ class SearchGameResultViewModel(
mAdPositionSet = adPositionSet
if (adPositionSet.isNotEmpty()) {
val decoratedItemDataList = ArrayList(itemDataList)
val ownerAdList = arrayListOf<AdConfig>()
val thirdPartyAdList = arrayListOf<AdConfig>()
@ -175,18 +212,18 @@ class SearchGameResultViewModel(
Single.zip(requestSingleList) {}
.compose(singleToMain())
.subscribe({
decorateListWithAd(itemDataList, decoratedItemDataList, list, cpmSubjectEntity)
decorateListWithAd(itemDataList, list)
}, {
decorateListWithAd(itemDataList, decoratedItemDataList, list, cpmSubjectEntity)
decorateListWithAd(itemDataList, list)
})
} else {
decorateListWithAd(itemDataList, decoratedItemDataList, list, cpmSubjectEntity)
decorateListWithAd(itemDataList, list)
}
} else {
decorateListWithThirdPartyAdOnly(decoratedItemDataList, thirdPartyAdList, list, cpmSubjectEntity)
decorateListWithThirdPartyAdOnly(itemDataList, thirdPartyAdList, list)
}
} else {
decorateWithWGameSubjectCPM(itemDataList, list, cpmSubjectEntity)
postResultList(itemDataList, list)
}
}
@ -197,27 +234,26 @@ class SearchGameResultViewModel(
}
private fun decorateListWithThirdPartyAdOnly(
decoratedItemDataList: ArrayList<SearchItemData>,
itemDataList: ArrayList<SearchItemData>,
thirdPartyAdList: List<AdConfig>,
list: List<GameEntity>,
cpmSubjectEntity: SearchSubjectEntity?
list: List<GameEntity>
) {
thirdPartyAdList.forEach {
decoratedItemDataList.add(it.position - 1, SearchItemData(ad = it.thirdPartyAd, adConfig = it))
itemDataList.add(it.position - 1, SearchItemData(ad = it.thirdPartyAd, adConfig = it))
SPUtils.setLong(Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + it.position, System.currentTimeMillis())
}
decorateWithWGameSubjectCPM(decoratedItemDataList, list, cpmSubjectEntity)
postResultList(itemDataList, list)
}
private fun decorateListWithAd(
itemDataList: ArrayList<SearchItemData>,
decoratedItemDataList: ArrayList<SearchItemData>,
list: List<GameEntity>,
cpmSubjectEntity: SearchSubjectEntity?
list: List<GameEntity>
) {
val adGameOneIdSet = HashSet<String>() // 展示样式为单个游戏时记录游戏ID避免重复
val decoratedItemDataList = mResultLiveData.value?.toArrayList() ?: itemDataList
val decoratedItemDataSize = decoratedItemDataList.size
for ((index, position) in mAdPositionSet!!.withIndex()) {
if (position < itemDataList.size + index + 1) {
if (position < decoratedItemDataSize + index + 1) {
val adConfig = mGameSearchAdList!!.safelyGetInRelease(index)
val showThirdPartyAd = adConfig?.displayRule?.adSource == AdDelegateHelper.AD_TYPE_SDK
val showOwnerAd = adConfig?.displayRule?.adSource == AdDelegateHelper.AD_TYPE_OWNER
@ -281,41 +317,7 @@ class SearchGameResultViewModel(
break
}
}
decorateWithWGameSubjectCPM(decoratedItemDataList, list, cpmSubjectEntity)
}
@SuppressLint("CheckResult")
private fun decorateWithWGameSubjectCPM(
resultList: ArrayList<SearchItemData>,
list: List<GameEntity>,
cpmSubjectEntity: SearchSubjectEntity?
) {
// 微信小游戏CPM专题搜索结果存在则请求CPM接口获取微信小游戏列表数据并将列表数据插入缓存的CPM专题中
// 再根据location的值固定为4将CPM专题插入搜索结果列表中
// 相关需求https://jira.shanqu.cc/browse/GHZSCY-6710
cpmSubjectEntity?.let { subject ->
if (subject.games.isNotEmpty()) {
Single.just(subject.games.toMutableList())
} else {
repository.getWGameCPMGameList()
.onErrorReturnItem(mutableListOf())
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
val cpmSearchItemData = SearchItemData(subject = subject.apply { games = it })
if (subject.location <= 0 || subject.location > resultList.size) {
resultList.add(cpmSearchItemData)
} else {
resultList.add(subject.location - 1, cpmSearchItemData)
}
postResultList(resultList, list)
},
{
postResultList(resultList, list)
})
} ?: postResultList(resultList, list)
postResultList(decoratedItemDataList, list)
}
@SuppressLint("CheckResult")
@ -431,7 +433,6 @@ class SearchGameResultViewModel(
companion object {
const val AD_SUBJECT_GAME_MAX_COUNT = 8
const val WGAME_CPM_SUBJECT_POSITION = 4
}
}

View File

@ -9,6 +9,9 @@ import retrofit2.http.POST
interface WGameCPMApiService {
@POST("geteditorrecommend")
fun getEditorRecommendList(@Body body: RequestBody): Single<WXMiniGameCPMEntity>
@POST("getuserrecommend")
fun getUserRecommendList(@Body body: RequestBody): Single<WXMiniGameCPMEntity>

View File

@ -9,13 +9,6 @@ build_time_without_divider=$(TZ=Asia/Shanghai date +'%Y%m%d%H%M')L
post_init_script=init.internal.gradle
# 开启 mapping 上传
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' '1 a plugins { id "io.sentry.android.gradle" version "3.7.0" } ' app/build.gradle
else
sed -i '1 a plugins { id "io.sentry.android.gradle" version "3.7.0" }' app/build.gradle
fi
git checkout module_common/build.gradle
git checkout gradle.properties