Merge branch 'feat/GHZSCY-5891' into 'dev'

feat: 新增页面分流器—客户端 https://jira.shanqu.cc/browse/GHZSCY-5891

See merge request halo/android/assistant-android!1876
This commit is contained in:
叶子维
2024-09-09 13:40:52 +08:00
23 changed files with 460 additions and 93 deletions

View File

@ -0,0 +1,52 @@
package com.gh.common.prioritychain
import android.content.Context
import android.view.LayoutInflater
import android.widget.FrameLayout
import com.airbnb.lottie.LottieAnimationView
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.databinding.LayoutCommunityHomeVideoGuideBinding
class CommunityHomeGuideHandler(
priority: Int,
private val context: Context,
private val decorView: FrameLayout?,
private val videoLottie: LottieAnimationView?
) : PriorityChainHandler(priority) {
init {
updateStatus(STATUS_VALID)
}
override fun onProcess(): Boolean {
return if (SPUtils.getBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, true)) {
showHomeVideoGuide(context, decorView, videoLottie)
processNext()
true
} else {
processNext()
false
}
}
companion object {
fun showHomeVideoGuide(context: Context, decorView: FrameLayout?, videoLottie: LottieAnimationView?) {
val guideLayoutBinding = LayoutCommunityHomeVideoGuideBinding.inflate(
LayoutInflater.from(context),
decorView,
true
)
guideLayoutBinding.root.setOnClickListener { view ->
decorView?.removeView(view)
SPUtils.setBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, false)
videoLottie?.playAnimation()
SPUtils.setLong(
Constants.SP_COMMUNITY_HOME_VIDEO_LOTTIE_LAST_PLAY_TIME,
System.currentTimeMillis()
)
}
}
}
}

View File

@ -57,10 +57,6 @@ object HomeBottomBarHelper {
return BottomTab(name = "我的光环", jsCode = animationCode, iconSelector = R.drawable.selector_ic_user, link = LinkEntity(type = TYPE_MY_HALO))
}
fun isDefaultHomeBottomTabDataExist(): Boolean {
return SPUtils.getString(KEY_HOME_BOTTOM_TAB).isNotEmpty()
}
@JvmStatic
fun getDefaultHomeBottomTabData(): List<BottomTab> {
try {

View File

@ -2716,4 +2716,52 @@ object NewFlatLogUtils {
parseAndPutMeta()(this)
}.let(::log)
}
// 用户访问分流器
fun logByPassBrowsing(
source: String,
bypassName: String,
bypassId: String,
branchId: String,
branchName: String,
inProcessTime: Int,
bypassVisitTime: Int,
linkType: String,
linkId: String,
linkText: String,
bypassStatus: Int
) {
json {
KEY_EVENT to "BypassBrowsing"
"source" to source
"bypass_name" to bypassName
"bypass_id" to bypassId
"branch_id" to branchId
"branch_name" to branchName
"inprocess_time" to inProcessTime
"bypass_visit_time" to bypassVisitTime
"link_type" to linkType
"link_id" to linkId
"link_text" to linkText
"bypass_status" to bypassStatus
parseAndPutMeta()(this)
}.let(::log)
}
// 分流失败
fun logFailByPass(
source: String,
bypassName: String,
bypassId: String,
defeatedReason: String
) {
json {
KEY_EVENT to "FailBypass"
"source" to source
"bypass_name" to bypassName
"bypass_id" to bypassId
"defeated_reason" to defeatedReason
parseAndPutMeta()(this)
}.let(::log)
}
}

View File

