diff --git a/app/src/main/java/com/gh/common/prioritychain/CommunityHomeGuideHandler.kt b/app/src/main/java/com/gh/common/prioritychain/CommunityHomeGuideHandler.kt new file mode 100644 index 0000000000..6cf1b512e1 --- /dev/null +++ b/app/src/main/java/com/gh/common/prioritychain/CommunityHomeGuideHandler.kt @@ -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() + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/HomeBottomBarHelper.kt b/app/src/main/java/com/gh/common/util/HomeBottomBarHelper.kt index a4d9fcd673..69f2e61883 100644 --- a/app/src/main/java/com/gh/common/util/HomeBottomBarHelper.kt +++ b/app/src/main/java/com/gh/common/util/HomeBottomBarHelper.kt @@ -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 { try { diff --git a/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt b/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt index 293829433d..fa3e502ce6 100644 --- a/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt +++ b/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt @@ -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) + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/ViewPagerFragmentHelper.kt b/app/src/main/java/com/gh/common/util/ViewPagerFragmentHelper.kt index 0b8026594d..30435f44c2 100644 --- a/app/src/main/java/com/gh/common/util/ViewPagerFragmentHelper.kt +++ b/app/src/main/java/com/gh/common/util/ViewPagerFragmentHelper.kt @@ -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) { diff --git a/app/src/main/java/com/gh/gamecenter/MainActivity.java b/app/src/main/java/com/gh/gamecenter/MainActivity.java index dbc9102aad..d1c5d63be1 100644 --- a/app/src/main/java/com/gh/gamecenter/MainActivity.java +++ b/app/src/main/java/com/gh/gamecenter/MainActivity.java @@ -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; diff --git a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt index fb8861fa93..02258eef71 100644 --- a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt @@ -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) { diff --git a/app/src/main/java/com/gh/gamecenter/entity/BottomTab.kt b/app/src/main/java/com/gh/gamecenter/entity/BottomTab.kt index 304ec886d7..88dc2cc857 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/BottomTab.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/BottomTab.kt @@ -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( diff --git a/app/src/main/java/com/gh/gamecenter/entity/DiverterData.kt b/app/src/main/java/com/gh/gamecenter/entity/DiverterData.kt new file mode 100644 index 0000000000..7f6b108f3d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/DiverterData.kt @@ -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 \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/DiverterEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/DiverterEntity.kt new file mode 100644 index 0000000000..96f56a28f4 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/DiverterEntity.kt @@ -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 diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt index aea0d7ce5e..20504457c5 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityHomeFragment.kt @@ -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 diff --git a/app/src/main/java/com/gh/gamecenter/fragment/ReloadFragment.kt b/app/src/main/java/com/gh/gamecenter/fragment/ReloadFragment.kt index 129e3a338e..79e61b314f 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/ReloadFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/ReloadFragment.kt @@ -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 } diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index 5f9e6b66e4..3890802e2e 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -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 getDataUnion(); + Single getDataUnion(@Query("diverter") String diverter); /** * 底部tab @@ -3390,4 +3391,16 @@ public interface ApiService { */ @GET("game_lists/hot_columns") Single> getHotColumns(); + + /** + * 分流器列表信息 + */ + @GET("app/{module}/diverter") + Single> getDiverterList(@Header("Install-First-Access") String isInstallFirstAccess, @Path("module") String module); + + /** + * 访问分流页面,更新分流访问次数 + */ + @PATCH("app/{module}/diverter_visit_time") + Single patchDiverterVisitTime(@Path("module") String module, @Body RequestBody body); } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/servers/gametest2/GameServerTestV2Fragment.kt b/app/src/main/java/com/gh/gamecenter/servers/gametest2/GameServerTestV2Fragment.kt index 086dab593a..ebf2f178d5 100644 --- a/app/src/main/java/com/gh/gamecenter/servers/gametest2/GameServerTestV2Fragment.kt +++ b/app/src/main/java/com/gh/gamecenter/servers/gametest2/GameServerTestV2Fragment.kt @@ -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 diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/BaseBottomTabFragment.kt b/app/src/main/java/com/gh/gamecenter/wrapper/BaseBottomTabFragment.kt index 877153df0d..7171a8e6af 100644 --- a/app/src/main/java/com/gh/gamecenter/wrapper/BaseBottomTabFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/wrapper/BaseBottomTabFragment.kt @@ -87,6 +87,11 @@ abstract class BaseBottomTabFragment : 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) { diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/MainFragmentStateAdapter.kt b/app/src/main/java/com/gh/gamecenter/wrapper/MainFragmentStateAdapter.kt index 577ac1b39c..8b31c62f2a 100644 --- a/app/src/main/java/com/gh/gamecenter/wrapper/MainFragmentStateAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/wrapper/MainFragmentStateAdapter.kt @@ -14,32 +14,45 @@ import com.gh.gamecenter.fragment.ReloadFragment class MainFragmentStateAdapter(private val mFragment: Fragment) : BaseDiffFragmentStateAdapter(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() } } diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperFragment.kt index 0c73efb2a9..c5e2670599 100644 --- a/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperFragment.kt @@ -80,8 +80,11 @@ class MainWrapperFragment : BaseBottomTabFragment(), 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(), OnBa if (bottomTab?.guide != null) { dismissBottomTabGuide() } + bottomTab?.let { + mViewModel?.handleBypassVisit(it) + } playTabAnimation(toCheck) changeBottomTabStyle(toCheck) EventBus.getDefault().post(EBReuse(Constants.FINISH_PULL_DOWN_PUSH)) diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperRepository.kt b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperRepository.kt index ae21687a65..d69962bd10 100644 --- a/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperRepository.kt +++ b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperRepository.kt @@ -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() val errorLiveData = MutableLiveData() + val diverterList = arrayListOf() + private val mTabSelectEventFlow = MutableSharedFlow() val tabSelectEventFlow = mTabSelectEventFlow as SharedFlow + 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>() { + override fun onSuccess(data: List) { + 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() { override fun onSuccess(data: DataUnionEntity) { @@ -163,7 +199,9 @@ class MainWrapperRepository { private fun processBottomTabData(bottomTabList: List) { 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() }) + companion object : SingletonHolder({ MainWrapperRepository() }) { + private const val BYPASS_TIME_OUT = 1000L + const val BYPASS_TYPE_BOTTOM_TAB = "bottom_tab" + } } sealed class MainSelectedEvent { diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperViewModel.kt b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperViewModel.kt index 3d6809cce5..1e94f675f5 100644 --- a/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperViewModel.kt @@ -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() + private val byPassVisitBottomTabIdSet = hashSetOf() + /** * 请求各种弹窗的数据 */ @@ -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" } } \ No newline at end of file diff --git a/app/src/main/java/com/halo/assistant/HaloApp.java b/app/src/main/java/com/halo/assistant/HaloApp.java index 9035311978..03e755b33d 100644 --- a/app/src/main/java/com/halo/assistant/HaloApp.java +++ b/app/src/main/java/com/halo/assistant/HaloApp.java @@ -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 diff --git a/app/src/main/res/layout/fragment_game_server_test_v2.xml b/app/src/main/res/layout/fragment_game_server_test_v2.xml index 09be2d95de..f88eff4e60 100644 --- a/app/src/main/res/layout/fragment_game_server_test_v2.xml +++ b/app/src/main/res/layout/fragment_game_server_test_v2.xml @@ -1,6 +1,7 @@ @@ -104,4 +105,26 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_game_server_test_v2_setting_guide.xml b/app/src/main/res/layout/layout_game_server_test_v2_setting_guide.xml deleted file mode 100644 index b9d445507e..0000000000 --- a/app/src/main/res/layout/layout_game_server_test_v2_setting_guide.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java index 0e0fb3302f..e68bc741d2 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java @@ -466,4 +466,7 @@ public class Constants { public static final String SP_BOTTOM_TAB_GUIDE_SET = "bottom_tab_guide_id_set"; public static final String SP_MULTI_TAB_NAV_GUIDE_SET = "multi_tab_nav_guide_id_set"; + + public static final String SP_BRAND_NEW_FIRST_LAUNCH_TIME = "brand_new_first_launch_time"; // 全新安装用户首次启动时间 + } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt index d0a9e21c3f..b76893896e 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt @@ -277,6 +277,9 @@ object SensorsBridge { private const val EVENT_SEARCH_DISCOVERY_CLICK = "SearchDiscoveryClick" + private const val EVENT_BYPASS_BROWSING = "BypassBrowsing" + private const val EVENT_FAIL_BYPASS = "FailBypass" + private var mIsSensorsEnabled = false private val mSensor by lazy { @@ -4390,4 +4393,56 @@ object SensorsBridge { trackEvent(EVENT_SEARCH_DISCOVERY_CLICK, json) } + /** + * 事件ID:BypassBrowsing + * 事件名称:分流器访问事件 + */ + fun trackByPassBrowsing( + source: String, + bypassName: String, + bypassId: String, + branchId: String, + branchName: String, + inProcessTime: Int, + bypassVisitTime: Int, + linkType: String, + linkId: String, + linkText: String, + bypassStatus: Int + ) { + val json = json { + "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 + } + trackEvent(EVENT_BYPASS_BROWSING, json) + } + + /** + * 事件ID:FailBypass + * 事件名称:分流失败 + */ + fun trackFailByPass( + source: String, + bypassName: String, + bypassId: String, + defeatedReason: String + ) { + val json = json { + "source" to source + "bypass_name" to bypassName + "bypass_id" to bypassId + "defeated_reason" to defeatedReason + } + trackEvent(EVENT_FAIL_BYPASS, json) + } + } \ No newline at end of file