Compare commits
66 Commits
pack/updat
...
v5.38.5-11
| Author | SHA1 | Date | |
|---|---|---|---|
| 448160d255 | |||
| 533f93a340 | |||
| d80b8a97a3 | |||
| f2e6d98788 | |||
| 238a83c5fe | |||
| ca71f23363 | |||
| c96b41c621 | |||
| e8ee63d52c | |||
| 90359cfffd | |||
| a3da883033 | |||
| a9f1437a52 | |||
| 8c95286fe5 | |||
| 8a835a94e3 | |||
| 14ec4aed3a | |||
| 07f956a1f0 | |||
| 6ef8d04e57 | |||
| 30248ef205 | |||
| 87b9bb0bf3 | |||
| b346882bfa | |||
| eeddb5ea51 | |||
| 78f9aa3ee4 | |||
| f34ad0675d | |||
| 36abcc9f19 | |||
| ae878eddf1 | |||
| ece519115b | |||
| b9e0a8e37a | |||
| 34ca8896ae | |||
| d6e19bfaff | |||
| 943fb4d4e0 | |||
| 2c5471d524 | |||
| cf527db60e | |||
| f261991a55 | |||
| ab66621751 | |||
| af262c624a | |||
| d2813ecbda | |||
| 0ea932e36d | |||
| 68151ed6f9 | |||
| e6d2361008 | |||
| 570b777c8e | |||
| 0e557b2246 | |||
| f155440814 | |||
| 62ba9fc7bf | |||
| 4637aa8808 | |||
| 81998e3aad | |||
| 82a8aa03ba | |||
| c876711578 | |||
| 41fcee7f4d | |||
| 0ccbc4d581 | |||
| f05fc73e3a | |||
| ca3c545f26 | |||
| 226539328e | |||
| bb7db78b0c | |||
| ede62c5363 | |||
| 12bb6824ea | |||
| 15c0950754 | |||
| f86b7fe12a | |||
| d12cdbd34b | |||
| 8436d4eda3 | |||
| be832037a7 | |||
| b620257825 | |||
| 6c88cace99 | |||
| f875fa1b14 | |||
| 8076c3a70a | |||
| bebab317a3 | |||
| c450ca570d | |||
| d239b0755f |
@ -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
|
||||
|
||||
@ -663,7 +663,8 @@
|
||||
android:name=".authorization.AuthorizationActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait">
|
||||
android:screenOrientation="portrait"
|
||||
android:taskAffinity=".auth">
|
||||
<intent-filter>
|
||||
<data android:scheme="ghzhushou_authorization" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
@ -31,6 +31,7 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
|
||||
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
|
||||
if (fragment == f) {
|
||||
visibleState?.let { commitExposure(it) }
|
||||
visibleState?.let { commitWXCPMExposure(it) }
|
||||
throttleBus.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
package com.gh.common.fragment
|
||||
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import java.lang.reflect.Field
|
||||
|
||||
fun FragmentManager.popBackStackAllowStateLoss() {
|
||||
popBackStackAllowStateLoss(-1, 0)
|
||||
}
|
||||
|
||||
fun FragmentManager.popBackStackAllowStateLoss(id: Int, flags: Int) {
|
||||
if (!isStateSaved) {
|
||||
popBackStack(id, flags)
|
||||
} else {
|
||||
hook { popBackStack(id, flags) }
|
||||
}
|
||||
}
|
||||
|
||||
fun FragmentManager.popBackStackAllowStateLoss(name: String?, flags: Int) {
|
||||
if (!isStateSaved) {
|
||||
popBackStack(name, flags)
|
||||
} else {
|
||||
hook { popBackStack(name, flags) }
|
||||
}
|
||||
}
|
||||
|
||||
fun FragmentManager.popBackStackImmediateAllowStateLoss() = popBackStackAllowStateLoss(-1, 0)
|
||||
|
||||
fun FragmentManager.popBackStackImmediateAllowStateLoss(id: Int, flags: Int) =
|
||||
if (!isStateSaved) {
|
||||
popBackStackImmediate(id, flags)
|
||||
} else {
|
||||
hook { popBackStackImmediate(id, flags) }
|
||||
}
|
||||
|
||||
fun FragmentManager.popBackStackImmediateAllowStateLoss(name: String?, flags: Int): Boolean =
|
||||
if (!isStateSaved) {
|
||||
popBackStackImmediate(name, flags)
|
||||
} else {
|
||||
hook { popBackStackImmediate(name, flags) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射将FragmentManager的mStateSaved和mStopped设为false,否则Activity在回调onSavedInstance以后,
|
||||
* 调用Fragment的popBackStack和popBackStackImmediate方法会触发“java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState”的异常。
|
||||
* @see <a href="https://sentry.shanqu.cc/organizations/lightgame/issues/418688/?project=22">Sentry-418688</a>
|
||||
*/
|
||||
private fun <T> FragmentManager.hook(callback: FragmentManager.() -> T): T {
|
||||
val mStateSavedField = getField(this::class.java,"mStateSaved")
|
||||
val stateSaved = mStateSavedField.get(this);
|
||||
mStateSavedField.set(this, false)
|
||||
val mStoppedField = getField(this::class.java,"mStopped")
|
||||
val stopped = mStateSavedField.get(this);
|
||||
mStoppedField.set(this, false)
|
||||
val result = callback.invoke(this)
|
||||
mStateSavedField.set(this, stateSaved)
|
||||
mStoppedField.set(this, stopped)
|
||||
return result
|
||||
}
|
||||
|
||||
@Throws(NoSuchFieldException::class)
|
||||
private fun getField(clazz: Class<*>, name: String): Field {
|
||||
var cls: Class<*>? = clazz
|
||||
while (cls != null) {
|
||||
try {
|
||||
val declaredField = cls.getDeclaredField(name)
|
||||
declaredField.isAccessible = true
|
||||
return declaredField
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
cls = cls.superclass
|
||||
}
|
||||
throw NoSuchFieldException()
|
||||
}
|
||||
@ -1,12 +1,9 @@
|
||||
package com.gh.common.iinterface
|
||||
|
||||
interface ISmartRefreshContent {
|
||||
/**
|
||||
* 启用/关闭 页面滑动
|
||||
* @param isScrollEnabled 是否启用
|
||||
*/
|
||||
fun setScrollEnabled(isScrollEnabled: Boolean)
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.scwang.smartrefresh.layout.constant.RefreshState
|
||||
|
||||
interface ISmartRefreshContent {
|
||||
fun onRefresh()
|
||||
|
||||
/**
|
||||
@ -14,4 +11,6 @@ interface ISmartRefreshContent {
|
||||
* @param isSwipeRefreshEnabled 是否启用
|
||||
*/
|
||||
fun setSwipeRefreshEnabled(isSwipeRefreshEnabled: Boolean)
|
||||
|
||||
fun onStateChanged(refreshLayout: RefreshLayout, oldState: RefreshState, newState: RefreshState)
|
||||
}
|
||||
@ -115,6 +115,12 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider {
|
||||
"本地下载"
|
||||
}
|
||||
|
||||
// 小游戏的启动不需要上报下载点击事件
|
||||
// @see https://jira.shanqu.cc/browse/GHZSCY-7013
|
||||
if (boundedObject is GameEntity && boundedObject.isMiniGame()) {
|
||||
return
|
||||
}
|
||||
|
||||
// 上报神策点击事件
|
||||
val customPageKV = customPageTrackData?.toKV() ?: arrayOf()
|
||||
SensorsBridge.trackEventWithExposureSource(
|
||||
@ -124,7 +130,7 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider {
|
||||
"game_name", gameName,
|
||||
"game_type", gameTypeInChinese,
|
||||
"download_status", downloadStatusInChinese,
|
||||
"button_name", downloadButton.text,
|
||||
"button_name", text,
|
||||
"game_schema_type", gameSchemaType,
|
||||
"download_type", downloadType,
|
||||
"page_name", GlobalActivityManager.getCurrentPageEntity().pageName,
|
||||
|
||||
@ -16,6 +16,7 @@ import com.gh.gamecenter.common.constant.Constants;
|
||||
import com.gh.gamecenter.common.constant.RouteConsts;
|
||||
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse;
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge;
|
||||
import com.gh.gamecenter.core.AppExecutor;
|
||||
import com.gh.gamecenter.core.provider.ISentryProvider;
|
||||
import com.gh.gamecenter.core.utils.GsonUtils;
|
||||
@ -39,6 +40,11 @@ import io.reactivex.schedulers.Schedulers;
|
||||
*/
|
||||
public class DataUtils {
|
||||
|
||||
// 神策 OAID 是否已绑定
|
||||
private static boolean isSensorOAIDBounded = false;
|
||||
// 原始的 OAID 是否已成功获取
|
||||
private static boolean originalOAIDIsReceived = false;
|
||||
|
||||
private DataUtils() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
@ -65,7 +71,6 @@ public class DataUtils {
|
||||
// 默认用 APP 级已存储的 GID 来使用,不使用外部 GID
|
||||
String savedGid = SPUtils.getString(Constants.GID);
|
||||
if (!TextUtils.isEmpty(savedGid)) {
|
||||
HaloApp.getInstance().setGid(savedGid);
|
||||
onGidReceived(savedGid);
|
||||
} else {
|
||||
GidHelper.getInstance().registerDevice(HaloApp.getInstance().getApplication(), new GidCallback() {
|
||||
@ -89,6 +94,8 @@ public class DataUtils {
|
||||
}
|
||||
|
||||
private static void onGidReceived(String gid) {
|
||||
bindValidOaidToSensor(false);
|
||||
|
||||
HaloApp.getInstance().setGid(gid);
|
||||
// 更新广告配置
|
||||
AdDelegateHelper.INSTANCE.requestAdConfig(false, "", null);
|
||||
@ -111,6 +118,35 @@ public class DataUtils {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 为神策绑定有效的 OAID
|
||||
*/
|
||||
public static void bindValidOaidToSensor(boolean fromOaidResult) {
|
||||
if (isSensorOAIDBounded) return;
|
||||
|
||||
String oaid = HaloApp.getInstance().getOAID();
|
||||
|
||||
// 来自于 oaid 获取回调,或者说原始 oaid 已经获取成功
|
||||
if (fromOaidResult || originalOAIDIsReceived) {
|
||||
originalOAIDIsReceived = true;
|
||||
// 遇到异常的 OAID
|
||||
if (Constants.INVALID_OAID_1.equals(oaid)
|
||||
|| Constants.INVALID_OAID_2.equals(oaid)
|
||||
|| Constants.INVALID_OAID_3.equals(oaid)
|
||||
|| TextUtils.isEmpty(oaid)) {
|
||||
// 若 gid 不为空,那么整合 gid 作为 oaid https://jira.shanqu.cc/browse/GHZSCY-7004
|
||||
if (HaloApp.getInstance().getGid() != null) {
|
||||
oaid = "GID" + HaloApp.getInstance().getGid();
|
||||
SensorsBridge.INSTANCE.setOAID(oaid);
|
||||
isSensorOAIDBounded = true;
|
||||
}
|
||||
} else {
|
||||
SensorsBridge.INSTANCE.setOAID(oaid);
|
||||
isSensorOAIDBounded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用 gid 绑定的实名信息
|
||||
*/
|
||||
|
||||
@ -139,6 +139,7 @@ public class DetailDownloadUtils {
|
||||
// 游戏包含多 APK 的情况
|
||||
viewHolder.getMultiVersionDownloadTv().setText("选择下载你的版本" + (TextUtils.isEmpty(downloadAddWord) ? "" : "-" + downloadAddWord));
|
||||
viewHolder.getMultiVersionDownloadTv().setVisibility(View.VISIBLE);
|
||||
viewHolder.getDownloadPb().setTag(com.gh.gamecenter.feature.R.string.download, viewHolder.getMultiVersionDownloadTv().getText());
|
||||
viewHolder.getDownloadPb().setText("");
|
||||
viewHolder.getDownloadPb().setButtonStyle(DownloadButton.ButtonStyle.NORMAL);
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity);
|
||||
|
||||
@ -284,7 +284,12 @@ object PackageInstaller {
|
||||
installIntent.setDataAndType(uri, "application/vnd.android.package-archive")
|
||||
}
|
||||
|
||||
updateSystemInstallerIfAvailable(context, installIntent)
|
||||
// 优选系统的安装器(遇到 Exception 就回落到正常的安装器)
|
||||
try {
|
||||
updateSystemInstallerIfAvailable(context, installIntent)
|
||||
} catch (ignored: Exception) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
InstallUtils.getInstance()
|
||||
.addInstall(PackageUtils.getPackageNameByPath(context, path))
|
||||
|
||||
@ -7,7 +7,7 @@ import com.gh.gamecenter.common.utils.isVGameDownloadInDualDownloadMode
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.GameInstall
|
||||
import com.gh.gamecenter.packagehelper.PackageRepository
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.vspace.VHelper
|
||||
|
||||
object PackageLauncher {
|
||||
@ -91,7 +91,7 @@ object PackageLauncher {
|
||||
val gameInstall = if (gameEntity != null) {
|
||||
GameInstall.transformGameInstall(gameEntity, packageName)
|
||||
} else {
|
||||
PackageRepository.gameInstalled.find { it.packageName == packageName }
|
||||
PackagesManager.getInstalledList().find { it.packageName == packageName }
|
||||
}
|
||||
|
||||
if (gameInstall != null) {
|
||||
|
||||
@ -542,10 +542,8 @@ public class PackageUtils {
|
||||
try {
|
||||
Intent intent = context.getApplicationContext().getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
return intent != null;
|
||||
} catch (IllegalArgumentException exception) {
|
||||
// 一些设备调用获取 intent 的时候会触发 Parcel.readException !
|
||||
exception.printStackTrace();
|
||||
return false;
|
||||
} catch (Exception exception) {
|
||||
return PackageHelper.INSTANCE.getLocalPackageNameSet().contains(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -15,6 +15,7 @@ import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import com.gh.common.fragment.popBackStackAllowStateLoss
|
||||
import com.gh.common.util.DataCollectionUtils
|
||||
import com.gh.common.util.LogUtils
|
||||
import com.gh.gamecenter.DisplayType.*
|
||||
@ -381,7 +382,7 @@ open class SearchActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
protected fun popBackToFragment(tag: String) {
|
||||
supportFragmentManager.popBackStack(tag, 0)
|
||||
supportFragmentManager.popBackStackAllowStateLoss(tag, 0)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
||||
@ -285,7 +285,7 @@ class SplashScreenActivity : BaseActivity() {
|
||||
val pushProvider = ARouter.getInstance().build(RouteConsts.provider.push).navigation() as? IPushProvider
|
||||
val registrationId = pushProvider?.getRegistrationId(this)
|
||||
if (!registrationId.isNullOrEmpty()) {
|
||||
SensorsBridge.profileAppend(KEY_REGISTRATION_ID, registrationId)
|
||||
SensorsBridge.profileSet(KEY_REGISTRATION_ID, registrationId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -242,10 +242,10 @@ class AuthorizationActivity : ToolBarActivity() {
|
||||
VHelper.launch(this, gamePkg, ignoreGApps = true, showLoading = showLoading)
|
||||
return
|
||||
}
|
||||
val remotePkgName = this.mRemotePkgName
|
||||
if (remotePkgName != null) {// 跳转回其他授权app
|
||||
startActivity(packageManager.getLaunchIntentForPackage(remotePkgName))
|
||||
}
|
||||
// val remotePkgName = this.mRemotePkgName
|
||||
// if (remotePkgName != null) {// 跳转回其他授权app
|
||||
// startActivity(packageManager.getLaunchIntentForPackage(remotePkgName))
|
||||
// }
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
@ -983,14 +983,17 @@ class DescAdapter(
|
||||
setExpandMaxLines(maxDesLines)
|
||||
setIsExpanded(Int.MAX_VALUE == maxDesLines)
|
||||
if (customColumn.isHtmlDes == true) {
|
||||
val decoratedDesBrief = (customColumn.desBrief ?: "").dropFontColorInDarkMode(contentTv.context)
|
||||
val decoratedDes = (customColumn.des ?: "").dropFontColorInDarkMode(contentTv.context)
|
||||
|
||||
shrankSpanned = HtmlCompat.fromHtml(
|
||||
customColumn.desBrief ?: "",
|
||||
decoratedDesBrief,
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT,
|
||||
PicassoImageGetter(contentTv),
|
||||
ExtraTagHandler()
|
||||
)
|
||||
expandedSpanned = HtmlCompat.fromHtml(
|
||||
customColumn.des ?: "",
|
||||
decoratedDes,
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT,
|
||||
PicassoImageGetter(contentTv),
|
||||
ExtraTagHandler()
|
||||
|
||||
@ -25,10 +25,10 @@ import com.gh.gamecenter.common.utils.toPx
|
||||
import com.gh.gamecenter.core.iinterface.IScrollable
|
||||
import com.gh.gamecenter.core.utils.MtaHelper
|
||||
import com.gh.gamecenter.core.utils.SpanBuilder
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.RatingComment
|
||||
import com.gh.gamecenter.eventbus.EBStar
|
||||
import com.gh.gamecenter.eventbus.EBTypeChange
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.gamedetail.GameDetailFragment
|
||||
import com.halo.assistant.HaloApp
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
@ -60,7 +60,7 @@ class RatingFragment : LazyListFragment<RatingComment, RatingViewModel>(), IScro
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == RATING_EDIT_REQUEST && resultCode == Activity.RESULT_OK) {
|
||||
mListViewModel.initData()
|
||||
mListViewModel?.initData()
|
||||
} else if (
|
||||
(requestCode == RATING_REPLAY_REQUEST || requestCode == RATING_PATCH_REQUEST)
|
||||
&& resultCode == Activity.RESULT_OK
|
||||
|
||||
@ -69,6 +69,9 @@ import com.gh.gamecenter.wrapper.SearchToolbarTabWrapperViewModel
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.scwang.smartrefresh.layout.constant.RefreshState
|
||||
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
@ -745,7 +748,7 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable {
|
||||
}
|
||||
}
|
||||
|
||||
override fun setScrollEnabled(isScrollEnabled: Boolean) {
|
||||
private fun setScrollEnabled(isScrollEnabled: Boolean) {
|
||||
if (::layoutManager.isInitialized) {
|
||||
layoutManager.isScrollVerticallyEnabled = isScrollEnabled
|
||||
}
|
||||
@ -761,6 +764,32 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStateChanged(refreshLayout: RefreshLayout, oldState: RefreshState, newState: RefreshState) {
|
||||
if (oldState == RefreshState.None && newState == RefreshState.PullDownToRefresh) {
|
||||
pauseVideo()
|
||||
} else if ((oldState == RefreshState.TwoLevelFinish || oldState == RefreshState.RefreshFinish) && newState == RefreshState.None) {
|
||||
val currentPlayer = scrollCalculatorHelper.currentPlayer
|
||||
if (currentPlayer == null) {
|
||||
scrollCalculatorHelper.playIfValid()
|
||||
} else if (currentPlayer.currentState == GSYVideoView.CURRENT_STATE_PAUSE) {
|
||||
resumeVideo()
|
||||
}
|
||||
}
|
||||
|
||||
when (newState) {
|
||||
RefreshState.TwoLevel -> {
|
||||
setScrollEnabled(false)
|
||||
}
|
||||
RefreshState.None -> {
|
||||
setScrollEnabled(true)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun scrollToTop() {
|
||||
if (::binding.isInitialized) {
|
||||
binding.gameList.stopScroll()
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ class SubjectTracker(private val pageLocation: PageLocation) {
|
||||
pageLocation.pageName,
|
||||
subject?.id ?: "",
|
||||
subject?.name ?: "",
|
||||
"最近在玩",
|
||||
"自定义页面",
|
||||
item.componentStyle,
|
||||
"",
|
||||
)
|
||||
|
||||
@ -40,7 +40,7 @@ class CustomGameGallerySlideViewHolder(
|
||||
override fun bindView(item: CustomPageItem) {
|
||||
super.bindView(item)
|
||||
if (item is CustomSubjectItem) {
|
||||
|
||||
binding.cardView.setCardBackgroundColor(com.gh.gamecenter.common.R.color.ui_container_1.toColor(binding.root.context))
|
||||
if (item.data == cachedSubject) return
|
||||
|
||||
cachedSubject = item.data
|
||||
@ -77,7 +77,6 @@ class CustomGameGallerySlideViewHolder(
|
||||
val gameList = item.data.data ?: emptyList()
|
||||
(recyclerView.adapter as? GameGallerySlideAdapter)?.submitList(gameList)
|
||||
}
|
||||
binding.cardView.setCardBackgroundColor(com.gh.gamecenter.common.R.color.text_FAFAFA.toColor(binding.root.context))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -29,7 +29,10 @@ class ScrollCalculatorHelper(
|
||||
|
||||
fun enableAndPlayIfValid() {
|
||||
isEnabled = true
|
||||
playIfValid()
|
||||
}
|
||||
|
||||
fun playIfValid() {
|
||||
if (mListRv.isAttachedToWindow
|
||||
&& mListRv.scrollState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
playVideo(mListRv)
|
||||
|
||||
@ -59,7 +59,7 @@ class MiniGameSearchDefaultRankAdapter(
|
||||
_id = rank.id,
|
||||
mName = rank.name,
|
||||
miniGameAppId = rank.link.link ?: "",
|
||||
miniGameType = Constants.WECHAT_MINI_GAME,
|
||||
miniGameCategory = Constants.WECHAT_MINI_GAME,
|
||||
usage = rank.usage ?: 0
|
||||
)
|
||||
holder.binding.playCount.visibility = View.VISIBLE
|
||||
|
||||
@ -21,11 +21,15 @@ class MiniGameSearchResultRepository(
|
||||
return api.getSearchMiniGameList(key, page, 20)
|
||||
}
|
||||
|
||||
override fun getSearchMiniGameCPM(key: String?): Observable<List<GameEntity>> {
|
||||
return Observable.just(emptyList())
|
||||
}
|
||||
|
||||
override fun getSearchSubject(key: String?, page: Int): Observable<List<SearchSubjectEntity>> {
|
||||
return Observable.just(emptyList())
|
||||
}
|
||||
|
||||
override fun getWGameCPMGameList(): Single<MutableList<GameEntity>> {
|
||||
return mWGameSubjectCPMDataSource.getRecommendCPMList(1)
|
||||
return mWGameSubjectCPMDataSource.getUserRecommendCPMList()
|
||||
}
|
||||
}
|
||||
@ -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> {
|
||||
|
||||
@ -14,7 +14,51 @@ 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,
|
||||
miniGameCategory = Constants.WECHAT_MINI_GAME,
|
||||
profit = Constants.WECHAT_MINI_GAME_PROFIT_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(
|
||||
@ -40,7 +84,8 @@ class WGameSubjectCPMRemoteDataSource(
|
||||
mBrief = info.briefIntro,
|
||||
miniGameUid = info.appID,
|
||||
miniGameAppId = info.userName,
|
||||
miniGameType = Constants.WECHAT_MINI_GAME_CPM,
|
||||
miniGameCategory = Constants.WECHAT_MINI_GAME,
|
||||
profit = Constants.WECHAT_MINI_GAME_PROFIT_CPM,
|
||||
miniGameAppStatus = 2,
|
||||
miniGameAppPath = info.wechatAppPath,
|
||||
miniGameExtData = info.extData,
|
||||
|
||||
@ -318,6 +318,12 @@ public interface ApiService {
|
||||
@GET
|
||||
Observable<List<GameEntity>> getSearchGame(@Url String url);
|
||||
|
||||
/**
|
||||
* 搜索CPM微信小游戏
|
||||
*/
|
||||
@GET("mini_game/search/configs")
|
||||
Observable<List<GameEntity>> getSearchWechatMiniCPMGame(@Query("keyword") String key);
|
||||
|
||||
/**
|
||||
* 游戏搜索的专题数据
|
||||
*/
|
||||
|
||||
@ -8,6 +8,8 @@ import io.reactivex.Single
|
||||
interface ISearchGameResultRepository {
|
||||
fun getSearchGame(key: String?, page: Int): Observable<List<GameEntity>>
|
||||
|
||||
fun getSearchMiniGameCPM(key: String?): Observable<List<GameEntity>>
|
||||
|
||||
fun getSearchSubject(key: String?, page: Int): Observable<List<SearchSubjectEntity>>
|
||||
|
||||
fun getWGameCPMGameList(): Single<MutableList<GameEntity>>
|
||||
|
||||
@ -86,7 +86,7 @@ class SearchSubjectItemViewHolder(var binding: SearchSubjectItemBinding) : Recyc
|
||||
|
||||
binding.run {
|
||||
subjectRv.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
|
||||
subjectRv.adapter = SearchSubjectAdapter(context, entity.getFilterGame(), "($type-专题)", key) {
|
||||
subjectRv.adapter = SearchSubjectAdapter(context, entity.getFilterGame(), entity.columnId, entity.name, "($type-专题)", key) {
|
||||
dao?.add(key)
|
||||
if (itemData.adConfig != null) {
|
||||
NewFlatLogUtils.logClickGameAd(
|
||||
|
||||
@ -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 -> {
|
||||
|
||||
@ -10,13 +10,20 @@ import com.gh.gamecenter.retrofit.service.ApiService
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import java.net.URLEncoder
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class SearchGameResultRepository(
|
||||
private val mApi: ApiService = RetrofitManager.getInstance().api,
|
||||
private val mNewApi: ApiService = RetrofitManager.getInstance().newApi,
|
||||
private val mWGameSubjectCPMDataSource: WGameSubjectCPMRemoteDataSource = WGameSubjectCPMRemoteDataSource()
|
||||
) : ISearchGameResultRepository {
|
||||
|
||||
private var currentSearchKey: String? = null
|
||||
|
||||
private var currentMiniGameCPMSearchList: MutableList<GameEntity>? = null
|
||||
|
||||
override fun getSearchGame(
|
||||
key: String?,
|
||||
page: Int
|
||||
@ -31,11 +38,26 @@ class SearchGameResultRepository(
|
||||
)
|
||||
}
|
||||
|
||||
override fun getSearchMiniGameCPM(key: String?): Observable<List<GameEntity>> {
|
||||
val currentMiniGameCPMSearchList = currentMiniGameCPMSearchList
|
||||
if (key == currentSearchKey && currentMiniGameCPMSearchList != null) {
|
||||
return Observable.just(currentMiniGameCPMSearchList)
|
||||
}
|
||||
return mNewApi.getSearchWechatMiniCPMGame(key)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.onErrorReturnItem(emptyList())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext {
|
||||
this.currentSearchKey = key
|
||||
this.currentMiniGameCPMSearchList = it
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSearchSubject(key: String?, page: Int): Observable<List<SearchSubjectEntity>> {
|
||||
return mApi.getSearchSubject(key, page)
|
||||
}
|
||||
|
||||
override fun getWGameCPMGameList(): Single<MutableList<GameEntity>> {
|
||||
return mWGameSubjectCPMDataSource.getRecommendCPMList(1)
|
||||
return mWGameSubjectCPMDataSource.getUserRecommendCPMList()
|
||||
}
|
||||
}
|
||||
@ -67,49 +67,60 @@ class SearchGameResultViewModel(
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun decorateListAndPost(list: MutableList<GameEntity>) {
|
||||
val itemDataList = ArrayList(
|
||||
list.mapIndexed { index, game ->
|
||||
SearchItemData(game = game, gamePosition = index, isFirst = index == 0)
|
||||
}
|
||||
)
|
||||
val itemDataList = ArrayList<SearchItemData>()
|
||||
val combineGameList = list.toMutableList()
|
||||
|
||||
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 }
|
||||
repository.getSearchMiniGameCPM(mSearchKey)
|
||||
.zipWith(repository.getSearchSubject(mSearchKey, mPage)) { cpmGameList, subjectList -> // CPM游戏搜索结果列表合并
|
||||
for (cpmGame in cpmGameList) {
|
||||
if (cpmGame.location <= 0 || cpmGame.location > list.size) {
|
||||
combineGameList.add(cpmGame)
|
||||
} else {
|
||||
val item = SearchItemData(subject = it)
|
||||
if (it.location <= 0 || it.location > itemDataList.size) {
|
||||
itemDataList.add(item)
|
||||
} else {
|
||||
itemDataList.add(it.location - 1, item)
|
||||
}
|
||||
combineGameList.add(cpmGame.location - 1, cpmGame)
|
||||
}
|
||||
}
|
||||
cpmSearchSubject to dataList
|
||||
itemDataList.addAll(
|
||||
combineGameList.mapIndexed { index, game ->
|
||||
SearchItemData(game = game, gamePosition = index, isFirst = index == 0)
|
||||
}
|
||||
)
|
||||
subjectList
|
||||
}
|
||||
.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()) {
|
||||
decorateListWithWGameCPM(cpmSearchSubjects, itemDataList, list)
|
||||
}
|
||||
}, {
|
||||
it.printStackTrace()
|
||||
@ -118,12 +129,50 @@ 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 decorateListWithWGameCPM(
|
||||
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 ->
|
||||
for (index in itemDataList.indices) {
|
||||
val itemData = itemDataList[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)
|
||||
)
|
||||
itemDataList[index] = newItemData
|
||||
}
|
||||
itemDataList
|
||||
}
|
||||
.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 +186,6 @@ class SearchGameResultViewModel(
|
||||
mAdPositionSet = adPositionSet
|
||||
|
||||
if (adPositionSet.isNotEmpty()) {
|
||||
val decoratedItemDataList = ArrayList(itemDataList)
|
||||
val ownerAdList = arrayListOf<AdConfig>()
|
||||
val thirdPartyAdList = arrayListOf<AdConfig>()
|
||||
|
||||
@ -175,18 +223,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 +245,25 @@ 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 decoratedItemDataSize = itemDataList.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
|
||||
@ -225,7 +271,7 @@ class SearchGameResultViewModel(
|
||||
if ((showThirdPartyAd && adConfig?.thirdPartyAd != null)
|
||||
|| (showOwnerAd && adConfig?.ownerAd == null && adConfig?.thirdPartyAd != null && showOnFailed)
|
||||
) {
|
||||
decoratedItemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd, adConfig = adConfig))
|
||||
itemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd, adConfig = adConfig))
|
||||
SPUtils.setLong(
|
||||
Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + adConfig.position,
|
||||
System.currentTimeMillis()
|
||||
@ -237,7 +283,7 @@ class SearchGameResultViewModel(
|
||||
if (!gameList.isNullOrEmpty()) {
|
||||
if (adConfig.ownerAd.adSource?.displayStyle == "game_zone") {
|
||||
// 游戏专题
|
||||
decoratedItemDataList.add(
|
||||
itemDataList.add(
|
||||
position - 1,
|
||||
SearchItemData(
|
||||
subject = SearchSubjectEntity(
|
||||
@ -259,7 +305,7 @@ class SearchGameResultViewModel(
|
||||
}
|
||||
randomGameEntity?.id?.let { adGameOneIdSet.add(it) }
|
||||
|
||||
decoratedItemDataList.add(
|
||||
itemDataList.add(
|
||||
position - 1,
|
||||
SearchItemData(game = randomGameEntity, adConfig = adConfig)
|
||||
)
|
||||
@ -270,7 +316,7 @@ class SearchGameResultViewModel(
|
||||
)
|
||||
} else if (showOnFailed && adConfig.thirdPartyAd != null) {
|
||||
// 自有广告为空时,显示第三方广告
|
||||
decoratedItemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd, adConfig = adConfig))
|
||||
itemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd, adConfig = adConfig))
|
||||
SPUtils.setLong(
|
||||
Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + adConfig.position,
|
||||
System.currentTimeMillis()
|
||||
@ -281,41 +327,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(itemDataList, list)
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
@ -343,7 +355,7 @@ class SearchGameResultViewModel(
|
||||
}
|
||||
|
||||
private fun postResultList(resultList: ArrayList<SearchItemData>, list: List<GameEntity>) {
|
||||
mResultLiveData.postValue(resultList)
|
||||
mResultLiveData.postValue(resultList.toMutableList())
|
||||
if (mPage == 1) {
|
||||
if (repository is MiniGameSearchResultRepository) {
|
||||
SensorsBridge.trackMiniGameSearchResultReturn(
|
||||
@ -362,7 +374,6 @@ class SearchGameResultViewModel(
|
||||
mSearchKey ?: "",
|
||||
SearchActivity.toTrackSearchType(mSearchType),
|
||||
list.isNotEmpty()
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@ -431,7 +442,6 @@ class SearchGameResultViewModel(
|
||||
|
||||
companion object {
|
||||
const val AD_SUBJECT_GAME_MAX_COUNT = 8
|
||||
const val WGAME_CPM_SUBJECT_POSITION = 4
|
||||
}
|
||||
|
||||
}
|
||||
@ -13,6 +13,8 @@ import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
class SearchSubjectAdapter(
|
||||
context: Context,
|
||||
private val mList: List<GameEntity>,
|
||||
private val mColumnId: String,
|
||||
private val mColumnName: String,
|
||||
private val mEntrance: String,
|
||||
private val mKey: String,
|
||||
private val clickCallback: ((GameEntity) -> Unit)
|
||||
@ -40,7 +42,10 @@ class SearchSubjectAdapter(
|
||||
MiniGameItemHelper.launchMiniGame(gameEntity)
|
||||
MiniGameItemHelper.trackMiniGameClick(
|
||||
gameEntity = gameEntity,
|
||||
gameColumnId = mColumnId,
|
||||
gameColumnName = mColumnName,
|
||||
location = "小游戏搜索结果列表",
|
||||
position = position,
|
||||
searchContent = mKey,
|
||||
)
|
||||
} else {
|
||||
|
||||
@ -9,6 +9,7 @@ import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.clearHtmlFormatCompletely
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.home.custom.model.CustomPageData
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
@ -141,7 +142,7 @@ class SearchGameListViewModel : ViewModel() {
|
||||
SearchActivity.toTrackSearchType(type),
|
||||
location,
|
||||
collection.id,
|
||||
collection.title
|
||||
collection.title.clearHtmlFormatCompletely()
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -102,6 +102,7 @@ abstract class BaseTabWrapperFragment : BaseLazyFragment(), IMultiTab {
|
||||
noDataStub = mCachedView.findViewById(R.id.reuse_no_data_stub)
|
||||
noConnectionStub = mCachedView.findViewById(R.id.reuse_no_connection_stub)
|
||||
loadingStub = mCachedView.findViewById(R.id.reuse_loading_stub)
|
||||
showLoading(true)
|
||||
|
||||
backgroundColor = com.gh.gamecenter.common.R.color.ui_background.toColor(requireContext())
|
||||
backgroundWhiteColor = com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext())
|
||||
|
||||
@ -61,6 +61,7 @@ import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.exposure.time.TimeUtil
|
||||
import com.gh.gamecenter.feature.game.GameItemViewHolder
|
||||
import com.gh.gamecenter.feature.utils.SentryHelper
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment
|
||||
import com.gh.gamecenter.home.custom.CustomPageFragment
|
||||
import com.gh.gamecenter.home.video.AutomaticVideoView
|
||||
@ -214,7 +215,16 @@ class SearchToolbarTabWrapperFragment : BaseTabWrapperFragment(), ISearchToolbar
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
binding = FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater)
|
||||
binding = try {
|
||||
FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater)
|
||||
} catch (e: Exception) {
|
||||
SentryHelper.onEvent("VIEW_BINDING_BIND_ERROR",
|
||||
"digest", e.localizedMessage,
|
||||
"gid", HaloApp.getInstance().gid
|
||||
)
|
||||
// 玄学,重试一次,该闪退闪退
|
||||
FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater)
|
||||
}
|
||||
mCachedView = binding.root
|
||||
return mCachedView
|
||||
}
|
||||
@ -557,6 +567,8 @@ class SearchToolbarTabWrapperFragment : BaseTabWrapperFragment(), ISearchToolbar
|
||||
oldState: RefreshState,
|
||||
newState: RefreshState
|
||||
) {
|
||||
getValidSmartRefreshContent()?.onStateChanged(refreshLayout, oldState, newState)
|
||||
|
||||
val isAutoScrollToTwoLevel = oldState == RefreshState.None && newState == RefreshState.TwoLevel
|
||||
val isDragToTwoLevel =
|
||||
oldState == RefreshState.ReleaseToRefresh && newState == RefreshState.ReleaseToTwoLevel
|
||||
@ -654,49 +666,17 @@ class SearchToolbarTabWrapperFragment : BaseTabWrapperFragment(), ISearchToolbar
|
||||
|
||||
if (isFinishTwoLevel) {
|
||||
pausePullDownPushVideo()
|
||||
showTwoLevel = false
|
||||
|
||||
if (isDragging) {
|
||||
autoFinishTwoLevelHandler?.removeMessages(KEY_AUTO_FINISH_TWO_LEVEL)
|
||||
elapsedHelper.pauseCounting()
|
||||
pullDownPush?.run {
|
||||
SensorsBridge.trackEvent(
|
||||
"DropDownPushClick",
|
||||
json {
|
||||
"action" to pullDownPushAction
|
||||
"button_name" to "关闭推送"
|
||||
"drop_down_push_id" to id
|
||||
"game_name" to game?.name
|
||||
"game_id" to game?.id
|
||||
"bottom_tab" to bottomTabName
|
||||
"several_tab_page_name" to multiTabNavName
|
||||
"several_tab_page_id" to multiTabNavId
|
||||
"position" to lastSelectedPosition
|
||||
"tab_content" to currentTabEntity?.name
|
||||
"custom_page_name" to currentTabEntity?.link?.text
|
||||
"custom_page_id" to currentTabEntity?.link?.link
|
||||
}
|
||||
)
|
||||
com.gh.common.util.NewFlatLogUtils.logHomePushClose(
|
||||
id,
|
||||
"主动收起",
|
||||
twoLevelOpenCount,
|
||||
game?.id ?: "",
|
||||
game?.name ?: "",
|
||||
elapsedHelper.elapsedTime
|
||||
)
|
||||
}
|
||||
finishTwoLevel("主动收起")
|
||||
}
|
||||
showTwoLevel = false
|
||||
}
|
||||
|
||||
when (newState) {
|
||||
RefreshState.TwoLevel -> {
|
||||
getValidSmartRefreshContent()?.setScrollEnabled(false)
|
||||
binding.refreshLayout.isDisableContent = false
|
||||
}
|
||||
RefreshState.None -> {
|
||||
getValidSmartRefreshContent()?.setScrollEnabled(true)
|
||||
|
||||
showTwoLevel = false
|
||||
isDragging = false
|
||||
twoLevelHeader.setFloorDuration(1000)
|
||||
@ -940,6 +920,7 @@ class SearchToolbarTabWrapperFragment : BaseTabWrapperFragment(), ISearchToolbar
|
||||
classicsHeader.visibility = View.INVISIBLE
|
||||
pullDownPushSet.add(id)
|
||||
SPUtils.setStringSet(Constants.SP_PULL_DOWN_PUSH_POP_UP_SET, pullDownPushSet)
|
||||
autoFinishTwoLevelCallback = finishCallback
|
||||
autoFinishTwoLevelHandler = object : Handler(Looper.getMainLooper()) {
|
||||
override fun handleMessage(msg: Message) {
|
||||
super.handleMessage(msg)
|
||||
@ -961,9 +942,6 @@ class SearchToolbarTabWrapperFragment : BaseTabWrapperFragment(), ISearchToolbar
|
||||
val autoFinishDelayTime = putAwaySwitch.toInt() * 1000L
|
||||
autoFinishTwoLevelHandler?.sendEmptyMessageDelayed(KEY_AUTO_FINISH_TWO_LEVEL, autoFinishDelayTime)
|
||||
}
|
||||
else -> {
|
||||
autoFinishTwoLevelCallback = finishCallback
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -971,6 +949,8 @@ class SearchToolbarTabWrapperFragment : BaseTabWrapperFragment(), ISearchToolbar
|
||||
}
|
||||
|
||||
override fun setPullDownPush(pullDownPush: PullDownPush?, pullDownPushHandler: PullDownPushHandler?) {
|
||||
if (!isAdded) return
|
||||
|
||||
if (autoVideoView == null) {
|
||||
binding.autoVideoViewStub.setOnInflateListener { _, inflated ->
|
||||
autoVideoView = LayoutAutoVideoViewBinding.bind(inflated).root
|
||||
|
||||
@ -21,6 +21,8 @@ import com.gh.gamecenter.entity.MultiTabNav
|
||||
import com.gh.gamecenter.entity.PullDownPush
|
||||
import com.gh.gamecenter.feature.utils.SentryHelper
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.scwang.smartrefresh.layout.api.RefreshLayout
|
||||
import com.scwang.smartrefresh.layout.constant.RefreshState
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
@ -148,10 +150,6 @@ class TabWrapperFragment: BaseTabWrapperFragment(), ISmartRefresh, ISmartRefresh
|
||||
}
|
||||
}
|
||||
|
||||
override fun setScrollEnabled(isScrollEnabled: Boolean) {
|
||||
getValidSmartRefreshContent()?.setScrollEnabled(isScrollEnabled)
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
getValidSmartRefreshContent()?.onRefresh()
|
||||
}
|
||||
@ -161,6 +159,10 @@ class TabWrapperFragment: BaseTabWrapperFragment(), ISmartRefresh, ISmartRefresh
|
||||
getCurrentTabEntity()?.showPullDownPush = !isSwipeRefreshEnabled
|
||||
}
|
||||
|
||||
override fun onStateChanged(refreshLayout: RefreshLayout, oldState: RefreshState, newState: RefreshState) {
|
||||
getValidSmartRefreshContent()?.onStateChanged(refreshLayout, oldState, newState)
|
||||
}
|
||||
|
||||
override fun setSmartRefreshEnabled(isEnable: Boolean) {
|
||||
(parentFragment as? ISmartRefresh)?.setSmartRefreshEnabled(isEnable)
|
||||
}
|
||||
|
||||
@ -434,6 +434,8 @@ class ToolbarWrapperFragment : LazyFragment(), ToolbarController, ISmartRefresh,
|
||||
oldState: RefreshState,
|
||||
newState: RefreshState
|
||||
) {
|
||||
(mContentFragment as? ISmartRefreshContent)?.onStateChanged(refreshLayout, oldState, newState)
|
||||
|
||||
val isAutoScrollToTwoLevel = oldState == RefreshState.None && newState == RefreshState.TwoLevel
|
||||
val isDragToTwoLevel =
|
||||
oldState == RefreshState.ReleaseToRefresh && newState == RefreshState.ReleaseToTwoLevel
|
||||
@ -550,12 +552,9 @@ class ToolbarWrapperFragment : LazyFragment(), ToolbarController, ISmartRefresh,
|
||||
|
||||
when (newState) {
|
||||
RefreshState.TwoLevel -> {
|
||||
(mContentFragment as? ISmartRefreshContent)?.setScrollEnabled(false)
|
||||
mBinding.refreshLayout.isDisableContent = false
|
||||
}
|
||||
RefreshState.None -> {
|
||||
(mContentFragment as? ISmartRefreshContent)?.setScrollEnabled(true)
|
||||
|
||||
mShowTwoLevel = false
|
||||
mIsDragging = false
|
||||
twoLevelHeader.setFloorDuration(1000)
|
||||
|
||||
@ -17,6 +17,7 @@ import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.ImageUtils
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.layoutInflater
|
||||
import com.gh.gamecenter.common.utils.safelyGetInRelease
|
||||
import com.gh.gamecenter.common.view.PageControllerAdapter
|
||||
import com.gh.gamecenter.common.view.PageTransformerAdapter
|
||||
import com.gh.gamecenter.common.view.ScrollEventListener
|
||||
@ -151,7 +152,8 @@ class VGameInstalledLaunchDialog : DialogFragment() {
|
||||
|
||||
launch.setOnClickListener { view ->
|
||||
val currentItem = pageControllerAdapter.currentItem
|
||||
val installedGame = installAdapter.currentList[currentItem]
|
||||
val installedGame =
|
||||
installAdapter.currentList.safelyGetInRelease(currentItem) ?: return@setOnClickListener
|
||||
val vDownloadEntity =
|
||||
VHelper.getVDownloadEntitySnapshot(installedGame.gameId, installedGame.packageName)
|
||||
?: return@setOnClickListener
|
||||
|
||||
@ -25,7 +25,6 @@ import com.facebook.imagepipeline.core.ImageTranscoderType;
|
||||
import com.facebook.imagepipeline.core.MemoryChunkType;
|
||||
import com.facebook.imagepipeline.decoder.ImageDecoderConfig;
|
||||
import com.gh.ad.AdDelegateHelper;
|
||||
import com.gh.ad.AdPluginDownloadHelper;
|
||||
import com.gh.base.GlobalActivityLifecycleObserver;
|
||||
import com.gh.common.FixedRateJobHelper;
|
||||
import com.gh.common.filter.RegionSettingHelper;
|
||||
@ -57,7 +56,6 @@ import com.gh.gamecenter.common.utils.DeviceUtils;
|
||||
import com.gh.gamecenter.common.utils.EnvHelper;
|
||||
import com.gh.gamecenter.common.utils.ExtensionsKt;
|
||||
import com.gh.gamecenter.common.utils.ImageUtils;
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge;
|
||||
import com.gh.gamecenter.core.AppExecutor;
|
||||
import com.gh.gamecenter.core.iinterface.IApplication;
|
||||
import com.gh.gamecenter.core.provider.IFlavorProvider;
|
||||
@ -331,7 +329,8 @@ public class HaloApp extends MultiDexApplication {
|
||||
OAIDHelper.INSTANCE.getOAID(HaloApp.this, (s, isSuccess) -> {
|
||||
setOAID(s);
|
||||
MetaUtil.INSTANCE.refreshMeta();
|
||||
SensorsBridge.INSTANCE.setOAID(s);
|
||||
|
||||
DataUtils.bindValidOaidToSensor(true);
|
||||
|
||||
if (getGid() != null) {
|
||||
DataUtils.getDeviceCertification(getGid());
|
||||
|
||||
@ -7,8 +7,8 @@ ext {
|
||||
targetSdkVersion = 30
|
||||
|
||||
// application info (每个大版本之间的 versionCode 增加 20)
|
||||
versionCode = 1112
|
||||
versionName = "5.38.2"
|
||||
versionCode = 1115
|
||||
versionName = "5.38.5"
|
||||
applicationId = "com.gh.gamecenter"
|
||||
applicationIdGat = "com.gh.gamecenter.intl"
|
||||
|
||||
@ -143,7 +143,7 @@ ext {
|
||||
|
||||
acloudPush = "3.8.8.1"
|
||||
jpushVersion = "5.4.0"
|
||||
jverifiationVersion = "3.1.7"
|
||||
jverifiationVersion = "3.2.5"
|
||||
honorPushVersion = "7.0.61.303"
|
||||
|
||||
volcTlsVersion = "1.1.4"
|
||||
|
||||
@ -27,7 +27,6 @@ import com.gh.gamecenter.feedback.view.suggest.SuggestionActivity
|
||||
import com.gh.gamecenter.floatingwindow.R
|
||||
import com.gh.gamecenter.floatingwindow.databinding.DialogQaFeedbackBinding
|
||||
import com.lightgame.dialog.BaseDialogFragment
|
||||
import org.bouncycastle.asn1.x500.style.RFC4519Style.title
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
class QaFeedbackDialogFragment : BaseDialogFragment() {
|
||||
|
||||
@ -39,7 +39,7 @@ object JPushHelper {
|
||||
// 绑定ID关系
|
||||
@SuppressLint("CheckResult")
|
||||
fun bindRegistrationId(registrationId: String?) {
|
||||
SensorsBridge.profileAppend(KEY_REGISTRATION_ID, registrationId ?: "")
|
||||
SensorsBridge.profileSet(KEY_REGISTRATION_ID, registrationId ?: "")
|
||||
|
||||
val appProvider = ARouter.getInstance().build(RouteConsts.provider.app).navigation() as? IAppProvider
|
||||
val data = mapOf(
|
||||
|
||||
@ -79,6 +79,7 @@ public class Constants {
|
||||
public static final String QQ_MINI_GAME = "qq";
|
||||
public static final String WECHAT_MINI_GAME = "wechat";
|
||||
public static final String WECHAT_MINI_GAME_CPM = "wechat_cpm";
|
||||
public static final String WECHAT_MINI_GAME_PROFIT_CPM = "CPM";
|
||||
/**
|
||||
* <a href="https://developers.weixin.qq.com/community/minigame/doc/000c461a17c6486f0641e27176b401">微信小游戏PCS参数</a>
|
||||
*/
|
||||
@ -473,6 +474,10 @@ public class Constants {
|
||||
|
||||
public static final String SP_BRAND_NEW_FIRST_LAUNCH_TIME = "brand_new_first_launch_time"; // 全新安装用户首次启动时间
|
||||
|
||||
public static final String INVALID_OAID_1 = "00000000-0000-0000-0000-000000000000";
|
||||
public static final String INVALID_OAID_2 = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
public static final String INVALID_OAID_3 = "00000000000000000000000000000000";
|
||||
|
||||
public static final String IS_RESERVE_ONLINE_REMINDER = "is_reserve_online_reminder";
|
||||
|
||||
public static final String TOOL_MAP_PACKAGE_NAME = "com.gh.toolmap";// 光环工具服务APP包名
|
||||
|
||||
@ -37,7 +37,7 @@ data class ExposureEntity(
|
||||
@SerializedName("mini_game_type")
|
||||
val miniGameType: String? = "",// 小游戏类型:QQ小游戏:qq 微信小游戏:wechat
|
||||
@SerializedName("mini_game_recommend_id")
|
||||
val miniGameRecommendId: String? = "",
|
||||
var miniGameRecommendId: String? = null,
|
||||
var speed: Long = 0,
|
||||
var certification: Int? = null, // 0表示未实名,1表示未成年,2表示成年
|
||||
|
||||
|
||||
@ -455,6 +455,17 @@ fun String.containHtmlTag(): Boolean {
|
||||
return matcher.find()
|
||||
}
|
||||
|
||||
fun String.dropFontColorInDarkMode(context: Context) : String {
|
||||
return if (DarkModeUtils.isDarkModeOn(context)) {
|
||||
val fontColorRegex = Pattern.compile("<font[^>]*color[^>]*>")
|
||||
val matcher = fontColorRegex.matcher(this)
|
||||
|
||||
return matcher.replaceAll("")
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户行为相关
|
||||
*/
|
||||
|
||||
@ -328,7 +328,7 @@ object SensorsBridge {
|
||||
}
|
||||
|
||||
fun setOAID(oaid: String) {
|
||||
if (mIsSensorsEnabled) mSensor?.setOAID(oaid)
|
||||
if (mIsSensorsEnabled || oaid.isNotEmpty()) mSensor?.setOAID(oaid)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@ -284,17 +284,20 @@ data class GameEntity(
|
||||
@SerializedName("message_private_id")
|
||||
var messageId: String = "",
|
||||
|
||||
// 小游戏服务商ID,目前仅用于微信小游戏CPM
|
||||
// 小游戏用户ID,目前仅用于微信小游戏CPM,对应着小游戏的appid字段
|
||||
@SerializedName("wxappid")
|
||||
var miniGameUid: String = "",
|
||||
// 小游戏ID,仅用于小游戏
|
||||
// 如果是微信小游戏,则对应着小游戏的user_name字段
|
||||
// 如果是QQ小游戏,则对应着小游戏的game_id字段
|
||||
@SerializedName("appid")
|
||||
var miniGameAppId: String = "",
|
||||
// 小游戏状态(1:下架 2:秒玩),仅用于小游戏
|
||||
@SerializedName("app_status")
|
||||
var miniGameAppStatus: Int = 0,
|
||||
// 小游戏类型(qq/wechat/wechat_cpm),仅用于小游戏
|
||||
// 小游戏类别(qq/wechat),仅用于小游戏
|
||||
@SerializedName("game_type")
|
||||
var miniGameType: String = "",
|
||||
var miniGameCategory: String = "",
|
||||
// 小游戏链接URL,仅用于小游戏
|
||||
@SerializedName("appidLink")
|
||||
var miniGameAppLink: String = "",
|
||||
@ -308,10 +311,13 @@ data class GameEntity(
|
||||
var usage: Int = 0,
|
||||
// 游戏封面图,仅用于微信小游戏
|
||||
var banner: String = "",
|
||||
// 收益来源(CPM),仅用于微信小游戏
|
||||
var profit: String = "",
|
||||
// 宣传图,仅用于微信小游戏
|
||||
@SerializedName("publicity_img")
|
||||
var publicityImg: List<String>? = null,
|
||||
|
||||
// 小游戏在搜索结果的位置,仅用于微信小游戏CPM搜索结果列表
|
||||
var location: Int = -1,
|
||||
@SerializedName("first_setting")
|
||||
var firstSetting: FirstSetting? = null,
|
||||
@SerializedName("bubble_text")
|
||||
@ -797,6 +803,11 @@ data class GameEntity(
|
||||
return category.equals("gjonline")
|
||||
}
|
||||
|
||||
val miniGameType: String get() = when(profit) {
|
||||
Constants.WECHAT_MINI_GAME_PROFIT_CPM -> Constants.WECHAT_MINI_GAME_CPM
|
||||
else -> miniGameCategory
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前游戏是否为小游戏
|
||||
*/
|
||||
|
||||
@ -115,7 +115,7 @@ data class ExposureEvent(
|
||||
miniGameUid = gameEntity?.miniGameUid ?: "",
|
||||
miniGameId = gameEntity?.miniGameAppId ?: "",
|
||||
miniGameType = gameEntity?.miniGameType ?: "",
|
||||
miniGameRecommendId = gameEntity?.miniGameRecommendId ?: "",
|
||||
miniGameRecommendId = gameEntity?.miniGameRecommendId,
|
||||
additional = gameEntity?.customPageTrackData?.toKV()
|
||||
)
|
||||
this.id = UUID.randomUUID().toString()
|
||||
|
||||
@ -109,12 +109,7 @@ object MiniGameItemHelper {
|
||||
gameName = gameEntity.name ?: ""
|
||||
)
|
||||
if (gameEntity.miniGameType == Constants.WECHAT_MINI_GAME_CPM) {
|
||||
WGameSubjectCPMListReportHelper.reportClick(
|
||||
miniAppUid = gameEntity.miniGameUid,
|
||||
gameName = gameEntity.name ?: "",
|
||||
operateTime = System.currentTimeMillis() / 1000,
|
||||
recommendId = gameEntity.miniGameRecommendId
|
||||
)
|
||||
WGameSubjectCPMListReportHelper.reportClick(gameEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import com.gh.gamecenter.common.utils.EnvHelper
|
||||
import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet
|
||||
import com.gh.gamecenter.common.utils.toRequestBody
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.WXMiniGameCPMResponse
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.retrofit.RetrofitManager
|
||||
@ -22,7 +23,38 @@ object WGameSubjectCPMListReportHelper {
|
||||
private val exposureRequestSet by lazy { hashSetOf<String>() }
|
||||
private val exposureCacheSet by lazy { FixedSizeLinkedHashSet<String>(100) }
|
||||
|
||||
/**
|
||||
* recommendId为空,正在调用CPM接口的点击事件集合
|
||||
*/
|
||||
private val pendingRecommendIdClickSet by lazy { hashSetOf<WGSubjectPendingClick>() }
|
||||
|
||||
fun reportClick(
|
||||
gameEntity: GameEntity,
|
||||
operateTime: Long = System.currentTimeMillis() / 1000
|
||||
) {
|
||||
val recommendId = gameEntity.miniGameRecommendId.ifEmpty {
|
||||
gameEntity.exposureEvent?.payload?.miniGameRecommendId // 如果游戏实体的recommendId为空,则取它绑定的曝光事件的recommendId
|
||||
} ?: return
|
||||
if (recommendId.isNotEmpty()) {
|
||||
reportClickInner(
|
||||
miniAppUid = gameEntity.miniGameUid,
|
||||
gameName = gameEntity.name ?: "",
|
||||
operateTime = System.currentTimeMillis() / 1000,
|
||||
recommendId = recommendId
|
||||
)
|
||||
return
|
||||
}
|
||||
// 没有曝光事件的点击事件上报是不合理的
|
||||
val exposureEvent = gameEntity.exposureEvent ?: return
|
||||
// 正在调用接口请求获取recommendId,需要等待recommendId回调以后再执行上报
|
||||
pendingRecommendIdClickSet.add(WGSubjectPendingClick(operateTime, exposureEvent))
|
||||
Utils.log(
|
||||
"WXGameCPMReport",
|
||||
"当前微信CPM小游戏正在请求CPM接口获取RecommendId,将延迟到RecommendId获取后上报:${gameEntity.name}, ${gameEntity.miniGameAppId}"
|
||||
)
|
||||
}
|
||||
|
||||
private fun reportClickInner(
|
||||
miniAppUid: String,
|
||||
gameName: String,
|
||||
operateTime: Long,
|
||||
@ -78,6 +110,69 @@ object WGameSubjectCPMListReportHelper {
|
||||
}
|
||||
|
||||
fun reportExposure(exposure: ExposureEvent) {
|
||||
val recommendId = exposure.payload.miniGameRecommendId ?: return
|
||||
if (recommendId.isEmpty()) {// recommendId为空时,需要请求CPM接口获取recommendId的值
|
||||
requestRecommendIdAndReportExposure(exposure)
|
||||
} else {
|
||||
reportExposureInner(exposure)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 先请求CPM接口获取RecommendId,再提交曝光事件
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private fun requestRecommendIdAndReportExposure(exposure: ExposureEvent) {
|
||||
val wxappid = exposure.payload.miniGameUid!!
|
||||
val meta = MetaUtil.getMeta()
|
||||
val request = mapOf(
|
||||
"head" to mapOf(
|
||||
"busiAppid" to EnvHelper.getWGameCPMBusiAppId(),
|
||||
"oaid" to (meta.oaid ?: ""),
|
||||
"manufacturer" to (meta.manufacturer ?: ""),
|
||||
"mode" to (meta.model ?: ""),
|
||||
"androidId" to (MetaUtil.getAndroidId()),
|
||||
"imei" to (MetaUtil.getIMEI())
|
||||
),
|
||||
"body" to mapOf(
|
||||
"appIDList" to listOf(wxappid)
|
||||
)
|
||||
)
|
||||
Utils.log(
|
||||
"WXGameCPMReport",
|
||||
"请求CPM接口获取RecommendId,${exposure.payload.gameName}"
|
||||
)
|
||||
RetrofitManager.getInstance()
|
||||
.wGameCPMApiService
|
||||
.getGameInfoList(request.toRequestBody())
|
||||
.subscribe({
|
||||
if (it.ret == 0 && it.appInfoList.isNotEmpty()) {
|
||||
exposure.payload.miniGameRecommendId = it.appInfoList[0].recommendID
|
||||
Utils.log(
|
||||
"WXGameCPMReport",
|
||||
"请求CPM接口获取RecommendId成功!${exposure.payload.gameName}, ${exposure.payload.miniGameRecommendId}"
|
||||
)
|
||||
reportExposureInner(exposure)// 上报曝光事件
|
||||
pendingRecommendIdClickSet.forEach { click ->
|
||||
if (click.exposure === exposure) {// 上报延迟的点击事件
|
||||
reportClickInner(
|
||||
miniAppUid = exposure.payload.miniGameUid ?: "",
|
||||
gameName = exposure.payload.gameName ?: "",
|
||||
operateTime = click.operateTime,
|
||||
recommendId = exposure.payload.miniGameRecommendId!!
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
pendingRecommendIdClickSet.removeAll {
|
||||
click -> click.exposure === exposure }
|
||||
}, {
|
||||
pendingRecommendIdClickSet.removeAll {
|
||||
click -> click.exposure === exposure }
|
||||
})
|
||||
}
|
||||
|
||||
private fun reportExposureInner(exposure: ExposureEvent) {
|
||||
val recommendId = exposure.payload.miniGameRecommendId ?: return
|
||||
val gameName = exposure.payload.gameName ?: ""
|
||||
if (exposureCacheSet.contains(recommendId)) {
|
||||
@ -173,4 +268,9 @@ object WGameSubjectCPMListReportHelper {
|
||||
val posCard: Int = 0,
|
||||
val posInCard: Int = 0
|
||||
)
|
||||
|
||||
class WGSubjectPendingClick(
|
||||
val operateTime: Long,
|
||||
val exposure: ExposureEvent
|
||||
)
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -18,4 +21,7 @@ interface WGameCPMApiService {
|
||||
@POST("reportexposure")
|
||||
fun reportExposure(@Body body: RequestBody): Single<WXMiniGameCPMResponse>
|
||||
|
||||
@POST("getgameinfolist")
|
||||
fun getGameInfoList(@Body body: RequestBody): Single<WXMiniGameCPMEntity>
|
||||
|
||||
}
|
||||
@ -97,6 +97,8 @@ public class WXEntryActivity extends Activity implements IWXAPIEventHandler, WeC
|
||||
&& !((SendAuth.Resp) baseResp).state.contains("qqminigame")
|
||||
) {
|
||||
WXAPIProxyFactory.getLiveData().postValue(baseResp);
|
||||
} else if (baseResp.getType() == ConstantsAPI.COMMAND_SUBSCRIBE_MESSAGE) {
|
||||
WXAPIProxyFactory.getLiveData().postValue(baseResp);
|
||||
}
|
||||
|
||||
String resultString = "";
|
||||
|
||||
@ -3,32 +3,50 @@ package com.gh.gamecenter.va
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import com.gh.gamecenter.core.iinterface.IApplication
|
||||
import com.google.auto.service.AutoService
|
||||
import com.lg.vspace.common.CommonApp
|
||||
import com.lg.vspace.App64
|
||||
|
||||
@AutoService(IApplication::class)
|
||||
class HaloApp : IApplication {
|
||||
|
||||
private val commonApp = CommonApp()
|
||||
private val app = App64()
|
||||
private val isDeviceSupportVa = Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
commonApp.attachBaseContext(base)
|
||||
if (isDeviceSupportVa) {
|
||||
app.attachBaseContext(base)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(application: Application) {
|
||||
commonApp.onCreate(application)
|
||||
if (isDeviceSupportVa) {
|
||||
app.onCreate(application)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLowMemory() {
|
||||
if (isDeviceSupportVa) {
|
||||
app.onLowMemory()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTerminate() {
|
||||
if (isDeviceSupportVa) {
|
||||
app.onTerminate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTrimMemory(level: Int) {
|
||||
if (isDeviceSupportVa) {
|
||||
app.onTrimMemory(level)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
if (isDeviceSupportVa) {
|
||||
app.onConfigurationChanged(newConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
Submodule ndownload updated: 22298a573c...2478b91b33
@ -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
|
||||
|
||||
|
||||
2
vasdk
2
vasdk
Submodule vasdk updated: 270ed85b7d...0a83d50210
Reference in New Issue
Block a user