@ -82,6 +82,7 @@ object ViewPagerFragmentHelper {
const val TYPE_TOOLKIT = "toolkit" // 工具箱
fun createFragment(parentFragment: Fragment?, bundle: Bundle, linkEntity: LinkEntity, isTabWrapper: Boolean): Fragment {
val superiorChain = if (parentFragment is ISuperiorChain) parentFragment else null
return when (linkEntity.type) {
// 游戏详情页
TYPE_GAME -> {
@ -90,11 +91,12 @@ object ViewPagerFragmentHelper {
}
// 我的光环
TYPE_MY_HALO -> {
val superiorChain = if (parentFragment is ISuperiorChain) parentFragment else null
HaloPersonalFragment().setSuperiorChain(superiorChain).with(bundle)
}
// 社区首页
TYPE_COMMUNITY_HOME -> CommunityHomeFragment().with(bundle)
TYPE_COMMUNITY_HOME -> {
CommunityHomeFragment().setSuperiorChain(superiorChain).with(bundle)
}
// 视频信息流
TYPE_VIDEO_STREAM -> {
bundle.putBoolean(EntranceConsts.KEY_IS_HOME_VIDEO, true)
@ -148,11 +150,11 @@ object ViewPagerFragmentHelper {
NewQuestionDetailFragment().with(bundle)
}
// 其他原来带Toolbar的Fragment
else -> createToolbarWrapperFragment(bundle, linkEntity, isTabWrapper)
else -> createToolbarWrapperFragment(parentFragment, bundle, linkEntity, isTabWrapper)
}
}
private fun createToolbarWrapperFragment(bundle: Bundle, entity: LinkEntity, isTabWrapper: Boolean): Fragment {
private fun createToolbarWrapperFragment(parentFragment: Fragment?, bundle: Bundle, entity: LinkEntity, isTabWrapper: Boolean): Fragment {
var className = ReloadFragment::class.java.name
when (entity.type) {

View File

@ -138,7 +138,6 @@ import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.HttpException;

View File

@ -59,6 +59,9 @@ class SplashScreenActivity : BaseActivity() {
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
mIsNewForThisVersion = HaloApp.getInstance().isNewForThisVersion
HaloApp.getInstance().isBrandNewInstall = SPUtils.getBoolean(Constants.SP_BRAND_NEW_USER, true)
if (HaloApp.getInstance().isBrandNewInstall) {
SPUtils.setLong(Constants.SP_BRAND_NEW_FIRST_LAUNCH_TIME, System.currentTimeMillis())
}
// 用户不是新版本,但应用最后更新时间不是上次的时间代表用户重新安装了当前版本
if (!mIsNewForThisVersion) {

View File

@ -26,7 +26,8 @@ data class BottomTab(
@SerializedName("is_default_page")
var default: Boolean = false, // 是否为默认显示页
var guide: Guide? = null, // 引导文案
var isTransparentStyle: Boolean = false // 本地字段透明底部Tab
var diverter: DiverterEntity? = null, // 分流器
var isTransparentStyle: Boolean = false, // 本地字段透明底部Tab
) : Parcelable {
@Parcelize
data class SearchStyle(

View File

@ -0,0 +1,31 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
class DiverterData(
@SerializedName("diverter_id")
val diverterId: String = "",
@SerializedName("diverter_name")
val diverterName: String = "",
@SerializedName("branch_id")
val branchId: String = "",
@SerializedName("branch_name")
val branchName: String = "",
@SerializedName("branch_type")
val branchType: String = "",
@SerializedName("inprocess_time")
val inprocessTime: Int = 0,
@SerializedName("bypass_visit_time")
val bypassVisitTime: Int = 0,
@SerializedName("link_id")
val linkId: String = "",
@SerializedName("link_type")
val linkType: String = "",
@SerializedName("link_text")
val linkText: String = "",
@SerializedName("bypass_status")
val bypassStatus: Int = 0,
): Parcelable

View File

@ -0,0 +1,15 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
class DiverterEntity(
@SerializedName("module_id")
val moduleId: String = "",
@SerializedName("module_index")
val moduleIndex: Int = -1,
@SerializedName("diverter_data")
val diverterData: DiverterData = DiverterData()
): Parcelable

View File

@ -23,6 +23,9 @@ import com.airbnb.lottie.SimpleColorFilter
import com.airbnb.lottie.model.KeyPath
import com.airbnb.lottie.value.LottieValueCallback
import com.gh.common.browse.BrowseTimer
import com.gh.common.iinterface.ISuperiorChain
import com.gh.common.prioritychain.CommunityHomeGuideHandler
import com.gh.common.prioritychain.PriorityChain
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.NewLogUtils
@ -42,7 +45,6 @@ import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.databinding.FragmentCommunityHomeBinding
import com.gh.gamecenter.databinding.LayoutCommunityHomeVideoGuideBinding
import com.gh.gamecenter.databinding.TabItemCommunityBinding
import com.gh.gamecenter.eventbus.EBSkip
import com.gh.gamecenter.eventbus.EBTypeChange
@ -81,6 +83,8 @@ class CommunityHomeFragment : LazyFragment() {
private var mNavigationBitmap: Bitmap? = null
private var mShowVideo = true
private var mBottomTabId = ""
private val mPriorityChain by lazy { PriorityChain() }
private var mSuperiorChain: ISuperiorChain? = null
private val browseTimer = BrowseTimer()
.withResult {
@ -156,6 +160,12 @@ class CommunityHomeFragment : LazyFragment() {
})
}
private fun addHomeVideoGuideHandler() {
val decorView = activity?.window?.decorView as? FrameLayout
val communityHomeGuideHandler = CommunityHomeGuideHandler(21, requireContext(), decorView, mBinding?.videoLottie)
mPriorityChain.addHandler(communityHomeGuideHandler)
}
override fun initRealView() {
super.initRealView()
@ -166,24 +176,7 @@ class CommunityHomeFragment : LazyFragment() {
mMainWrapperViewModel?.bottomTabListLiveData?.observe(this) { tabList ->
mBinding?.videoAndSearchContainer?.goneIf(!mShowVideo) {
val decorView = requireActivity().window.decorView as? FrameLayout
if (SPUtils.getBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, true)) {
val guideLayoutBinding = LayoutCommunityHomeVideoGuideBinding.inflate(
LayoutInflater.from(requireContext()),
decorView,
true
)
guideLayoutBinding.root.setOnClickListener { view ->
decorView?.removeView(view)
SPUtils.setBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, false)
mBinding?.videoLottie?.playAnimation()
SPUtils.setLong(
Constants.SP_COMMUNITY_HOME_VIDEO_LOTTIE_LAST_PLAY_TIME,
System.currentTimeMillis()
)
}
}
addHomeVideoGuideHandler()
// 每日首次进入社区tab视频lottie播放3次
val lastPlayTime = SPUtils.getLong(Constants.SP_COMMUNITY_HOME_VIDEO_LOTTIE_LAST_PLAY_TIME, 0L) / 1000
@ -285,6 +278,14 @@ class CommunityHomeFragment : LazyFragment() {
DisplayUtils.transparentStatusBar(requireActivity())
DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn)
NewLogUtils.logCommunityHomeEvent("view_community")
mSuperiorChain?.registerInferiorChain(mPriorityChain)
}
override fun onFragmentPause() {
super.onFragmentPause()
mSuperiorChain?.unregisterInferiorChain(mPriorityChain)
}
fun setCurrentItem(index: Int) {
@ -836,6 +837,11 @@ class CommunityHomeFragment : LazyFragment() {
fun getTopBgView() = mBinding?.topBg
fun setSuperiorChain(superiorChain: ISuperiorChain?): CommunityHomeFragment {
this.mSuperiorChain = superiorChain
return this
}
companion object {
var TAB_SELECTED_COLOR: Int = R.color.text_primary
var TAB_DEFAULT_COLOR: Int = R.color.community_forum_more

View File

@ -23,7 +23,7 @@ class ReloadFragment: BaseLazyFragment() {
super.onCreate(savedInstanceState)
mBinding.reuseLoading.root.visibility = View.VISIBLE
mBinding.reuseNoConnection.connectionReloadTv.setOnClickListener {
MainWrapperRepository.getInstance().getDataUnion()
MainWrapperRepository.getInstance().init()
mBinding.reuseNoConnection.root.visibility = View.GONE
mBinding.reuseLoading.root.visibility = View.VISIBLE
}

View File

@ -29,6 +29,7 @@ import com.gh.gamecenter.entity.DeviceDialogEntity;
import com.gh.gamecenter.entity.DialogEntity;
import com.gh.gamecenter.entity.DiscoveryGameCardEntity;
import com.gh.gamecenter.entity.DiscoveryGameCardLabel;
import com.gh.gamecenter.entity.DiverterEntity;
import com.gh.gamecenter.entity.FollowCommonContentCollection;
import com.gh.gamecenter.entity.FollowDynamicEntity;
import com.gh.gamecenter.entity.FollowUserEntity;
@ -107,6 +108,7 @@ import com.gh.gamecenter.feature.entity.BackgroundImageEntity;
import com.gh.gamecenter.feature.entity.CommentEntity;
import com.gh.gamecenter.feature.entity.CommentnumEntity;
import com.gh.gamecenter.feature.entity.ConcernEntity;
import com.gh.gamecenter.feature.entity.FloatingWindowEntity;
import com.gh.gamecenter.feature.entity.ForumVideoEntity;
import com.gh.gamecenter.feature.entity.GameEntity;
import com.gh.gamecenter.feature.entity.LibaoEntity;
@ -126,7 +128,6 @@ import com.gh.gamecenter.feature.entity.SimulatorEntity;
import com.gh.gamecenter.feature.entity.UserEntity;
import com.gh.gamecenter.feature.entity.ViewsEntity;
import com.gh.gamecenter.feature.entity.WXSubscribeMsgConfig;
import com.gh.gamecenter.feature.entity.FloatingWindowEntity;
import com.gh.gamecenter.gamedetail.entity.BigEvent;
import com.gh.gamecenter.gamedetail.entity.NewGameDetailEntity;
import com.gh.gamecenter.home.custom.model.CustomPageData;
@ -3358,7 +3359,7 @@ public interface ApiService {
* 页面数据聚合[底部tab+多tab导航页+默认数据]
*/
@GET("app/data_union")
Single<DataUnionEntity> getDataUnion();
Single<DataUnionEntity> getDataUnion(@Query("diverter") String diverter);
/**
* 底部tab
@ -3390,4 +3391,16 @@ public interface ApiService {
*/
@GET("game_lists/hot_columns")
Single<List<SubjectEntity>> getHotColumns();
/**
* 分流器列表信息
*/
@GET("app/{module}/diverter")
Single<List<DiverterEntity>> getDiverterList(@Header("Install-First-Access") String isInstallFirstAccess, @Path("module") String module);
/**
* 访问分流页面,更新分流访问次数
*/
@PATCH("app/{module}/diverter_visit_time")
Single<ResponseBody> patchDiverterVisitTime(@Path("module") String module, @Body RequestBody body);
}

View File

@ -9,20 +9,18 @@ import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.core.view.isVisible
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.LazyFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.BugFixedPopupWindow
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.doOnEnd
import com.gh.gamecenter.core.utils.doOnStart
import com.gh.gamecenter.databinding.FragmentGameServerTestV2Binding
import com.gh.gamecenter.databinding.LayoutGameServerTestV2SettingBinding
import com.gh.gamecenter.databinding.LayoutGameServerTestV2SettingGuideBinding
import com.gh.gamecenter.feature.entity.PageLocation
import com.gh.gamecenter.mygame.MyGameActivity
@ -70,7 +68,7 @@ class GameServerTestV2Fragment : LazyFragment() {
if (SPUtils.getBoolean(Constants.SP_SHOW_GAME_SERVER_TEST_V2_SETTING_GUIDE, true)) {
mBaseHandler.post {
showSettingView {
showGuidePopupWindow()
showGuide()
}
}
SPUtils.setBoolean(Constants.SP_SHOW_GAME_SERVER_TEST_V2_SETTING_GUIDE, false)
@ -293,6 +291,19 @@ class GameServerTestV2Fragment : LazyFragment() {
}.start()
}
private fun showGuide() {
mBinding?.settingGuideContainer?.setOnClickListener {
dismissGuide()
}
mBinding?.settingGuideContainer?.isVisible = true
}
private fun dismissGuide() {
mBinding?.settingGuideIv?.animate()?.alpha(0F)?.setDuration(200L)?.doOnEnd {
mBinding?.settingGuideContainer?.isVisible = false
}?.start()
}
private fun getItemTextView(type: String): TextView {
return TextView(requireContext()).apply {
text = type
@ -332,22 +343,11 @@ class GameServerTestV2Fragment : LazyFragment() {
.commitAllowingStateLoss()
}
private fun showGuidePopupWindow(): PopupWindow {
val guideBinding = LayoutGameServerTestV2SettingGuideBinding.inflate(layoutInflater)
return BugFixedPopupWindow(
guideBinding.root,
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
isFocusable = true
isTouchable = true
isOutsideTouchable = true
animationStyle = R.style.popup_window_ease_in_and_out_anim_style
showAsDropDown(mBinding?.optionIv, 0, (-4F).dip2px())
}
}
override fun onBackPressed(): Boolean {
if (mBinding?.settingGuideContainer?.isVisible == true) {
dismissGuide()
return true
}
if (mSettingBinding != null) {
dismissSettingView()
return true

View File

@ -87,6 +87,11 @@ abstract class BaseBottomTabFragment<T : ViewBinding> : ToolbarFragment() {
protected fun initViewPager() {
mViewPager?.run {
isUserInputEnabled = false
// 去掉默认动画
setPageTransformer { page, _ ->
page.translationX = 0F
page.alpha = 1F
}
adapter = provideAdapter()
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {

View File

@ -14,32 +14,45 @@ import com.gh.gamecenter.fragment.ReloadFragment
class MainFragmentStateAdapter(private val mFragment: Fragment) : BaseDiffFragmentStateAdapter<BottomTab>(mFragment) {
override fun createFragment(data: BottomTab?, position: Int): Fragment {
if (data != null) {
val bundle = Bundle()
val superiorChain = if (mFragment is ISuperiorChain) mFragment else null
mFragment.arguments?.let { bundle.putAll(it) }
if (data.link == null) return ReloadFragment()
bundle.putBoolean(EntranceConsts.KEY_IS_HOME, true)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_ID, data.id)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_NAME, data.name)
bundle.putInt(EntranceConsts.KEY_POSITION, position)
bundle.putParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST, arrayListOf(ExposureSource("底部tab", data.name)))
bundle.putBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, true)
return when (data.link.type) {
if (data == null) return ReloadFragment()
val bundle = Bundle()
val superiorChain = if (mFragment is ISuperiorChain) mFragment else null
mFragment.arguments?.let { bundle.putAll(it) }
bundle.putBoolean(EntranceConsts.KEY_IS_HOME, true)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_ID, data.id)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_NAME, data.name)
bundle.putInt(EntranceConsts.KEY_POSITION, position)
val exposureSourceList = arrayListOf(ExposureSource("底部tab", data.name))
data.diverter?.let {
exposureSourceList.add(
ExposureSource(
"分流器",
"${it.diverterData.diverterName}+${it.diverterData.diverterId}"
)
)
}
bundle.putParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST, exposureSourceList)
bundle.putBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, true)
return if (data.link == null) {
ReloadFragment()
} else {
when (data.link!!.type) {
ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE -> {
bundle.putParcelable(LinkEntity::class.java.simpleName, data.link)
SearchToolbarTabWrapperFragment().setSuperiorChain(superiorChain).apply { arguments = bundle }
}
ViewPagerFragmentHelper.TYPE_MULTI_TAB_NAV -> {
bundle.putParcelable(BottomTab.SearchStyle::class.java.simpleName, data.searchStyle)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, data.link.link)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, data.link.text)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, data.link!!.link)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, data.link!!.text)
SearchToolbarTabWrapperFragment().setSuperiorChain(superiorChain).apply { arguments = bundle }
}
else -> ViewPagerFragmentHelper.createFragment(mFragment, bundle, data.link, false)
else -> ViewPagerFragmentHelper.createFragment(mFragment, bundle, data.link!!, false)
}
} else {
return ReloadFragment()
}
}

View File

@ -80,8 +80,11 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
initBottomTab(mBottomTabList)
showBottomTabGuideIfExists()
mAdapter.submitList(mBottomTabList)
val defaultBottomTabIndex = mViewModel!!.defaultBottomTabIndex
val defaultBottomTab = mBottomTabList.getOrNull(defaultBottomTabIndex) ?: return@observe
mViewPager?.doOnNextLayout {
setCurrentItem(mViewModel!!.defaultBottomTabIndex)
mViewModel?.handleBypassVisit(defaultBottomTab)
setCurrentItem(defaultBottomTabIndex)
}
}
}
@ -213,6 +216,9 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
if (bottomTab?.guide != null) {
dismissBottomTabGuide()
}
bottomTab?.let {
mViewModel?.handleBypassVisit(it)
}
playTabAnimation(toCheck)
changeBottomTabStyle(toCheck)
EventBus.getDefault().post(EBReuse(Constants.FINISH_PULL_DOWN_PUSH))

View File

@ -3,14 +3,19 @@ package com.gh.gamecenter.wrapper
import android.annotation.SuppressLint
import androidx.lifecycle.MutableLiveData
import com.gh.common.util.HomeBottomBarHelper
import com.gh.common.util.HomeBottomBarHelper.isDefaultHomeBottomTabDataExist
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.ViewPagerFragmentHelper
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.entity.LaunchRedirect
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.core.utils.GsonUtils
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.SingletonHolder
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.entity.BottomTab
import com.gh.gamecenter.entity.DataUnionEntity
import com.gh.gamecenter.entity.DiverterEntity
import com.gh.gamecenter.entity.MultiTabNav
import com.gh.gamecenter.home.custom.model.CustomPageData
import com.gh.gamecenter.retrofit.RetrofitManager
@ -21,6 +26,8 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
class MainWrapperRepository {
private val mNewApi = RetrofitManager.getInstance().newApi
@ -46,9 +53,17 @@ class MainWrapperRepository {
val customPageLiveData = MutableLiveData<CustomPageData?>()
val errorLiveData = MutableLiveData<Exception>()
val diverterList = arrayListOf<DiverterEntity>()
private val mTabSelectEventFlow = MutableSharedFlow<MainSelectedEvent>()
val tabSelectEventFlow = mTabSelectEventFlow as SharedFlow<MainSelectedEvent>
fun init() {
// 若 timeout 后数据未加载完成,则即便还没有回调也使用默认数据生成底部 tab
emitDefaultTabDataAfterTimeout()
getBypassList()
}
/**
* 发送首次启动跳转事件的选中事件
*/
@ -133,16 +148,37 @@ class MainWrapperRepository {
}
}
@SuppressLint("CheckResult")
fun getBypassList() {
val isInstallFirstAccess = TimeUtils.isToday(SPUtils.getLong(Constants.SP_BRAND_NEW_FIRST_LAUNCH_TIME) / 1000).toString()
mNewApi.getDiverterList(isInstallFirstAccess, BYPASS_TYPE_BOTTOM_TAB)
.subscribeOn(Schedulers.io())
.timeout(BYPASS_TIME_OUT, TimeUnit.MILLISECONDS)
.subscribe(object : BiResponse<List<DiverterEntity>>() {
override fun onSuccess(data: List<DiverterEntity>) {
diverterList.clear()
diverterList.addAll(data)
getDataUnion()
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
getDataUnion()
val reasonString = if (exception is TimeoutException) "分流判断超时" else "分流页面返回为空"
SensorsBridge.trackFailByPass("底部tab", "", "", reasonString)
NewFlatLogUtils.logFailByPass("底部tab", "", "", reasonString)
}
})
}
@SuppressLint("CheckResult")
fun getDataUnion() {
// 若历史 tab 数据存在,优先使用作为占位
if (isDefaultHomeBottomTabDataExist()) {
processBottomTabData(emptyList())
} else {
// 若 timeout 后数据未加载完成,则即便还没回调 onFailure 也生成两个底部 tab
emitDefaultTabDataAfterTimeout()
var diverter = ""
diverterList.forEach {
diverter += "${it.moduleIndex}:${it.diverterData.branchId},"
}
mNewApi.dataUnion
mNewApi.getDataUnion(diverter)
.subscribeOn(Schedulers.io())
.subscribe(object : BiResponse<DataUnionEntity>() {
override fun onSuccess(data: DataUnionEntity) {
@ -163,7 +199,9 @@ class MainWrapperRepository {
private fun processBottomTabData(bottomTabList: List<BottomTab>) {
if (bottomTabList.isNotEmpty()) {
HomeBottomBarHelper.updateDefaultHomeBottomTabData(bottomTabList)
diverterList.forEach {
bottomTabList.getOrNull(it.moduleIndex)?.diverter = it
}
var preSelectedTab: BottomTab? = null
@ -206,11 +244,16 @@ class MainWrapperRepository {
}
}
HomeBottomBarHelper.updateDefaultHomeBottomTabData(bottomTabList)
bottomTabListLiveData.postValue(bottomTabList)
defaultNavId = bottomTabList.find { it.link?.type == ViewPagerFragmentHelper.TYPE_MULTI_TAB_NAV && it.default }?.link?.link ?: ""
defaultCustomPageId = bottomTabList.find { it.link?.type == ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE && it.default }?.link?.link ?: ""
} else {
HomeBottomBarHelper.getDefaultHomeBottomTabData().run {
val defaultIndex = indexOfFirst { it.default }
if (defaultIndex != -1) {
defaultBottomTabIndex = defaultIndex
}
bottomTabListLiveData.postValue(this)
defaultNavId = find { it.link?.type == ViewPagerFragmentHelper.TYPE_MULTI_TAB_NAV && it.default }?.link?.link ?: ""
defaultCustomPageId = find { it.link?.type == ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE && it.default }?.link?.link ?: ""
@ -248,7 +291,10 @@ class MainWrapperRepository {
}
}
companion object : SingletonHolder<MainWrapperRepository>({ MainWrapperRepository() })
companion object : SingletonHolder<MainWrapperRepository>({ MainWrapperRepository() }) {
private const val BYPASS_TIME_OUT = 1000L
const val BYPASS_TYPE_BOTTOM_TAB = "bottom_tab"
}
}
sealed class MainSelectedEvent {

View File

@ -1,15 +1,22 @@
package com.gh.gamecenter.wrapper
import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.*
import com.gh.common.util.CheckLoginUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.RealNameHelper
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.common.utils.toRequestBody
import com.gh.gamecenter.entity.BottomTab
import com.gh.gamecenter.entity.DiverterEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.livedata.Event
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.retrofit.RetrofitManager
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.map
@ -23,6 +30,7 @@ import retrofit2.HttpException
class MainWrapperViewModel(application: Application, private val mRepository: MainWrapperRepository) :
AndroidViewModel(application) {
private val mApi = RetrofitManager.getInstance().api
private val mNewApi = RetrofitManager.getInstance().newApi
val defaultBottomTabIndex
get() = mRepository.defaultBottomTabIndex
@ -45,6 +53,8 @@ class MainWrapperViewModel(application: Application, private val mRepository: Ma
val bottomDoubleTabAction = MutableLiveData<BottomTab>()
private val byPassVisitBottomTabIdSet = hashSetOf<String>()
/**
* 请求各种弹窗的数据
*/
@ -91,6 +101,58 @@ class MainWrapperViewModel(application: Application, private val mRepository: Ma
})
}
fun handleBypassVisit(bottomTab: BottomTab) {
if (byPassVisitBottomTabIdSet.contains(bottomTab.id)) return
bottomTab.diverter?.let {
val source = "底部tab${bottomTab.name}"
SensorsBridge.trackByPassBrowsing(
source,
it.diverterData.diverterName,
it.diverterData.diverterId,
it.diverterData.branchId,
it.diverterData.branchName,
it.diverterData.inprocessTime,
it.diverterData.bypassVisitTime,
it.diverterData.linkType,
it.diverterData.linkId,
it.diverterData.linkText,
it.diverterData.bypassStatus,
)
NewFlatLogUtils.logByPassBrowsing(
source,
it.diverterData.diverterName,
it.diverterData.diverterId,
it.diverterData.branchId,
it.diverterData.branchName,
it.diverterData.inprocessTime,
it.diverterData.bypassVisitTime,
it.diverterData.linkType,
it.diverterData.linkId,
it.diverterData.linkText,
it.diverterData.bypassStatus,
)
patchBypassVisitTime(it)
byPassVisitBottomTabIdSet.add(bottomTab.id)
}
}
@SuppressLint("CheckResult")
private fun patchBypassVisitTime(diverterEntity: DiverterEntity) {
val data = mapOf(
"module_id" to diverterEntity.moduleId,
"diverter_id" to diverterEntity.diverterData.diverterId,
"branch_type" to diverterEntity.diverterData.branchType
)
mNewApi.patchDiverterVisitTime(MainWrapperRepository.BYPASS_TYPE_BOTTOM_TAB, data.toRequestBody())
.compose(singleToMain())
.subscribe({
Utils.log(TAG, "patchDiverterVisitTime onSuccess: ${it.string()}")
}, {
Utils.log(TAG, "patchDiverterVisitTime onError: ${it.message}")
})
}
class Factory(
private val mApplication: Application,
) : ViewModelProvider.NewInstanceFactory() {
@ -100,6 +162,7 @@ class MainWrapperViewModel(application: Application, private val mRepository: Ma
}
companion object {
private const val TAG = "MainWrapperViewModel"
const val SHOULD_SHOW_OPENING_DIALOG = "show_opening_dialog"
}
}

View File

@ -369,7 +369,7 @@ public class HaloApp extends MultiDexApplication {
GlobalPriorityChainHelper.INSTANCE.preStart(isNewForThisVersion);
MainWrapperRepository.Companion.getInstance().getDataUnion();
MainWrapperRepository.Companion.getInstance().init();
// QQ小游戏开放互联初始化
IQGameProvider provider = (IQGameProvider) ARouter