From d32d00a4fa2f7a33199e56810d76c47714b7e70c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=99=A8?= Date: Tue, 10 Sep 2024 09:18:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=B8=B8=E6=88=8F=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E6=96=B0=E5=A2=9E=E5=88=86=E7=B1=BB=E5=AF=BC?= =?UTF-8?q?=E8=88=AA=E2=80=94=E5=AE=A2=E6=88=B7=E7=AB=AF=20https://jira.sh?= =?UTF-8?q?anqu.cc/browse/GHZSCY-5585?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 6 + .../provider/SearchTabUtilsProviderImpl.kt | 36 ++ .../java/com/gh/common/util/DirectUtils.kt | 1 + .../com/gh/common/util/NewFlatLogUtils.kt | 24 +- .../java/com/gh/gamecenter/SearchActivity.kt | 73 +-- .../amway/search/AmwaySearchActivity.kt | 3 - .../search/ForumContentSearchListFragment.kt | 30 +- .../search/ForumContentSearchListViewModel.kt | 6 +- .../forum/search/ForumOrUserSearchActivity.kt | 24 - .../forum/search/UserSearchListFragment.kt | 29 +- .../forum/search/UserSearchListViewModel.kt | 9 +- .../gamedetail/GameDetailFragment.kt | 1 + .../home/custom/model/CustomPageData.kt | 7 +- .../qa/dialog/ChooseForumActivity.kt | 18 - .../qa/dialog/ChooseForumContainerFragment.kt | 46 +- .../dialog/ChooseForumContainerViewModel.kt | 20 +- .../retrofit/service/ApiService.java | 6 + .../search/SearchGameFirstItemViewHolder.kt | 7 +- .../search/SearchGameIndexAdapter.kt | 17 - .../search/SearchGameIndexItemViewHolder.kt | 10 - .../search/SearchGameListRepository.kt | 20 + .../search/SearchGameResultAdapter.kt | 36 +- .../search/SearchGameResultFragment.kt | 23 +- .../search/SearchGameResultViewModel.kt | 45 +- .../gh/gamecenter/search/SearchTabActivity.kt | 39 ++ .../gamecenter/search/SearchTabRepository.kt | 15 + .../search/adapter/SearchGameListAdapter.kt | 145 +++++ .../search/adapter/SearchTabAdapter.kt | 72 +++ .../search/fragment/SearchGameListFragment.kt | 118 ++++ .../search/fragment/SearchTabFragment.kt | 138 +++++ .../SearchGameListFooterViewHolder.kt | 60 ++ .../viewholder/SearchGameListViewHolder.kt | 119 ++++ .../viewmodel/SearchGameListViewModel.kt | 141 +++++ .../viewmodel/SearchTabActivityViewModel.kt | 26 + .../search/viewmodel/SearchTabViewModel.kt | 29 + .../com/gh/gamecenter/vote/VoteViewModel.kt | 2 - .../res/drawable/search_tab_indicator.xml | 14 + .../res/layout/fragment_search_game_list.xml | 32 ++ .../main/res/layout/fragment_search_tab.xml | 48 ++ .../feedback/view/qa/HelpContentAdapter.kt | 15 +- .../feedback/view/qa/HelpContentFragment.kt | 40 +- .../feedback/view/qa/QaSearchActivity.kt | 8 +- .../common/constant/EntranceConsts.java | 4 + .../gamecenter/common/constant/RouteConsts.kt | 1 + .../gamecenter/common/utils/SensorsBridge.kt | 533 ++++++++++++++---- .../core/provider/ISearchTabUtilsProvider.kt | 13 + .../feature/entity/SettingsEntity.kt | 30 +- 47 files changed, 1854 insertions(+), 285 deletions(-) create mode 100644 app/src/main/java/com/gh/common/provider/SearchTabUtilsProviderImpl.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/SearchGameListRepository.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/SearchTabActivity.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/SearchTabRepository.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/adapter/SearchGameListAdapter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/adapter/SearchTabAdapter.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/fragment/SearchGameListFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/fragment/SearchTabFragment.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/viewholder/SearchGameListFooterViewHolder.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/viewholder/SearchGameListViewHolder.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchGameListViewModel.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabActivityViewModel.kt create mode 100644 app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabViewModel.kt create mode 100644 app/src/main/res/drawable/search_tab_indicator.xml create mode 100644 app/src/main/res/layout/fragment_search_game_list.xml create mode 100644 app/src/main/res/layout/fragment_search_tab.xml create mode 100644 module_core/src/main/java/com/gh/gamecenter/core/provider/ISearchTabUtilsProvider.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4bae8cb9b7..b6e2e2bd8b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -794,6 +794,12 @@ android:screenOrientation="portrait" android:theme="@style/AppCompatTheme.APP" /> + + diff --git a/app/src/main/java/com/gh/common/provider/SearchTabUtilsProviderImpl.kt b/app/src/main/java/com/gh/common/provider/SearchTabUtilsProviderImpl.kt new file mode 100644 index 0000000000..e8a0cdf2f6 --- /dev/null +++ b/app/src/main/java/com/gh/common/provider/SearchTabUtilsProviderImpl.kt @@ -0,0 +1,36 @@ +package com.gh.common.provider + +import android.content.Context +import androidx.fragment.app.Fragment +import androidx.lifecycle.LiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.alibaba.android.arouter.facade.annotation.Route +import com.gh.gamecenter.common.constant.RouteConsts +import com.gh.gamecenter.core.provider.ISearchTabUtilsProvider +import com.gh.gamecenter.search.viewmodel.SearchTabViewModel + +@Route(path = RouteConsts.provider.searchTabUtils, name = "SearchTabUtils暴露服务") +class SearchTabUtilsProviderImpl : ISearchTabUtilsProvider { + + override fun obtainParentViewModel(fragment: Fragment): ViewModel { + val store = (fragment.parentFragment ?: fragment).viewModelStore + val factory = fragment.defaultViewModelProviderFactory + return ViewModelProvider( + store, + factory + ).get(SearchTabViewModel::class.java) + } + + override fun getKeyAndTypeLiveData(viewModel: ViewModel?): LiveData>? = + if (viewModel is SearchTabViewModel) { + viewModel.searchKeyAndType + } else { + null + } + + + override fun init(context: Context?) { + // no implement + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/DirectUtils.kt b/app/src/main/java/com/gh/common/util/DirectUtils.kt index afb12201be..2b49d71c36 100644 --- a/app/src/main/java/com/gh/common/util/DirectUtils.kt +++ b/app/src/main/java/com/gh/common/util/DirectUtils.kt @@ -78,6 +78,7 @@ import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity import com.gh.gamecenter.qa.subject.CommunitySubjectActivity import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.gamecenter.SearchActivity import com.gh.gamecenter.servers.GameServerTestActivity import com.gh.gamecenter.servers.GameServersActivity import com.gh.gamecenter.servers.gametest2.GameServerTestV2Activity 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 fa3e502ce6..89e047cc3b 100644 --- a/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt +++ b/app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt @@ -661,12 +661,14 @@ object NewFlatLogUtils { @JvmStatic fun logSearchBbs( searchType: String, - searchKey: String + searchKey: String, + location: String ) { json { KEY_EVENT to "search_bbs" "search_type" to searchType "key" to searchKey + KEY_LOCATION to location parseAndPutMeta()() }.let(::log) } @@ -678,7 +680,8 @@ object NewFlatLogUtils { bbsId: String, sequence: Int, name: String, - button: String + button: String, + location: String ) { json { KEY_EVENT to "search_bbs_click" @@ -688,6 +691,7 @@ object NewFlatLogUtils { "sequence" to sequence "name" to name "button" to button + KEY_LOCATION to location parseAndPutMeta()() }.let(::log) } @@ -695,12 +699,14 @@ object NewFlatLogUtils { @JvmStatic fun logSearchUser( searchType: String, - searchKey: String + searchKey: String, + location: String ) { json { KEY_EVENT to "search_user" "search_type" to searchType "key" to searchKey + KEY_LOCATION to location parseAndPutMeta()() }.let(::log) } @@ -711,7 +717,8 @@ object NewFlatLogUtils { searchKey: String, userId: String, name: String, - sequence: Int + sequence: Int, + location: String ) { json { KEY_EVENT to "search_user_click" @@ -720,6 +727,7 @@ object NewFlatLogUtils { "user_id" to userId "name" to name "sequence" to sequence + KEY_LOCATION to location parseAndPutMeta()() }.let(::log) } @@ -1344,7 +1352,13 @@ object NewFlatLogUtils { } // 上传存档弹窗点击事件 - fun logCloudArchiveUploadDialogClick(gameId: String, gameName: String, cloudSaveId: String, cloudSaveName: String, isSuccess: Boolean) { + fun logCloudArchiveUploadDialogClick( + gameId: String, + gameName: String, + cloudSaveId: String, + cloudSaveName: String, + isSuccess: Boolean + ) { val json = json { KEY_EVENT to "cloud_save_upload_dialog_click" KEY_GAME_ID to gameId diff --git a/app/src/main/java/com/gh/gamecenter/SearchActivity.kt b/app/src/main/java/com/gh/gamecenter/SearchActivity.kt index 97a4ba8ad2..5b17bebf64 100644 --- a/app/src/main/java/com/gh/gamecenter/SearchActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/SearchActivity.kt @@ -22,6 +22,7 @@ import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.db.ISearchHistoryDao import com.gh.gamecenter.db.SearchHistoryDao import com.gh.gamecenter.eventbus.EBSearch +import com.gh.gamecenter.search.SearchTabActivity import com.gh.gamecenter.search.SearchDefaultFragment import com.gh.gamecenter.search.SearchGameIndexFragment import com.gh.gamecenter.search.SearchGameResultFragment @@ -52,7 +53,11 @@ open class SearchActivity : BaseActivity() { private var mPublishSubject: PublishSubject? = null - private var mSourceEntrance: String = "" + /** + * 神策的 SourceEntrance 字段相当于 火山云的 location + * 如果有火山云埋点需要上传 location 字段,那么直接使用此字段的值就行 + */ + protected var mSourceEntrance: String = "" override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) @@ -117,27 +122,10 @@ open class SearchActivity : BaseActivity() { updateDisplayType(DEFAULT) } - trackSearchPageShow() - } - - protected open fun trackSearchPageShow() { - val bottomTab = intent.getStringExtra(EntranceConsts.KEY_BOTTOM_TAB_NAME) ?: "" - val multiTabId = intent.getStringExtra(EntranceConsts.KEY_MULTI_TAB_NAV_ID) ?: "" - val multiTabName = intent.getStringExtra(EntranceConsts.KEY_MULTI_TAB_NAV_NAME) ?: "" - val customPageId = intent.getStringExtra(EntranceConsts.KEY_CUSTOM_PAGE_ID) ?: "" - val customPageName = intent.getStringExtra(EntranceConsts.KEY_CUSTOM_PAGE_NAME) ?: "" - val searchBoxPattern = intent.getStringExtra(EntranceConsts.KEY_SEARCH_BOX_PATTERN) ?: "" - - SensorsBridge.trackSearchPageShow( + SensorsBridge.trackGameSearchPageShow( GlobalActivityManager.getCurrentPageEntity().pageId, GlobalActivityManager.getCurrentPageEntity().pageName, - intent.getStringExtra(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: "", - bottomTab, - multiTabId, - multiTabName, - customPageId, - customPageName, - searchBoxPattern + mSourceEntrance ) } @@ -206,12 +194,12 @@ open class SearchActivity : BaseActivity() { updateDisplayType(GAME_DIGEST) LogUtils.uploadSearchGame("searching", "搜索页", key, "自动搜索") - SensorsBridge.trackSearchButtonClick( + SensorsBridge.trackGameSearchPageButtonClick( GlobalActivityManager.getCurrentPageEntity().pageId, GlobalActivityManager.getCurrentPageEntity().pageName, + mSourceEntrance, key ?: "", - TRACK_SEARCH_TYPE_INPUT, - mSourceEntrance + TRACK_SEARCH_TYPE_AUTO ) } } @@ -239,14 +227,13 @@ open class SearchActivity : BaseActivity() { updateDisplayType(GAME_DETAIL) LogUtils.uploadSearchGame("searching", "搜索页", key, "默认搜索") - SensorsBridge.trackSearchButtonClick( + SensorsBridge.trackGameSearchPageButtonClick( GlobalActivityManager.getCurrentPageEntity().pageId, GlobalActivityManager.getCurrentPageEntity().pageName, + mSourceEntrance, key ?: "", - TRACK_SEARCH_TYPE_DEFAULT, - mSourceEntrance + TRACK_SEARCH_TYPE_DEFAULT ) -// MtaHelper.onEvent("游戏搜索", "默认搜索", key) } protected open fun handleHotSearch(key: String?) { @@ -263,14 +250,13 @@ open class SearchActivity : BaseActivity() { updateDisplayType(GAME_DETAIL) LogUtils.uploadSearchGame("searching", "搜索页", key, "历史搜索") - SensorsBridge.trackSearchButtonClick( + SensorsBridge.trackGameSearchPageButtonClick( GlobalActivityManager.getCurrentPageEntity().pageId, GlobalActivityManager.getCurrentPageEntity().pageName, + mSourceEntrance, key ?: "", - TRACK_SEARCH_TYPE_HISTORY, - mSourceEntrance + TRACK_SEARCH_TYPE_HISTORY ) -// MtaHelper.onEvent("游戏搜索", "历史搜索", key) } protected open fun handleManualSearch() { @@ -284,16 +270,16 @@ open class SearchActivity : BaseActivity() { } else if (newSearchKey != mSearchKey || mDisplayType != GAME_DETAIL) { mSearchKey = newSearchKey if (!TextUtils.isEmpty(mSearchKey)) { - SensorsBridge.trackSearchButtonClick( - GlobalActivityManager.getCurrentPageEntity().pageId, - GlobalActivityManager.getCurrentPageEntity().pageName, - newSearchKey, - TRACK_SEARCH_TYPE_INPUT, - mSourceEntrance - ) - mDao.add(mSearchKey) updateDisplayType(GAME_DETAIL) + + SensorsBridge.trackGameSearchPageButtonClick( + GlobalActivityManager.getCurrentPageEntity().pageId, + GlobalActivityManager.getCurrentPageEntity().pageName, + mSourceEntrance, + newSearchKey, + TRACK_SEARCH_TYPE_INPUT + ) } else { toast("请输入搜索内容") } @@ -415,13 +401,15 @@ open class SearchActivity : BaseActivity() { private const val KEY_SEARCH_IMMEDIATELY = "search_immediately" private const val HINT_TEXT = "搜索游戏..." + const val TRACK_SEARCH_TYPE_AUTO = "自动搜索" const val TRACK_SEARCH_TYPE_INPUT = "输入搜索" const val TRACK_SEARCH_TYPE_DEFAULT = "默认搜索" const val TRACK_SEARCH_TYPE_HISTORY = "历史搜索" @JvmStatic fun toTrackSearchType(type: String) = when (type) { - SearchType.AUTO.value, SearchType.MANUAL.value -> TRACK_SEARCH_TYPE_INPUT + SearchType.AUTO.value -> TRACK_SEARCH_TYPE_AUTO + SearchType.MANUAL.value -> TRACK_SEARCH_TYPE_INPUT SearchType.HISTORY.value -> TRACK_SEARCH_TYPE_HISTORY else -> TRACK_SEARCH_TYPE_DEFAULT } @@ -433,7 +421,8 @@ open class SearchActivity : BaseActivity() { hint: String, entrance: String, sourceEntrance: String - ): Intent = getIntent(context, searchImmediately, hint, entrance, sourceEntrance, "", "", "", "", "", "") + ): Intent = + getIntent(context, searchImmediately, hint, entrance, sourceEntrance, "", "", "", "", "", "") @JvmStatic fun getIntent( @@ -449,7 +438,7 @@ open class SearchActivity : BaseActivity() { customPageName: String = "", searchBoxPattern: String = "" ): Intent { - val intent = Intent(context, SearchActivity::class.java) + val intent = Intent(context, SearchTabActivity::class.java) intent.putExtra(KEY_SEARCH_IMMEDIATELY, searchImmediately) intent.putExtra(EntranceConsts.KEY_HINT, hint) intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance) diff --git a/app/src/main/java/com/gh/gamecenter/amway/search/AmwaySearchActivity.kt b/app/src/main/java/com/gh/gamecenter/amway/search/AmwaySearchActivity.kt index 742c978d3e..00f856885f 100644 --- a/app/src/main/java/com/gh/gamecenter/amway/search/AmwaySearchActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/amway/search/AmwaySearchActivity.kt @@ -78,9 +78,6 @@ class AmwaySearchActivity : SearchActivity() { transaction.commitAllowingStateLoss() } - // 安利墙搜索页不需要神策埋点 - override fun trackSearchPageShow() = Unit - companion object { fun getIntent(context: Context): Intent { val intent = Intent(context, AmwaySearchActivity::class.java) diff --git a/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListFragment.kt index e2671e0301..77fbce96ba 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/search/ForumContentSearchListFragment.kt @@ -1,7 +1,9 @@ package com.gh.gamecenter.forum.search +import android.os.Bundle import android.view.View import androidx.core.content.ContextCompat +import androidx.fragment.app.viewModels import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.NewFlatLogUtils import com.gh.common.util.NewLogUtils @@ -24,12 +26,19 @@ import com.gh.gamecenter.feature.entity.AnswerEntity import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper import com.gh.gamecenter.forum.search.CommunitySearchEventListener.Companion.SEARCH_BUTTON_VIEW_CONTENT_DETAIL import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity.Companion.LOCATION_COMMUNITY +import com.gh.gamecenter.search.viewmodel.SearchTabViewModel import com.gh.gamecenter.video.detail.CustomManager import com.shuyu.gsyvideoplayer.video.base.GSYVideoView class ForumContentSearchListFragment : LazyListFragment(), CommunitySearchEventListener { + private val parentTabViewModel by viewModels( + ownerProducer = { + parentFragment ?: this + } + ) + private var mBbsId = "" private var mSearchKey = "" private var mSearchType = SearchType.DEFAULT.value @@ -42,6 +51,8 @@ class ForumContentSearchListFragment : LazyListFragment { return mAdapter ?: ForumContentSearchListAdapter( @@ -79,6 +90,11 @@ class ForumContentSearchListFragment : LazyListFragment + setSearchKeyAndType(key, type) + } } override fun getItemDecoration(): RecyclerView.ItemDecoration? { @@ -260,13 +281,16 @@ class ForumContentSearchListFragment : LazyListFragment() { + private val parentTabViewModel by viewModels( + ownerProducer = { parentFragment ?: this } + ) + private var mSearchKey = "" private var mAdapter: UserSearchListAdapter? = null private var mSearchType = SearchType.DEFAULT.value + private var mIsAutoLoad = true override fun provideListAdapter(): ListAdapter<*> { return mAdapter ?: UserSearchListAdapter(requireContext(), mEntrance, mListViewModel) { userId, name, position -> - SensorsBridge.trackSearchResultClick( + SensorsBridge.trackUserSearchResultClick( GlobalActivityManager.getCurrentPageEntity().pageId, GlobalActivityManager.getCurrentPageEntity().pageName, mSearchKey, SearchActivity.toTrackSearchType(mSearchType), - mListViewModel.sourceEntrance + mListViewModel.sourceEntrance, + userId ) NewFlatLogUtils.logSearchUserClick( SearchType.fromString(mSearchType).toChinese(), mSearchKey, userId, name, - position + 1 + position + 1, + mListViewModel.sourceEntrance ) }.apply { mAdapter = this } } @@ -54,6 +64,11 @@ class UserSearchListFragment : LazyListFragment + setSearchKeyAndType(key, type) + } + } override fun getItemDecoration(): RecyclerView.ItemDecoration? = null diff --git a/app/src/main/java/com/gh/gamecenter/forum/search/UserSearchListViewModel.kt b/app/src/main/java/com/gh/gamecenter/forum/search/UserSearchListViewModel.kt index bf8064d7c6..c7ea50760c 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/search/UserSearchListViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/search/UserSearchListViewModel.kt @@ -34,7 +34,8 @@ class UserSearchListViewModel(application: Application) : if (page == 1) { NewFlatLogUtils.logSearchUser( SearchType.fromString(searchType).toChinese(), - searchKey + searchKey, + sourceEntrance ) } return RetrofitManager.getInstance().api.searchUsers(searchKey, page) @@ -44,13 +45,13 @@ class UserSearchListViewModel(application: Application) : mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) if (page == 1) { - SensorsBridge.trackSearchResultReturn( + SensorsBridge.trackUserSearchResultReturn( GlobalActivityManager.getCurrentPageEntity().pageId, GlobalActivityManager.getCurrentPageEntity().pageName, + sourceEntrance, searchKey, SearchActivity.toTrackSearchType(searchType), - it.isNotEmpty(), - sourceEntrance + it.isNotEmpty() ) } } diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt index d5c267116e..2da9fba3cc 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt @@ -86,6 +86,7 @@ import com.gh.gamecenter.home.video.ScrollCalculatorHelper import com.gh.gamecenter.login.user.UserViewModel import com.gh.gamecenter.newsdetail.NewsDetailActivity import com.gh.gamecenter.packagehelper.PackageViewModel +import com.gh.gamecenter.SearchActivity import com.gh.gamecenter.simulatorgame.SimulatorGameActivity import com.gh.gamecenter.tag.TagsActivity import com.gh.gamecenter.video.detail.CustomManager diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt index 1eb0639c9f..9d13482d23 100644 --- a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt @@ -220,7 +220,9 @@ class CustomPageData( @SerializedName("stamp") private val _stamp: String? = null, @SerializedName("style") - private val _style: String? = null + private val _style: String? = null, + @SerializedName("tags") + private val _tags: List? = null ) { val id: String @@ -269,6 +271,9 @@ class CustomPageData( @IgnoredOnParcel private var filteredGames: List? = null + val tags: List + get() = _tags ?: emptyList() + data class User( @SerializedName("_id") private val _id: String? = null, diff --git a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumActivity.kt index c455ecaa9f..b53f8749fd 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumActivity.kt @@ -14,18 +14,13 @@ import com.gh.gamecenter.common.base.activity.BaseActivity import com.gh.gamecenter.common.base.adapter.FragmentAdapter import com.gh.common.util.* import com.gh.gamecenter.R -import com.gh.gamecenter.SearchActivity -import com.gh.gamecenter.SearchActivity.Companion.TRACK_SEARCH_TYPE_INPUT import com.gh.gamecenter.SearchType -import com.gh.gamecenter.common.base.GlobalActivityManager import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.doOnPageSelected import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.core.utils.doOnEnd import com.gh.gamecenter.databinding.DialogChooseForumBinding import com.gh.gamecenter.common.entity.CommunityEntity -import com.gh.gamecenter.common.utils.SensorsBridge -import com.gh.gamecenter.forum.detail.ForumDetailFragment.Companion.SOURCE_ENTRANCE /** * 选择论坛 @@ -57,13 +52,6 @@ class ChooseForumActivity : BaseActivity() { } else { switchUI(true) mSearchResultFragment?.setSearchKeyAndType(searchKey, SearchType.MANUAL.value) - SensorsBridge.trackSearchButtonClick( - GlobalActivityManager.getCurrentPageEntity().pageId, - GlobalActivityManager.getCurrentPageEntity().pageName, - searchKey, - TRACK_SEARCH_TYPE_INPUT, - sourceEntrance - ) } } binding.searchEt.setOnTouchListener { _, event -> @@ -82,12 +70,6 @@ class ChooseForumActivity : BaseActivity() { binding.maskView.setOnClickListener { binding.closeIv.performClick() } binding.forumContainer.translationY = (DisplayUtils.getScreenHeight()).toFloat() binding.forumContainer.animate().translationY(0f).setDuration(300).start() - - SensorsBridge.trackSearchPageShow( - GlobalActivityManager.getCurrentPageEntity().pageId, - GlobalActivityManager.getCurrentPageEntity().pageName, - SOURCE_ENTRANCE - ) } private fun initViewPager() { diff --git a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerFragment.kt index b3a9008057..ec9195fd1f 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerFragment.kt @@ -1,8 +1,10 @@ package com.gh.gamecenter.qa.dialog +import android.os.Bundle import androidx.core.content.ContextCompat import androidx.core.os.bundleOf import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.NewFlatLogUtils import com.gh.common.util.NewLogUtils @@ -18,9 +20,14 @@ import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.core.utils.HtmlUtils import com.gh.gamecenter.entity.ForumEntity import com.gh.gamecenter.forum.detail.ForumDetailActivity +import com.gh.gamecenter.search.viewmodel.SearchTabViewModel class ChooseForumContainerFragment : LazyListFragment() { + private val parentTabViewModel by viewModels( + ownerProducer = { parentFragment ?: this } + ) + private var mAdapter: ChooseForumContainerAdapter? = null private var type: String = "" private var mSearchKey: String = "" @@ -29,6 +36,13 @@ class ChooseForumContainerFragment : LazyListFragment NewFlatLogUtils.logSearchBbsClick( - mSearchType, mSearchKey, forumEntity.id, position + 1, HtmlUtils.stripHtml(forumEntity.name), button + mSearchType, + mSearchKey, + forumEntity.id, + position + 1, + HtmlUtils.stripHtml(forumEntity.name), + button, + mListViewModel.sourceEntrance + ) + + SensorsBridge.trackForumSearchResultClick( + GlobalActivityManager.getCurrentPageEntity().pageId, + GlobalActivityManager.getCurrentPageEntity().pageName, + mListViewModel.sourceEntrance, + mListViewModel.searchKey, + SearchActivity.toTrackSearchType(mSearchType), + forumEntity.id, + HtmlUtils.stripHtml(forumEntity.name), + forumEntity.typeChinese ) } ) { enity, position -> - SensorsBridge.trackSearchResultClick( - GlobalActivityManager.getCurrentPageEntity().pageId, - GlobalActivityManager.getCurrentPageEntity().pageName, - mSearchKey, - SearchActivity.toTrackSearchType(mSearchType), - mListViewModel.sourceEntrance - ) if (requireActivity() is ChooseForumActivity) { (requireActivity() as ChooseForumActivity).chooseSuccess(enity) val bbsType = if (enity.type == "game_bbs") "游戏论坛" else "综合论坛" @@ -82,9 +106,13 @@ class ChooseForumContainerFragment : LazyListFragment + setSearchKeyAndType(key, type) + }) } override fun onChanged(ts: MutableList?) { diff --git a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerViewModel.kt b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerViewModel.kt index 60962dff93..6a945834ec 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/dialog/ChooseForumContainerViewModel.kt @@ -1,6 +1,5 @@ package com.gh.gamecenter.qa.dialog -import android.annotation.SuppressLint import android.app.Application import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -13,7 +12,6 @@ import com.gh.gamecenter.common.baselist.LoadType import com.gh.gamecenter.common.retrofit.BiResponse import com.gh.gamecenter.common.utils.SensorsBridge import com.gh.gamecenter.entity.ForumEntity -import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity.Companion.LOCATION_COMMUNITY import com.gh.gamecenter.login.user.UserManager import com.gh.gamecenter.retrofit.RetrofitManager import com.gh.gamecenter.retrofit.service.ApiService @@ -29,7 +27,7 @@ class ChooseForumContainerViewModel( val type: String, var searchKey: String, var searchType: String, - var sourceEntrance: String + var sourceEntrance: String // 神策sourceEntrance 相当火山云 location ) : ListViewModel(application) { @@ -47,7 +45,7 @@ class ChooseForumContainerViewModel( override fun provideDataObservable(page: Int): Observable> { mPage = page if (mPage == 1 && type == ChooseForumContainerFragment.ChooseForumType.SEARCH.value) { - NewFlatLogUtils.logSearchBbs(SearchType.fromString(searchType).toChinese(), searchKey) + NewFlatLogUtils.logSearchBbs(SearchType.fromString(searchType).toChinese(), searchKey, sourceEntrance) } return when (type) { ChooseForumContainerFragment.ChooseForumType.ATTENTION.value -> { @@ -74,13 +72,13 @@ class ChooseForumContainerViewModel( mResultLiveData.addSource>(mListLiveData) { mResultLiveData.postValue(it) if (mPage == 1) { - SensorsBridge.trackSearchResultReturn( + SensorsBridge.trackForumSearchResultReturn( GlobalActivityManager.getCurrentPageEntity().pageId, GlobalActivityManager.getCurrentPageEntity().pageName, + sourceEntrance, searchKey, SearchActivity.toTrackSearchType(searchType), - it.isNotEmpty(), - sourceEntrance + it.isNotEmpty() ) } } @@ -119,7 +117,13 @@ class ChooseForumContainerViewModel( class Factory(val type: String, val searchKey: String, val searchType: String, private val sourceEntrance: String) : ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { - return ChooseForumContainerViewModel(HaloApp.getInstance().application, type, searchKey, searchType, sourceEntrance) as T + return ChooseForumContainerViewModel( + HaloApp.getInstance().application, + type, + searchKey, + searchType, + sourceEntrance + ) as T } } } \ No newline at end of file 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 3890802e2e..95b5cd2408 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 @@ -3403,4 +3403,10 @@ public interface ApiService { */ @PATCH("app/{module}/diverter_visit_time") Single patchDiverterVisitTime(@Path("module") String module, @Body RequestBody body); + /** + * 游戏单搜索(复用 游戏单合集-内容列表 (适用于刷新轮换) 接口实体) + */ + @GET("game_lists/search") + Single> searchGameList(@Query("keyword") String keyword, @Query("page") int page); + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt index 614cc89a4e..6771e99f0b 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt @@ -11,8 +11,8 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.gh.common.databind.BindingAdapters import com.gh.common.util.DirectUtils +import com.gh.common.util.NewFlatLogUtils import com.gh.gamecenter.R -import com.gh.gamecenter.SearchType import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.HtmlUtils @@ -24,6 +24,7 @@ import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.exposure.ExposureType import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.gh.gamecenter.SearchType import com.gh.gamecenter.search.SearchGameResultAdapter.Companion.setItemCLick import com.gh.gamecenter.search.SearchGameResultAdapter.Companion.showContentTag import com.lzf.easyfloat.utils.DisplayUtils @@ -51,7 +52,7 @@ class SearchGameFirstItemViewHolder( game?.let { val link = bannerItem.link ?: return@let // 阿里云埋点 - com.gh.common.util.NewFlatLogUtils.logGameSearchFirstGameBannerClick( + NewFlatLogUtils.logGameSearchFirstGameBannerClick( it.id, HtmlUtils.stripHtml(it.name), position + 1, @@ -86,7 +87,7 @@ class SearchGameFirstItemViewHolder( game?.let { val link = cardItem.link ?: return@let // 阿里云埋点 - com.gh.common.util.NewFlatLogUtils.logGameSearchFirstGameCardClick( + NewFlatLogUtils.logGameSearchFirstGameCardClick( it.id, HtmlUtils.stripHtml(it.name), cardItem.cardName, diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt index eb3fd30689..e7aa2b1052 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt @@ -17,11 +17,9 @@ import com.gh.common.util.LogUtils import com.gh.common.util.NewLogUtils import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.R -import com.gh.gamecenter.SearchActivity import com.gh.gamecenter.SearchType import com.gh.gamecenter.adapter.viewholder.GameViewHolder import com.gh.gamecenter.adapter.viewholder.SearchHistoryViewHolder -import com.gh.gamecenter.common.base.GlobalActivityManager import com.gh.gamecenter.common.baselist.ListAdapter import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.ItemViewType @@ -418,13 +416,6 @@ class SearchGameIndexAdapter( gameEntity.adSpaceId, gameEntity.gameAdSourceId ) - SensorsBridge.trackSearchResultClick( - GlobalActivityManager.getCurrentPageEntity().pageId, - GlobalActivityManager.getCurrentPageEntity().pageName, - key, - SearchActivity.toTrackSearchType(type), - sourceEntrance - ) } @@ -527,14 +518,6 @@ class SearchGameIndexAdapter( gameEntity.adSpaceId, gameEntity.gameAdSourceId ) - - SensorsBridge.trackSearchResultClick( - GlobalActivityManager.getCurrentPageEntity().pageId, - GlobalActivityManager.getCurrentPageEntity().pageName, - key, - SearchActivity.toTrackSearchType(type), - sourceEntrance - ) } } } diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexItemViewHolder.kt index 2c2f3c9a09..9b23e8c078 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexItemViewHolder.kt @@ -7,8 +7,6 @@ import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.DirectUtils import com.gh.common.util.NewFlatLogUtils import com.gh.gamecenter.R -import com.gh.gamecenter.SearchActivity.Companion.toTrackSearchType -import com.gh.gamecenter.common.base.GlobalActivityManager import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.DrawableView @@ -89,14 +87,6 @@ class SearchSubjectItemViewHolder(var binding: SearchSubjectItemBinding) : Recyc subjectRv.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) subjectRv.adapter = SearchSubjectAdapter(context, entity.getFilterGame(), "($type-专题)") { dao?.add(key) - SensorsBridge.trackSearchButtonClick( - GlobalActivityManager.getCurrentPageEntity().pageId, - GlobalActivityManager.getCurrentPageEntity().pageName, - key, - toTrackSearchType(type), - sourceEntrance - ) - if (itemData.adConfig != null) { NewFlatLogUtils.logClickGameAd( itemData.adConfig.id, diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameListRepository.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameListRepository.kt new file mode 100644 index 0000000000..528738737d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameListRepository.kt @@ -0,0 +1,20 @@ +package com.gh.gamecenter.search + +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.gamecenter.retrofit.service.ApiService +import io.reactivex.Single + +class SearchGameListRepository private constructor(private val newApi: ApiService) { + + fun searchGameList( + key: String, + page: Int + ): Single> = newApi.searchGameList(key, page) + + companion object { + + fun newInstance() = + SearchGameListRepository(RetrofitManager.getInstance().newApi) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt index 38424c001e..2462f2d509 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt @@ -185,7 +185,13 @@ class SearchGameResultAdapter( } bottomDivider.visibility = View.VISIBLE } - holder.bindSubjectItem(mContext, mEntityList[position], SearchType.fromString(type).toChinese(), key, sourceEntrance = sourceEntrance) + holder.bindSubjectItem( + mContext, + mEntityList[position], + SearchType.fromString(type).toChinese(), + key, + sourceEntrance = sourceEntrance + ) } is SearchGameFirstItemViewHolder -> { @@ -688,6 +694,16 @@ class SearchGameResultAdapter( } if (gameEntity.isMiniGame()) { + SensorsBridge.trackMiniGameSearchResultClick( + GlobalActivityManager.getCurrentPageEntity().pageId, + GlobalActivityManager.getCurrentPageEntity().pageName, + sourceEntrance, + key, + SearchActivity.toTrackSearchType(type), + gameEntity.id, + gameEntity.name ?: "", + gameEntity.categoryChinese + ) MiniGameItemHelper.launchMiniGame(gameEntity.miniGameAppId, gameEntity.miniGameType) MiniGameItemHelper.trackMiniGameClick( gameEntity = gameEntity, @@ -695,6 +711,16 @@ class SearchGameResultAdapter( searchContent = key, ) } else { + SensorsBridge.trackGameSearchResultClick( + GlobalActivityManager.getCurrentPageEntity().pageId, + GlobalActivityManager.getCurrentPageEntity().pageName, + sourceEntrance, + key, + SearchActivity.toTrackSearchType(type), + gameEntity.id, + gameEntity.name ?: "", + gameEntity.categoryChinese + ) GameDetailActivity.startGameDetailActivity( context, gameEntity, StringUtils.buildString( @@ -730,14 +756,6 @@ class SearchGameResultAdapter( gameEntity.name ?: "" ) } - - SensorsBridge.trackSearchResultClick( - GlobalActivityManager.getCurrentPageEntity().pageId, - GlobalActivityManager.getCurrentPageEntity().pageName, - key, - SearchActivity.toTrackSearchType(type), - sourceEntrance - ) } diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt index 1f06f17227..f5f02dbdd8 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt @@ -11,6 +11,8 @@ import android.view.animation.Animation import android.view.animation.TranslateAnimation import android.widget.TextView import androidx.core.content.ContextCompat +import androidx.fragment.app.viewModels +import androidx.lifecycle.Observer import androidx.recyclerview.widget.RecyclerView import com.gh.common.exposure.ExposureListener import com.gh.common.util.* @@ -18,8 +20,6 @@ import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R -import com.gh.gamecenter.SearchActivity -import com.gh.gamecenter.SearchType import com.gh.gamecenter.common.baselist.ListFragment import com.gh.gamecenter.common.baselist.LoadType import com.gh.gamecenter.common.constant.EntranceConsts @@ -33,6 +33,9 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.help.HelpAndFeedbackBridge +import com.gh.gamecenter.SearchActivity +import com.gh.gamecenter.SearchType +import com.gh.gamecenter.search.viewmodel.SearchTabViewModel import com.halo.assistant.HaloApp import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity @@ -42,6 +45,10 @@ import org.greenrobot.eventbus.ThreadMode open class SearchGameResultFragment : ListFragment() { + private val parentTabViewModel by viewModels( + ownerProducer = { parentFragment ?: this } + ) + private var mAdapter: SearchGameResultAdapter? = null private var mExposureListener: ExposureListener? = null private lateinit var mShowAction: Animation @@ -53,6 +60,7 @@ open class SearchGameResultFragment : ListFragment + setParams(key, type) + } } private fun handleCloseMenuVisibility(recyclerView: RecyclerView) { @@ -251,6 +267,7 @@ open class SearchGameResultFragment : ListFragment mGameEntityList = ArrayList(list) @@ -295,14 +300,27 @@ class SearchGameResultViewModel( private fun postResultList(resultList: ArrayList, list: List) { mResultLiveData.postValue(resultList) if (mPage == 1) { - SensorsBridge.trackSearchResultReturn( - GlobalActivityManager.getCurrentPageEntity().pageId, - GlobalActivityManager.getCurrentPageEntity().pageName, - mSearchKey ?: "", - SearchActivity.toTrackSearchType(mSearchType), - list.isNotEmpty(), - sourceEntrance - ) + if (repository is MiniGameSearchResultRepository) { + SensorsBridge.trackMiniGameSearchResultReturn( + GlobalActivityManager.getCurrentPageEntity().pageId, + GlobalActivityManager.getCurrentPageEntity().pageName, + sourceEntrance, + mSearchKey ?: "", + SearchActivity.toTrackSearchType(mSearchType), + list.isNotEmpty() + ) + } else { + SensorsBridge.trackGameSearchResultReturn( + GlobalActivityManager.getCurrentPageEntity().pageId, + GlobalActivityManager.getCurrentPageEntity().pageName, + sourceEntrance, + mSearchKey ?: "", + SearchActivity.toTrackSearchType(mSearchType), + list.isNotEmpty() + + ) + } + } } @@ -355,7 +373,14 @@ class SearchGameResultViewModel( private val mSourceEntrance: String ) : ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { - return SearchGameResultViewModel(mApplication, mSearchKey, mIsManuallySearch, repository, mSearchType, mSourceEntrance) as T + return SearchGameResultViewModel( + mApplication, + mSearchKey, + mIsManuallySearch, + repository, + mSearchType, + mSourceEntrance + ) as T } } diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchTabActivity.kt b/app/src/main/java/com/gh/gamecenter/search/SearchTabActivity.kt new file mode 100644 index 0000000000..8e2e31b3d8 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/SearchTabActivity.kt @@ -0,0 +1,39 @@ +package com.gh.gamecenter.search + +import android.os.Bundle +import androidx.activity.viewModels +import com.gh.gamecenter.DisplayType +import com.gh.gamecenter.R +import com.gh.gamecenter.SearchActivity +import com.gh.gamecenter.search.fragment.SearchTabFragment +import com.gh.gamecenter.search.viewmodel.SearchTabActivityViewModel + +class SearchTabActivity : SearchActivity() { + + private val viewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + viewModel.loadTabs() + } + + override fun updateDisplayType(type: DisplayType) { + viewModel.updateSearchKeyAndType(mSearchKey, mSearchType.value) + val lastDisplayType = mDisplayType + mDisplayType = type + // 如果上一次展示的是SearchTabFragment,并且当前需要展示的也是SearchTabFragment,则无需重复显示 + if (lastDisplayType != DisplayType.DEFAULT && type != DisplayType.DEFAULT) { + return + } + + if (type == DisplayType.DEFAULT) { + super.updateDisplayType(type) + } else { + val transaction = supportFragmentManager.beginTransaction() + val fragment = supportFragmentManager.findFragmentByTag(SearchTabFragment::class.java.name) + ?: SearchTabFragment.newInstance(mSourceEntrance) + transaction.replace(R.id.search_result, fragment) + transaction.commitAllowingStateLoss() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchTabRepository.kt b/app/src/main/java/com/gh/gamecenter/search/SearchTabRepository.kt new file mode 100644 index 0000000000..56f164cb58 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/SearchTabRepository.kt @@ -0,0 +1,15 @@ +package com.gh.gamecenter.search + +import com.gh.common.constant.Config +import com.gh.gamecenter.feature.entity.SettingsEntity + +class SearchTabRepository private constructor() { + + fun getSearchTabs(): List = + Config.getNewApiSettingsEntity()?.search?.navigations ?: emptyList() + + companion object { + + fun newInstance() = SearchTabRepository() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/adapter/SearchGameListAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/adapter/SearchGameListAdapter.kt new file mode 100644 index 0000000000..5344271a75 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/adapter/SearchGameListAdapter.kt @@ -0,0 +1,145 @@ +package com.gh.gamecenter.search.adapter + +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.gh.gamecenter.common.baselist.LoadStatus +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.search.viewholder.SearchGameListFooterViewHolder +import com.gh.gamecenter.search.viewholder.SearchGameListViewHolder +import com.gh.gamecenter.search.viewmodel.SearchGameListViewModel + +class SearchGameListAdapter( + private val viewModel: SearchGameListViewModel +) : ListAdapter( + createDiffUtil() +) { + + private var _recyclerView: RecyclerView? = null + + private var _loadStatus = LoadStatus.INIT + + fun updateStatus(loadStatus: LoadStatus) { + _loadStatus = loadStatus + if (itemCount > 0) { + _recyclerView?.post { + notifyItemChanged(itemCount - 1) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return if (viewType == ITEM_TYPE_CONTENT) { + SearchGameListViewHolder(parent.toBinding(), object : SearchGameListViewHolder.SearchGameListListener { + + override fun navigateToGameDetailPage( + gameId: String, + collection: CustomPageData.LinkColumnCollection.CustomSubjectEntity + ) { + viewModel.navigateToGameDetailPage(gameId, collection) + } + + override fun navigateToHomePage( + userId: String, + collection: CustomPageData.LinkColumnCollection.CustomSubjectEntity + ) { + viewModel.navigateToHomePage(userId, collection) + } + + override fun navigateGameCollectionDetailPage(collection: CustomPageData.LinkColumnCollection.CustomSubjectEntity) { + viewModel.navigateGameCollectionDetailPage(collection) + } + + }) + } else { + SearchGameListFooterViewHolder(parent.toBinding(), object : SearchGameListFooterViewHolder.FooterListener { + override fun loadMore() { + viewModel.onLoadMore() + } + + override fun retry() { + viewModel.retry() + } + + override fun toTop() { + _recyclerView?.scrollToPosition(0) + } + + }) + } + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + when (holder) { + is SearchGameListViewHolder -> { + holder.bind(getItem(position)) + } + + is SearchGameListFooterViewHolder -> { + holder.bind(_loadStatus) + } + } + } + + + override fun getItemCount(): Int { + val dataCount = super.getItemCount() + + return if (dataCount > 0) { + dataCount + 1 + } else { + 0 + } + } + + override fun getItemViewType(position: Int): Int { + return if (position < itemCount - 1) { + ITEM_TYPE_CONTENT + } else { + ITEM_TYPE_FOOTER + } + } + + override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + _recyclerView = recyclerView + } + + override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { + _recyclerView = null + } + + companion object { + + private const val ITEM_TYPE_CONTENT = 0 + private const val ITEM_TYPE_FOOTER = 1 + + fun createDiffUtil() = + object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: CustomPageData.LinkColumnCollection.CustomSubjectEntity, + newItem: CustomPageData.LinkColumnCollection.CustomSubjectEntity + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: CustomPageData.LinkColumnCollection.CustomSubjectEntity, + newItem: CustomPageData.LinkColumnCollection.CustomSubjectEntity + ): Boolean { + return oldItem.id == newItem.id + && oldItem.user == newItem.user + && oldItem.count == newItem.count + && oldItem.games.size == newItem.games.size + && oldItem.adIconActive == newItem.adIconActive + && oldItem.intro == newItem.intro + && oldItem.title == newItem.title + && oldItem.image == newItem.image + && oldItem.tags.size == newItem.tags.size + } + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/adapter/SearchTabAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/adapter/SearchTabAdapter.kt new file mode 100644 index 0000000000..7198c3c6ea --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/adapter/SearchTabAdapter.kt @@ -0,0 +1,72 @@ +package com.gh.gamecenter.search.adapter + +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.gh.gamecenter.SearchActivity +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.feature.entity.SettingsEntity +import com.gh.gamecenter.feedback.view.qa.HelpContentFragment +import com.gh.gamecenter.forum.search.ForumContentSearchListFragment +import com.gh.gamecenter.forum.search.UserSearchListFragment +import com.gh.gamecenter.minigame.MiniGameSearchResultFragment +import com.gh.gamecenter.qa.dialog.ChooseForumContainerFragment +import com.gh.gamecenter.search.SearchGameResultFragment +import com.gh.gamecenter.search.fragment.SearchGameListFragment + +class SearchTabAdapter( + private val location: String, + private val searchType: String?, + private val tabs: List, + private val fragment: Fragment +) : FragmentStateAdapter(fragment) { + + override fun getItemCount() = tabs.size + + override fun createFragment(position: Int): Fragment { + val navigation = tabs[position] + val bundle = Bundle().apply { + putBoolean(EntranceConsts.KEY_IS_AUTO_LOAD, false) + putString(EntranceConsts.KEY_LOCATION, location) + } + val fragment = when (navigation.type) { + SEARCH_TYPE_GAME -> SearchGameResultFragment() + + SEARCH_TYPE_BBS -> ChooseForumContainerFragment.getInstance(ChooseForumContainerFragment.ChooseForumType.SEARCH) + + SEARCH_TYPE_BBS_CONTENT -> ForumContentSearchListFragment() + + SEARCH_TYPE_USER -> UserSearchListFragment() + + SEARCH_TYPE_MINI_GAME -> MiniGameSearchResultFragment() + + SEARCH_TYPE_QA -> HelpContentFragment().apply { + arguments = Bundle().apply { + putString(EntranceConsts.KEY_SEARCH_TYPE, SearchActivity.toTrackSearchType(searchType ?: "")) + } + } + + SEARCH_TYPE_GAME_LIST -> SearchGameListFragment() + + else -> SearchGameResultFragment() + }.also { + if (it.arguments != null) { + it.arguments?.putAll(bundle) + } else { + it.arguments = bundle + } + + } + return fragment + } + + companion object { + private const val SEARCH_TYPE_GAME = "game" + private const val SEARCH_TYPE_BBS = "bbs" + private const val SEARCH_TYPE_BBS_CONTENT = "bbs_content" + private const val SEARCH_TYPE_USER = "user" + private const val SEARCH_TYPE_QA = "qa" + private const val SEARCH_TYPE_MINI_GAME = "mini_game" + private const val SEARCH_TYPE_GAME_LIST = "game_list" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/fragment/SearchGameListFragment.kt b/app/src/main/java/com/gh/gamecenter/search/fragment/SearchGameListFragment.kt new file mode 100644 index 0000000000..a07c5fcb1c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/fragment/SearchGameListFragment.kt @@ -0,0 +1,118 @@ +package com.gh.gamecenter.search.fragment + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import androidx.lifecycle.Observer +import com.gh.common.util.DirectUtils +import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.activity.BaseActivity +import com.gh.gamecenter.common.base.fragment.LazyFragment +import com.gh.gamecenter.common.baselist.LoadStatus +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.view.FixLinearLayoutManager +import com.gh.gamecenter.databinding.FragmentSearchGameListBinding +import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity +import com.gh.gamecenter.livedata.EventObserver +import com.gh.gamecenter.search.adapter.SearchGameListAdapter +import com.gh.gamecenter.search.viewmodel.SearchGameListViewModel +import com.gh.gamecenter.search.viewmodel.SearchTabViewModel + +class SearchGameListFragment : LazyFragment() { + + private val viewModel by viewModels() + + private val parentViewModel by viewModels( + ownerProducer = { parentFragment ?: this } + ) + + private var searchKey = "" + private var location: String = "" + + private lateinit var binding: FragmentSearchGameListBinding + + private val adapter by lazy(LazyThreadSafetyMode.NONE) { + SearchGameListAdapter(viewModel) + } + + override fun onCreate(savedInstanceState: Bundle?) { + searchKey = arguments?.getString(EntranceConsts.KEY_SEARCHKEY, "") ?: "" + location = arguments?.getString(EntranceConsts.KEY_LOCATION, "") ?: "" + super.onCreate(savedInstanceState) + } + + + override fun getRealLayoutId(): Int = R.layout.fragment_search_game_list + + override fun onRealLayoutInflated(inflatedView: View) { + binding = FragmentSearchGameListBinding.bind(inflatedView) + } + + @SuppressLint("NotifyDataSetChanged") + override fun initRealView() { + super.initRealView() + parentViewModel.searchKeyAndType.observe(viewLifecycleOwner, Observer { (key, type) -> + // 清空上一次的搜索结果 + adapter.submitList(null) + // 上面调用了 adapter.submitList(null) ,这里为什么还要调用 notifyDataSetChanged 呢? + // 因为adapter.submitList(null)最终调用的adapter.notifyItemRangeRemoved()方法更新RecyclerView + // 此方法不会立马清空RecyclerView 的内容,而是在postOnAnimation执行相关的移除工作,这里为了让RecyclerView的内容立马清空,需要再次调用adapter.notifyDataSetChanged() + adapter.notifyDataSetChanged() + viewModel.loadFirstData(key, type, location) + }) + binding.rvGameList.layoutManager = FixLinearLayoutManager(context) + binding.rvGameList.adapter = adapter + + binding.reuseNoData.reuseResetLoadTv.setOnClickListener { + viewModel.retry() + } + binding.reuseNoConnection.connectionReloadTv.setOnClickListener { + viewModel.retry() + } + with(viewModel) { + loadStatus.observe(viewLifecycleOwner) { + binding.rvGameList.goneIf(it == LoadStatus.INIT_LOADING || it == LoadStatus.INIT_FAILED || it == LoadStatus.INIT_EMPTY) + binding.reuseNoConnection.root.goneIf(it != LoadStatus.INIT_FAILED) + binding.reuseLoading.root.goneIf(it != LoadStatus.INIT_LOADING) + binding.reuseNoData.root.goneIf(it != LoadStatus.INIT_EMPTY) + adapter.updateStatus(it) + } + + dataList.observe(viewLifecycleOwner, Observer { + adapter.submitList(it) + }) + + gameDetailDestination.observe(viewLifecycleOwner, EventObserver { + GameDetailActivity.startGameDetailActivity( + requireContext(), + it, + BaseActivity.mergeEntranceAndPath(location, "游戏单搜索"), + null + ) + }) + + homeDestination.observe(viewLifecycleOwner, EventObserver { + DirectUtils.directToHomeActivity( + requireContext(), + it, + 0, + location, + "游戏单搜索" + ) + }) + + gameCollectionDetailDestination.observe(viewLifecycleOwner, EventObserver { + startActivity( + GameCollectionDetailActivity.getIntent( + requireContext(), + it, + isFromSquare = false, + ) + ) + }) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/fragment/SearchTabFragment.kt b/app/src/main/java/com/gh/gamecenter/search/fragment/SearchTabFragment.kt new file mode 100644 index 0000000000..73763e8a3a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/fragment/SearchTabFragment.kt @@ -0,0 +1,138 @@ +package com.gh.gamecenter.search.fragment + +import android.graphics.Color +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.BaseFragment +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.databinding.FragmentSearchTabBinding +import com.gh.gamecenter.feature.entity.SettingsEntity +import com.gh.gamecenter.search.SearchGameResultFragment +import com.gh.gamecenter.search.adapter.SearchTabAdapter +import com.gh.gamecenter.search.viewmodel.SearchTabActivityViewModel +import com.gh.gamecenter.search.viewmodel.SearchTabViewModel +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayout.OnTabSelectedListener +import com.google.android.material.tabs.TabLayoutMediator + +class SearchTabFragment : BaseFragment() { + + private lateinit var binding: FragmentSearchTabBinding + + private val activityViewModel by activityViewModels() + + private val viewModel by viewModels() + + private lateinit var adapter: SearchTabAdapter + + private var location = "" + + private var _key: String? = null + private var _searchType: String? = null + + override fun getLayoutId(): Int = 0 + + override fun getInflatedLayout() = + FragmentSearchTabBinding.inflate(LayoutInflater.from(context)) + .also { + binding = it + }.root + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + location = arguments?.getString(EntranceConsts.KEY_LOCATION) ?: "" + with(activityViewModel) { + searchKeyAndType.observe(this@SearchTabFragment) { (key, type) -> + if (key != null) { + _key = key + _searchType = type + viewModel.updateSearchKeyAndType(key, type) + } + } + + + } + } + + override fun initView(view: View?) { + super.initView(view) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(activityViewModel) { + tabs.observe(viewLifecycleOwner) { + if (it.size > 1) { + initTabs(it) + } else { + initSingleFragment() + } + } + + } + } + + private fun initTabs(tabs: List) { + binding.gTabs.goneIf(false) + binding.flContainer.goneIf(true) + + binding.tabLayout.addOnTabSelectedListener(object : OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + viewModel.trackGameSearchPageTabClick(location, tab.text?.toString(), tab.position) + } + + override fun onTabUnselected(tab: TabLayout.Tab?) { + + } + + override fun onTabReselected(tab: TabLayout.Tab?) { + + } + + }) + + if (tabs.size > 5) { + binding.tabLayout.tabMode = TabLayout.MODE_SCROLLABLE + } else { + binding.tabLayout.tabMode = TabLayout.MODE_FIXED + } + adapter = SearchTabAdapter(location, _searchType, tabs, this) + binding.viewPager.adapter = adapter + TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position -> + tab.text = tabs[position].title + tab.customView?.setBackgroundColor(Color.RED) + }.attach() + } + + private fun initSingleFragment() { + binding.gTabs.goneIf(true) + binding.flContainer.goneIf(false) + val transaction = childFragmentManager.beginTransaction() + val fragment = childFragmentManager.findFragmentByTag(SearchGameResultFragment::class.java.name) + ?: SearchGameResultFragment().apply { + arguments = Bundle().apply { + putBoolean(EntranceConsts.KEY_IS_AUTO_LOAD, false) + } + } + transaction.replace(R.id.fl_container, fragment, SearchGameResultFragment::class.java.name) + transaction.commitAllowingStateLoss() + + } + + companion object { + + fun newInstance(location: String) = + SearchTabFragment().apply { + arguments = Bundle().apply { + putString(EntranceConsts.KEY_LOCATION, location) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/viewholder/SearchGameListFooterViewHolder.kt b/app/src/main/java/com/gh/gamecenter/search/viewholder/SearchGameListFooterViewHolder.kt new file mode 100644 index 0000000000..a593e69016 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/viewholder/SearchGameListFooterViewHolder.kt @@ -0,0 +1,60 @@ +package com.gh.gamecenter.search.viewholder + +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.gh.gamecenter.common.R +import com.gh.gamecenter.common.baselist.LoadStatus +import com.gh.gamecenter.common.databinding.RefreshFooterviewBinding +import com.gh.gamecenter.common.utils.goneIf + +class SearchGameListFooterViewHolder( + val binding: RefreshFooterviewBinding, + private val listener: FooterListener +) : ViewHolder(binding.root) { + + private var _loadStatus = LoadStatus.INIT + + fun bind(loadStatus: LoadStatus) { + if (_loadStatus == loadStatus) return + _loadStatus = loadStatus + when (loadStatus) { + LoadStatus.LIST_LOADING -> { + binding.footerviewLoading.goneIf(false) + binding.footerviewHint.goneIf(false) + binding.footerviewHint.setText(R.string.loading) + } + + LoadStatus.LIST_FAILED -> { + binding.footerviewLoading.goneIf(true) + binding.footerviewHint.goneIf(false) + binding.footerviewHint.setText(R.string.loading_failed_retry) + } + + LoadStatus.LIST_OVER -> { + binding.footerviewLoading.goneIf(true) + binding.footerviewHint.goneIf(false) + binding.footerviewHint.setText(R.string.load_over_hint) + } + + LoadStatus.LIST_LOADED, LoadStatus.INIT_LOADED -> listener.loadMore() + + else -> Unit + } + itemView.setOnClickListener { + when (loadStatus) { + LoadStatus.LIST_FAILED -> listener.retry() + LoadStatus.LIST_OVER -> listener.toTop() + + else -> Unit + } + } + } + + interface FooterListener { + + fun loadMore() + + fun retry() + + fun toTop() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/viewholder/SearchGameListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/search/viewholder/SearchGameListViewHolder.kt new file mode 100644 index 0000000000..0ecb918814 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/viewholder/SearchGameListViewHolder.kt @@ -0,0 +1,119 @@ +package com.gh.gamecenter.search.viewholder + +import android.content.Context +import android.graphics.Color +import android.view.View +import android.view.ViewGroup +import android.view.ViewGroup.MarginLayoutParams +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.view.updateLayoutParams +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.gh.common.util.CheckLoginUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.databinding.GameCollectionSquareItemBinding +import com.gh.gamecenter.home.custom.model.CustomPageData + +class SearchGameListViewHolder( + val binding: GameCollectionSquareItemBinding, + private val listener: SearchGameListListener +) : ViewHolder(binding.root) { + + private val context: Context + get() = binding.root.context + + private val posterWidth by lazy(LazyThreadSafetyMode.NONE) { + context.resources.displayMetrics.widthPixels - 32F.dip2px() + } + + fun bind(item: CustomPageData.LinkColumnCollection.CustomSubjectEntity) { + with(binding) { + + root.updateLayoutParams { + if (this is MarginLayoutParams) { + topMargin = 16F.dip2px() + } + } + poster.setTag(ImageUtils.TAG_TARGET_WIDTH, posterWidth) + ImageUtils.display(poster, item.image) + ImageUtils.display(userIv, item.user.icon) + titleTv.text = item.title.fromHtml() + item.games.take(3).forEachIndexed { index, game -> + when (index) { + 0 -> iconIvOne.displayGameIcon(game) + 1 -> iconIvTwo.displayGameIcon(game) + 2 -> iconIvThree.displayGameIcon(game) + } + } + item.games.size.let { + iconIvOne.goneIf(it == 0) + iconIvTwo.goneIf(it < 2) + iconIvThree.goneIf(it < 3) + } + + countTv.goneIf(item.count.game < 4 || item.games.isEmpty()) + countTv.text = "+ ${item.count.game - 3}" + hotTv.text = if (item.count.hot > 10000) "10000+" else item.count.hot.toString() + + userTv.text = item.user.name + playedGameProgress.max = item.count.game + playedGameProgress.progress = item.count.gamePlayed ?: 0 + playedGameTv.text = + "玩过 ${item.count.gamePlayed} / ${item.count.game} 款" + stampIv.goneIf(item.stamp.isEmpty()) + stampIv.setBackgroundResource(if (item.stamp == "official") R.drawable.ic_official else R.drawable.ic_chosen) + + tagContainer.removeAllViews() + tagContainer.visibleIf(!CheckLoginUtils.isLogin() || item.count.game == 0) { + item.tags.forEachIndexed { index, tag -> + // 添加分隔线 + if (index != 0) tagContainer.addView(View(context).apply { + layoutParams = ViewGroup.LayoutParams(1F.dip2px(), 14F.dip2px()) + setBackgroundColor(Color.parseColor("#33FFFFFF")) + }) + // 添加标签 + tagContainer.addView(TextView(context).apply { + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + setPadding(if (index == 0) 0 else 6F.dip2px(), 0, 6F.dip2px(), 0) + text = tag.name + setTextColor(R.color.white.toColor()) + textSize = 10F + }) + } + } + playedGamesContainer.visibleIf(CheckLoginUtils.isLogin() && item.count.game != 0) + adLabelTv.goneIf(!item.adIconActive) + arrayOf(iconIvOne, iconIvTwo, iconIvThree).forEachIndexed { index, ivIcon -> + ivIcon.setOnClickListener { + val game = item.games.getOrNull(index) + game?.id?.let { gameId -> + listener.navigateToGameDetailPage(gameId, item) + } + } + } + + userContainer.setOnClickListener { + listener.navigateToHomePage(item.user.id, item) + } + root.setOnClickListener { + listener.navigateGameCollectionDetailPage(item) + } + } + } + + interface SearchGameListListener { + + fun navigateToGameDetailPage( + gameId: String, + collection: CustomPageData.LinkColumnCollection.CustomSubjectEntity + ) + + fun navigateToHomePage(userId: String, collection: CustomPageData.LinkColumnCollection.CustomSubjectEntity) + + fun navigateGameCollectionDetailPage(collection: CustomPageData.LinkColumnCollection.CustomSubjectEntity) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchGameListViewModel.kt b/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchGameListViewModel.kt new file mode 100644 index 0000000000..b6f6d155ef --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchGameListViewModel.kt @@ -0,0 +1,141 @@ +package com.gh.gamecenter.search.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.gh.gamecenter.SearchActivity +import com.gh.gamecenter.common.base.GlobalActivityManager +import com.gh.gamecenter.common.baselist.LoadStatus +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.utils.singleToMain +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.livedata.Event +import com.gh.gamecenter.search.SearchGameListRepository +import io.reactivex.disposables.CompositeDisposable + +class SearchGameListViewModel : ViewModel() { + + private val repository = SearchGameListRepository.newInstance() + + private val compositeDisposable = CompositeDisposable() + + private var page = 1 + + private var key = "" + private var type = "" + private var location = "" + + private val _loadStatus = MutableLiveData() + val loadStatus: LiveData = _loadStatus + + private val _dataList = MutableLiveData?>() + val dataList: LiveData?> = _dataList + + fun loadFirstData(key: String, type: String, location: String) { + this.key = key + this.type = type + this.location = location + page = 1 + _loadStatus.value = LoadStatus.INIT_LOADING + searchGameList() + } + + fun onLoadMore() { + page++ + _loadStatus.value = LoadStatus.LIST_LOADING + searchGameList() + } + + fun retry() { + _loadStatus.value = if (page == 1) { + LoadStatus.INIT_LOADING + } else { + LoadStatus.LIST_LOADED + } + searchGameList() + } + + private fun searchGameList() { + repository.searchGameList(key, page) + .compose(singleToMain()) + .subscribe(object : BiResponse>() { + override fun onSuccess(data: List) { + if (page == 1) { + _dataList.value = data + _loadStatus.value = if (data.isEmpty()) { + LoadStatus.INIT_EMPTY + } else { + LoadStatus.INIT_LOADED + } + SensorsBridge.trackGameCollectSearchResultReturn( + GlobalActivityManager.getCurrentPageEntity().pageId, + GlobalActivityManager.getCurrentPageEntity().pageName, + location, + key, + SearchActivity.toTrackSearchType(type), + data.isNotEmpty() + ) + } else { + addData(data) + _loadStatus.value = if (data.isEmpty()) { + LoadStatus.LIST_OVER + } else { + LoadStatus.LIST_LOADED + } + } + + } + + override fun onFailure(exception: Exception) { + _loadStatus.value = if (page == 1) { + LoadStatus.INIT_FAILED + } else { + LoadStatus.LIST_FAILED + } + } + }).let(compositeDisposable::add) + } + + private fun addData(newData: List) { + val oldData = dataList.value + _dataList.value = if (oldData == null) { + newData + } else { + oldData + newData + } + } + + private val _gameDetailDestination = MutableLiveData>() + val gameDetailDestination: LiveData> = _gameDetailDestination + fun navigateToGameDetailPage(gameId: String, collection: CustomPageData.LinkColumnCollection.CustomSubjectEntity) { + trackGameCollectSearchResultClick(collection) + _gameDetailDestination.value = Event(gameId) + } + + private val _homeDestination = MutableLiveData>() + val homeDestination: LiveData> = _homeDestination + fun navigateToHomePage(userId: String, collection: CustomPageData.LinkColumnCollection.CustomSubjectEntity) { + trackGameCollectSearchResultClick(collection) + _homeDestination.value = Event(userId) + } + + private val _gameCollectionDetailDestination = MutableLiveData>() + val gameCollectionDetailDestination: LiveData> = _gameCollectionDetailDestination + fun navigateGameCollectionDetailPage(collection: CustomPageData.LinkColumnCollection.CustomSubjectEntity) { + trackGameCollectSearchResultClick(collection) + _gameCollectionDetailDestination.value = Event(collection.id) + } + + private fun trackGameCollectSearchResultClick(collection: CustomPageData.LinkColumnCollection.CustomSubjectEntity) { + SensorsBridge.trackGameCollectSearchResultClick( + GlobalActivityManager.getCurrentPageEntity().pageId, + GlobalActivityManager.getCurrentPageEntity().pageName, + key, + SearchActivity.toTrackSearchType(type), + location, + collection.id, + collection.title + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabActivityViewModel.kt b/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabActivityViewModel.kt new file mode 100644 index 0000000000..83ab4853ef --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabActivityViewModel.kt @@ -0,0 +1,26 @@ +package com.gh.gamecenter.search.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.gh.gamecenter.feature.entity.SettingsEntity +import com.gh.gamecenter.search.SearchTabRepository + +class SearchTabActivityViewModel : ViewModel() { + + private val repository = SearchTabRepository.newInstance() + + private val _searchKeyAndType = MutableLiveData>() + val searchKeyAndType: LiveData> = _searchKeyAndType + + fun updateSearchKeyAndType(key: String?, type: String) { + _searchKeyAndType.value = key to type + } + + private val _tabs = MutableLiveData>() + val tabs: LiveData> = _tabs + + fun loadTabs() { + _tabs.value = repository.getSearchTabs() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabViewModel.kt b/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabViewModel.kt new file mode 100644 index 0000000000..5d4965aa12 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabViewModel.kt @@ -0,0 +1,29 @@ +package com.gh.gamecenter.search.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.gh.gamecenter.common.base.GlobalActivityManager +import com.gh.gamecenter.common.utils.SensorsBridge +import io.reactivex.disposables.CompositeDisposable + +class SearchTabViewModel : ViewModel() { + + private val _searchKeyAndType = MutableLiveData>() + val searchKeyAndType: LiveData> = _searchKeyAndType + fun updateSearchKeyAndType(key: String, type: String) { + _searchKeyAndType.value = key to type + } + + fun trackGameSearchPageTabClick(location: String, text: String?, position: Int) { + SensorsBridge.trackGameSearchPageTabClick( + GlobalActivityManager.getCurrentPageEntity().pageId, + GlobalActivityManager.getCurrentPageEntity().pageName, + location, + searchKeyAndType.value?.first ?: "", + searchKeyAndType.value?.second ?: "", + text ?: "", + position + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/vote/VoteViewModel.kt b/app/src/main/java/com/gh/gamecenter/vote/VoteViewModel.kt index 760c303f2d..8104817d9e 100644 --- a/app/src/main/java/com/gh/gamecenter/vote/VoteViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/vote/VoteViewModel.kt @@ -1,11 +1,9 @@ package com.gh.gamecenter.vote -import android.annotation.SuppressLint import android.app.Application import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider -import com.gh.gamecenter.SearchType import com.gh.gamecenter.common.baselist.ListViewModel import com.gh.gamecenter.common.baselist.LoadType import com.gh.gamecenter.common.retrofit.ApiResponse diff --git a/app/src/main/res/drawable/search_tab_indicator.xml b/app/src/main/res/drawable/search_tab_indicator.xml new file mode 100644 index 0000000000..23091ba4dc --- /dev/null +++ b/app/src/main/res/drawable/search_tab_indicator.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search_game_list.xml b/app/src/main/res/layout/fragment_search_game_list.xml new file mode 100644 index 0000000000..1988bcedd0 --- /dev/null +++ b/app/src/main/res/layout/fragment_search_game_list.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search_tab.xml b/app/src/main/res/layout/fragment_search_tab.xml new file mode 100644 index 0000000000..550a8ed612 --- /dev/null +++ b/app/src/main/res/layout/fragment_search_tab.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/qa/HelpContentAdapter.kt b/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/qa/HelpContentAdapter.kt index 6be93bf74a..c4c6309c66 100644 --- a/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/qa/HelpContentAdapter.kt +++ b/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/qa/HelpContentAdapter.kt @@ -5,9 +5,11 @@ import android.text.TextUtils import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.common.base.GlobalActivityManager import com.gh.gamecenter.common.baselist.ListAdapter import com.gh.gamecenter.common.baselist.NormalListViewModel import com.gh.gamecenter.common.constant.ItemViewType +import com.gh.gamecenter.common.utils.SensorsBridge import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.common.utils.toColor @@ -23,7 +25,9 @@ class HelpContentAdapter( private val mViewModel: NormalListViewModel, private val mQaId: String?, private val mQaCollectionId: String?, - private val mNavigationTitle: String + private val mNavigationTitle: String, + private val searchType: String, + private val location: String ) : ListAdapter(context) { var searchKey = "" @@ -107,6 +111,15 @@ class HelpContentAdapter( mContext.javaClass.simpleName ) } + SensorsBridge.trackQASearchResultClick( + GlobalActivityManager.getCurrentPageEntity().pageId, + GlobalActivityManager.getCurrentPageEntity().pageName, + location, + searchKey, + searchType, + entity.id, + entity.title + ) } } diff --git a/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/qa/HelpContentFragment.kt b/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/qa/HelpContentFragment.kt index f02deb083b..3608931612 100644 --- a/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/qa/HelpContentFragment.kt +++ b/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/qa/HelpContentFragment.kt @@ -7,12 +7,17 @@ import android.widget.TextView import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.RecyclerView +import com.alibaba.android.arouter.launcher.ARouter +import com.gh.gamecenter.common.base.GlobalActivityManager import com.gh.gamecenter.common.baselist.ListFragment import com.gh.gamecenter.common.baselist.LoadStatus import com.gh.gamecenter.common.baselist.LoadType import com.gh.gamecenter.common.baselist.NormalListViewModel import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.constant.RouteConsts +import com.gh.gamecenter.common.utils.SensorsBridge import com.gh.gamecenter.common.utils.toDrawable +import com.gh.gamecenter.core.provider.ISearchTabUtilsProvider import com.gh.gamecenter.core.utils.UrlFilterUtils import com.gh.gamecenter.feedback.HaloApp import com.gh.gamecenter.feedback.R @@ -40,12 +45,27 @@ class HelpContentFragment : ListFragment + updateSearchKey(key) + } } override fun provideDataObservable(page: Int): Observable> { @@ -125,6 +152,17 @@ class HelpContentFragment : ListFragment>? +} \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/SettingsEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/SettingsEntity.kt index 7be6421d30..41ea57819a 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/SettingsEntity.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/SettingsEntity.kt @@ -75,8 +75,14 @@ class SettingsEntity( @SerializedName("hot_list") var rankList: List? = listOf(), @SerializedName("wechat_game_search_list") - var wechatGameSearchList: List? = listOf()// 微信小游戏-搜索榜单 + var wechatGameSearchList: List? = listOf(),// 微信小游戏-搜索榜单 + @SerializedName("navigations") + private val _navigations: List? = null ) { + + val navigations: List + get() = _navigations ?: emptyList() + class RankList( var title: String = "", var content: ArrayList = arrayListOf(), @@ -103,6 +109,19 @@ class SettingsEntity( var exposureEvent: ExposureEvent? = null } } + + data class Navigation( + @SerializedName("type") + private val _type: String? = null, + @SerializedName("title") + private val _title: String? = null + ) { + val type: String + get() = _type ?: "" + + val title: String + get() = _title ?: "" + } } class Suggestion( @@ -239,10 +258,11 @@ class SettingsEntity( var text: String = "", var display: Display? = Display() ) { - val typeChinese: String get() = when(type) { - "revolving" -> "走马灯" - else -> "图片" - } + val typeChinese: String + get() = when (type) { + "revolving" -> "走马灯" + else -> "图片" + } } class PermissionPopupAppliedVersions(