diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8907046ee7..8adc9161ce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -72,6 +72,7 @@ android_build: only: - dev - dev-5.33.0 + - feat/GHZS-3956 # 代码检查 sonarqube_analysis: @@ -103,6 +104,7 @@ sonarqube_analysis: only: - dev - dev-5.33.0 + - feat/GHZS-3956 ## 发送简易检测结果报告 send_sonar_report: @@ -121,6 +123,7 @@ send_sonar_report: only: - dev - dev-5.33.0 + - feat/GHZS-3956 oss-upload&send-email: tags: @@ -152,4 +155,5 @@ oss-upload&send-email: - /usr/local/bin/python /ci-android-mail-jira-comment.py only: - dev - - dev-5.33.0 \ No newline at end of file + - dev-5.33.0 + - feat/GHZS-3956 \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 660f6b789d..b4fec8acbb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -99,8 +99,9 @@ android:name="android.permission.GET_TASKS" tools:node="remove" /> - + + + @@ -701,7 +706,7 @@ android:screenOrientation="portrait" /> + + + @@ -833,7 +844,7 @@ - + + android:exported="false" + android:theme="@style/Theme.Transparent" /> - \ No newline at end of file diff --git a/app/src/main/assets/lottie/tab_mine.json b/app/src/main/assets/lottie/tab_mine.json new file mode 100644 index 0000000000..89252432e9 --- /dev/null +++ b/app/src/main/assets/lottie/tab_mine.json @@ -0,0 +1 @@ +{"v":"5.12.2","fr":60,"ip":0,"op":40,"w":66,"h":66,"nm":"icon_tab_my","ddd":0,"assets":[{"id":"comp_0","nm":"icon","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"highlight","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33.134,34.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":0.733},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,-0.004],[0.493,0],[0,0.004],[-0.493,0]],"o":[[0,0.004],[-0.493,0],[0,-0.004],[0.493,0]],"v":[[0.893,1.488],[0,1.496],[-0.893,1.488],[0,1.483]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0.267},"t":10,"s":[{"i":[[0,-1.018],[1.793,0],[0,1.018],[-1.793,0]],"o":[[0,1.018],[-1.793,0],[0,-1.018],[1.793,0]],"v":[[2.914,-0.344],[0,1.5],[-2.914,-0.344],[0,-1.5]],"c":true}]},{"i":{"x":0.333,"y":1},"o":{"x":0.667,"y":0},"t":18,"s":[{"i":[[0,-1.181],[1.407,0],[0,1.181],[-1.407,0]],"o":[[0,1.181],[-1.407,0],[0,-1.181],[1.407,0]],"v":[[2.226,-0.341],[0,1.74],[-2.211,-0.341],[0,-1.74]],"c":true}]},{"i":{"x":0.333,"y":1},"o":{"x":0.667,"y":0},"t":26,"s":[{"i":[[0,-0.905],[1.381,0],[0,0.905],[-1.381,0]],"o":[[0,0.905],[-1.381,0],[0,-0.905],[1.381,0]],"v":[[2.5,-0.306],[0,1.333],[-2.5,-0.306],[0,-1.333]],"c":true}]},{"t":32,"s":[{"i":[[0,-1.018],[1.381,0],[0,1.018],[-1.381,0]],"o":[[0,1.018],[-1.381,0],[0,-1.018],[1.381,0]],"v":[[2.5,-0.344],[0,1.5],[-2.5,-0.344],[0,-1.5]],"c":true}]}],"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"color","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"body","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,34.204,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.866,0],[0,3.866],[3.866,0],[0,-3.866]],"o":[[3.866,0],[0,-3.866],[-3.866,0],[0,3.866]],"v":[[0,4.599],[7,-2.401],[0,-9.401],[-7,-2.401]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0.207,0.359],[2.438,0],[1.189,-2.104],[-0.361,-0.204],[-0.204,0.361],[-1.915,0],[-0.932,-1.613],[-0.359,0.207]],"o":[[-1.198,-2.072],[-2.462,0],[-0.204,0.361],[0.361,0.204],[0.925,-1.638],[1.897,0],[0.207,0.359],[0.359,-0.207]],"v":[[5.848,8.225],[0,4.849],[-5.88,8.282],[-5.596,9.304],[-4.574,9.02],[0,6.349],[4.549,8.976],[5.574,9.25]],"c":true},"ix":2},"nm":"路径 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"合并路径 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.267,0.639,1,0.5,0.241,0.596,1,1,0.216,0.553,1],"ix":9}},"s":{"a":0,"k":[-3.812,-4.384],"ix":5},"e":{"a":0,"k":[6.345,8.129],"ix":6},"t":1,"nm":"color","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"Union","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"icon","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,33,0],"ix":2,"l":2},"a":{"a":0,"k":[33,33,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":8,"s":[70,70,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":18,"s":[110,110,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":26,"s":[90,90,100]},{"t":32,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"w":66,"h":66,"ip":0,"op":60,"st":0,"bm":0}],"markers":[],"props":{}} \ No newline at end of file diff --git a/app/src/main/assets/tab_mine.gif b/app/src/main/assets/tab_mine.gif deleted file mode 100644 index c980a0edb6..0000000000 Binary files a/app/src/main/assets/tab_mine.gif and /dev/null differ diff --git a/app/src/main/java/com/gh/base/BaseRichEditorActivity.kt b/app/src/main/java/com/gh/base/BaseRichEditorActivity.kt index 40e0409e60..d3566f9377 100644 --- a/app/src/main/java/com/gh/base/BaseRichEditorActivity.kt +++ b/app/src/main/java/com/gh/base/BaseRichEditorActivity.kt @@ -44,7 +44,7 @@ import org.json.JSONObject import java.io.File // TODO: 移动到module_bbs模块 -abstract class BaseRichEditorActivity : ToolBarActivity(), +abstract class BaseRichEditorActivity constructor() : ToolBarActivity(), KeyboardHeightObserver, UploadVideoListener { lateinit var mRichEditor: RichEditor diff --git a/app/src/main/java/com/gh/base/DownloadToolbarActivity.kt b/app/src/main/java/com/gh/base/DownloadToolbarActivity.kt index c040701a9c..9c8b81e4ea 100644 --- a/app/src/main/java/com/gh/base/DownloadToolbarActivity.kt +++ b/app/src/main/java/com/gh/base/DownloadToolbarActivity.kt @@ -46,7 +46,7 @@ abstract class DownloadToolbarActivity : ToolBarActivity() { override fun inflateMenu(res: Int) { super.inflateMenu(res) - if (showDownloadMenu()) { + if (!getBoolean(Constants.SP_TEENAGER_MODE) && showDownloadMenu()) { createDownloadMenu(res) } } @@ -60,7 +60,7 @@ abstract class DownloadToolbarActivity : ToolBarActivity() { } val downloadMenuView = mActionMenuView.menu.findItem(R.id.menu_download).actionView mDownloadCountHint = downloadMenuView?.findViewById(R.id.menu_download_count_hint) - mDownloadCountHint?.typeface = Typeface.createFromAsset(assets, "fonts/d_din_bold_only_number.ttf") + mDownloadCountHint?.typeface = Typeface.createFromAsset(assets, Constants.DIN_FONT_PATH) } override fun onMenuItemClick(item: MenuItem?): Boolean { diff --git a/app/src/main/java/com/gh/common/DefaultUrlHandler.kt b/app/src/main/java/com/gh/common/DefaultUrlHandler.kt index e8e5e01a5b..3b9ca778bf 100644 --- a/app/src/main/java/com/gh/common/DefaultUrlHandler.kt +++ b/app/src/main/java/com/gh/common/DefaultUrlHandler.kt @@ -15,9 +15,6 @@ import com.gh.common.util.DirectUtils.directToLegacyVideoDetail import com.gh.common.util.DirectUtils.directToLinkPage import com.gh.common.util.DirectUtils.directToQa import com.gh.common.util.PackageUtils -import com.gh.gamecenter.LibaoDetailActivity -import com.gh.gamecenter.MainActivity -import com.gh.gamecenter.NewsDetailActivity import com.gh.gamecenter.WebActivity import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts @@ -34,12 +31,12 @@ import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.entity.ActivityLabelEntity import com.gh.gamecenter.entity.SubjectRecommendEntity import com.gh.gamecenter.entity.VideoLinkEntity -import com.gh.gamecenter.eventbus.EBSkip import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.utils.PlatformUtils -import com.gh.gamecenter.fragment.MainWrapperFragment import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity +import com.gh.gamecenter.libao.LibaoDetailActivity +import com.gh.gamecenter.newsdetail.NewsDetailActivity import com.gh.gamecenter.qa.BbsType import com.gh.gamecenter.qa.video.publish.VideoPublishActivity import com.gh.gamecenter.subject.SubjectActivity @@ -262,10 +259,7 @@ object DefaultUrlHandler { ) } EntranceConsts.HOST_VIDEO_STREAMING_HOME -> { - intent = Intent(context, MainActivity::class.java) - intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP - intent.putExtra(MainActivity.SWITCH_TO_VIDEO, true) - context.startActivity(intent) + DirectUtils.directToHomeVideoTab(context) } EntranceConsts.HOST_VIDEO_STREAMING_DESC -> { directToGameDetailVideoStreaming(context, id, entrance) @@ -366,9 +360,8 @@ object DefaultUrlHandler { } EntranceConsts.HOST_FORUM -> { - val position = uri.getQueryParameter(EntranceConsts.KEY_POSITION)?.toInt() - DirectUtils.directToForum(context, position ?: 0) + DirectUtils.directToHomeCommunityTab(context) } EntranceConsts.HOST_UPLOAD_VIDEO_NEW -> { @@ -505,13 +498,11 @@ object DefaultUrlHandler { EntranceConsts.HOST_GAME_LIBRARY -> { DirectUtils.directToMainActivity(context) - EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_GAME)) } EntranceConsts.HOST_HOME_GAME_COLLECTION_SQUARE -> { DirectUtils.directToMainActivity(context) - EventBus.getDefault() - .post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_HOME)) + DirectUtils.directToHomeDefaultTab(context) EventBus.getDefault().post(EBReuse(host)) } @@ -598,6 +589,7 @@ object DefaultUrlHandler { DirectUtils.directToGameDetail( context, gameId, + "", entrance, autoDownload = false, traceEvent = null diff --git a/app/src/main/java/com/gh/common/chain/CertificationHandler.kt b/app/src/main/java/com/gh/common/chain/CertificationHandler.kt deleted file mode 100644 index 070505b083..0000000000 --- a/app/src/main/java/com/gh/common/chain/CertificationHandler.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.gh.common.chain - -import android.content.Context -import com.gh.common.dialog.CertificationDialog -import com.gh.gamecenter.feature.entity.GameEntity - -class CertificationHandler : DownloadChainHandler() { - - override fun handleRequest(context: Context, gameEntity: GameEntity, asVGame: Boolean) { - CertificationDialog.showCertificationDialog(context, gameEntity) { - if (hasNext()) { - getNext()?.handleRequest(context, gameEntity, asVGame) - } else { - processEndCallback?.invoke(asVGame, null) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/constant/Config.java b/app/src/main/java/com/gh/common/constant/Config.java index b5fa944512..d0dfb2494e 100644 --- a/app/src/main/java/com/gh/common/constant/Config.java +++ b/app/src/main/java/com/gh/common/constant/Config.java @@ -8,7 +8,6 @@ import android.text.TextUtils; import androidx.annotation.Nullable; -import com.gh.common.util.AdHelper; import com.gh.common.util.PackageHelper; import com.gh.common.util.PackageUtils; import com.gh.gamecenter.BuildConfig; @@ -26,21 +25,18 @@ import com.gh.gamecenter.entity.GameGuidePopupEntity; import com.gh.gamecenter.entity.NewApiSettingsEntity; import com.gh.gamecenter.entity.NewSettingsEntity; import com.gh.gamecenter.entity.VSetting; -import com.gh.gamecenter.feature.entity.NewsEntity; import com.gh.gamecenter.feature.entity.SettingsEntity; import com.gh.gamecenter.feature.entity.SimulatorEntity; import com.gh.gamecenter.feature.utils.ContentBlockedHelper; import com.gh.gamecenter.retrofit.RetrofitManager; import com.gh.vspace.VHelper; import com.halo.assistant.HaloApp; -import com.lightgame.utils.Utils; import org.greenrobot.eventbus.EventBus; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; -import java.util.List; import java.util.Locale; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -76,51 +72,10 @@ public class Config { private static GameGuidePopupEntity mGameGuidePopupEntity; private static SharedPreferences mDefaultSharedPreferences; - public static final String FIX_DOWNLOAD_KEY = "isFixDownload"; - public static final String FIX_PLUGIN_KEY = "isFixPlugin"; - public static final int VIDEO_PAGE_SIZE = 21; // 视频列表大多都是一行3个 public static boolean isShow() { - if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE)) return false; - - if (getPreferences().getBoolean(FIX_DOWNLOAD_KEY, false)) return true; - - if (!isExistDownloadFilter()) return false; - - for (SettingsEntity.Download entity : getSettings().getDownload()) { - if ("all".equals(entity.getGame())) { - if (entity.getPluginfy() && "normal".equals(entity.getPolicy()) && filterTime(entity.getTime())) { - return true; - } - } - } - return false; - } - - - public static boolean isShowDownload(String gameId) { - - if (getPreferences().getBoolean(FIX_DOWNLOAD_KEY, false)) return true; - - if (TextUtils.isEmpty(gameId) || !isExistDownloadFilter()) - return false; - - for (SettingsEntity.Download entity : getSettings().getDownload()) { - if (gameId.equals(entity.getGame())) { - if ("normal".equals(entity.getPolicy()) && filterTime(entity.getTime())) { - return true; - } else { - return false; - } - } else if ("all".equals(entity.getGame())) { - if ("normal".equals(entity.getPolicy()) && filterTime(entity.getTime())) { - return true; - } - } - } - - return false; + return !SPUtils.getBoolean(Constants.SP_TEENAGER_MODE); } /** @@ -136,74 +91,10 @@ public class Config { return mNewApiSettingsEntity.getInstall().getVpnRequired().getShouldShowVpnOption(); } - public static boolean isShowPlugin(String gameId) { - SharedPreferences preferences = getPreferences(); - boolean isFixPlugin = preferences.getBoolean(FIX_PLUGIN_KEY, false); - if (isFixPlugin) return true; - - if (TextUtils.isEmpty(gameId) || !isExistDownloadFilter()) - return false; - - for (SettingsEntity.Download entity : getSettings().getDownload()) { - if (gameId.equals(entity.getGame())) { - if (entity.getPluginfy() && filterTime(entity.getTime())) { - return true; - } else { - return false; - } - } - - if ("all".equals(entity.getGame())) { - if (entity.getPluginfy() && filterTime(entity.getTime())) { - preferences.edit().putBoolean(FIX_PLUGIN_KEY, true).apply(); - return true; - } - } - } - - return false; - } - - public static boolean isShowPlugin() { - SharedPreferences preferences = getPreferences(); - boolean isFixPlugin = preferences.getBoolean(FIX_PLUGIN_KEY, false); - if (isFixPlugin) return true; - - if (!isExistDownloadFilter()) - return false; - - for (SettingsEntity.Download entity : getSettings().getDownload()) { - if ("all".equals(entity.getGame())) { - if (entity.getPluginfy() && filterTime(entity.getTime())) { - preferences.edit().putBoolean(FIX_PLUGIN_KEY, true).apply(); - return true; - } - } - } - - return false; - } - - private static boolean filterTime(SettingsEntity.Download.TimeEntity timeEntity) { - long end = timeEntity.getEnd(); - long start = timeEntity.getStart(); - long curTime = Utils.getTime(HaloApp.getInstance().getApplication()); - - if ((start == 0 || curTime >= start) && (end == 0 || curTime <= end)) { - return true; - } - - return false; - } - public static void setSettings(SettingsEntity settingsEntity) { getPreferences().edit().putString(SETTINGS_KEY, GsonUtils.toJson(settingsEntity)).apply(); mSettingsEntity = settingsEntity; - // 更新设置状态 - mSettingsEntity.showArticleEntrance(); - mSettingsEntity.showCommunityEntrance(); - // 加载完设置后刷新下 PackageHelper.initList(); } @@ -331,27 +222,6 @@ public class Config { return mGameGuidePopupEntity; } - private static boolean isExistDownloadFilter() { - if (getSettings() == null || getSettings().getDownload() == null || getSettings().getDownload().size() == 0) { - return false; - } else { - return true; - } - } - - public static void filterPluginArticle(List list) { - if (isShowPlugin() || list == null) return; - - for (int i = 0; i < list.size(); i++) { - NewsEntity newsEntity = list.get(i); - String title = newsEntity.getTitle(); - if (!TextUtils.isEmpty(title) && title.contains("插件")) { - list.remove(i); - i--; - } - } - } - public static SharedPreferences getPreferences() { if (mDefaultSharedPreferences == null) { mDefaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication()); @@ -382,9 +252,6 @@ public class Config { GsonUtils.toJson(response.getSuggestion())); edit.apply(); - if (!getPreferences().getBoolean(Config.FIX_DOWNLOAD_KEY, false) && Config.isShow()) { - getPreferences().edit().putBoolean(Config.FIX_DOWNLOAD_KEY, true).apply(); - } if (!SPUtils.getBoolean(Constants.SP_TEENAGER_MODE)) { EventBus.getDefault().post(new EBReuse("Refresh")); } diff --git a/app/src/main/java/com/gh/common/databind/BindingAdapters.java b/app/src/main/java/com/gh/common/databind/BindingAdapters.java index 1746347638..380ec71e77 100644 --- a/app/src/main/java/com/gh/common/databind/BindingAdapters.java +++ b/app/src/main/java/com/gh/common/databind/BindingAdapters.java @@ -15,7 +15,6 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.gh.common.chain.BrowserInstallHandler; -import com.gh.common.chain.CertificationHandler; import com.gh.common.chain.DownloadChainBuilder; import com.gh.common.chain.DownloadChainHandler; import com.gh.common.chain.CheckDownloadHandler; @@ -119,7 +118,6 @@ public class BindingAdapters { // 判断是否显示按钮 if (gameEntity != null - && Config.isShowDownload(gameEntity.getId()) && !"光环助手".equals(gameEntity.getName())) { progressBar.setVisibility(View.VISIBLE); } else { @@ -166,7 +164,6 @@ public class BindingAdapters { builder.addHandler(new BrowserInstallHandler()); builder.addHandler(new PackageCheckHandler()); builder.addHandler(new DownloadDialogHelperHandler()); - builder.addHandler(new CertificationHandler()); builder.addHandler(new VersionNumberHandler()); builder.addHandler(new LandPageAddressHandler()); builder.addHandler(new OverseaDownloadHandler()); @@ -195,7 +192,6 @@ public class BindingAdapters { DownloadChainBuilder builder = new DownloadChainBuilder(); builder.addHandler(new UnsupportedFeatureHandler()); builder.addHandler(new GamePermissionHandler()); - builder.addHandler(new CertificationHandler()); builder.addHandler(new VersionNumberHandler()); builder.setProcessEndCallback((asVGame, isSubscribe) -> { @@ -550,11 +546,10 @@ public class BindingAdapters { } } - public static void setGameName(TextView view, GameEntity game, boolean isShowPlatform, @Nullable Boolean isShowSuffix) { - if (isShowSuffix == null) isShowSuffix = true; // 默认显示 + public static void setGameName(TextView view, GameEntity game, boolean isShowPlatform) { String gameName; if (isShowPlatform && game.getApk().size() > 0) { - gameName = String.format("%s - %s", !isShowSuffix ? game.getNameWithoutSuffix() : game.getName(), + gameName = String.format("%s - %s", game.getName(), PlatformUtils.getInstance(view.getContext()).getPlatformName( game.getApk().get(0).getPlatform())); if (!gameName.equals((String) view.getTag(R.string.tag_game_name_id))) { @@ -562,7 +557,7 @@ public class BindingAdapters { view.setTag(R.string.tag_game_name_id, gameName); } } else { - gameName = !isShowSuffix ? game.getNameWithoutSuffix() : game.getName(); + gameName = game.getName(); if (gameName != null && !gameName.equals((String) view.getTag(R.string.tag_game_name_id))) { view.setText(gameName); view.setTag(R.string.tag_game_name_id, gameName); diff --git a/app/src/main/java/com/gh/common/dialog/CertificationDialog.kt b/app/src/main/java/com/gh/common/dialog/CertificationDialog.kt deleted file mode 100644 index 48647a7985..0000000000 --- a/app/src/main/java/com/gh/common/dialog/CertificationDialog.kt +++ /dev/null @@ -1,220 +0,0 @@ -package com.gh.common.dialog - -import android.annotation.SuppressLint -import android.app.Activity -import android.app.Dialog -import android.content.Context -import android.content.Intent -import android.graphics.Color -import android.graphics.Paint -import android.graphics.drawable.ColorDrawable -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.widget.CheckBox -import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity -import com.gh.gamecenter.common.avoidcallback.AvoidOnResultManager -import com.gh.gamecenter.common.avoidcallback.Callback -import com.gh.gamecenter.common.constant.Constants -import com.gh.common.util.* -import com.gh.gamecenter.R -import com.gh.gamecenter.ShellActivity -import com.gh.gamecenter.common.callback.ConfirmListener -import com.gh.gamecenter.common.constant.EntranceConsts -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.feature.entity.AuthDialogEntity -import com.gh.gamecenter.feature.entity.AuthDialogLevel -import com.gh.gamecenter.feature.entity.GameEntity -import com.gh.gamecenter.login.user.UserManager -import com.google.gson.reflect.TypeToken -import com.halo.assistant.fragment.user.UserInfoEditFragment -import com.lightgame.utils.AppManager - -class CertificationDialog( - context: Context, - private val authDialogEntity: AuthDialogEntity, - val gameEntity: GameEntity, - val listener: ConfirmListener -) : - Dialog(context, R.style.GhAlertDialog) { - - private lateinit var view: View - private lateinit var detailedDesTv: TextView - private lateinit var noRemindAgainCb: CheckBox - private lateinit var actionLeftTv: TextView - private lateinit var actionRightTv: TextView - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - view = LayoutInflater.from(context).inflate(R.layout.dialog_sertification, null) - setContentView(view) - detailedDesTv = view.findViewById(R.id.detailedDesTv) - noRemindAgainCb = view.findViewById(R.id.noRemindAgainCb) - actionLeftTv = view.findViewById(R.id.actionLeftTv) - actionRightTv = view.findViewById(R.id.actionRightTv) - - detailedDesTv.paint.flags = Paint.UNDERLINE_TEXT_FLAG - detailedDesTv.paint.isAntiAlias = true - - detailedDesTv.setOnClickListener { - DirectUtils.directToWebView(context, authDialogEntity.link) - } - - when (authDialogEntity.level) { - AuthDialogLevel.MUST_PASS.value -> { - actionLeftTv.text = "暂不下载" - actionRightTv.text = "去实名认证" - noRemindAgainCb.visibility = View.GONE - actionLeftTv.setOnClickListener { - SensorsBridge.trackVerificationPopupClick(actionLeftTv.text.toString()) - dismiss() - } - actionRightTv.setOnClickListener { - SensorsBridge.trackVerificationPopupClick(actionRightTv.text.toString()) - if (UserManager.getInstance().isLoggedIn) { - gotoAuthPage() - } else { - gotoLoginPage() - } - } - } - AuthDialogLevel.ALWAYS_HINT.value -> { - actionLeftTv.text = "去实名认证" - actionRightTv.text = "继续下载" - noRemindAgainCb.visibility = View.GONE - actionLeftTv.setOnClickListener { - SensorsBridge.trackVerificationPopupClick(actionLeftTv.text.toString()) - if (UserManager.getInstance().isLoggedIn) { - gotoAuthPage() - } else { - gotoLoginPage() - } - } - actionRightTv.setOnClickListener { - SensorsBridge.trackVerificationPopupClick(actionRightTv.text.toString()) - listener.onConfirm() - dismiss() - } - } - AuthDialogLevel.OPTIONAL_HINT.value -> { - actionLeftTv.text = "去实名认证" - actionRightTv.text = "继续下载" - noRemindAgainCb.visibility = View.VISIBLE - actionLeftTv.setOnClickListener { - SensorsBridge.trackVerificationPopupClick(actionLeftTv.text.toString()) - if (noRemindAgainCb.isChecked) { - SPUtils.setBoolean(gameEntity.id, true) - } - if (UserManager.getInstance().isLoggedIn) { - gotoAuthPage() - } else { - gotoLoginPage() - } - } - actionRightTv.setOnClickListener { - SensorsBridge.trackVerificationPopupClick(actionRightTv.text.toString()) - if (noRemindAgainCb.isChecked) { - SPUtils.getBoolean(gameEntity.id, true) - } - listener.onConfirm() - dismiss() - } - } - } - - SensorsBridge.trackVerificationDialogShow( - gameId = gameEntity.id, - gameName = gameEntity.name ?: "", - gameType = gameEntity.categoryChinese, - articleType = "", - verificationType = "游戏实名" - ) - } - - //跳转登录页面 - private fun gotoLoginPage() { - val currentActivity = AppManager.getInstance().currentActivity() ?: return - - CheckLoginUtils.checkLogin( - currentActivity as AppCompatActivity, - null, true, "实名认证弹窗" - ) { - if (UserManager.getInstance().isAuth) { - listener.onConfirm() - dismiss() - } - } - } - - //跳转实名认证页面 - private fun gotoAuthPage() { - val currentActivity = AppManager.getInstance().currentActivity() ?: return - - AvoidOnResultManager.getInstance(currentActivity as AppCompatActivity) - .startForResult( - ShellActivity.getIntent( - context, - ShellActivity.Type.REAL_NAME_INFO, - ).apply { - putExtra(EntranceConsts.KEY_SOURCE_ENTRANCE, "游戏实名") - putExtra(EntranceConsts.KEY_GAME_ID, gameEntity.id) - }, object : Callback { - override fun onActivityResult(resultCode: Int, data: Intent?) { - if (resultCode == Activity.RESULT_OK && data != null) { - val isAuthSuccess = - data.getBooleanExtra(UserInfoEditFragment.AUTH_SUCCESS, false) - if (isAuthSuccess) { - listener.onConfirm() - dismiss() - } - } - } - }) - } - - - companion object { - @JvmStatic - fun showCertificationDialog(context: Context, game: GameEntity, listener: ConfirmListener) { - //1.先判断是否登录 是执行2 否执行3 - //2.判断是否实名认证 是终止 否执行3 - //3.判断是否需要弹出认证弹窗接口 - if (UserManager.getInstance().isLoggedIn) { - if (UserManager.getInstance().isAuth) {//已实名认证 - listener.onConfirm() - } else { - authDialog(context, game, listener) - } - } else { - authDialog(context, game, listener) - } - } - - @SuppressLint("CheckResult") - private fun authDialog(context: Context, game: GameEntity, listener: ConfirmListener) { - var authDialog: AuthDialogEntity? = null - if (game.authDialog != null) { - authDialog = game.authDialog - } - if (authDialog == null) { - val datas = SPUtils.getString(Constants.SP_AUTH_DIALOG) - val type = object : TypeToken>() {}.type - val authDialogs = GsonUtils.gson.fromJson>(datas, type) - if (!authDialogs.isNullOrEmpty()) { - authDialog = authDialogs.find { it.gameCategory == game.category } - } - } - val isCloseAuthDialog = SPUtils.getBoolean(game.id, false) - if (authDialog != null && (authDialog.level != AuthDialogLevel.OPTIONAL_HINT.value || !isCloseAuthDialog)) { - val dialog = CertificationDialog(context, authDialog, game, listener) - dialog.show() - } else { - listener.onConfirm() - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/dialog/PrivacyPolicyDialogFragment.kt b/app/src/main/java/com/gh/common/dialog/PrivacyPolicyDialogFragment.kt index c65b8a7e48..4d99d6c002 100644 --- a/app/src/main/java/com/gh/common/dialog/PrivacyPolicyDialogFragment.kt +++ b/app/src/main/java/com/gh/common/dialog/PrivacyPolicyDialogFragment.kt @@ -29,6 +29,15 @@ class PrivacyPolicyDialogFragment : BaseDialogFragment() { private var mCallBack: ((isSuccess: Boolean) -> Unit)? = null private val mBinding by lazy { DialogPrivacyProtocolBinding.inflate(layoutInflater) } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState != null) { + dismiss() + return + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, diff --git a/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt b/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt index 14005467f3..eabef3281a 100644 --- a/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt +++ b/app/src/main/java/com/gh/common/dialog/ReserveDialog.kt @@ -24,6 +24,15 @@ class ReserveDialog : BaseDialogFragment() { private lateinit var mReserveList: List private var mDismissListener: (() -> Unit)? = null + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState != null) { + dismiss() + return + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { val binding: DialogReserveBinding = DialogReserveBinding.inflate(layoutInflater, null, false) diff --git a/app/src/main/java/com/gh/common/history/HistoryHelper.kt b/app/src/main/java/com/gh/common/history/HistoryHelper.kt index 64a30ae0b0..3e2a952b68 100644 --- a/app/src/main/java/com/gh/common/history/HistoryHelper.kt +++ b/app/src/main/java/com/gh/common/history/HistoryHelper.kt @@ -71,7 +71,6 @@ object HistoryHelper { historyGame.iconFloat = gameEntity.iconFloat historyGame.name = gameEntity.name historyGame.tagStyle = gameEntity.tagStyle - historyGame.tag = gameEntity.getTag() historyGame.subtitle = gameEntity.subtitle historyGame.subtitleStyle = gameEntity.subtitleStyle return historyGame diff --git a/app/src/main/java/com/gh/common/iinterface/IMultiTab.kt b/app/src/main/java/com/gh/common/iinterface/IMultiTab.kt new file mode 100644 index 0000000000..efef87a109 --- /dev/null +++ b/app/src/main/java/com/gh/common/iinterface/IMultiTab.kt @@ -0,0 +1,10 @@ +package com.gh.common.iinterface + +import com.gh.gamecenter.entity.MultiTabNav + +interface IMultiTab { + fun provideMultiTabId(): String + fun provideMultiTabName(): String + fun provideCurrentTabEntity(): MultiTabNav.LinkMultiTabNav? + fun provideLastSelectedPosition(): Int +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/iinterface/ISearchToolbarTab.kt b/app/src/main/java/com/gh/common/iinterface/ISearchToolbarTab.kt new file mode 100644 index 0000000000..71e18adfc7 --- /dev/null +++ b/app/src/main/java/com/gh/common/iinterface/ISearchToolbarTab.kt @@ -0,0 +1,10 @@ +package com.gh.common.iinterface + +import com.gh.gamecenter.entity.BottomTab + +interface ISearchToolbarTab { + fun onScrollChanged(totalHeight: Int, offset: Int, isDarkModeChanged: Boolean) + fun changeAppBarColor(color: Int, pageId: String) + fun setSearchStyle(searchStyle: BottomTab.SearchStyle) + fun getCurrentTabIndex(): Int? +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/iinterface/ISmartRefresh.kt b/app/src/main/java/com/gh/common/iinterface/ISmartRefresh.kt new file mode 100644 index 0000000000..de0d86f9bc --- /dev/null +++ b/app/src/main/java/com/gh/common/iinterface/ISmartRefresh.kt @@ -0,0 +1,12 @@ +package com.gh.common.iinterface + +import com.gh.common.prioritychain.PullDownPushHandler +import com.gh.gamecenter.entity.PullDownPush + +interface ISmartRefresh { + fun setSmartRefreshEnabled(isEnable: Boolean) + fun finishTwoLevel(action: String) + fun finishRefresh() + fun popupPullDownPush(finishCallback: () -> Unit) + fun setPullDownPush(pullDownPush: PullDownPush?, pullDownPushHandler: PullDownPushHandler?) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/iinterface/ISmartRefreshContent.kt b/app/src/main/java/com/gh/common/iinterface/ISmartRefreshContent.kt new file mode 100644 index 0000000000..d8c91f6ebe --- /dev/null +++ b/app/src/main/java/com/gh/common/iinterface/ISmartRefreshContent.kt @@ -0,0 +1,17 @@ +package com.gh.common.iinterface + +interface ISmartRefreshContent { + /** + * 启用/关闭 页面滑动 + * @param isScrollEnabled 是否启用 + */ + fun setScrollEnabled(isScrollEnabled: Boolean) + + fun onRefresh() + + /** + * 启用/关闭 SwipeRefreshLayout 的下拉刷新 + * @param isSwipeRefreshEnabled 是否启用 + */ + fun setSwipeRefreshEnabled(isSwipeRefreshEnabled: Boolean) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/iinterface/ISuperiorChain.kt b/app/src/main/java/com/gh/common/iinterface/ISuperiorChain.kt new file mode 100644 index 0000000000..b9094322c1 --- /dev/null +++ b/app/src/main/java/com/gh/common/iinterface/ISuperiorChain.kt @@ -0,0 +1,9 @@ +package com.gh.common.iinterface + +import com.gh.common.prioritychain.PriorityChain + +interface ISuperiorChain { + fun registerInferiorChain(chain: PriorityChain) + + fun unregisterInferiorChain(chain: PriorityChain) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/AccelerateNotificationHandler.kt b/app/src/main/java/com/gh/common/prioritychain/AccelerateNotificationHandler.kt index fa1a3f6261..5683c92f5a 100644 --- a/app/src/main/java/com/gh/common/prioritychain/AccelerateNotificationHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/AccelerateNotificationHandler.kt @@ -14,7 +14,7 @@ import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.databinding.PopupAccelerateNotificationBinding import com.gh.gamecenter.feature.entity.GameEntity -import com.gh.gamecenter.fragment.MainWrapperViewModel +import com.gh.gamecenter.wrapper.MainWrapperViewModel class AccelerateNotificationHandler(priority: Int) : PriorityChainHandler(priority) { @@ -42,7 +42,7 @@ class AccelerateNotificationHandler(priority: Int) : PriorityChainHandler(priori processNext() } else { updateStatus(STATUS_VALID) - onProcess() + process() } } else { if (gameEntityList == null) { @@ -53,7 +53,7 @@ class AccelerateNotificationHandler(priority: Int) : PriorityChainHandler(priori } } - override fun onProcess() { + override fun onProcess() : Boolean { when (getStatus()) { STATUS_VALID -> { val accelerateSet = @@ -64,6 +64,7 @@ class AccelerateNotificationHandler(priority: Int) : PriorityChainHandler(priori } accelerateSet.add(mGameList!![0].messageId) SPUtils.setStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET, accelerateSet) + return true } else { processNext() } @@ -73,6 +74,8 @@ class AccelerateNotificationHandler(priority: Int) : PriorityChainHandler(priori processNext() } } + + return false } companion object { diff --git a/app/src/main/java/com/gh/common/prioritychain/CustomFloatingWindowHandler.kt b/app/src/main/java/com/gh/common/prioritychain/CustomFloatingWindowHandler.kt new file mode 100644 index 0000000000..ec1ada9d46 --- /dev/null +++ b/app/src/main/java/com/gh/common/prioritychain/CustomFloatingWindowHandler.kt @@ -0,0 +1,82 @@ +package com.gh.common.prioritychain + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.gh.gamecenter.floatingwindow.FloatingWindowEntity +import com.gh.gamecenter.livedata.Event + +class CustomFloatingWindowHandler(priority: Int) : PriorityChainHandler(priority) { + + private val data = arrayListOf() + + private val _showFloatingAction = MutableLiveData>>() + val showFloatingAction: LiveData>> = _showFloatingAction + + fun setData(newData: List) { + data.clear() + data.addAll(newData) + doPreProcess() + } + + private fun doPreProcess() { +// debugOnly { +// data.clear() +// data.add( +// FloatingWindowEntity( +// id = "audire", +// image = "https://jira.shanqu.cc/secure/attachment/57822/57822_image-2023-12-01-17-53-04-492.png", +// link = WelcomeDialogEntity( +// id = null, +// time = null, +// packages = null, +// floatingWindowId = null, +// shouldShowExitAnimation = false +// ), +// pushType = "maiorum", +// expandable = false, +// expandedImage = "fermentum", +// bigPopupNotice = "dignissim" +// ) +// ) +// } + + if (getStatus() == STATUS_PENDING) { + if (data.isNotEmpty()) { + updateStatus(STATUS_VALID) + process() + } else { + processNext() + } + } else { + if (data.isNotEmpty()) { + updateStatus(STATUS_VALID) + } else { + updateStatus(STATUS_INVALID) + } + } + } + + override fun onProcess(): Boolean { + when (getStatus()) { + STATUS_VALID -> { + _showFloatingAction.value = Event(data) + processNext() +// floatingWindowProvider.showFloatingWindowOnly( +// mFragment!!, +// mRecyclerView!!, +// mWindowList!!, +// ) { +// val welcomeDialog = WelcomeDialogFragment.getInstance(it, true, mFragment) +// welcomeDialog.show(mFragment!!.childFragmentManager, "WelcomeDialog") +// } + } + + STATUS_INVALID -> { + processNext() + } + } + + return false + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/FloatingWindowHandler.kt b/app/src/main/java/com/gh/common/prioritychain/FloatingWindowHandler.kt index d63cc9fdde..dd6ac401cf 100644 --- a/app/src/main/java/com/gh/common/prioritychain/FloatingWindowHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/FloatingWindowHandler.kt @@ -42,7 +42,7 @@ class FloatingWindowHandler(priority: Int) : PriorityChainHandler(priority) { if (getStatus() == STATUS_PENDING) { if (!mWindowList.isNullOrEmpty()) { updateStatus(STATUS_VALID) - onProcess() + process() } else { processNext() } @@ -55,7 +55,7 @@ class FloatingWindowHandler(priority: Int) : PriorityChainHandler(priority) { } } - override fun onProcess() { + override fun onProcess(): Boolean { when (getStatus()) { STATUS_VALID -> { val floatingWindowProvider = @@ -65,7 +65,7 @@ class FloatingWindowHandler(priority: Int) : PriorityChainHandler(priority) { // 强校验所有条件均通过才能显示 if (floatingWindowProvider == null || mFragment == null || mFragment?.isAdded == false || mRecyclerView == null) { processNext() - return + return false } floatingWindowProvider.showFloatingWindowOnly( @@ -82,6 +82,8 @@ class FloatingWindowHandler(priority: Int) : PriorityChainHandler(priority) { processNext() } } + + return false } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/GlobalPriorityChainHelper.kt b/app/src/main/java/com/gh/common/prioritychain/GlobalPriorityChainHelper.kt new file mode 100644 index 0000000000..2079c58616 --- /dev/null +++ b/app/src/main/java/com/gh/common/prioritychain/GlobalPriorityChainHelper.kt @@ -0,0 +1,244 @@ +package com.gh.common.prioritychain + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.Application.ActivityLifecycleCallbacks +import android.os.Bundle +import android.preference.PreferenceManager +import androidx.fragment.app.FragmentActivity +import com.gh.common.iinterface.ISuperiorChain +import com.gh.common.util.CheckLoginUtils +import com.gh.common.util.PackageUtils +import com.gh.gamecenter.SplashScreenActivity +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.entity.SimpleGameEntity +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.entity.DialogEntity +import com.gh.gamecenter.feature.entity.WelcomeDialogEntity +import com.gh.gamecenter.login.user.UserManager +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers + +/** + * 全局的 APP 优先级弹窗链管理器 + * + * 弹窗的优先级为 + * 启动跳转(-101) > 更新弹窗(-100) > 隐私政策弹窗(-99) > 消息通知权限弹窗(0) > 预约弹窗(1) > 启动弹窗(2) + */ +object GlobalPriorityChainHelper : ISuperiorChain { + + private val api = RetrofitManager.getInstance().api + private var inferiorChain: PriorityChain? = null + private val mainChain: PriorityChain by lazy { PriorityChain { inferiorChain?.start() } } + + /** + * 当前 activity 是否使用于应用全局弹窗的弹出 + * 排除启动页和其它非 FragmentActivity + */ + fun isThisActivityValid(activity: Activity?): Boolean { + return activity is FragmentActivity + && !activity.isFinishing + && activity !is SplashScreenActivity + } + + /** + * 预启动所有的优先级弹窗管理链 + */ + fun preStart() { + val launchRedirectHandler = LaunchRedirectHandler(-101) + val updateDialogHandler = UpdateDialogHandler(-100) + val privacyPolicyDialogHandler = PrivacyPolicyDialogHandler(-99) + val notificationPermissionDialogHandler = NotificationPermissionDialogHandler(0) + val reserveDialogHandler = ReserveDialogHandler(1) + val welcomeDialogHandler = WelcomeDialogHandler(2) + + mainChain.addHandler(launchRedirectHandler) + mainChain.addHandler(updateDialogHandler) + mainChain.addHandler(privacyPolicyDialogHandler) + mainChain.addHandler(welcomeDialogHandler) + mainChain.addHandler(reserveDialogHandler) + mainChain.addHandler(notificationPermissionDialogHandler) + + launchRedirectHandler.doPreProcess() + updateDialogHandler.doPreProcess() + requestOpeningDialogData(welcomeDialogHandler, privacyPolicyDialogHandler) + requestReserveDialogData(reserveDialogHandler) + } + + /** + * 启动优先级弹窗管理链的执行 + */ + fun start() { + if (mainChain.isHandlerQueueEmpty()) return + + mainChain.start() + + observeLifecycle() + } + + private fun observeLifecycle() { + HaloApp.getInstance().registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + // do nothing + } + + override fun onActivityStarted(activity: Activity) { + // do nothing + } + + override fun onActivityResumed(activity: Activity) { + // 优先级弹窗管理链为空时取消注册,避免无用调用 + if (mainChain.isHandlerQueueEmpty()) { + HaloApp.getInstance().unregisterActivityLifecycleCallbacks(this) + } else { + resume() + } + } + + override fun onActivityPaused(activity: Activity) { + // do nothing + } + + override fun onActivityStopped(activity: Activity) { + // do nothing + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + // do nothing + } + + override fun onActivityDestroyed(activity: Activity) { + // do nothing + } + }) + } + + /** + * 恢复链的处理 + */ + fun resume() { + mainChain.resume() + } + + /** + * 请求首页启动弹窗相关的数据并执行相关 handler 的 preProcess + */ + @SuppressLint("CheckResult") + private fun requestOpeningDialogData( + welcomeDialogHandler: WelcomeDialogHandler, + privacyPolicyDialogHandler: PrivacyPolicyDialogHandler + ) { + val sp = PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance()) + + val lastId = SPUtils.getString(sp, Constants.SP_LAST_OPENING_ID, "") + val lastTime = SPUtils.getLong(sp, Constants.SP_LAST_OPENING_TIME, 0) + val openType = if (HaloApp.getInstance().isNewForThisVersion) "first" else "not_first_time" + + api.getOpeningDialog(HaloApp.getInstance().channel, lastId, lastTime, openType) + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: DialogEntity) { + var welcomeDialogEntity: WelcomeDialogEntity? = null + var privacyPolicyDialogEntity: DialogEntity.PrivacyPolicyEntity? = null + + // 全新安装忽略隐私弹窗 + if (data.privacyPolicyDialog != null) { + val id = data.privacyPolicyDialog.id + val lastAcceptedId = SPUtils.getString(Constants.SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID, "") + if (HaloApp.getInstance().isBrandNewInstall) { + SPUtils.setString(Constants.SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID, id) + } else { + if (id != lastAcceptedId) { + privacyPolicyDialogEntity = data.privacyPolicyDialog + } + } + } + +// debugOnly { +// privacyPolicyDialogEntity = DialogEntity.PrivacyPolicyEntity( +// "5f6f5f3d9d9b4e0001c3b3a5", +// "privacy_policy", +// "ONCE", +// "INFORM", +// "隐私政策", +// 1601116800000) +// } + + privacyPolicyDialogHandler.doPreProcess(privacyPolicyDialogEntity) + + // 类型为游戏时判断是否本地已安装该游戏,已安装不弹弹窗 + if (data.welcomeDialog != null) { + welcomeDialogEntity = data.welcomeDialog + + if (data.welcomeDialog.type == "game") { + if (data.welcomeDialog.packages != null) { + for (packageName in data.welcomeDialog.packages!!) { + if (PackageUtils.isInstalled(HaloApp.getInstance(), packageName)) { + welcomeDialogEntity = null + break + } + } + } + } + } + + welcomeDialogHandler.doPreProcess(welcomeDialogEntity) + } + + override fun onFailure(exception: Exception) { + privacyPolicyDialogHandler.doPreProcess(null) + welcomeDialogHandler.doPreProcess(null) + } + }) + } + + /** + * 请求预约弹窗相关的数据 + */ + @SuppressLint("CheckResult") + private fun requestReserveDialogData(reserveDialogHandler: ReserveDialogHandler) { +// debugOnly { +// reserveDialogHandler.doPreProcess(arrayListOf(SimpleGameEntity( +// "5f6f5f3d9d9b4e0001c3b3a5", +// "5f6f5f3d9d9b4e0001c3b3a5", +// "https://and-static.ghzs.com/image/game/icon/2022/11/18/63772b0d398daaa7c5067298.png", +// "5f6f5f3d9d9b4e0001c3b3a5", +// ))) +// return +// } + + if (CheckLoginUtils.isLogin()) { + api.getReserveDialog(UserManager.getInstance().userId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse>() { + override fun onSuccess(data: List) { + reserveDialogHandler.doPreProcess(data) + } + + override fun onFailure(exception: Exception) { + reserveDialogHandler.doPreProcess(null) + } + }) + } else { + reserveDialogHandler.doPreProcess(null) + } + } + + override fun registerInferiorChain(chain: PriorityChain) { + inferiorChain = chain + if (mainChain.isHandlerQueueEmpty()) { + inferiorChain?.resume() + } + } + + override fun unregisterInferiorChain(chain: PriorityChain) { + if (inferiorChain == chain) { + inferiorChain = null + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/HomePushHandler.kt b/app/src/main/java/com/gh/common/prioritychain/HomePushHandler.kt deleted file mode 100644 index ee4440dda9..0000000000 --- a/app/src/main/java/com/gh/common/prioritychain/HomePushHandler.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.gh.common.prioritychain - -import com.gh.gamecenter.fragment.HomeSearchToolWrapperFragment - -class HomePushHandler(priority: Int): PriorityChainHandler(priority) { - - private var mHomeFragment: HomeSearchToolWrapperFragment? = null - - /** - * 提前预处理显示弹窗的内容 - */ - fun doPreProcess(homeFragment: HomeSearchToolWrapperFragment?, shouldShow: Boolean) { - mHomeFragment = homeFragment - - if (getStatus() == STATUS_PENDING) { - if (shouldShow && homeFragment != null) { - updateStatus(STATUS_VALID) - onProcess() - } else { - processNext() - } - } else { - if (shouldShow && homeFragment != null) { - updateStatus(STATUS_VALID) - } else { - updateStatus(STATUS_INVALID) - } - } - } - - override fun onProcess() { - when (getStatus()) { - STATUS_VALID -> { - // 目前首页下拉二楼是首页最后一个弹窗类的东西,还没实现回调,如果有其它要在它后面弹出的,需要自行在它的实现结果后添加回调 - mHomeFragment?.popUpHomePushIfNeeded { - processNext() - } - } - STATUS_INVALID -> { - processNext() - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/LaunchRedirectHandler.kt b/app/src/main/java/com/gh/common/prioritychain/LaunchRedirectHandler.kt new file mode 100644 index 0000000000..9c30994bb2 --- /dev/null +++ b/app/src/main/java/com/gh/common/prioritychain/LaunchRedirectHandler.kt @@ -0,0 +1,86 @@ +package com.gh.common.prioritychain + +import android.annotation.SuppressLint +import com.gh.common.util.DirectUtils +import com.gh.gamecenter.BuildConfig +import com.gh.gamecenter.common.entity.LaunchRedirect +import com.gh.gamecenter.common.entity.LaunchRedirectWrapper +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.utils.singleToMain +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.utils.CurrentActivityHolder +import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.gamecenter.wrapper.MainWrapperRepository +import com.halo.assistant.HaloApp + +/** + * 初次启动跳转 + */ +class LaunchRedirectHandler(priority: Int) : PriorityChainHandler(priority) { + + private var launchData: LaunchRedirect? = null + + @SuppressLint("CheckResult") + fun doPreProcess() { +// if (true) { + if (HaloApp.getInstance().isBrandNewInstall) { + RetrofitManager.getInstance().newApi + .getLaunchRedirect(BuildConfig.VERSION_NAME, HaloApp.getInstance().channel) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: LaunchRedirectWrapper) { + launchData = data.launchRedirect + + if (launchData == null) { + updateStatus(STATUS_INVALID) + processNext() + return + } + + // 尝试提前设置 tab default 避免首页数据加载完成,tab 选中会闪烁 + if (launchData?.type == "bottom_tab") { + MainWrapperRepository.getInstance().sendSelectTabEvent(launchData!!) + } + + if (getStatus() == STATUS_PENDING) { + updateStatus(STATUS_VALID) + process() + } else { + updateStatus(STATUS_VALID) + } + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + updateStatus(STATUS_INVALID) + processNext() + } + }) + } else { + updateStatus(STATUS_INVALID) + processNext() + } + } + + override fun onProcess(): Boolean { + val currentActivity = CurrentActivityHolder.getCurrentActivity() + + if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) { + if (getStatus() == STATUS_VALID) { + // 当 type 为 "bottom_tab" 时上面 doPreProcess 中已经处理过了,但再选中一次好像也没有什么问题,先不特殊处理这个 case 了 + DirectUtils.directToLinkPage(currentActivity!!, launchData!!, "首次启动跳转", "") + // 跳转页面不管回调,延迟 500ms 后执行下一个 handler + AppExecutor.uiExecutor.executeWithDelay({ + processNext() + }, 500L) + + return true + } else if (getStatus() == STATUS_INVALID) { + processNext() + } + } + + return false + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/NotificationPermissionDialogHandler.kt b/app/src/main/java/com/gh/common/prioritychain/NotificationPermissionDialogHandler.kt index 12d0e1e48f..ce0ad5c251 100644 --- a/app/src/main/java/com/gh/common/prioritychain/NotificationPermissionDialogHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/NotificationPermissionDialogHandler.kt @@ -6,15 +6,18 @@ import com.gh.gamecenter.login.user.UserManager class NotificationPermissionDialogHandler(priority: Int) : PriorityChainHandler(priority) { - override fun onProcess() { + override fun onProcess(): Boolean { // 仅登录后再启动光环时请求一次权限 if (UserManager.getInstance().isLoggedIn) { NotificationHelper.showNotificationHintDialog(NotificationUgc.LOGIN) { processNext() } + return true } else { processNext() } + + return false } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/PriorityChain.kt b/app/src/main/java/com/gh/common/prioritychain/PriorityChain.kt index 7588c2eea7..09739e5fb6 100644 --- a/app/src/main/java/com/gh/common/prioritychain/PriorityChain.kt +++ b/app/src/main/java/com/gh/common/prioritychain/PriorityChain.kt @@ -1,22 +1,54 @@ package com.gh.common.prioritychain +import com.lightgame.utils.Utils import java.util.* import java.util.concurrent.PriorityBlockingQueue -class PriorityChain { +class PriorityChain(private val completeCallback: (() -> Unit)? = null) { - private val mHandlerQueue: Queue = PriorityBlockingQueue() + private val handlerQueue: Queue = PriorityBlockingQueue() + /** + * 添加 handler 到队列中 + */ fun addHandler(handler: PriorityChainHandler) { - mHandlerQueue.add(handler.also { + handlerQueue.add(handler.also { it.setPriorityChain(this) }) } + /** + * 启动队列中的 handler + */ fun start() { - mHandlerQueue.poll()?.process(mHandlerQueue) + handlerQueue.peek()?.let { + it.injectQueue(handlerQueue) + it.process() + } } - fun isHandlerQueueEmpty() = mHandlerQueue.isEmpty() + /** + * 恢复队列中的 handler + */ + fun resume() { + val handler = handlerQueue.peek() + + if (handler?.getStatus() == PriorityChainHandler.STATUS_HANDLING) { + Utils.log(PriorityChainHandler.TAG, "${handler.javaClass.simpleName} 处于执行中状态,不用恢复") + } else { + handler?.injectQueue(handlerQueue) + handler?.process() + } + } + + /** + * 队列是否为空 + */ + fun isHandlerQueueEmpty() = handlerQueue.isEmpty() + + + fun onHandleComplete() { + completeCallback?.invoke() + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/PriorityChainHandler.kt b/app/src/main/java/com/gh/common/prioritychain/PriorityChainHandler.kt index 47dc1b5c20..c9aebaafe3 100644 --- a/app/src/main/java/com/gh/common/prioritychain/PriorityChainHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/PriorityChainHandler.kt @@ -5,54 +5,76 @@ import java.util.* abstract class PriorityChainHandler(private val mPriority: Int) : Comparable { - private var mStatus = STATUS_UNKNOWN - private var mQueue: Queue? = null + private var status = STATUS_UNKNOWN + private var queue: Queue? = null - private var mPriorityChain: PriorityChain? = null + private var priorityChain: PriorityChain? = null /** * 获取当前 handler 的状态 * - 等待结果返回 (STATUS_PENDING) * - 无需执行 (STATUS_INVALID) * - 可执行 (STATUS_VALID) + * - 执行中 (STATUS_HANDLING) * - 未知 (STATUS_UNKNOWN) */ - fun getStatus(): Int = mStatus + fun getStatus(): Int = status fun updateStatus(status: Int) { - Utils.log(TAG, "${javaClass.simpleName} updateStatus $status") + Utils.log(TAG, "${javaClass.simpleName} updateStatus ${this.status} $status") - mStatus = status + this.status = status } fun setPriorityChain(priorityChain: PriorityChain) { - mPriorityChain = priorityChain + this.priorityChain = priorityChain } - fun process(queue: Queue) { - Utils.log(TAG, "${javaClass.simpleName} process $mStatus") + fun injectQueue(queue: Queue) { + this.queue = queue + } + + fun process() { + Utils.log(TAG, "${javaClass.simpleName} process $status") - mQueue = queue // 若当前 handler 未经处理,将其状态改为 pending - if (mStatus == STATUS_UNKNOWN) { + if (status == STATUS_UNKNOWN) { updateStatus(STATUS_PENDING) } - onProcess() + if (status == STATUS_HANDLING) { + Utils.log(TAG, "${javaClass.simpleName} 已经处于执行中状态,不用再次执行") + return + } + + val isHandling = onProcess() + + if (isHandling) { + updateStatus(STATUS_HANDLING) + } } /** * 执行相关功能的地方 + * @return 是否满足执行条件并处于执行中状态 */ - abstract fun onProcess() + abstract fun onProcess(): Boolean /** * 分发给下一个 handler 处理 */ fun processNext() { - Utils.log(TAG, "${javaClass.simpleName} processNext $mStatus") + Utils.log(TAG, "${javaClass.simpleName} processNext") - mQueue?.poll()?.process(mQueue!!) + queue?.remove(this) + if (queue?.isEmpty() == true) { + priorityChain?.onHandleComplete() + } else { + queue?.peek()?.let { + it.injectQueue(queue!!) + it.process() + } + } } override fun compareTo(other: PriorityChainHandler): Int { @@ -64,6 +86,7 @@ abstract class PriorityChainHandler(private val mPriority: Int) : Comparable { - PrivacyPolicyDialogFragment.show(mActivity!!, mPrivacyPolicyEntity) { _: Boolean? -> + override fun onProcess(): Boolean { + val currentActivity = CurrentActivityHolder.getCurrentActivity() + + if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) { + when (getStatus()) { + STATUS_VALID -> { + PrivacyPolicyDialogFragment.show(currentActivity as FragmentActivity, mPrivacyPolicyEntity) { _: Boolean? -> + processNext() + } + + return true + } + + STATUS_INVALID -> { processNext() } } - STATUS_INVALID -> { - processNext() - } } + + return false } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/PullDownPushHandler.kt b/app/src/main/java/com/gh/common/prioritychain/PullDownPushHandler.kt new file mode 100644 index 0000000000..3ddf612cb4 --- /dev/null +++ b/app/src/main/java/com/gh/common/prioritychain/PullDownPushHandler.kt @@ -0,0 +1,48 @@ +package com.gh.common.prioritychain + +import com.gh.common.iinterface.ISmartRefresh + +class PullDownPushHandler(priority: Int): PriorityChainHandler(priority) { + + private var mSmartRefreshFragment: ISmartRefresh? = null + + /** + * 提前预处理显示弹窗的内容 + */ + fun doPreProcess(smartRefreshFragment: ISmartRefresh?, shouldShow: Boolean) { + mSmartRefreshFragment = smartRefreshFragment + + if (getStatus() == STATUS_PENDING) { + if (shouldShow && smartRefreshFragment != null) { + updateStatus(STATUS_VALID) + process() + } else { + processNext() + } + } else { + if (shouldShow && smartRefreshFragment != null) { + updateStatus(STATUS_VALID) + } else { + updateStatus(STATUS_INVALID) + } + } + } + + override fun onProcess(): Boolean { + when (getStatus()) { + STATUS_VALID -> { + mSmartRefreshFragment?.popupPullDownPush { + processNext() + } + + return true + } + STATUS_INVALID -> { + processNext() + } + } + + return false + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt b/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt index cb385d42b6..c24bc7ff8d 100644 --- a/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/ReserveDialogHandler.kt @@ -1,20 +1,18 @@ package com.gh.common.prioritychain -import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity import com.gh.common.dialog.ReserveDialog import com.gh.gamecenter.common.entity.SimpleGameEntity -import com.gh.gamecenter.message.MessageUnreadRepository +import com.gh.gamecenter.core.utils.CurrentActivityHolder class ReserveDialogHandler(priority: Int) : PriorityChainHandler(priority) { - private var mFragment: Fragment? = null private var mReserveData: List? = null /** * 提前预处理显示弹窗的内容 */ - fun doPreProcess(fragment: Fragment, reserveData: List?) { - mFragment = fragment + fun doPreProcess(reserveData: List?) { mReserveData = reserveData if (getStatus() == STATUS_PENDING) { @@ -22,7 +20,7 @@ class ReserveDialogHandler(priority: Int) : PriorityChainHandler(priority) { processNext() } else { updateStatus(STATUS_VALID) - onProcess() + process() } } else { if (reserveData.isNullOrEmpty()) { @@ -33,21 +31,28 @@ class ReserveDialogHandler(priority: Int) : PriorityChainHandler(priority) { } } - override fun onProcess() { - when (getStatus()) { - STATUS_VALID -> { - val reserveDialog = ReserveDialog.getInstance(mReserveData!!) - reserveDialog.setOnDismissListener { - MessageUnreadRepository.loadMessageUnreadData() + override fun onProcess(): Boolean { + val currentActivity = CurrentActivityHolder.getCurrentActivity() + + if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) { + when (getStatus()) { + STATUS_VALID -> { + val reserveDialog = ReserveDialog.getInstance(mReserveData!!) + reserveDialog.setOnDismissListener { + processNext() + } + reserveDialog.show((currentActivity as FragmentActivity).supportFragmentManager, "reserveDialog") + + return true + } + + STATUS_INVALID -> { processNext() } - reserveDialog.show(mFragment!!.childFragmentManager, "reserveDialog") - } - - STATUS_INVALID -> { - processNext() } } + + return false } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/UpdateDialogHandler.kt b/app/src/main/java/com/gh/common/prioritychain/UpdateDialogHandler.kt index 8d4061e0de..7f1ebb4ff2 100644 --- a/app/src/main/java/com/gh/common/prioritychain/UpdateDialogHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/UpdateDialogHandler.kt @@ -1,17 +1,47 @@ package com.gh.common.prioritychain -import android.content.Context -import com.gh.gamecenter.manager.UpdateManager +import androidx.fragment.app.FragmentActivity +import com.gh.gamecenter.core.utils.CurrentActivityHolder +import com.gh.gamecenter.update.UpdateHelper -class UpdateDialogHandler(context: Context, priority: Int) : PriorityChainHandler(priority) { +class UpdateDialogHandler(priority: Int) : PriorityChainHandler(priority) { - private val mUpdateManager = UpdateManager.getInstance(context) - - override fun onProcess() { - mUpdateManager.checkUpdate(true, null) - mUpdateManager.setDismissCallback { - processNext() + fun doPreProcess() { + UpdateHelper.getUpdate(false) { + if (getStatus() == STATUS_PENDING) { + if (UpdateHelper.isUpdateValid(false)) { + updateStatus(STATUS_VALID) + process() + } else { + updateStatus(STATUS_INVALID) + processNext() + } + } else { + if (UpdateHelper.isUpdateValid(false)) { + updateStatus(STATUS_VALID) + } else { + updateStatus(STATUS_INVALID) + } + } } } + override fun onProcess(): Boolean { + val currentActivity = CurrentActivityHolder.getCurrentActivity() + + if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) { + if (getStatus() == STATUS_VALID) { + UpdateHelper.showUpdateDialog(currentActivity as FragmentActivity) { + processNext() + } + + return true + } else if (getStatus() == STATUS_INVALID) { + processNext() + } + } + + return false + } + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/prioritychain/WelcomeDialogHandler.kt b/app/src/main/java/com/gh/common/prioritychain/WelcomeDialogHandler.kt index 11ce439917..ade6e6c093 100644 --- a/app/src/main/java/com/gh/common/prioritychain/WelcomeDialogHandler.kt +++ b/app/src/main/java/com/gh/common/prioritychain/WelcomeDialogHandler.kt @@ -1,32 +1,31 @@ package com.gh.common.prioritychain import android.graphics.Bitmap -import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity import com.gh.gamecenter.common.callback.BiCallback import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.core.utils.CurrentActivityHolder import com.gh.gamecenter.feature.entity.WelcomeDialogEntity -import com.gh.gamecenter.fragment.MainWrapperViewModel import com.gh.gamecenter.fragment.WelcomeDialogFragment +import com.gh.gamecenter.wrapper.MainWrapperViewModel import com.halo.assistant.HaloApp -class WelcomeDialogHandler(priority: Int): PriorityChainHandler(priority) { +class WelcomeDialogHandler(priority: Int) : PriorityChainHandler(priority) { - private var mFragment: Fragment? = null - private var mWelcomeDialogEntity: WelcomeDialogEntity? = null + private var welcomeDialogEntity: WelcomeDialogEntity? = null - fun doPreProcess(fragment: Fragment, welcomeDialogEntity: WelcomeDialogEntity?) { - mFragment = fragment - mWelcomeDialogEntity = welcomeDialogEntity + fun doPreProcess(welcomeDialogEntity: WelcomeDialogEntity?) { + this.welcomeDialogEntity = welcomeDialogEntity val preLoadClosure = { // 判断启动本次应用是否已经弹窗,不是的话弹启动弹窗 if (HaloApp.get(MainWrapperViewModel.SHOULD_SHOW_OPENING_DIALOG, false) == null) { HaloApp.put(MainWrapperViewModel.SHOULD_SHOW_OPENING_DIALOG, false) - ImageUtils.getBitmap(mWelcomeDialogEntity!!.icon, object : BiCallback { + ImageUtils.getBitmap(this.welcomeDialogEntity!!.icon, object : BiCallback { override fun onFirst(first: Bitmap) { if (getStatus() == STATUS_PENDING) { updateStatus(STATUS_VALID) - onProcess() + process() } else { updateStatus(STATUS_VALID) } @@ -56,24 +55,28 @@ class WelcomeDialogHandler(priority: Int): PriorityChainHandler(priority) { } } - override fun onProcess() { - when (getStatus()) { - STATUS_VALID -> { - if (mFragment == null || !mFragment!!.isAdded) { - updateStatus(STATUS_INVALID) - processNext() - } else { - val welcomeDialog = WelcomeDialogFragment.getInstance(mWelcomeDialogEntity) + override fun onProcess(): Boolean { + val currentActivity = CurrentActivityHolder.getCurrentActivity() + + if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) { + when (getStatus()) { + STATUS_VALID -> { + val welcomeDialog = WelcomeDialogFragment.getInstance(welcomeDialogEntity) welcomeDialog.setOnDismissListener { processNext() } - welcomeDialog.show(mFragment!!.childFragmentManager, "WelcomeDialog") + welcomeDialog.show((currentActivity as FragmentActivity).supportFragmentManager, "WelcomeDialog") + + return true + } + + STATUS_INVALID -> { + processNext() } } - STATUS_INVALID -> { - processNext() - } } + + return false } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/provider/BindingAdaptersProviderImpl.kt b/app/src/main/java/com/gh/common/provider/BindingAdaptersProviderImpl.kt index 9377ed2d79..17636e69ca 100644 --- a/app/src/main/java/com/gh/common/provider/BindingAdaptersProviderImpl.kt +++ b/app/src/main/java/com/gh/common/provider/BindingAdaptersProviderImpl.kt @@ -16,9 +16,8 @@ class BindingAdaptersProviderImpl : IBindingAdaptersProvider { view: TextView, game: GameEntity, isShowPlatform: Boolean, - isShowSuffix: Boolean ) { - BindingAdapters.setGameName(view, game, isShowPlatform, isShowSuffix) + BindingAdapters.setGameName(view, game, isShowPlatform) } override fun setGameTags(layout: LinearLayout, gameEntity: GameEntity) { diff --git a/app/src/main/java/com/gh/common/provider/ConfigProviderImpl.kt b/app/src/main/java/com/gh/common/provider/ConfigProviderImpl.kt index 1534e4fb94..676f776c57 100644 --- a/app/src/main/java/com/gh/common/provider/ConfigProviderImpl.kt +++ b/app/src/main/java/com/gh/common/provider/ConfigProviderImpl.kt @@ -100,10 +100,6 @@ class ConfigProviderImpl : IConfigProvider { return Config.getNightModeSetting()?.setting ?: false } - override fun isShowPlugin(gameId: String): Boolean { - return Config.isShowPlugin(gameId) - } - override fun init(context: Context?) { // Do nothing } diff --git a/app/src/main/java/com/gh/common/provider/DownloadButtonClickedProviderImpl.kt b/app/src/main/java/com/gh/common/provider/DownloadButtonClickedProviderImpl.kt index 4a67de59ee..f0bcd19533 100644 --- a/app/src/main/java/com/gh/common/provider/DownloadButtonClickedProviderImpl.kt +++ b/app/src/main/java/com/gh/common/provider/DownloadButtonClickedProviderImpl.kt @@ -9,6 +9,7 @@ import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.entity.GameUpdateEntity +import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.provider.IDownloadButtonClickedProvider @@ -29,6 +30,7 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider { var gameSchemaType = "" var packageName = "" var exposureSourceList: List? = null + var customPageTrackData: CustomPageTrackData? = null val boundedObject = downloadButton.getObject() @@ -55,6 +57,7 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider { gameSchemaType = boundedObject.gameBitChinese packageName = boundedObject.getUniquePackageName() ?: "" exposureSourceList = boundedObject.exposureEvent?.source + customPageTrackData = boundedObject.customPageTrackData } is GameUpdateEntity -> { @@ -80,11 +83,13 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider { // 上报 UI 状态为启动的点击事件 (样式为启动,或者文案包含启动都算能启动) if (downloadButton.buttonStyle == DownloadButton.ButtonStyle.LAUNCH_OR_OPEN - || downloadButton.text.contains("启动")) { + || downloadButton.text.contains("启动") + ) { // boundedObject 里找不到游戏类型时,尝试从已安装列表中获取 if (gameCategory.isEmpty() && packageName.isNotEmpty()) { - gameCategory = PackageRepository.gameInstalled.find { it.packageName == packageName }?.category ?: "" + gameCategory = + PackageRepository.gameInstalled.find { it.packageName == packageName }?.category ?: "" } NewFlatLogUtils.logGameLaunchButtonClicked( @@ -98,7 +103,8 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider { // 预约状态不上报 if (downloadButton.buttonStyle == DownloadButton.ButtonStyle.NORMAL - || downloadButton.buttonStyle == DownloadButton.ButtonStyle.PLUGIN) { + || downloadButton.buttonStyle == DownloadButton.ButtonStyle.PLUGIN + ) { val text = downloadButton.text.ifEmpty { downloadButton.getTag(R.string.download) ?: "" @@ -111,6 +117,7 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider { } // 上报神策点击事件 + val customPageKV = customPageTrackData?.toKV() ?: arrayOf() SensorsBridge.trackEventWithExposureSource( "DownLoadbuttonClick", exposureSourceList, @@ -127,6 +134,7 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider { "last_page_name", GlobalActivityManager.getLastPageEntity().pageName, "last_page_id", GlobalActivityManager.getLastPageEntity().pageId, "last_page_business_id", GlobalActivityManager.getLastPageEntity().pageBusinessId, + *customPageKV ) } } diff --git a/app/src/main/java/com/gh/common/provider/GameSubstituteRepositoryProviderImpl.kt b/app/src/main/java/com/gh/common/provider/GameSubstituteRepositoryProviderImpl.kt deleted file mode 100644 index 730d4638f8..0000000000 --- a/app/src/main/java/com/gh/common/provider/GameSubstituteRepositoryProviderImpl.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.gh.common.provider - -import android.content.Context -import com.alibaba.android.arouter.facade.annotation.Route -import com.gh.common.util.GameSubstituteRepositoryHelper -import com.gh.gamecenter.common.constant.RouteConsts -import com.gh.gamecenter.core.provider.IGameSubstituteRepositoryProvider - -@Route(path = RouteConsts.provider.gameSubstituteRepository, name = "GameSubstituteRepositoryHelper暴露服务") -class GameSubstituteRepositoryProviderImpl : IGameSubstituteRepositoryProvider { - - override fun updateHistoricInstalledGameSet() { - GameSubstituteRepositoryHelper.updateHistoricInstalledGameSet() - } - - override fun init(context: Context?) { - // Do nothing - } -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/provider/MainProviderImpl.kt b/app/src/main/java/com/gh/common/provider/MainProviderImpl.kt index c84cb7337d..a2ea2a0f06 100644 --- a/app/src/main/java/com/gh/common/provider/MainProviderImpl.kt +++ b/app/src/main/java/com/gh/common/provider/MainProviderImpl.kt @@ -2,14 +2,15 @@ package com.gh.common.provider import android.content.Context import com.alibaba.android.arouter.facade.annotation.Route +import com.gh.common.util.DirectUtils import com.gh.gamecenter.MainActivity import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.core.provider.IMainProvider @Route(path = RouteConsts.provider.mainActivity, name = "MainActivity暴露服务") class MainProviderImpl : IMainProvider { - override fun skipToMainActivity(context: Context, position: Int) { - MainActivity.skipToMainActivity(context, position) + override fun skipToMainActivity(context: Context, type: String) { + DirectUtils.directToHomeDefaultTab(context) } override fun init(context: Context?) { diff --git a/app/src/main/java/com/gh/common/provider/UpdateManagerProviderImpl.kt b/app/src/main/java/com/gh/common/provider/UpdateManagerProviderImpl.kt index f4d75ab36b..1dd72c10f0 100644 --- a/app/src/main/java/com/gh/common/provider/UpdateManagerProviderImpl.kt +++ b/app/src/main/java/com/gh/common/provider/UpdateManagerProviderImpl.kt @@ -1,16 +1,30 @@ package com.gh.common.provider import android.content.Context -import android.os.Handler +import androidx.fragment.app.FragmentActivity import com.alibaba.android.arouter.facade.annotation.Route +import com.gh.common.util.DialogUtils import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.core.provider.IUpdateManagerProvider -import com.gh.gamecenter.manager.UpdateManager +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.update.UpdateHelper @Route(path = RouteConsts.provider.updateManager, name = "UpdateManager暴露服务") class UpdateManagerProviderImpl: IUpdateManagerProvider { - override fun checkUpdate(context: Context, isAutoCheck: Boolean, handler: Handler?) { - UpdateManager.getInstance(context).checkUpdate(isAutoCheck, handler) + override fun checkUpdate(activity: FragmentActivity, ignoreSuppressOption: Boolean) { + val dialog = DialogUtils.showWaitDialog(activity, "检查更新中...") + + UpdateHelper.getUpdate(ignoreSuppressOption) { + dialog.dismiss() + + if (UpdateHelper.isUpdateValid(ignoreSuppressOption)) { + UpdateHelper.showUpdateDialog(activity) { + // Do nothing + } + } else { + ToastUtils.toast("您的光环助手已是最新版本") + } + } } override fun init(context: Context?) { diff --git a/app/src/main/java/com/gh/common/util/DataUtils.java b/app/src/main/java/com/gh/common/util/DataUtils.java index 04fbfd7304..51380fb117 100644 --- a/app/src/main/java/com/gh/common/util/DataUtils.java +++ b/app/src/main/java/com/gh/common/util/DataUtils.java @@ -129,11 +129,6 @@ public class DataUtils { AdDelegateHelper.INSTANCE.requestAdConfig(false, "", null); }); - // 避免重复调用 - if (!TextUtils.isEmpty(gid)) { - GameSubstituteRepositoryHelper.updateHistoricInstalledGameSet(); - } - getDeviceCertification(gid); // 避免初始化顺序问题导致 MetaUtil 一直持有空的 gid @@ -203,9 +198,8 @@ public class DataUtils { RealNameHelper.updateCertificationStatus(0); } - EventBus.getDefault().post(new EBReuse(Constants.EB_REALNAME_RESULT)); + RealNameHelper.INSTANCE.onRealNameInfoUpdated(); -// new GhContentProvider().localInsert( HaloApp.getInstance().getApplication(),values); try { // Unknown URL content://com.gh.gamecenter.provider/certification // TODO 将 com.gh.gamecenter 改成 BuildConfig.ApplicationID diff --git a/app/src/main/java/com/gh/common/util/DefaultSearchHintHelper.kt b/app/src/main/java/com/gh/common/util/DefaultSearchHintHelper.kt new file mode 100644 index 0000000000..1433a5661d --- /dev/null +++ b/app/src/main/java/com/gh/common/util/DefaultSearchHintHelper.kt @@ -0,0 +1,80 @@ +package com.gh.common.util + +import android.view.animation.AlphaAnimation +import android.view.animation.Animation +import android.view.animation.ScaleAnimation +import android.widget.TextView +import com.gh.common.constant.Config + +/** + * 光环游戏搜索栏默认文案轮换 + */ +object DefaultSearchHintHelper { + private val hintList = ArrayList() + + init { + loadData() + } + + fun loadData() { + hintList.clear() + Config.getSettings()?.search?.defaultData?.also { + if (it.isNotEmpty()) { + for (h in it) { + hintList.add(h.removeSuffix(".")) + } + } + } + } + + fun setSearchHint(searchTv: TextView) { + var hintIndex = 0 + val scaleAnimation = ScaleAnimation( + 0.4F, 1.0F, 0.4F, 1.0F, + Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F + ) + scaleAnimation.duration = 500 + val alphaAnimation = AlphaAnimation(1F, 0.2F) + alphaAnimation.duration = 300 + alphaAnimation.startOffset = 5000 + scaleAnimation.setAnimationListener(object : Animation.AnimationListener { + override fun onAnimationStart(animation: Animation) {} + override fun onAnimationEnd(animation: Animation) { + searchTv.animation = alphaAnimation + alphaAnimation.start() + } + + override fun onAnimationRepeat(animation: Animation) {} + }) + alphaAnimation.setAnimationListener(object : Animation.AnimationListener { + override fun onAnimationStart(animation: Animation) {} + override fun onAnimationEnd(animation: Animation) { + // 切换数据 + if (hintList.isNotEmpty()) { + if (hintIndex > hintList.size - 1) { + hintIndex = 0 + } + searchTv.hint = hintList[hintIndex] + hintIndex++ + searchTv.animation = scaleAnimation + scaleAnimation.start() + } + } + + override fun onAnimationRepeat(animation: Animation) {} + }) + + if (hintList.isEmpty()) { + loadData() + } + + if (hintList.isNotEmpty()) { + if (hintList.size > 1) { + searchTv.animation = alphaAnimation + } else { + val hint = hintList[0] + searchTv.hint = hint + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java b/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java index 20234c4bde..07037c43cf 100644 --- a/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java +++ b/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java @@ -367,8 +367,7 @@ public class DetailDownloadUtils { String downloadAddWord = gameEntity.getDownloadAddWord(); // 不满足条件的部分游戏隐藏下载按钮 - if (Config.isShowDownload(gameEntity.getId()) - && !"光环助手".equals(gameEntity.getName())) { + if (!"光环助手".equals(gameEntity.getName())) { viewHolder.getDownloadBottom().setVisibility(View.VISIBLE); } else { viewHolder.getDownloadBottom().setVisibility(View.GONE); 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 bc9034bf30..9fe8745528 100644 --- a/app/src/main/java/com/gh/common/util/DirectUtils.kt +++ b/app/src/main/java/com/gh/common/util/DirectUtils.kt @@ -33,29 +33,22 @@ import com.gh.gamecenter.common.entity.* import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.provider.IQGameProvider -import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.discovery.DiscoveryActivity import com.gh.gamecenter.download.DownloadFragment.Companion.INDEX_UPDATE -import com.gh.gamecenter.entity.GameCollectionListEntity -import com.gh.gamecenter.entity.SubjectData -import com.gh.gamecenter.entity.SubjectRecommendEntity -import com.gh.gamecenter.entity.VideoLinkEntity -import com.gh.gamecenter.eventbus.EBSkip +import com.gh.gamecenter.entity.* import com.gh.gamecenter.feature.entity.GameDetailServer import com.gh.gamecenter.feature.entity.GameEntity -import com.gh.gamecenter.feature.entity.LibaoEntity import com.gh.gamecenter.feature.entity.MeEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.exposure.ExposureEvent.Companion.createEvent import com.gh.gamecenter.feature.exposure.ExposureType import com.gh.gamecenter.feature.provider.IConcernInfoProvider import com.gh.gamecenter.forum.detail.ForumDetailActivity -import com.gh.gamecenter.forum.home.CommunityHomeFragment +import com.gh.gamecenter.forum.home.CommunityActivity import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity -import com.gh.gamecenter.fragment.MainWrapperFragment import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailActivity -import com.gh.gamecenter.game.commoncollection.detail.CommonCollectionDetailActivity +import com.gh.gamecenter.game.commoncollection.detail.CustomCommonCollectionDetailActivity import com.gh.gamecenter.game.upload.GameSubmissionActivity import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity import com.gh.gamecenter.gamecollection.hotlist.GameCollectionHotListActivity @@ -68,7 +61,9 @@ import com.gh.gamecenter.gamedetail.fuli.kaifu.ServersSubscribedGameListActivity import com.gh.gamecenter.gamedetail.history.HistoryApkListActivity import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity import com.gh.gamecenter.help.HelpAndFeedbackBridge +import com.gh.gamecenter.libao.LibaoDetailActivity import com.gh.gamecenter.login.user.UserManager +import com.gh.gamecenter.newsdetail.NewsDetailActivity import com.gh.gamecenter.personalhome.UserHomeActivity import com.gh.gamecenter.personalhome.background.PersonalityBackgroundActivity import com.gh.gamecenter.personalhome.border.AvatarBorderActivity @@ -88,19 +83,20 @@ import com.gh.gamecenter.servers.gametest2.GameServerTestV2Activity import com.gh.gamecenter.setting.SettingBridge import com.gh.gamecenter.subject.SubjectActivity import com.gh.gamecenter.tag.TagsActivity -import com.gh.gamecenter.toolbox.ToolBoxBlockActivity +import com.gh.gamecenter.toolbox.ToolBoxActivity import com.gh.gamecenter.video.data.VideoDataActivity import com.gh.gamecenter.video.detail.VideoDetailActivity import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel import com.gh.gamecenter.video.game.GameVideoActivity import com.gh.gamecenter.video.videomanager.VideoManagerActivity +import com.gh.gamecenter.wrapper.MainWrapperRepository +import com.gh.gamecenter.wrapper.ToolbarWrapperActivity import com.gh.vspace.VDownloadManagerActivity import com.halo.assistant.HaloApp import com.halo.assistant.fragment.WebFragment import com.lightgame.utils.Utils import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers -import org.greenrobot.eventbus.EventBus import retrofit2.HttpException import java.net.URLEncoder import java.util.* @@ -160,7 +156,21 @@ object DirectUtils { "explore_column", "game_explore", "column_test_v2", - "game_list_collection" + "game_list_collection", + "multi_tab_nav", + "custom_page", + "bottom_tab", + "qq_mini_game_column_detail", + "game_hot_list", + "libao_center", + "article_center", + "video_stream", + "libao", + "community_home", + "bbs_section", + "qa", + "feedback", + "toolkit" ) fun directToLinkPage( @@ -185,6 +195,7 @@ object DirectUtils { if (exposureEvent != null) { directToGameDetail( context, linkEntity.link + ?: "", linkEntity.text ?: "", BaseActivity.mergeEntranceAndPath(entrance, path), traceEvent = exposureEvent ) } else { @@ -299,6 +310,7 @@ object DirectUtils { "", linkEntity.blockId, linkEntity.blockName, + "", exposureEvent ) @@ -308,7 +320,7 @@ object DirectUtils { "wechat_bind" -> context.startActivity(WebActivity.getBindWechatIntent(context)) - "video", "bbs_video", "video_stream", "视频" -> directToVideoDetail( + "video", "bbs_video", "视频" -> directToVideoDetail( context, videoId = linkEntity.link!!, fromLocation = VideoDetailContainerViewModel.Location.VIDEO_CHOICENESS.value, @@ -317,6 +329,14 @@ object DirectUtils { sourceEntrance = sourceEntrance ) + "video_stream" -> directToLegacyVideoDetail( + context, + "", + VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value, + referer = "视频流-$entrance", + isHomeVideo = true + ) + "game_video" -> directToGameVideo(context, linkEntity.link ?: "", entrance, path) "libao", "礼包" -> directToGiftDetail(context, linkEntity.link ?: "", entrance) @@ -387,17 +407,17 @@ object DirectUtils { "setting" -> context.startActivity(SettingBridge.getSettingIntent(context, false, entrance)) - "index_page" -> directToHomeTab(context) + "index_page" -> directToHomeDefaultTab(context) "video_upload" -> context.startActivity(VideoManagerActivity.getIntent(context, "", entrance)) - "bbs" -> directToForum(context) + "bbs", "community_home" -> directToHomeCommunityTab(context) "user_page" -> directToHomeActivity(context, UserManager.getInstance().userId, "", entrance) - "video_tab" -> directToVideoTab(context) + "video_tab" -> directToHomeVideoTab(context) - "toolkit" -> context.startActivity(ToolBoxBlockActivity.getIntent(context, entrance)) + "toolkit" -> context.startActivity(ToolBoxActivity.getIntent(context, entrance)) "column_test" -> context.startActivity( GameServerTestActivity.getIntent( @@ -418,7 +438,7 @@ object DirectUtils { } ?: "" } - "halo_tab" -> directToPersonalTab(context) + "halo_tab" -> directToHomeMyHaloTab(context) "common_collection" -> directToCommonCollectionDetail( context, @@ -457,6 +477,29 @@ object DirectUtils { "qq_mini_game_column" -> directToQGameHome(context) + // QQ游戏专题详情页 + "qq_mini_game_column_detail" -> { + directToSubject( + context = context, + id = linkEntity.link ?: "", + subjectName = linkEntity.text, + entrance = BaseActivity.mergeEntranceAndPath(entrance, path), + exposureEvent = exposureEvent, + isQQMiniGame = true + ) + } + + // 资讯中心 + "article_center" -> { + context.startActivity(InfoActivity.getIntent(context)) + } + // 礼包中心 + "libao_center" -> { + directToGift(context, entrance) + } + + "game_hot_list" -> directToGameCollectionHotList(context, entrance) + "game_list_collection" -> directToGameCollectionListDetail( context, linkEntity.link ?: "", @@ -465,6 +508,34 @@ object DirectUtils { entrance ) + "multi_tab_nav" -> context.startActivity( + ToolbarWrapperActivity.getMultiTabNavIntent( + context, + linkEntity.link ?: "", + linkEntity.text ?: "" + ) + ) + + "custom_page" -> context.startActivity( + ToolbarWrapperActivity.getCustomPageIntent( + context, + linkEntity.link ?: "", + linkEntity.text ?: "" + ) + ) + + // 选中首页底部 tab + "bottom_tab" -> { + val intent = Intent(context, MainActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + } + context.startActivity(intent) + + if (linkEntity is LaunchRedirect) { + MainWrapperRepository.getInstance().sendSelectTabEvent(linkEntity) + } + } + "" -> { // do nothing } @@ -519,7 +590,9 @@ object DirectUtils { columnName: String = "", blockId: String = "", blockName: String = "", - exposureEvent: ExposureEvent? = null + style: String = "", + exposureEvent: ExposureEvent? = null, + showSubjectTab: Boolean = false ) { if (id.isEmpty()) return val bundle = Bundle() @@ -529,7 +602,9 @@ object DirectUtils { bundle.putString(KEY_COLUMNNAME, columnName) bundle.putString(KEY_BLOCK_ID, blockId) bundle.putString(KEY_BLOCK_NAME, blockName) + bundle.putString(KEY_COLUMN_COLLECTION_STYLE, style) bundle.putInt(KEY_POSITION, position) + bundle.putBoolean(KEY_SHOW_SUBJECT_TAB, showSubjectTab) if (exposureEvent != null) { bundle.putParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST, ArrayList(exposureEvent.source)) } @@ -584,7 +659,7 @@ object DirectUtils { if (id.isEmpty()) return val bundle = Bundle() bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER) - bundle.putString(KEY_TO, NewsDetailActivity::class.java.simpleName) + bundle.putString(KEY_TO, NewsDetailActivity::class.java.name) bundle.putString(KEY_NEWSID, id) if (entrance?.contains("隐私政策") == true) { bundle.putBoolean(KEY_HIDE_USELESS_INFO, true) @@ -600,7 +675,7 @@ object DirectUtils { if (id.isEmpty()) return val bundle = Bundle() bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER) - bundle.putString(KEY_TO, NewsDetailActivity::class.java.simpleName) + bundle.putString(KEY_TO, NewsDetailActivity::class.java.name) bundle.putString(KEY_NEWSID, id) bundle.putBoolean(KEY_HIDE_USELESS_INFO, hideUselessInfo) jumpActivity(context, bundle) @@ -679,6 +754,7 @@ object DirectUtils { fun directToGameDetail( context: Context, id: String, + name: String = "", entrance: String? = null, autoDownload: Boolean? = null, tab: String? = "", @@ -698,7 +774,7 @@ object DirectUtils { } } if (traceEvent != null) { - val clickEvent = createEvent(GameEntity(id), traceEvent.source, appendTrace(traceEvent), ExposureType.CLICK) + val clickEvent = createEvent(GameEntity(id = id, name = name), traceEvent.source, appendTrace(traceEvent), ExposureType.CLICK) log(clickEvent) bundle.putParcelable(KEY_TRACE_EVENT, clickEvent) } @@ -852,6 +928,31 @@ object DirectUtils { jumpActivity(context, bundle) } + @JvmStatic + fun directToDownloadManager( + context: Context, + entrance: String = "", + bottomTab: String = "", + gameColumnId: String = "", + gameColumnName: String = "", + customPageId: String = "", + customPageName: String = "", + multiTabId: String = "", + multiTabName: String = "" + ) { + val bundle = Bundle() + bundle.putString(KEY_ENTRANCE, entrance) + bundle.putString(KEY_TO, DownloadManagerActivity.TAG) + bundle.putString(KEY_BOTTOM_TAB_NAME, bottomTab) + bundle.putString(KEY_GAME_COLUMN_ID, gameColumnId) + bundle.putString(KEY_GAME_COLUMN_NAME, gameColumnName) + bundle.putString(KEY_CUSTOM_PAGE_ID, customPageId) + bundle.putString(KEY_CUSTOM_PAGE_NAME, customPageName) + bundle.putString(KEY_MULTI_TAB_NAV_ID, multiTabId) + bundle.putString(KEY_MULTI_TAB_NAV_NAME, multiTabName) + jumpActivity(context, bundle) + } + @JvmStatic fun directToDownloadManagerAndStartUpdate( context: Context, @@ -872,7 +973,7 @@ object DirectUtils { fun directToToolbox(context: Context, gameId: String, toolboxUrl: String, entrance: String = ENTRANCE_BROWSER) { val bundle = Bundle() bundle.putString(KEY_ENTRANCE, entrance) - bundle.putString(KEY_TO, ToolBoxBlockActivity::class.java.name) + bundle.putString(KEY_TO, ToolBoxActivity::class.java.name) bundle.putString(KEY_GAMEID, gameId) bundle.putString(KEY_URL, toolboxUrl) jumpActivity(context, bundle) @@ -996,12 +1097,7 @@ object DirectUtils { @JvmStatic fun directToGiftDetail(context: Context, giftId: String, entrance: String? = null) { if (giftId.isEmpty()) return - val bundle = Bundle() - bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER) - bundle.putString(KEY_TO, LibaoDetailActivity::class.java.simpleName) - bundle.putString(EntranceConsts.KEY_ID, giftId) - HaloApp.put(LibaoEntity.TAG, null) - jumpActivity(context, bundle) + context.startActivity(LibaoDetailActivity.getIntentById(context, giftId, entrance)) } /** @@ -1018,21 +1114,7 @@ object DirectUtils { */ @JvmStatic fun directToCommunity(context: Context, community: CommunityEntity?) { -// if (MainActivity::class.java.name != RunningUtils.getTopActivity(context)) { -// val intent = Intent(context, MainActivity::class.java) -// intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP -// context.startActivity(intent) -// -// UserManager.getInstance().setCommunityData(community) -// -// // 这里换个线程操作是为了做一点延时 -// AppExecutor.ioExecutor.execute { -// EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_BBS)) -// EventBus.getDefault().post(EBReuse(CommunityFragment.EB_RETRY_PAGE)) -// } -// } else { directForumDetail(context, community?.id) -// } } @JvmStatic @@ -1474,85 +1556,61 @@ object DirectUtils { } /** - * 到首页-首页 tab + * 到首页-默认选中的 tab */ @JvmStatic - fun directToHomeTab(context: Context) { + fun directToHomeDefaultTab(context: Context) { if (HaloApp.getInstance().isRunningForeground) { val intent = Intent(context, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP context.startActivity(intent) - // 这里换个线程操作是为了做一点延时 - runOnIoThread { - EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_HOME)) - } + MainWrapperRepository.getInstance().sendReSelectDefaultTabEvent() } else { - jumpActivity(context, Bundle().apply { putInt(KEY_POSITION, MainWrapperFragment.INDEX_HOME) }) + jumpActivity(context, Bundle()) } } - /** - * 到首页-论坛 tab - * @param position 论坛的子 tab 位置 + * 到首页-社区 tab + * @param position 社区子 tab 位置 */ @JvmStatic - fun directToForum(context: Context, position: Int = 0) { - if (HaloApp.getInstance().isRunningForeground) { - val intent = Intent(context, MainActivity::class.java) - intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP - context.startActivity(intent) - - // 这里换个线程操作是为了做一点延时 - runOnIoThread { - EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_BBS)) - EventBus.getDefault().post(EBSkip(CommunityHomeFragment.EB_TAB, position)) - } - } else { - jumpActivity(context, Bundle().apply { - putInt(KEY_POSITION, MainWrapperFragment.INDEX_BBS) - putInt(KEY_SUB_POSITION, position) - }) - } + fun directToHomeCommunityTab(context: Context) { + val bundle = Bundle() + bundle.putString(KEY_TO, CommunityActivity::class.java.name) + jumpActivity(context, bundle) } /** * 到首页-视频 tab */ @JvmStatic - fun directToVideoTab(context: Context) { + fun directToHomeVideoTab(context: Context) { if (HaloApp.getInstance().isRunningForeground) { val intent = Intent(context, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP context.startActivity(intent) - - // 这里换个线程操作是为了做一点延时 - runOnIoThread { - EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_VIDEO)) - } } else { - jumpActivity(context, Bundle().apply { putInt(KEY_POSITION, MainWrapperFragment.INDEX_VIDEO) }) + jumpActivity(context, Bundle()) } + + MainWrapperRepository.getInstance().sendSelectTabEvent(ViewPagerFragmentHelper.TYPE_VIDEO_STREAM) } /** * 到首页-我的光环 tab */ - @JvmStatic - fun directToPersonalTab(context: Context) { + fun directToHomeMyHaloTab(context: Context) { if (HaloApp.getInstance().isRunningForeground) { val intent = Intent(context, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP context.startActivity(intent) - - // 这里换个线程操作是为了做一点延时 - runOnIoThread { - EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_PERSONAL)) - } } else { - jumpActivity(context, Bundle().apply { putInt(KEY_POSITION, MainWrapperFragment.INDEX_PERSONAL) }) + jumpActivity(context, Bundle()) } + + MainWrapperRepository.getInstance().sendSelectTabEvent(ViewPagerFragmentHelper.TYPE_MY_HALO) } /** @@ -1851,7 +1909,7 @@ object DirectUtils { ) { if (collectionId.isEmpty()) return val bundle = Bundle() - bundle.putString(KEY_TO, CommonCollectionDetailActivity::class.java.name) + bundle.putString(KEY_TO, CustomCommonCollectionDetailActivity::class.java.name) bundle.putString(KEY_BLOCK_ID, blockId) bundle.putString(KEY_BLOCK_NAME, blockName) bundle.putString(KEY_ENTRANCE, entrance) @@ -1989,15 +2047,35 @@ object DirectUtils { bbsId: String, entrance: String, sourceEntrance: String, - forumName: String + forumName: String, + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + customPageId: String = "", + customPageName: String = "", + searchBoxPattern: String = "" ) { - context.startActivity(ForumOrUserSearchActivity.getIntent(context, bbsId, entrance, sourceEntrance, forumName)) + context.startActivity( + ForumOrUserSearchActivity.getIntent( + context, + bbsId, + entrance, + sourceEntrance, + forumName, + bottomTab, + multiTabId, + multiTabName, + customPageId, + customPageName, + searchBoxPattern + ) + ) } @JvmStatic - fun directToVGameDownload(context: Context, switchToDownloadingTab: Boolean = false) { + fun directToVGameDownload(context: Context, entrance: String, switchToDownloadingTab: Boolean = false) { val bundle = Bundle() - bundle.putString(KEY_ENTRANCE, ENTRANCE_BROWSER) + bundle.putString(KEY_ENTRANCE, entrance) bundle.putString(KEY_TO, VDownloadManagerActivity::class.java.name) bundle.putInt(KEY_POSITION, if (switchToDownloadingTab) 1 else 0) jumpActivity(context, bundle) @@ -2065,9 +2143,27 @@ object DirectUtils { fun directToQGameSearch( context: Context, hint: String, - sourceEntrance: String + sourceEntrance: String, + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + customPageId: String = "", + customPageName: String = "", + searchBoxPattern: String = "" ) { - context.startActivity(QGameSearchActivity.getIntent(context, hint, sourceEntrance)) + context.startActivity( + QGameSearchActivity.getIntent( + context, + hint, + sourceEntrance, + bottomTab, + multiTabId, + multiTabName, + customPageId, + customPageName, + searchBoxPattern + ) + ) } @SuppressLint("CheckResult") @@ -2160,4 +2256,62 @@ object DirectUtils { context.startActivity(ServersCalendarManagementActivity.getIntent(context)) } } + + fun directToSearch( + context: Context, + searchType: String, + searchHint: String, + entrance: String, + sourceEntrance: String, + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + customPageId: String = "", + customPageName: String = "", + searchBoxPattern: String = "" + ) { + when (searchType) { + BottomTab.SearchStyle.TYPE_HALO_GAME -> context.startActivity( + SearchActivity.getIntent( + context, + false, + searchHint, + entrance, + sourceEntrance, + bottomTab, + multiTabId, + multiTabName, + customPageId, + customPageName, + searchBoxPattern + ) + ) + + BottomTab.SearchStyle.TYPE_QQ_MINI_GAME -> directToQGameSearch( + context, + "请输入小游戏关键词", + sourceEntrance, + bottomTab, + multiTabId, + multiTabName, + customPageId, + customPageName, + searchBoxPattern + ) + + BottomTab.SearchStyle.TYPE_BBS -> directToForumOrUserSearch( + context, + "", + entrance, + sourceEntrance, + "", + bottomTab, + multiTabId, + multiTabName, + customPageId, + customPageName, + searchBoxPattern + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt index 812e2a1263..c9a3a8aa83 100644 --- a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt +++ b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt @@ -6,6 +6,7 @@ import android.text.TextUtils import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.collection.ArrayMap +import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import com.gh.common.chain.* import com.gh.common.constant.Config @@ -199,7 +200,7 @@ object DownloadItemUtils { holder.gameDownloadTips?.visibility = View.GONE updateNormalItem(context, holder, gameEntity, briefStyle, isShowRecommendStar) } else { - holder.multiVersionDownloadTv?.visibility = View.VISIBLE + holder.multiVersionDownloadTv?.isVisible = !SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) updatePluginItem(context, holder, gameEntity, briefStyle, isShowRecommendStar) } updateDownloadButton( @@ -219,7 +220,7 @@ object DownloadItemUtils { pluginLocation: PluginLocation? = PluginLocation.only_game ) { // 控制是否显示下载按钮 - downloadBtn.goneIf(!Config.isShowDownload(gameEntity.id) || context.getString(R.string.app_name) == gameEntity.name) + downloadBtn.goneIf(context.getString(R.string.app_name) == gameEntity.name) // 青少年模式或者需要特殊处理显示查看 if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) || gameEntity.isSpecialDownload()) { downloadBtn.text = "查看" @@ -443,6 +444,7 @@ object DownloadItemUtils { briefStyle: String?, isShowRecommendStar: Boolean = false ) { + updateItemViewStatus(holder, briefStyle, gameEntity.columnRecommend, isShowRecommendStar) val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity) if (downloadEntity != null) { if (downloadEntity.isSimulatorGame()) { @@ -457,7 +459,6 @@ object DownloadItemUtils { return } } - updateItemViewStatus(holder, briefStyle, gameEntity.columnRecommend, isShowRecommendStar) } // 更新插件的条目,有多个apk包 @@ -776,8 +777,8 @@ object DownloadItemUtils { return } if (gameEntity.isReservable) { - if (!ReservationRepository.thisGameHasBeenReserved(gameEntity.id)) { - downloadBtn.setOnClickListener { + downloadBtn.setOnClickListener { + if (!ReservationRepository.thisGameHasBeenReserved(gameEntity.id)) { SensorsBridge.trackEvent( "AppointmentGame", "game_name", @@ -802,9 +803,7 @@ object DownloadItemUtils { refreshCallback?.onCallback() } } - } - } else { - downloadBtn.setOnClickListener { + } else { allStateClickCallback?.onCallback() clickCallback?.onCallback() if ("download" == gameEntity.reserveStatus) { @@ -912,7 +911,6 @@ object DownloadItemUtils { DownloadChainBuilder().apply { addHandler(GamePermissionHandler()) addHandler(CheckStoragePermissionHandler()) - addHandler(CertificationHandler()) addHandler(VersionNumberHandler()) } .setProcessEndCallback { _, _ -> @@ -954,7 +952,6 @@ object DownloadItemUtils { addHandler(BrowserInstallHandler()) addHandler(PackageCheckHandler()) addHandler(DownloadDialogHelperHandler()) - addHandler(CertificationHandler()) addHandler(OverseaDownloadHandler()) addHandler(LandPageAddressHandler()) addHandler(CheckDownloadHandler()) @@ -973,7 +970,6 @@ object DownloadItemUtils { addHandler(BrowserInstallHandler()) addHandler(PackageCheckHandler()) addHandler(DownloadDialogHelperHandler()) - addHandler(CertificationHandler()) addHandler(VersionNumberHandler()) addHandler(LandPageAddressHandler()) addHandler(OverseaDownloadHandler()) @@ -991,7 +987,6 @@ object DownloadItemUtils { addHandler(GamePermissionHandler()) addHandler(PackageCheckHandler()) addHandler(DownloadDialogHelperHandler()) - addHandler(CertificationHandler()) addHandler(VersionNumberHandler()) addHandler(LandPageAddressHandler()) addHandler(OverseaDownloadHandler()) @@ -1010,7 +1005,6 @@ object DownloadItemUtils { } else { DownloadChainBuilder().apply { addHandler(DownloadDialogHelperHandler()) - addHandler(CertificationHandler()) addHandler(CheckDownloadHandler()) } .setProcessEndCallback { _, isSubscribe -> diff --git a/app/src/main/java/com/gh/common/util/DownloadObserver.kt b/app/src/main/java/com/gh/common/util/DownloadObserver.kt index 698a048db8..beeb5712fc 100644 --- a/app/src/main/java/com/gh/common/util/DownloadObserver.kt +++ b/app/src/main/java/com/gh/common/util/DownloadObserver.kt @@ -24,6 +24,7 @@ import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.SimulatorEntity import com.gh.gamecenter.feature.utils.PlatformUtils @@ -190,9 +191,10 @@ object DownloadObserver { confirmClickCallback = { currentActivity.startActivity( ShellActivity.getIntent( - currentActivity, - ShellActivity.Type.REAL_NAME_INFO, - )) + currentActivity, + ShellActivity.Type.REAL_NAME_INFO, + ) + ) } ) } else { @@ -328,8 +330,13 @@ object DownloadObserver { simulator = newSimulator ?: simulator } SimulatorDownloadManager.getInstance().showDownloadDialog( - currentActivity, simulator, - SimulatorDownloadManager.SimulatorLocation.LAUNCH, downloadEntity.gameId, gameName, downloadEntity.categoryChinese, null + currentActivity, + simulator, + SimulatorDownloadManager.SimulatorLocation.LAUNCH, + downloadEntity.gameId, + gameName, + downloadEntity.categoryChinese, + null ) SimulatorGameManager.recordDownloadSimulatorGame(downloadEntity.gameId, simulator.type) SimulatorGameManager.postPlayedGame(downloadEntity.gameId, downloadEntity.packageName) @@ -344,7 +351,8 @@ object DownloadObserver { } else { if (PackageUtils.isCanLaunchSetup(mApplication, downloadEntity.path) || downloadType == Constants.VGAME - || downloadType == Constants.DUAL_DOWNLOAD_VGAME) { + || downloadType == Constants.DUAL_DOWNLOAD_VGAME + ) { downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES" tryWithDefaultCatch { NewFlatLogUtils.logGameInstall( @@ -476,13 +484,20 @@ object DownloadObserver { ) if (downloadEntity.asVGame()) { + val customTrackDataJson = downloadEntity.customPageTrackDataJson + val kvs = if (customTrackDataJson.isNullOrBlank()) { + arrayOf() + } else { + GsonUtils.fromJson(customTrackDataJson, CustomPageTrackData::class.java).toKV() + } SensorsBridge.trackEventWithExposureSource( "HaloFunGameDownloadDone", exposureEvent?.source, "game_name", downloadEntity.name, "game_id", downloadEntity.gameId, "game_type", downloadEntity.categoryChinese, - "game_schema_type", if (downloadEntity.getMetaExtra(VHelper.KEY_BIT) == "32") "32位" else "64位" + "game_schema_type", if (downloadEntity.getMetaExtra(VHelper.KEY_BIT) == "32") "32位" else "64位", + *kvs ) } else if (downloadEntity.gameId == Constants.HALO_FUN_GAME_ID) { SensorsBridge.trackEvent( @@ -494,24 +509,32 @@ object DownloadObserver { if (downloadEntity.gameId != Constants.GHZS_GAME_ID && downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) != Constants.SIMULATOR_DOWNLOAD - && downloadEntity.gameId != Constants.HALO_FUN_GAME_ID) { - SensorsBridge.trackEventWithExposureSource( - "DownloadProcessFinish", - exposureEvent?.source, - "game_id", downloadEntity.gameId, - "game_name", downloadEntity.meta[Constants.GAME_NAME] ?: "", - "game_type", downloadEntity.meta[Constants.GAME_CATEGORY_IN_CHINESE] ?: "", - "game_label", downloadEntity.tags.joinToString(","), - "game_schema_type", if (downloadEntity.getMetaExtra(VHelper.KEY_BIT) == "32") "32位" else "64位", - "page_name", getCurrentPageEntity().pageName, - "page_id", getCurrentPageEntity().pageId, - "page_business_id", getCurrentPageEntity().pageBusinessId, - "last_page_name", getLastPageEntity().pageName, - "last_page_id", getLastPageEntity().pageId, - "last_page_business_id", getLastPageEntity().pageBusinessId, - "download_status", downloadEntity.meta[Constants.DOWNLOAD_STATUS_IN_CHINESE] ?: "", - "download_type", if (downloadEntity.asVGame()) "畅玩下载" else "本地下载", - ) + && downloadEntity.gameId != Constants.HALO_FUN_GAME_ID + ) { + val trackJson = downloadEntity.customPageTrackDataJson + val kvs = if (!trackJson.isNullOrBlank()) { + GsonUtils.fromJson(trackJson, CustomPageTrackData::class.java).toKV() + } else { + arrayOf() + } + SensorsBridge.trackEventWithExposureSource( + "DownloadProcessFinish", + exposureEvent?.source, + "game_id", downloadEntity.gameId, + "game_name", downloadEntity.meta[Constants.GAME_NAME] ?: "", + "game_type", downloadEntity.meta[Constants.GAME_CATEGORY_IN_CHINESE] ?: "", + "game_label", downloadEntity.tags.joinToString(","), + "game_schema_type", if (downloadEntity.getMetaExtra(VHelper.KEY_BIT) == "32") "32位" else "64位", + "page_name", getCurrentPageEntity().pageName, + "page_id", getCurrentPageEntity().pageId, + "page_business_id", getCurrentPageEntity().pageBusinessId, + "last_page_name", getLastPageEntity().pageName, + "last_page_id", getLastPageEntity().pageId, + "last_page_business_id", getLastPageEntity().pageBusinessId, + "download_status", downloadEntity.meta[Constants.DOWNLOAD_STATUS_IN_CHINESE] ?: "", + "download_type", if (downloadEntity.asVGame()) "畅玩下载" else "本地下载", + *kvs + ) } DataCollectionUtils.uploadDownload(mApplication, downloadEntity, "完成") diff --git a/app/src/main/java/com/gh/common/util/GameActivityDownloadHelper.kt b/app/src/main/java/com/gh/common/util/GameActivityDownloadHelper.kt index 99414c4740..b2d13211a3 100644 --- a/app/src/main/java/com/gh/common/util/GameActivityDownloadHelper.kt +++ b/app/src/main/java/com/gh/common/util/GameActivityDownloadHelper.kt @@ -6,7 +6,6 @@ import android.text.TextUtils import androidx.appcompat.app.AppCompatActivity import com.gh.common.DefaultJsApi import com.gh.common.constant.Config -import com.gh.common.dialog.CertificationDialog import com.gh.common.exposure.ExposureManager import com.gh.common.history.HistoryHelper import com.gh.common.repository.ReservationRepository @@ -262,10 +261,14 @@ object GameActivityDownloadHelper { traceEvent: ExposureEvent ) { GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info) { - CertificationDialog.showCertificationDialog(context, gameEntity) { - DialogUtils.checkDownload(context, apk.size, gameEntity.id, gameEntity.name, gameEntity.categoryChinese) { isSubscribe: Boolean -> - download(context, gameEntity, apk, false, isSubscribe, entrance, location, traceEvent) - } + DialogUtils.checkDownload( + context, + apk.size, + gameEntity.id, + gameEntity.name, + gameEntity.categoryChinese + ) { isSubscribe: Boolean -> + download(context, gameEntity, apk, false, isSubscribe, entrance, location, traceEvent) } } DataLogUtils.uploadGameLog(context, gameEntity.id, gameEntity.name, entrance) @@ -282,16 +285,14 @@ object GameActivityDownloadHelper { ) { VHelper.validateVSpaceBeforeAction(context, gameEntity) { GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info) { - CertificationDialog.showCertificationDialog(context, gameEntity) { - DialogUtils.checkDownload( - context, - apk.size, - gameEntity.id, - gameEntity.name, - gameEntity.categoryChinese - ) { isSubscribe: Boolean -> - download(context, gameEntity, apk, true, isSubscribe, entrance, location, traceEvent) - } + DialogUtils.checkDownload( + context, + apk.size, + gameEntity.id, + gameEntity.name, + gameEntity.categoryChinese + ) { isSubscribe: Boolean -> + download(context, gameEntity, apk, true, isSubscribe, entrance, location, traceEvent) } } } @@ -309,10 +310,14 @@ object GameActivityDownloadHelper { if (gameEntity.pluggableCollection != null) { DownloadDialog.showDownloadDialog(context, gameEntity, traceEvent, entrance, location) } else { - CertificationDialog.showCertificationDialog(context, gameEntity) { - DialogUtils.checkDownload(context, apk.size, gameEntity.id, gameEntity.name, gameEntity.categoryChinese) { isSubscribe: Boolean -> - plugin(context, gameEntity, apk, entrance, location, isSubscribe, traceEvent) - } + DialogUtils.checkDownload( + context, + apk.size, + gameEntity.id, + gameEntity.name, + gameEntity.categoryChinese + ) { isSubscribe: Boolean -> + plugin(context, gameEntity, apk, entrance, location, isSubscribe, traceEvent) } } } diff --git a/app/src/main/java/com/gh/common/util/GameSubstituteRepositoryHelper.kt b/app/src/main/java/com/gh/common/util/GameSubstituteRepositoryHelper.kt index 4ae5ef8c4c..d9a6b87c48 100644 --- a/app/src/main/java/com/gh/common/util/GameSubstituteRepositoryHelper.kt +++ b/app/src/main/java/com/gh/common/util/GameSubstituteRepositoryHelper.kt @@ -1,21 +1,16 @@ package com.gh.common.util -import android.annotation.SuppressLint import android.content.Context import android.text.TextUtils import com.gh.gamecenter.core.utils.SPUtils -import com.gh.gamecenter.common.utils.doOnMainProcessOnly -import com.gh.gamecenter.common.utils.toJson import com.gh.gamecenter.common.utils.toObject import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.entity.SubjectEntity -import com.gh.gamecenter.login.user.UserManager -import com.gh.gamecenter.common.retrofit.BiResponse import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp -import com.lightgame.utils.Utils import io.reactivex.schedulers.Schedulers +import okhttp3.ResponseBody /** * 补充游戏库管理类 @@ -26,15 +21,10 @@ object GameSubstituteRepositoryHelper { private const val KEY_GAME_REPOSITORY = "game_substitute_repository" private var mApi = RetrofitManager.getInstance().api - private var mHistoricInstalledGameIdSet = hashSetOf() // 历史已安装过的游戏 ID 列表 private var mApplicationContext = HaloApp.getInstance().application private val mGameSubSp by lazy { mApplicationContext.getSharedPreferences(SP_GAME_SUB, Context.MODE_PRIVATE) } - private var mGameCollectionList: List = arrayListOf() // 游戏补充库 - - init { - loadSavedRepository() - } + private var mGameCollectionList: List? = null // 游戏补充库 /** * 获取游戏补充库 @@ -43,48 +33,26 @@ object GameSubstituteRepositoryHelper { fun updateGameSubstituteRepository() { mApi.reserveColumns .subscribeOn(Schedulers.io()) - .subscribe(object : Response>() { - override fun onResponse(response: List?) { + .subscribe(object : Response() { + override fun onResponse(response: ResponseBody?) { super.onResponse(response) - updateGameRepository(response) + if (response != null) { + updateGameRepository(response) + } } }) } - /** - * 联网获取历史已安装过的游戏 ID 列表 - * 包括根据用户 ID 获取的已玩游戏 ID 列表,以及根据 gid 获取的已下载游戏 ID 列表 - */ - @JvmStatic - @SuppressLint("CheckResult") - fun updateHistoricInstalledGameSet() { - mApplicationContext.doOnMainProcessOnly { - val single = if (UserManager.getInstance().isLoggedIn) { - mApi.getIdListOfPlayedGames(UserManager.getInstance().userId, Utils.getTime(mApplicationContext)) - } else { - mApi.getIdListOfDownloadedGames(HaloApp.getInstance().gid, Utils.getTime(mApplicationContext)) - } - single.subscribeOn(Schedulers.io()).subscribe(object : BiResponse>() { - override fun onSuccess(data: List) { - mHistoricInstalledGameIdSet = data.toHashSet() - } - }) + private fun updateGameRepository(response: ResponseBody) { + val responseContent = response.string() + SPUtils.setString(mGameSubSp, KEY_GAME_REPOSITORY, responseContent) + + if (mGameCollectionList?.isEmpty() == true) { + mGameCollectionList = responseContent.toObject() } } - private fun loadSavedRepository() { - mGameCollectionList = SPUtils.getString(mGameSubSp, KEY_GAME_REPOSITORY).toObject() ?: arrayListOf() - } - - private fun updateGameRepository(subjects: List?) { - if (subjects == null) return - - SPUtils.setString(mGameSubSp, KEY_GAME_REPOSITORY, subjects.toJson()) - - mGameCollectionList = subjects - } - /** * 从补充游戏库相应的专题中取出一个与其它游戏都不相同的游戏,为空时即为游戏用完或不存在该相应专题 * @param collectionId 补充游戏库相应专题 ID @@ -92,7 +60,7 @@ object GameSubstituteRepositoryHelper { */ private fun getValidGame(collectionId: String?, gameIdList: HashSet): GameEntity? { collectionId?.let { - val collection = mGameCollectionList.find { it.id == collectionId } + val collection = mGameCollectionList?.find { it.id == collectionId } collection?.let { val game = collection.data?.find { game -> isThisGameValid(game, gameIdList) } game?.let { @@ -113,10 +81,12 @@ object GameSubstituteRepositoryHelper { * @param relatedCollectionId 关联的替换合集 ID */ fun replaceGames( - gameList: MutableList, + gameList: MutableList?, displayingGameIdSet: HashSet, relatedCollectionId: String, ) { + if (gameList == null) return + // 需要被替换的游戏 position 列表 val positionOfGameToBeReplacedList = arrayListOf() @@ -131,21 +101,13 @@ object GameSubstituteRepositoryHelper { // 这个 position 的游戏是否需要被替换 var thisPositionNeedToBeReplaced = false - // 从 游戏ID 判断当前游戏是否需要被替换 - if (mHistoricInstalledGameIdSet.contains(game.id)) { - positionOfGameToBeReplacedList.add(index) - thisPositionNeedToBeReplaced = true - } - // 检查是否已安装该游戏里同包名的 APK - if (!thisPositionNeedToBeReplaced) { - for (apk in game.getApk()) { - if (PackageHelper.validLocalPackageNameSet.contains(apk.packageName)) { - // 将该位置的游戏标记为需要替换 - positionOfGameToBeReplacedList.add(index) - thisPositionNeedToBeReplaced = true - break - } + for (apk in game.getApk()) { + if (PackageHelper.validLocalPackageNameSet.contains(apk.packageName)) { + // 将该位置的游戏标记为需要替换 + positionOfGameToBeReplacedList.add(index) + thisPositionNeedToBeReplaced = true + break } } @@ -168,7 +130,14 @@ object GameSubstituteRepositoryHelper { } } + // 若待替换的位置列表不为空,尝试执行替换 if (positionOfGameToBeReplacedList.isNotEmpty()) { + if (mGameCollectionList == null) { + mGameCollectionList = SPUtils.getString(mGameSubSp, KEY_GAME_REPOSITORY).toObject() ?: arrayListOf() + } + + if (mGameCollectionList.isNullOrEmpty()) return + for (position in positionOfGameToBeReplacedList) { val validGame = getValidGame(relatedCollectionId, displayingGameIdSet) validGame?.let { @@ -188,9 +157,6 @@ object GameSubstituteRepositoryHelper { game: GameEntity, gameIdList: HashSet ): Boolean { - // 该游戏是否在历史已安装列表中 - if (mHistoricInstalledGameIdSet.contains(game.id)) return false - // 该游戏是否与专题里已包含的游戏存在关联关系 for (relatedId in game.relatedGameIds!!) { if (gameIdList.contains(relatedId)) { 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 7b3afa1964..d91b669ecc 100644 --- a/app/src/main/java/com/gh/common/util/HomeBottomBarHelper.kt +++ b/app/src/main/java/com/gh/common/util/HomeBottomBarHelper.kt @@ -1,15 +1,20 @@ package com.gh.common.util +import com.gh.common.util.ViewPagerFragmentHelper.TYPE_MY_HALO +import com.gh.gamecenter.R import com.gh.gamecenter.common.entity.Display +import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.common.utils.toJson import com.gh.gamecenter.core.utils.GsonUtils import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.entity.BottomTab import com.gh.gamecenter.entity.SubjectRecommendEntity +import com.google.gson.reflect.TypeToken import com.halo.assistant.HaloApp object HomeBottomBarHelper { private const val GAME_BAR_KEY = "game_bar_key" - private const val VIDEO_BAR_KEY = "video_bar_key" + private const val KEY_HOME_BOTTOM_TAB = "home_bottom_tab" @JvmStatic fun getDefaultGameBarData(): SubjectRecommendEntity { @@ -39,41 +44,37 @@ object HomeBottomBarHelper { } @JvmStatic - fun getDefaultVideoData(): SubjectRecommendEntity { + fun getDefaultBottomTabHomeEntity(): BottomTab { + val animationCode = HaloApp.getInstance().application.assets + .open("lottie/tab_home.json") + .bufferedReader().use { it.readText() } + return BottomTab(name = "首页", jsCode = animationCode, iconSelector = R.drawable.selector_ic_home, default = true) + } + + @JvmStatic + fun getDefaultBottomTabMineEntity(): BottomTab { + val animationCode = HaloApp.getInstance().application.assets + .open("lottie/tab_mine.json") + .bufferedReader().use { it.readText() } + return BottomTab(name = "我的光环", jsCode = animationCode, iconSelector = R.drawable.selector_ic_user, link = LinkEntity(type = TYPE_MY_HALO)) + } + + @JvmStatic + fun getDefaultHomeBottomTabData(): List { try { - val json = SPUtils.getString(VIDEO_BAR_KEY) + val json = SPUtils.getString(KEY_HOME_BOTTOM_TAB) if (json.isNotEmpty()) { - return GsonUtils.fromJson(json, SubjectRecommendEntity::class.java) + val type = object : TypeToken>() {}.type + return GsonUtils.gson.fromJson(json, type) } } catch (ignore: Exception) { } - return getDefaultVideoEntity() + return listOf(getDefaultBottomTabHomeEntity(), getDefaultBottomTabMineEntity()) } @JvmStatic - fun getDefaultVideoEntity(): SubjectRecommendEntity { - val animationCode = HaloApp.getInstance().application.assets - .open("lottie/tab_video.json") - .bufferedReader().use { it.readText() } - return SubjectRecommendEntity( - type = "video_stream", - text = "视频", - name = "视频", - position = 4, - animationCode = animationCode, - default = false, - display = Display() - ) - } - - @JvmStatic - fun updateDefaultGameBarData(data: SubjectRecommendEntity) { - SPUtils.setString(GAME_BAR_KEY, data.toJson()) - } - - @JvmStatic - fun updateDefaultVideoBarData(data: SubjectRecommendEntity) { - SPUtils.setString(VIDEO_BAR_KEY, data.toJson()) + fun updateDefaultHomeBottomTabData(data: List) { + SPUtils.setString(KEY_HOME_BOTTOM_TAB, data.toJson()) } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/HomePluggableHelper.kt b/app/src/main/java/com/gh/common/util/HomePluggableHelper.kt index 79a9cec282..f556f7a6c6 100644 --- a/app/src/main/java/com/gh/common/util/HomePluggableHelper.kt +++ b/app/src/main/java/com/gh/common/util/HomePluggableHelper.kt @@ -1,13 +1,18 @@ package com.gh.common.util -import com.gh.gamecenter.common.utils.toastInInternalRelease +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.tracker.Tracker import com.gh.gamecenter.common.utils.tryCatchInRelease +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.core.utils.MD5Utils +import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.entity.HomePluggableFilterEntity import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.PluginDisplayStatus import com.gh.gamecenter.room.AppDatabase /** - * 首页插件化区域辅助类 + * 插件化区域辅助类 */ object HomePluggableHelper { @@ -18,19 +23,67 @@ object HomePluggableHelper { val apkList = gameEntity.getApk() if (apkList.isNotEmpty()) { val apk = apkList.first() - val tag = if (isNever) "never" else apk.version ?: "" - tryCatchInRelease { - mHomePluggableFilterDao.addData( - HomePluggableFilterEntity( - pkgName = apk.packageName, - tag = tag, - active = isNever - ) - ) + val apkHash = MD5Utils.getContentMD5(gameEntity.id + apk.version) + val tag = if (isNever) "never" else apkHash + runOnIoThread { + tryCatchInRelease { + val filterData = mHomePluggableFilterDao.getDataByPkgName(apk.packageName) + if (filterData != null && !filterData.tag.contains(tag)) { + filterData.tag = filterData.tag + "," + tag + mHomePluggableFilterDao.addData(filterData) + } else { + mHomePluggableFilterDao.addData( + HomePluggableFilterEntity( + pkgName = apk.packageName, + tag = tag, + active = true + ) + ) + } + } } } } + /** + * 获取插件化区域显示样式 + */ + fun getPluginDisplayedStyle(pluginList: List?): PluginDisplayStatus { + if (pluginList.isNullOrEmpty()) { + return PluginDisplayStatus.DEFAULT + } + + val shouldShowCollapsedStyle = shouldShowCollapsedStyle() + return if (shouldShowCollapsedStyle) { + PluginDisplayStatus.CLOSE + } else { + setPluginAreaShowed() + + if (pluginList.size > 2) { + PluginDisplayStatus.OPEN_TWO_AND_BUTTON + } else { + PluginDisplayStatus.OPEN + } + } + } + + /** + * 是否需要把插件化区域显示为收起样式 + * https://jira.shanqu.cc/browse/GHZS-4725 + */ + private fun shouldShowCollapsedStyle(): Boolean { + val launchId = SPUtils.getString(Constants.SP_PLUGIN_AREA_SHOWED_LAUNCH_ID) + return if (launchId.isEmpty()) { + false + } else { + Tracker.launchId != launchId + } + } + + private fun setPluginAreaShowed() { + SPUtils.setString(Constants.SP_PLUGIN_AREA_SHOWED_LAUNCH_ID, Tracker.launchId) + } + @JvmStatic fun showHomePluggable(gameEntity: GameEntity): Boolean { val apkList = gameEntity.getApk() @@ -38,9 +91,9 @@ object HomePluggableHelper { val apk = apkList.first() tryCatchInRelease { val filterData = mHomePluggableFilterDao.getDataByPkgName(apk.packageName) - if (filterData?.active == true) { + if (filterData != null) { val filterTag = filterData.tag - return filterTag != "never" && apk.version != filterTag + return !filterTag.contains("never") && !filterTag.contains(MD5Utils.getContentMD5(gameEntity.id + apk.version)) } } } @@ -55,20 +108,4 @@ object HomePluggableHelper { return emptyList() } - @JvmStatic - fun activationFilterData() { - try { - val filterList = mHomePluggableFilterDao.getDataByActive(false) - - if (filterList != null) { - for (entity in filterList) { - entity.active = true - } - mHomePluggableFilterDao.addData(filterList) - } - } catch (e: Exception) { - e.printStackTrace() - toastInInternalRelease("插件化筛选出现异常") - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/PackageUtils.java b/app/src/main/java/com/gh/common/util/PackageUtils.java index c48339a8f3..20636c8ff3 100644 --- a/app/src/main/java/com/gh/common/util/PackageUtils.java +++ b/app/src/main/java/com/gh/common/util/PackageUtils.java @@ -151,14 +151,8 @@ public class PackageUtils { } } - // 镜像游戏,使用镜像 Apk 替换掉原来的 ApkNormal - if (RegionSettingHelper.shouldThisGameDisplayMirrorInfo(gameEntity.getId())) { - gameEntity.setApkNormal(gameEntity.getApk()); - } - // 非插件游戏更新 - for (ApkEntity apkEntity : gameEntity.getApkNormal()) { - + for (ApkEntity apkEntity : gameEntity.getApk()) { // ghVersion 不存在即是非插件游戏 if (TextUtils.isEmpty(apkEntity.getGhVersion())) { int versionCodeFromRequest = apkEntity.getVersionCode(); diff --git a/app/src/main/java/com/gh/common/util/RealNameHelper.kt b/app/src/main/java/com/gh/common/util/RealNameHelper.kt index ceca3adebd..6607cd031a 100644 --- a/app/src/main/java/com/gh/common/util/RealNameHelper.kt +++ b/app/src/main/java/com/gh/common/util/RealNameHelper.kt @@ -1,5 +1,7 @@ package com.gh.common.util +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import com.gh.gamecenter.core.utils.CurrentActivityHolder import com.gh.download.DownloadManager import com.gh.gamecenter.ShellActivity @@ -14,10 +16,19 @@ import com.lightgame.download.DownloadStatus object RealNameHelper { - var pendingInstallPkgPath = "" + private var pendingInstallPkgPath = "" // 0表示未实名,1表示未成年,2表示成年 - private var mCertificationStatus: Int = 0 + private var certificationStatus: Int = 0 + + private val _realNameInfoUpdateLiveData = MutableLiveData() + val realNameInfoUpdateLiveData = _realNameInfoUpdateLiveData as LiveData + + fun updatePendingInstallPkgPath(path: String) { + pendingInstallPkgPath = path + } + + fun getPendingInstallPkgPath() = pendingInstallPkgPath /** * 弹未成年人不能下载游戏弹窗 @@ -130,11 +141,15 @@ object RealNameHelper { } } + fun onRealNameInfoUpdated() { + _realNameInfoUpdateLiveData.postValue(Any()) + } + /** * 获取实名状态 0表示未实名,1表示未成年,2表示成年 */ fun getCertificationStatus(): Int { - return mCertificationStatus + return certificationStatus } /** @@ -142,7 +157,7 @@ object RealNameHelper { */ @JvmStatic fun updateCertificationStatus(newCertificationStatus: Int) { - mCertificationStatus = newCertificationStatus + certificationStatus = newCertificationStatus } } \ 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 new file mode 100644 index 0000000000..73306675d7 --- /dev/null +++ b/app/src/main/java/com/gh/common/util/ViewPagerFragmentHelper.kt @@ -0,0 +1,256 @@ +package com.gh.common.util + +import android.os.Bundle +import androidx.fragment.app.Fragment +import com.gh.common.iinterface.ISuperiorChain +import com.gh.gamecenter.amway.AmwayFragment +import com.gh.gamecenter.category2.CategoryV2Fragment +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.discovery.DiscoveryFragment +import com.gh.gamecenter.entity.SubjectData +import com.gh.gamecenter.feedback.view.help.HelpAndFeedbackFragment +import com.gh.gamecenter.feedback.view.qa.HelpContainerFragment +import com.gh.gamecenter.forum.detail.ForumDetailFragment +import com.gh.gamecenter.forum.home.CommunityHomeFragment +import com.gh.gamecenter.fragment.ReloadFragment +import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailFragment +import com.gh.gamecenter.game.commoncollection.detail.CustomCommonCollectionDetailFragment +import com.gh.gamecenter.gamecollection.hotlist.GameCollectionHotListWrapperFragment +import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment +import com.gh.gamecenter.gamedetail.GameDetailFragment +import com.gh.gamecenter.info.InfoWrapperFragment +import com.gh.gamecenter.libao.LibaoDetailFragment +import com.gh.gamecenter.libao.LibaoFragment +import com.gh.gamecenter.newsdetail.NewsDetailFragment +import com.gh.gamecenter.personal.HaloPersonalFragment +import com.gh.gamecenter.qa.article.detail.ArticleDetailFragment +import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailFragment +import com.gh.gamecenter.qa.subject.AskSubjectFragment +import com.gh.gamecenter.qa.video.detail.ForumVideoDetailFragment +import com.gh.gamecenter.servers.GameServersPublishFragment +import com.gh.gamecenter.servers.gametest2.GameServerTestV2Fragment +import com.gh.gamecenter.subject.SubjectFragment +import com.gh.gamecenter.tag.TagsActivity +import com.gh.gamecenter.tag.TagsListFragment +import com.gh.gamecenter.toolbox.ToolboxFragment +import com.gh.gamecenter.video.detail.HomeVideoFragment +import com.gh.gamecenter.wrapper.ToolbarWrapperFragment +import com.halo.assistant.fragment.WebFragment + +/** + * 通用跳转Fragment方法 + * @see WIKI + */ +object ViewPagerFragmentHelper { + const val WRAPPER_FRAGMENT_NAME = "WrapperFragmentName" + + const val TYPE_MULTI_TAB_NAV = "multi_tab_nav" + const val TYPE_CUSTOM_PAGE = "custom_page" + + const val TYPE_GAME = "game" // 游戏详情页 + const val TYPE_MY_HALO = "my_halo" // 我的光环 + const val TYPE_COMMUNITY_HOME = "community_home" // 社区首页 + const val TYPE_COMMUNITY = "community" // 论坛 + const val TYPE_COMMUNITY_SECTION = "bbs_section" // 论坛子版块 + const val TYPE_COMMUNITY_ARTICLE = "community_article" // 帖子 + const val TYPE_QUESTION = "question" // 提问 + const val TYPE_VIDEO_STREAM = "video_stream" // 视频信息流 + const val TYPE_VIDEO = "video" // 视频贴 + const val TYPE_AMWAY = "top_game_comment" // 安利墙 + const val TYPE_GAME_HOT_LIST = "game_hot_list" // 游戏单热搜榜 + const val TYPE_GAME_LIST = "game_list" // 游戏单广场 + const val TYPE_FEEDBACK = "feedback" // 帮助与反馈 + const val TYPE_COLUMN = "column" // 游戏专题详情页 + const val TYPE_QQ_MINI_GAME_COLUMN = "qq_mini_game_column_detail" // QQ游戏专题详情页 + const val TYPE_COLUMN_COLLECTION = "column_collection" // 专题合集详情页 + const val TYPE_SERVER = "server" // 开服表 + const val TYPE_COLUMN_TEST = "column_test_v2" // 新游开测 + const val TYPE_CATEGORY_V2 = "category_v2" // 分类2.0 + const val TYPE_COMMON_COLLECTION = "common_collection" // 通用内容合集详情页 + const val TYPE_WEB = "web" // web链接 + const val TYPE_EXPLORE = "game_explore" // 发现页 + const val TYPE_LIBAO = "libao" // 礼包详情页 + const val TYPE_LIBAO_CENTER = "libao_center" // 礼包中心 + const val TYPE_TAG = "tag" // 游戏标签详情 + const val TYPE_ARTICLE = "article" // 文章 + const val TYPE_COMMUNITY_COLUMN = "community_column" // 社区专题 + const val TYPE_ARTICLE_CENTER = "article_center" // 资讯中心 + const val TYPE_QA = "qa" // QA + const val TYPE_TOOLKIT = "toolkit" // 工具箱 + + fun createFragment(parentFragment: Fragment?, bundle: Bundle, linkEntity: LinkEntity, isTabWrapper: Boolean): Fragment { + return when (linkEntity.type) { + // 游戏详情页 + TYPE_GAME -> { + bundle.putString(EntranceConsts.KEY_GAMEID, linkEntity.link) + GameDetailFragment().with(bundle) + } + // 我的光环 + TYPE_MY_HALO -> { + val superiorChain = if (parentFragment is ISuperiorChain) parentFragment else null + HaloPersonalFragment().setSuperiorChain(superiorChain).with(bundle) + } + // 社区首页 + TYPE_COMMUNITY_HOME -> CommunityHomeFragment().with(bundle) + // 视频信息流 + TYPE_VIDEO_STREAM -> { + bundle.putBoolean(EntranceConsts.KEY_IS_HOME_VIDEO, true) + HomeVideoFragment().with(bundle) + } + // 安利墙 + TYPE_AMWAY -> { + AmwayFragment().with(bundle) + } + // 论坛 + TYPE_COMMUNITY -> { + bundle.putString(EntranceConsts.KEY_BBS_ID, linkEntity.link) + ForumDetailFragment().with(bundle) + } + // 论坛子版块 + TYPE_COMMUNITY_SECTION -> { + bundle.putString(EntranceConsts.KEY_BBS_ID, linkEntity.community?.id) + bundle.putString(EntranceConsts.KEY_BBS_SECTION_ID, linkEntity.link) + ForumDetailFragment().with(bundle) + } + // 游戏单热搜榜 + TYPE_GAME_HOT_LIST -> { + GameCollectionHotListWrapperFragment().with(bundle) + } + // 视频贴 + TYPE_VIDEO -> { + bundle.putString(EntranceConsts.KEY_VIDEO_ID, linkEntity.link) + ForumVideoDetailFragment().with(bundle) + } + // 帮助与反馈 + TYPE_FEEDBACK -> { + HelpAndFeedbackFragment().with(bundle) + } + // 帖子 + TYPE_COMMUNITY_ARTICLE -> { + bundle.putString(EntranceConsts.KEY_COMMUNITY_ARTICLE_ID, linkEntity.link) + bundle.putParcelable(EntranceConsts.KEY_COMMUNITY_DATA, linkEntity.community) + ArticleDetailFragment().with(bundle) + } + // 提问 + TYPE_QUESTION -> { + bundle.putString(EntranceConsts.KEY_QUESTIONS_ID, linkEntity.link) + NewQuestionDetailFragment().with(bundle) + } + // 其他原来带Toolbar的Fragment + else -> createToolbarWrapperFragment(bundle, linkEntity, isTabWrapper) + } + } + + private fun createToolbarWrapperFragment(bundle: Bundle, entity: LinkEntity, isTabWrapper: Boolean): Fragment { + var className = ReloadFragment::class.java.name + + when (entity.type) { + // 游戏单广场 + TYPE_GAME_LIST -> { + className = GameCollectionSquareFragment::class.java.name + } + // 游戏专题详情页/QQ游戏专题详情页 + TYPE_COLUMN, TYPE_QQ_MINI_GAME_COLUMN -> { + className = SubjectFragment::class.java.name + bundle.putParcelable( + EntranceConsts.KEY_SUBJECT_DATA, + SubjectData(entity.link, entity.text, false, isQQMiniGame = entity.type == "qq_mini_game_column_detail") + ) + bundle.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, !isTabWrapper) + } + // 专题合集详情页 + TYPE_COLUMN_COLLECTION -> { + className = ColumnCollectionDetailFragment::class.java.name + bundle.putString(EntranceConsts.KEY_COLLECTION_ID, entity.link) + bundle.putInt(EntranceConsts.KEY_POSITION, 0) + bundle.putString(EntranceConsts.KEY_COLUMNNAME, entity.text) + bundle.putBoolean(EntranceConsts.KEY_IS_COLUMN_COLLECTION, true) + } + // 开服表 + TYPE_SERVER -> { + className = GameServersPublishFragment::class.java.name + bundle.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, !isTabWrapper) + } + // 新游开测 + TYPE_COLUMN_TEST -> { + className = GameServerTestV2Fragment::class.java.name + } + // 分类2.0 + TYPE_CATEGORY_V2 -> { + className = CategoryV2Fragment::class.java.name + bundle.putString(EntranceConsts.KEY_CATEGORY_ID, entity.link) + bundle.putString(EntranceConsts.KEY_CATEGORY_TITLE, entity.text) + bundle.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, !isTabWrapper) + } + // 通用内容合集详情页 + TYPE_COMMON_COLLECTION -> { + className = CustomCommonCollectionDetailFragment::class.java.name + bundle.putString(EntranceConsts.KEY_COLLECTION_ID, entity.link) + bundle.putString(EntranceConsts.KEY_COLUMNNAME, entity.text) + } + // web链接 + TYPE_WEB -> { + className = WebFragment::class.java.name + bundle.putString(EntranceConsts.KEY_URL, entity.link) + bundle.putBoolean(WebFragment.KEY_OPEN_NATIVE_PAGE, false) + bundle.putBoolean(WebFragment.KEY_ENABLE_HORIZONTAL_SCROLL_DISPATCH, true) + if (entity.link != null && entity.link!!.contains("leave_web_page_handle_back_pressed=true")) { + bundle.putBoolean(WebFragment.KEY_LEAVE_WEB_PAGE_TO_HANDLE_BACK_PRESSED, true) + } + } + // 发现页 + TYPE_EXPLORE -> { + className = DiscoveryFragment::class.java.name + } + // 礼包详情页 + TYPE_LIBAO -> { + className = LibaoDetailFragment::class.java.name + bundle.putString(EntranceConsts.KEY_ID, entity.link) + } + // 游戏标签详情 + TYPE_TAG -> { + className = TagsListFragment::class.java.name + bundle.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, !isTabWrapper) + bundle.putString(TagsActivity.TAG, entity.text) + } + // 文章 + TYPE_ARTICLE -> { + className = NewsDetailFragment::class.java.name + bundle.putString(EntranceConsts.KEY_NEWSID, entity.link) + } + // 社区专题 + TYPE_COMMUNITY_COLUMN -> { + className = AskSubjectFragment::class.java.name + bundle.putString(EntranceConsts.KEY_COLUMN_ID, entity.link) + bundle.putParcelable(EntranceConsts.KEY_COMMUNITY_DATA, entity.community) + } + // QA + TYPE_QA -> { + className = HelpContainerFragment::class.java.name + bundle.putString(EntranceConsts.KEY_QA_ID, entity.link) + bundle.putString(EntranceConsts.KEY_NAVIGATION_TITLE, entity.text) + } + // 资讯中心 + TYPE_ARTICLE_CENTER -> { + className = InfoWrapperFragment::class.java.name + } + // 礼包中心 + TYPE_LIBAO_CENTER -> { + className = LibaoFragment::class.java.name + } + // 工具箱 + TYPE_TOOLKIT -> { + className = ToolboxFragment::class.java.name + } + } + + return if (isTabWrapper) { + // 多Tab导航页不需要Toolbar + (Class.forName(className).getDeclaredConstructor().newInstance() as Fragment).apply { arguments = bundle } + } else { + bundle.putString(WRAPPER_FRAGMENT_NAME, className) + ToolbarWrapperFragment().with(bundle) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/download/DownloadManager.java b/app/src/main/java/com/gh/download/DownloadManager.java index 1b9360c8b6..d5dedccad5 100644 --- a/app/src/main/java/com/gh/download/DownloadManager.java +++ b/app/src/main/java/com/gh/download/DownloadManager.java @@ -21,6 +21,8 @@ import com.gh.gamecenter.common.utils.SensorsBridge; import com.gh.gamecenter.core.AppExecutor; import com.gh.gamecenter.common.constant.Constants; import com.gh.gamecenter.feature.entity.TagStyleEntity; +import com.gh.gamecenter.feature.entity.CustomPageTrackData; +import com.gh.gamecenter.feature.entity.TagStyleEntity; import com.gh.gamecenter.feature.exposure.ExposureEvent; import com.gh.common.exposure.ExposureUtils; import com.gh.gamecenter.common.exposure.meta.MetaUtil; @@ -69,6 +71,7 @@ import org.greenrobot.eventbus.EventBus; import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -399,6 +402,13 @@ public class DownloadManager implements DownloadStatusListener { } downloadEntity.setTags(tags); + // 将自定义页面相关埋点数据放入 downloadEntity 中供下载完成时取出使用 + CustomPageTrackData customPageTrackData = gameEntity.getCustomPageTrackData(); + if (customPageTrackData != null) { + String trackJson = GsonUtils.toJson(customPageTrackData); + downloadEntity.setCustomPageTrackData(trackJson); + } + if (isSubscribe) { DownloadManager.getInstance().subscribe(downloadEntity); } else { @@ -407,12 +417,19 @@ public class DownloadManager implements DownloadStatusListener { } if (asVGame) { - SensorsBridge.trackEventWithExposureSource("HaloFunGameDownloadClick", - downloadExposureEvent.getSource(), + String[] vaKvs = { "game_name", gameEntity.getName(), "game_id", gameEntity.getId(), "game_type", gameEntity.getCategoryChinese(), - "game_schema_type", gameEntity.getGameBitChinese()); + "game_schema_type", gameEntity.getGameBitChinese() + }; + List vaList = new ArrayList<>(Arrays.asList(vaKvs)); + if (customPageTrackData != null) { + vaList.addAll(Arrays.asList(customPageTrackData.toKV())); + } + SensorsBridge.trackEventWithExposureSource("HaloFunGameDownloadClick", + downloadExposureEvent.getSource(), + vaList.toArray(new String[0])); } String trackDownloadType = ""; @@ -422,8 +439,8 @@ public class DownloadManager implements DownloadStatusListener { trackDownloadType = "本地下载"; } - SensorsBridge.trackEventWithExposureSource("DownloadProcessBegin", - downloadExposureEvent.getSource(), + + String[] arrayKv = { "game_id", gameEntity.getId(), "game_name", gameEntity.getName(), "game_type", gameEntity.getCategoryChinese(), @@ -437,6 +454,15 @@ public class DownloadManager implements DownloadStatusListener { "last_page_business_id", GlobalActivityManager.getLastPageEntity().getPageBusinessId(), "download_status", gameEntity.getDownloadStatusChinese(), "download_type", trackDownloadType + }; + + List kvs = new ArrayList<>(Arrays.asList(arrayKv)); + if (customPageTrackData != null) { + kvs.addAll(Arrays.asList(customPageTrackData.toKV())); + } + + SensorsBridge.trackEventWithExposureSource("DownloadProcessBegin", + downloadExposureEvent.getSource(), kvs.toArray(new String[0]) ); //TODO remove @@ -463,6 +489,7 @@ public class DownloadManager implements DownloadStatusListener { SentryHelper.INSTANCE.onEvent("CLEAR_DELETED_TASK_ERROR", "exception_digest", e.getLocalizedMessage()); } } + /** * 添加一个下载任务 *

diff --git a/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt b/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt index acbe1a0d25..2b8f896bcc 100644 --- a/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt +++ b/app/src/main/java/com/gh/download/dialog/DownloadDialogAdapter.kt @@ -6,13 +6,13 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.DirectUtils -import com.gh.gamecenter.NewsDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.common.callback.OnViewClickListener import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.databinding.* import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.newsdetail.NewsDetailActivity import com.lightgame.adapter.BaseRecyclerAdapter class DownloadDialogAdapter( diff --git a/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt b/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt index 77209b1cd5..60844b373e 100644 --- a/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt +++ b/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt @@ -7,7 +7,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.Config -import com.gh.common.dialog.CertificationDialog import com.gh.common.dialog.DeviceRemindDialog import com.gh.common.dialog.PackageCheckDialogFragment import com.gh.common.util.* @@ -343,33 +342,28 @@ class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : Bas context as AppCompatActivity, gameEntity ) { - CertificationDialog.showCertificationDialog( + DialogUtils.checkDownload( context, - gameEntity - ) { - DialogUtils.checkDownload( + apkEntity.size, + gameEntity.id, + gameEntity.name, + gameEntity.categoryChinese + ) { isSubscribe -> + DownloadManager.createDownload( context, - apkEntity.size, - gameEntity.id, - gameEntity.name, - gameEntity.categoryChinese - ) { isSubscribe -> - DownloadManager.createDownload( - context, - apkEntity, - gameEntity, - false, - false, - entrance, - location, - isSubscribe, traceEvent - ) + apkEntity, + gameEntity, + false, + false, + entrance, + location, + isSubscribe, traceEvent + ) - DeviceRemindDialog.showDeviceRemindDialog( - context, - gameEntity - ) - } + DeviceRemindDialog.showDeviceRemindDialog( + context, + gameEntity + ) } } } diff --git a/app/src/main/java/com/gh/gamecenter/DownloadManagerActivity.java b/app/src/main/java/com/gh/gamecenter/DownloadManagerActivity.java index e0ad46b0b1..a54115e212 100644 --- a/app/src/main/java/com/gh/gamecenter/DownloadManagerActivity.java +++ b/app/src/main/java/com/gh/gamecenter/DownloadManagerActivity.java @@ -70,7 +70,7 @@ public class DownloadManagerActivity extends ToolBarActivity { @Override public boolean onMenuItemClick(MenuItem item) { NewFlatLogUtils.logHaloFunManageShow("下载管理"); - DirectUtils.directToVGameDownload(this, false); + DirectUtils.directToVGameDownload(this, "下载管理", false); return true; } diff --git a/app/src/main/java/com/gh/gamecenter/GameDetailActivity.kt b/app/src/main/java/com/gh/gamecenter/GameDetailActivity.kt index 3c2926e9b5..38c828fb3e 100644 --- a/app/src/main/java/com/gh/gamecenter/GameDetailActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/GameDetailActivity.kt @@ -8,11 +8,13 @@ import android.view.View import com.gh.base.DownloadToolbarActivity import com.gh.common.exposure.ExposureManager.log import com.gh.common.exposure.ExposureTraceUtils.appendTrace +import com.gh.gamecenter.common.base.GlobalActivityManager import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.toArrayList import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.entity.GamePlatform +import com.gh.gamecenter.feature.entity.CustomPageTrackData import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.exposure.ExposureEvent.Companion.createEvent @@ -170,7 +172,12 @@ class GameDetailActivity : DownloadToolbarActivity() { } @JvmStatic - fun startGameDetailActivity(context: Context, gameId: String, entrance: String?, traceEvent: ExposureEvent?) { + fun startGameDetailActivity( + context: Context, + gameId: String, + entrance: String?, + traceEvent: ExposureEvent? + ) { startGameDetailActivity(context, gameId, entrance, -1, traceEvent = traceEvent) } @@ -226,7 +233,8 @@ class GameDetailActivity : DownloadToolbarActivity() { platformName: String? = null, packageName: String? = null, platforms: List? = null, - traceEvent: ExposureEvent? = null + traceEvent: ExposureEvent? = null, + customPageTrackData: CustomPageTrackData? = null ) { val bundle = Bundle() @@ -280,6 +288,13 @@ class GameDetailActivity : DownloadToolbarActivity() { } bundle.putString(EntranceConsts.KEY_GAMEID, gameId) bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance) + bundle.putParcelable(EntranceConsts.KEY_CUSTOM_PAGE_TRACK_DATA, customPageTrackData) + bundle.putString(EntranceConsts.KEY_LAST_PAGE_ID, GlobalActivityManager.getLastPageEntity().pageId) + bundle.putString(EntranceConsts.KEY_LAST_PAGE_NAME, GlobalActivityManager.getLastPageEntity().pageName) + bundle.putString( + EntranceConsts.KEY_LAST_PAGE_BUSINESS_ID, + GlobalActivityManager.getLastPageEntity().pageBusinessId + ) context.startActivity( getTargetIntent( context, diff --git a/app/src/main/java/com/gh/gamecenter/MainActivity.java b/app/src/main/java/com/gh/gamecenter/MainActivity.java index 02b31a20af..32d4418e36 100644 --- a/app/src/main/java/com/gh/gamecenter/MainActivity.java +++ b/app/src/main/java/com/gh/gamecenter/MainActivity.java @@ -17,7 +17,6 @@ import static com.gh.gamecenter.common.utils.ExtensionsKt.observableToMain; import static com.gh.gamecenter.login.utils.LoginHelper.WEIBO_SCOPE; import android.annotation.SuppressLint; -import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -48,6 +47,7 @@ import com.gh.common.constant.Config; import com.gh.common.filter.RegionSettingHelper; import com.gh.common.history.HistoryDatabase; import com.gh.common.history.HistoryHelper; +import com.gh.common.prioritychain.GlobalPriorityChainHelper; import com.gh.common.repository.ReservationRepository; import com.gh.common.simulator.SimulatorGameManager; import com.gh.common.util.AdHelper; @@ -56,7 +56,6 @@ import com.gh.common.util.DataUtils; import com.gh.common.util.DirectUtils; import com.gh.common.util.EntranceUtils; import com.gh.common.util.ErrorHelper; -import com.gh.common.util.HomePluggableHelper; import com.gh.common.util.LogUtils; import com.gh.common.util.LunchType; import com.gh.common.util.PackageInstaller; @@ -92,11 +91,9 @@ import com.gh.gamecenter.core.utils.SentryHelper; import com.gh.gamecenter.core.utils.ToastUtils; import com.gh.gamecenter.core.utils.UrlFilterUtils; import com.gh.gamecenter.entity.StartupAdEntity; -import com.gh.gamecenter.eventbus.EBSkip; import com.gh.gamecenter.feature.entity.GameEntity; import com.gh.gamecenter.feature.utils.PlatformUtils; -import com.gh.gamecenter.fragment.MainWrapperFragment; -import com.gh.gamecenter.fragment.MainWrapperViewModel; +import com.gh.gamecenter.home.custom.model.CustomPageShareRepository; import com.gh.gamecenter.home.skip.PackageSkipActivity; import com.gh.gamecenter.login.user.UserManager; import com.gh.gamecenter.login.utils.QuickLoginHelper; @@ -105,6 +102,9 @@ import com.gh.gamecenter.packagehelper.PackageViewModel; import com.gh.gamecenter.retrofit.RetrofitManager; import com.gh.gamecenter.room.AppDatabase; import com.gh.gamecenter.room.dao.SimulatorGameDao; +import com.gh.gamecenter.wrapper.MainWrapperFragment; +import com.gh.gamecenter.wrapper.MainWrapperRepository; +import com.gh.gamecenter.wrapper.MainWrapperViewModel; import com.gh.vspace.VHelper; import com.google.android.exoplayer2.upstream.cache.Cache; import com.google.gson.JsonSyntaxException; @@ -113,13 +113,11 @@ import com.halo.assistant.HaloApp; import com.lightgame.download.DownloadEntity; import com.lightgame.download.DownloadStatus; import com.lightgame.download.FileUtils; -import com.lightgame.utils.AppManager; import com.lightgame.utils.Utils; import com.sina.weibo.sdk.auth.AuthInfo; import com.sina.weibo.sdk.openapi.IWBAPI; import com.sina.weibo.sdk.openapi.WBAPIFactory; -import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import org.jetbrains.annotations.NotNull; @@ -133,14 +131,11 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; import io.reactivex.SingleSource; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.Function; import io.reactivex.schedulers.Schedulers; -import kotlin.jvm.functions.Function1; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.HttpException; @@ -148,9 +143,6 @@ import tv.danmaku.ijk.media.exo2.ExoSourceManager; public class MainActivity extends BaseActivity { - public static final String EB_SKIP_MAIN = "MainActivity"; - public static final String SWITCH_TO_COMMUNITY = "switch_to_community"; - public static final String SWITCH_TO_VIDEO = "switch_to_video"; public static final String SHOW_AD = "show_ad"; public static final int COUNTDOWN_AD = 100; public static final int COUNTDOWN_MAX_COUNT = 3; @@ -229,7 +221,7 @@ public class MainActivity extends BaseActivity { DialogHelper.showCenterWarningDialog(this, "发生闪退", "光环助手发生了闪退,建议安装到最新版本修复异常" , "马上反馈", "马上安装修复", () -> { - DirectUtils.directToGameDetail(this, Constants.GHZS_GAME_ID, "crash", true, "desc", null); + DirectUtils.directToGameDetail(this, Constants.GHZS_GAME_ID, "", "crash", true, "desc", null); return null; }, () -> { @@ -263,10 +255,6 @@ public class MainActivity extends BaseActivity { ReservationRepository.refreshReservations(); - if (getIntent().getBooleanExtra(SWITCH_TO_VIDEO, false)) { - handler.postDelayed(() -> mMainWrapperFragment.setCurrentItem(2), 800); - } - // 跳转至其它页面 if (getIntent() != null && getIntent().getExtras() != null @@ -300,7 +288,6 @@ public class MainActivity extends BaseActivity { // 初始化PlatformUtils PlatformUtils.getInstance(getApplicationContext()); - HomePluggableHelper.activationFilterData(); // 启动app删除视频缓存文件 try { @@ -414,15 +401,12 @@ public class MainActivity extends BaseActivity { } @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { + protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (savedInstanceState != null) { - int currentPageIndex = savedInstanceState.getInt(CURRENT_PAGE, -1); - if (currentPageIndex >= 0) { - EventBus.getDefault().post(new EBSkip(MainActivity.EB_SKIP_MAIN, currentPageIndex)); - if (currentPageIndex == MainWrapperFragment.INDEX_VIDEO) { - DisplayUtils.setLightStatusBar(this, false); - } + int bottomTabPosition = savedInstanceState.getInt(CURRENT_PAGE, -1); + if (bottomTabPosition != -1) { + MainWrapperRepository.Companion.getInstance().sendSelectTabEvent(bottomTabPosition, -1); } } } @@ -453,6 +437,7 @@ public class MainActivity extends BaseActivity { protected void onDestroy() { super.onDestroy(); + CustomPageShareRepository.Companion.getInstance().onClear(); AdDelegateHelper.INSTANCE.cancelSplashAd(this); handler.removeCallbacksAndMessages(null); @@ -595,10 +580,12 @@ public class MainActivity extends BaseActivity { ExtensionsKt.removeFromParent(startSdkAdIcpContainer, true); } - // 通知优先级高的弹窗可以显示了 - AppExecutor.getUiExecutor().execute(() -> { - mMainWrapperFragment.showDialog(); - }); + onSplashHidden(); + } + + private void onSplashHidden() { + // 通知全局弹窗可以进行显示 + AppExecutor.getUiExecutor().execute(GlobalPriorityChainHelper.INSTANCE::start); } /** @@ -747,8 +734,8 @@ public class MainActivity extends BaseActivity { } Utils.log(bundle); - if (bundle.getInt(EntranceConsts.KEY_POSITION) != -1) { - EventBus.getDefault().post(new EBSkip(MainActivity.EB_SKIP_MAIN, bundle.getInt(EntranceConsts.KEY_POSITION))); + if (!TextUtils.isEmpty(bundle.getString(EntranceConsts.KEY_TYPE, ""))) { + MainWrapperRepository.Companion.getInstance().sendSelectTabEvent(bundle.getString(EntranceConsts.KEY_TYPE)); } } }, 500); @@ -788,8 +775,6 @@ public class MainActivity extends BaseActivity { } else { toast("抱歉,暂未找到相关内容"); } - LogUtils.uploadPackageSkip("external_jump", "进入游戏库", "", ""); - EventBus.getDefault().post(new EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_GAME)); return true; } return false; @@ -884,9 +869,8 @@ public class MainActivity extends BaseActivity { protected void onSaveInstanceState(@NotNull Bundle outState) { super.onSaveInstanceState(outState); outState.clear(); - outState.putInt(CURRENT_PAGE, mMainWrapperFragment.getCurrentItem()); if (mMainWrapperFragment != null) { - outState.putInt(BaseFragment_ViewPager.ARGS_INDEX, mMainWrapperFragment.getCurrentItem()); + outState.putInt(CURRENT_PAGE, mMainWrapperFragment.getCurrentItem()); } } @@ -914,10 +898,6 @@ public class MainActivity extends BaseActivity { && getIntent().getBooleanExtra(EntranceConsts.KEY_REQUIRE_REDIRECT, false)) { doSkip(); } - - if (getIntent().getBooleanExtra(SWITCH_TO_VIDEO, false)) { - mMainWrapperFragment.setCurrentItem(2); - } } // 连接上网络事件 @@ -948,25 +928,6 @@ public class MainActivity extends BaseActivity { return "游戏首页"; } - public static void skipToMainActivity(Context context, int position) { - Activity activity = AppManager.getInstance().findActivity(MainActivity.class); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 - && activity != null && !activity.isDestroyed()) { - EventBus.getDefault().post(new EBSkip(MainActivity.EB_SKIP_MAIN, position)); - } else { - Timer timer = new Timer(); - timer.schedule(new TimerTask() { - @Override - public void run() { - EventBus.getDefault().post(new EBSkip(MainActivity.EB_SKIP_MAIN, position)); - } - }, 300); - } - - Intent intent = MainActivity.getMainIntent(context); - context.startActivity(intent); - } - //需要提前初始化微博sdk,否则第一次分享或授权登录会失败 private void initWBSDK() { IWBAPI mWBAPI = WBAPIFactory.createWBAPI(this); diff --git a/app/src/main/java/com/gh/gamecenter/SearchActivity.kt b/app/src/main/java/com/gh/gamecenter/SearchActivity.kt index 9761905478..9a43b48cd1 100644 --- a/app/src/main/java/com/gh/gamecenter/SearchActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/SearchActivity.kt @@ -116,10 +116,23 @@ open class SearchActivity : BaseActivity() { } 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( GlobalActivityManager.getCurrentPageEntity().pageId, GlobalActivityManager.getCurrentPageEntity().pageName, - intent.getStringExtra(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: "" + intent.getStringExtra(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: "", + bottomTab, + multiTabId, + multiTabName, + customPageId, + customPageName, + searchBoxPattern ) } @@ -362,12 +375,33 @@ open class SearchActivity : BaseActivity() { hint: String, entrance: String, sourceEntrance: String + ): Intent = getIntent(context, searchImmediately, hint, entrance, sourceEntrance, "", "", "", "", "", "") + + @JvmStatic + fun getIntent( + context: Context, + searchImmediately: Boolean, + hint: String, + entrance: String, + sourceEntrance: String, + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + customPageId: String = "", + customPageName: String = "", + searchBoxPattern: String = "" ): Intent { val intent = Intent(context, SearchActivity::class.java) intent.putExtra(KEY_SEARCH_IMMEDIATELY, searchImmediately) intent.putExtra(EntranceConsts.KEY_HINT, hint) intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance) intent.putExtra(EntranceConsts.KEY_SOURCE_ENTRANCE, sourceEntrance) + intent.putExtra(EntranceConsts.KEY_BOTTOM_TAB_NAME, bottomTab) + intent.putExtra(EntranceConsts.KEY_MULTI_TAB_NAV_ID, multiTabId) + intent.putExtra(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, multiTabName) + intent.putExtra(EntranceConsts.KEY_CUSTOM_PAGE_ID, customPageId) + intent.putExtra(EntranceConsts.KEY_CUSTOM_PAGE_NAME, customPageName) + intent.putExtra(EntranceConsts.KEY_SEARCH_BOX_PATTERN, searchBoxPattern) return intent } } diff --git a/app/src/main/java/com/gh/gamecenter/SkipActivity.java b/app/src/main/java/com/gh/gamecenter/SkipActivity.java index e0b473257b..cc6e98b66f 100644 --- a/app/src/main/java/com/gh/gamecenter/SkipActivity.java +++ b/app/src/main/java/com/gh/gamecenter/SkipActivity.java @@ -68,7 +68,6 @@ import com.gh.gamecenter.video.videomanager.VideoManagerActivity; import com.gh.vspace.shortcut.OnCreateShortcutResult; import com.gh.vspace.shortcut.ShortcutManager; import com.gh.vspace.shortcut.ShortcutPermissionTipsDialog; -import com.halo.assistant.HaloApp; import com.lightgame.config.CommonDebug; import com.lightgame.utils.Utils; import com.muugi.shortcut.core.Executor; @@ -145,7 +144,7 @@ public class SkipActivity extends BaseActivity { DirectUtils.directToArticle(this, path, ENTRANCE_BROWSER); break; case HOST_GAME: - DirectUtils.directToGameDetail(this, path, ENTRANCE_BROWSER, "true".equals(uri.getQueryParameter("auto_download")), to, null); + DirectUtils.directToGameDetail(this, path, "", ENTRANCE_BROWSER, "true".equals(uri.getQueryParameter("auto_download")), to, null); break; case HOST_COLUMN: DirectUtils.directToSubject(this, path, uri.getQueryParameter(KEY_NAME), ENTRANCE_BROWSER, null, false); @@ -171,17 +170,7 @@ public class SkipActivity extends BaseActivity { DirectUtils.directToToolbox(this, uri.getQueryParameter("gameId"), uri.getQueryParameter("toolboxUrl"), ENTRANCE_BROWSER); break; case HOST_COMMUNITY: - // 把切换放到 MainActivity 处理 - if (HaloApp.getInstance().isRunningForeground) { - intent = new Intent(this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(MainActivity.SWITCH_TO_COMMUNITY, true); - } else { - bundle = new Bundle(); - bundle.putBoolean(MainActivity.SWITCH_TO_COMMUNITY, true); - intent = SplashScreenActivity.getSplashScreenIntent(this, bundle); - } - startActivity(intent); + DirectUtils.directToHomeCommunityTab(this); break; // 社区文章格式一 case "community.article": @@ -254,17 +243,7 @@ public class SkipActivity extends BaseActivity { TextUtils.isEmpty(sectionName) ? "" : sectionName, false, ""); break; case HOST_VIDEO_STREAMING_HOME: - // 把切换放到 MainActivity 处理 - if (HaloApp.getInstance().isRunningForeground) { - intent = new Intent(this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(MainActivity.SWITCH_TO_VIDEO, true); - } else { - bundle = new Bundle(); - bundle.putBoolean(MainActivity.SWITCH_TO_VIDEO, true); - intent = SplashScreenActivity.getSplashScreenIntent(this, bundle); - } - startActivity(intent); + DirectUtils.directToHomeVideoTab(this); break; case HOST_VIDEO_STREAMING_DESC: DirectUtils.directToGameDetailVideoStreaming(this, path, ENTRANCE_BROWSER); @@ -324,7 +303,7 @@ public class SkipActivity extends BaseActivity { DirectUtils.directCategoryDirectory(this, path, title, ENTRANCE_BROWSER, "浏览器"); break; case HOST_COLUMN_COLLECTION: - DirectUtils.directToColumnCollection(this, path, -1, ENTRANCE_BROWSER, "", "", "", null); + DirectUtils.directToColumnCollection(this, path, -1, ENTRANCE_BROWSER, "", "", "", "", null,false); break; case EntranceConsts.HOST_BLOCK: name = uri.getQueryParameter("name"); @@ -397,8 +376,7 @@ public class SkipActivity extends BaseActivity { DirectUtils.directToGameRatingDetail(this, uri.getQueryParameter(EntranceConsts.KEY_GAME_ID), uri.getQueryParameter(EntranceConsts.KEY_COMMENT_ID), ENTRANCE_BROWSER); break; case EntranceConsts.HOST_FORUM: - position = uri.getQueryParameter("position"); - DirectUtils.directToForum(this, TextUtils.isEmpty(position) ? 0 : Integer.parseInt(position)); + DirectUtils.directToHomeCommunityTab(this); break; case EntranceConsts.HOST_HELP_AND_FEEDBACK: if ("vgame".equals(suggestionType)) { @@ -419,7 +397,8 @@ public class SkipActivity extends BaseActivity { JSONObject extJsonObject = new JSONObject(extJson); String qqGameId = extJsonObject.optString("aid"); DirectUtils.directToQGameById(this, qqGameId); - } catch (JSONException ignored) {} + } catch (JSONException ignored) { + } break; default: EntranceUtils.jumpActivity(this, new Bundle()); // 跳转至首页 diff --git a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt index 011a4bad7b..934f67c888 100644 --- a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt @@ -11,6 +11,9 @@ import android.preference.PreferenceManager import android.view.KeyEvent import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat +import androidx.core.text.bold +import androidx.core.text.buildSpannedString +import androidx.core.text.color import com.alibaba.android.arouter.facade.annotation.Route import com.alibaba.android.arouter.launcher.ARouter import com.gh.common.dialog.NewPrivacyPolicyDialogFragment @@ -78,25 +81,37 @@ class SplashScreenActivity : BaseActivity() { if (!PackageFlavorHelper.IS_TEST_FLAVOR) { showPrivacyDialog() } else { + val spanBuilder = buildSpannedString { + append("这个弹窗只会在右上角有环境标签的测试包出现" + + "\n进入应用以后还可以到关于我们页面长按应用图标重新选择") + bold { + color(R.color.text_theme.toColor(this@SplashScreenActivity)) { + append("\n点击这里进行预设置渠道") + } + } + } + // Test dex2oat executeDex2OatInAdvance() DialogHelper.showDialog( - this, - "选择接口环境", - "这个弹窗只会在右上角有环境标签的测试包出现\n进入应用以后还可以到关于我们页面长按应用图标重新选择", - "正式环境", - "测试环境", - { + context = this, + title ="选择环境", + content = spanBuilder, + confirmText = "正式环境", + cancelText = "测试环境", + confirmClickCallback = { SPUtils.setBoolean(Constants.SP_IS_DEV_ENV, false) showPrivacyDialog() }, - { + cancelClickCallback = { SPUtils.setBoolean(Constants.SP_IS_DEV_ENV, true) showPrivacyDialog() }, - false, - "", - "" + uiModificationCallback = { + it.contentTv.setOnClickListener { + EnvHelper.showChangeChannelDialog(this@SplashScreenActivity) + } + } ) } } else { @@ -262,7 +277,6 @@ class SplashScreenActivity : BaseActivity() { runOnIoThread { mViewModel?.deviceDialogSetting() mViewModel?.filterDetailTags() - mViewModel?.authDialog() mViewModel?.postMark(mIsNewForThisVersion) mViewModel?.regulationTestStatus() checkAndPostUsageStats() diff --git a/app/src/main/java/com/gh/gamecenter/SplashScreenViewModel.kt b/app/src/main/java/com/gh/gamecenter/SplashScreenViewModel.kt index 051157ee0e..9c0fb63e8d 100644 --- a/app/src/main/java/com/gh/gamecenter/SplashScreenViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/SplashScreenViewModel.kt @@ -13,7 +13,6 @@ import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.common.utils.toRequestBody import com.gh.gamecenter.core.utils.GsonUtils import com.gh.gamecenter.core.utils.SPUtils -import com.gh.gamecenter.feature.entity.AuthDialogEntity import com.gh.gamecenter.entity.DeviceDialogEntity import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp @@ -77,18 +76,6 @@ class SplashScreenViewModel(application: Application) : AndroidViewModel(applica }) } - /** - * 获取游戏实名认证弹窗 - */ - fun authDialog() { - mApi.authDialog() - .subscribe(object : Response?>() { - override fun onResponse(response: List?) { - super.onResponse(response) - SPUtils.setString(Constants.SP_AUTH_DIALOG, GsonUtils.toJson(response)) - } - }) - } /** * 判断新老用户 diff --git a/app/src/main/java/com/gh/gamecenter/ToolBoxActivity.java b/app/src/main/java/com/gh/gamecenter/ToolBoxActivity.java deleted file mode 100644 index 97ed2b33ff..0000000000 --- a/app/src/main/java/com/gh/gamecenter/ToolBoxActivity.java +++ /dev/null @@ -1,283 +0,0 @@ -//package com.gh.gamecenter; -// -//import android.content.Context; -//import android.content.Intent; -//import android.os.Bundle; -//import android.text.TextUtils; -//import android.view.View; -//import android.view.inputmethod.EditorInfo; -//import android.widget.EditText; -//import android.widget.LinearLayout; -//import android.widget.TextView; -// -//import com.gh.gamecenter.common.callback.OnRequestCallBackListener; -//import com.gh.gamecenter.common.base.activity.ToolBarActivity; -//import com.gh.common.util.EntranceUtils; -//import com.gh.gamecenter.core.utils.MtaHelper; -//import com.gh.gamecenter.common.utils.TextHelper; -//import com.gh.gamecenter.core.utils.UrlFilterUtils; -//import com.gh.gamecenter.common.view.VerticalItemDecoration; -//import com.gh.gamecenter.adapter.ToolBoxRvAdapter; -//import com.gh.gamecenter.common.entity.ToolBoxEntity; -//import com.gh.gamecenter.retrofit.Response; -//import com.gh.gamecenter.retrofit.RetrofitManager; -//import com.gh.gamecenter.common.entity.SuggestType; -//import com.google.android.material.appbar.AppBarLayout; -//import com.lightgame.utils.Util_System_Keyboard; -//import com.lightgame.utils.Utils; -// -//import java.util.List; -// -//import androidx.annotation.NonNull; -//import androidx.annotation.Nullable; -//import androidx.recyclerview.widget.LinearLayoutManager; -//import androidx.recyclerview.widget.RecyclerView; -//import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -//import butterknife.BindView; -//import butterknife.OnClick; -//import io.reactivex.android.schedulers.AndroidSchedulers; -//import io.reactivex.schedulers.Schedulers; -// -///** -// * Created by khy on 24/05/17. -// */ -// -//public class ToolBoxActivity extends ToolBarActivity implements SwipeRefreshLayout.OnRefreshListener, OnRequestCallBackListener { -// -// @BindView(R.id.et_search) -// public EditText searchEt; -// @BindView(R.id.tv_search) -// public TextView searchTv; -// @BindView(R.id.tv_back) -// public View backTv; -// @BindView(R.id.toolbox_appbar) -// AppBarLayout mAppBar; -// @BindView(R.id.toolbox_rv) -// RecyclerView mToolboxRv; -// @BindView(R.id.reuse_none_data) -// LinearLayout mNoneData; -// @BindView(R.id.reuse_tv_none_data) -// TextView mNoneDataTv; -// @BindView(R.id.reuse_no_connection) -// LinearLayout mNoConnection; -// @BindView(R.id.toolbox_refresh) -// SwipeRefreshLayout mRefresh; -// @BindView(R.id.reuse_ll_loading) -// View mLoading; -// -// private LinearLayoutManager mLayoutManager; -// private ToolBoxRvAdapter mRvAdapter; -// private ToolBoxRvAdapter mNormalRvAdapter; -// -// private boolean mIsSearch; // 记录页面状态 搜索页面/普通页面 -// private String mSearchKey; // 记录搜索关键字 -// -// Runnable runnable = () -> changeAdapter(true); -// -// @NonNull -// public static Intent getIntent(Context context, String entrance) { -// Intent intent = new Intent(context, ToolBoxActivity.class); -// intent.putExtra(EntranceUtils.KEY_ENTRANCE, entrance); -// return intent; -// } -// -// @Override -// protected int getLayoutId() { -// return R.layout.activity_toolbox; -// } -// -// @Override -// protected void onCreate(Bundle savedInstanceState) { -// super.onCreate(savedInstanceState); -// setNavigationTitle("光环工具箱"); -// -// mRefresh.setColorSchemeResources(R.color.theme); -// mRefresh.setOnRefreshListener(this); -// -// // 跳转到工具箱 https://gitlab.ghzs.com/pm/halo-app-issues/issues/636 -// String gameId = getIntent().getStringExtra(EntranceUtils.KEY_GAMEID); -// String targetUrl = getIntent().getStringExtra(EntranceUtils.KEY_URL); -// if (!TextUtils.isEmpty(targetUrl) && !TextUtils.isEmpty(gameId)) { -// findGameAndOpenItsToolboxWebview(gameId, targetUrl); -// } -// -// mLayoutManager = new LinearLayoutManager(this); -// mToolboxRv.setLayoutManager(mLayoutManager); -// mRvAdapter = new ToolBoxRvAdapter(this, this, mIsSearch, mSearchKey); -// mToolboxRv.addItemDecoration(new VerticalItemDecoration(this, 8, false)); -// mToolboxRv.setAdapter(mRvAdapter); -// -// mNormalRvAdapter = mRvAdapter; -// -// mToolboxRv.addOnScrollListener(new RecyclerView.OnScrollListener() { -// @Override -// public void onScrollStateChanged(RecyclerView recyclerView, int newState) { -// super.onScrollStateChanged(recyclerView, newState); -// if (newState == RecyclerView.SCROLL_STATE_IDLE && mLayoutManager.findLastVisibleItemPosition() + 1 == mRvAdapter -// .getItemCount()) { -// if (!mRvAdapter.isOver() && !mRvAdapter.isLoading() && !mRvAdapter.isNetworkError()) { -// mRvAdapter.loadData(); -// } -// } -// } -// }); -// -// mAppBar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> { -// if (verticalOffset == 0) { -// mRefresh.setEnabled(true); -// } else { -// mRefresh.setEnabled(false); -// } -// int totalScrollRange = appBarLayout.getTotalScrollRange(); -// if (totalScrollRange == -verticalOffset) { -// Util_System_Keyboard.hideSoftKeyboard(this); -// } -// }); -// -// initSearch(); -// } -// -// private void findGameAndOpenItsToolboxWebview(String gameId, String url) { -// RetrofitManager.getInstance() -// .getApi() -// .getGameToolBoxData(1, UrlFilterUtils.getFilterQuery("game_id", gameId)) -// .subscribeOn(Schedulers.io()) -// .observeOn(AndroidSchedulers.mainThread()) -// .subscribe(new Response>() { -// @Override -// public void onResponse(@Nullable List response) { -// if (response == null) return; -// -// for (ToolBoxEntity toolbox : response) { -// if (url.equals(toolbox.getUrl())) { -// Intent intent = WebActivity.getWebByCollectionTools(ToolBoxActivity.this, toolbox, false); -// startActivity(intent); -// } -// } -// } -// }); -// } -// -// private void initSearch() { -// backTv.setOnClickListener(v -> search(false, "")); -// -// TextHelper.limitTheLengthOfEditText(searchEt, 20, () -> { -// Utils.toast(this, "最多输入20字"); -// }); -// -// searchTv.setOnClickListener(v -> { -// MtaHelper.onEvent("我的光环_新", "工具箱", "点击搜索"); -// if (TextUtils.isEmpty(searchEt.getText().toString())) { -// Utils.toast(this, R.string.search_hint); -// return; -// } -// Util_System_Keyboard.hideSoftKeyboard(this,searchEt); -// search(true, searchEt.getText().toString()); -// }); -// -// searchEt.setOnFocusChangeListener((v, hasFocus) -> { -// if (!hasFocus) { -// Util_System_Keyboard.hideSoftKeyboard(this, searchEt); -// } -// }); -// -// searchEt.setOnEditorActionListener((v, actionId, event) -> { -// if (actionId == EditorInfo.IME_ACTION_SEARCH) { -// searchTv.performClick(); -// } -// return false; -// }); -// } -// -// -// @OnClick({R.id.reuse_no_connection, R.id.reuse_none_data}) -// public void onClick(View view) { -// if (view.getId() == R.id.reuse_no_connection) { -// mLoading.setVisibility(View.VISIBLE); -// mNoConnection.setVisibility(View.GONE); -// mLoading.postDelayed(runnable, 1000); -// } else if (view.getId() == R.id.reuse_none_data) { -// if (mIsSearch) { -// // TODO反馈 -// MtaHelper.onEvent("我的光环_新", "工具箱", "点击反馈"); -// SuggestionActivity.startSuggestionActivity(this, SuggestType.functionSuggest, null, null); -// } -// } -// } -// -// @Override -// public void loadDone() { -// mRefresh.setRefreshing(false); -// mNoneData.setVisibility(View.GONE); -// mNoConnection.setVisibility(View.GONE); -// mLoading.setVisibility(View.GONE); -// } -// -// @Override -// public void loadDone(Object obj) { -// -// } -// -// @Override -// public void loadEmpty() { -// mRefresh.setRefreshing(false); -// mNoneData.setVisibility(View.VISIBLE); -// mNoConnection.setVisibility(View.GONE); -// mLoading.setVisibility(View.GONE); -// if (mIsSearch) { -// mNoneDataTv.setText("未找到结果,点我反馈"); -// } else { -// mNoneDataTv.setText(getResources().getString(R.string.game_empty)); -// } -// } -// -// @Override -// public void loadError() { -// mRefresh.setRefreshing(false); -// mNoneData.setVisibility(View.GONE); -// mNoConnection.setVisibility(View.VISIBLE); -// mLoading.setVisibility(View.GONE); -// } -// -// @Override -// public void onRefresh() { -// mRefresh.postDelayed(runnable, 1000); -// } -// -// public void search(boolean isSearch, String searchKey) { -// if (mNoneData.getVisibility() == View.VISIBLE) { -// mNoneData.setVisibility(View.GONE); -// } -// if (isSearch) { -// mLoading.setVisibility(View.VISIBLE); -// } -// mIsSearch = isSearch; -// mSearchKey = searchKey; -// changeAdapter(false); -// } -// -// private void changeAdapter(boolean isRefresh) { -// if (mIsSearch) { -// mRvAdapter = new ToolBoxRvAdapter(this, this, mIsSearch, mSearchKey); -// } else { -// if (mNormalRvAdapter != null && !isRefresh) { -// mRvAdapter = mNormalRvAdapter; -// } else { -// mRvAdapter = new ToolBoxRvAdapter(this, this, mIsSearch, null); -// mNormalRvAdapter = mRvAdapter; -// } -// } -// mToolboxRv.setAdapter(mRvAdapter); -// -// if (mSearchKey != null) { -// searchEt.setText(mSearchKey); -// searchEt.setSelection(searchEt.getText().length()); -// } -// -// if (mIsSearch) { -// backTv.setVisibility(View.VISIBLE); -// } else { -// backTv.setVisibility(View.GONE); -// } -// } -//} diff --git a/app/src/main/java/com/gh/gamecenter/adapter/GameNewsAdapter.java b/app/src/main/java/com/gh/gamecenter/adapter/GameNewsAdapter.java index 13077a281f..82983259cd 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/GameNewsAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/adapter/GameNewsAdapter.java @@ -12,25 +12,24 @@ import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.ViewHolder; -import com.gh.common.constant.Config; import com.gh.common.util.DataCollectionUtils; -import com.gh.gamecenter.core.utils.DisplayUtils; import com.gh.common.util.NewsUtils; -import com.gh.gamecenter.core.utils.StringUtils; -import com.gh.gamecenter.common.utils.TextHelper; -import com.gh.gamecenter.core.utils.UrlFilterUtils; -import com.gh.gamecenter.NewsDetailActivity; import com.gh.gamecenter.NewsSearchActivity; import com.gh.gamecenter.R; -import com.gh.gamecenter.common.viewholder.FooterViewHolder; import com.gh.gamecenter.adapter.viewholder.GameNewsSearchViewHolder; import com.gh.gamecenter.adapter.viewholder.GameNewsTypeListViewHolder; import com.gh.gamecenter.adapter.viewholder.NewsTextViewHolder; +import com.gh.gamecenter.common.retrofit.Response; +import com.gh.gamecenter.common.utils.TextHelper; +import com.gh.gamecenter.common.viewholder.FooterViewHolder; +import com.gh.gamecenter.core.utils.DisplayUtils; +import com.gh.gamecenter.core.utils.StringUtils; +import com.gh.gamecenter.core.utils.UrlFilterUtils; import com.gh.gamecenter.databinding.GameNewsSearchItemBinding; import com.gh.gamecenter.databinding.GameNewsTypeItemBinding; import com.gh.gamecenter.databinding.NewsTextItemBinding; import com.gh.gamecenter.feature.entity.NewsEntity; -import com.gh.gamecenter.common.retrofit.Response; +import com.gh.gamecenter.newsdetail.NewsDetailActivity; import com.gh.gamecenter.retrofit.RetrofitManager; import com.lightgame.adapter.BaseRecyclerAdapter; import com.lightgame.utils.Utils; @@ -110,7 +109,6 @@ public class GameNewsAdapter extends BaseRecyclerAdapter { @Override public List apply(List list) { // 去除重复数据 - Config.filterPluginArticle(list); return NewsUtils.removeDuplicateData(mNewsList, list); } }) diff --git a/app/src/main/java/com/gh/gamecenter/adapter/MessageDetailAdapter.java b/app/src/main/java/com/gh/gamecenter/adapter/MessageDetailAdapter.java index 9d13c8f3de..b2ee32acc2 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/MessageDetailAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/adapter/MessageDetailAdapter.java @@ -18,35 +18,35 @@ import com.gh.common.util.ConcernContentUtils; import com.gh.common.util.DataCollectionUtils; import com.gh.common.util.DialogUtils; import com.gh.common.util.DirectUtils; -import com.gh.gamecenter.common.utils.ExtensionsKt; -import com.gh.gamecenter.core.utils.DisplayUtils; -import com.gh.gamecenter.common.constant.EntranceConsts; -import com.gh.gamecenter.common.utils.ImageUtils; -import com.gh.gamecenter.core.utils.MtaHelper; import com.gh.common.util.NewsUtils; -import com.gh.gamecenter.core.utils.NumberUtils; -import com.gh.gamecenter.core.utils.StringUtils; -import com.gh.gamecenter.common.utils.TextHelper; -import com.gh.gamecenter.common.utils.TimestampUtils; -import com.gh.gamecenter.NewsDetailActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.ShareCardActivity; import com.gh.gamecenter.ShareCardPicActivity; import com.gh.gamecenter.WebActivity; import com.gh.gamecenter.adapter.viewholder.CommentHeadViewHolder; import com.gh.gamecenter.adapter.viewholder.CommentViewHolder; +import com.gh.gamecenter.common.constant.EntranceConsts; +import com.gh.gamecenter.common.retrofit.JSONObjectResponse; +import com.gh.gamecenter.common.retrofit.OkHttpCache; +import com.gh.gamecenter.common.retrofit.Response; +import com.gh.gamecenter.common.utils.ExtensionsKt; +import com.gh.gamecenter.common.utils.ImageUtils; +import com.gh.gamecenter.common.utils.TextHelper; +import com.gh.gamecenter.common.utils.TimestampUtils; import com.gh.gamecenter.common.viewholder.FooterViewHolder; -import com.gh.gamecenter.feature.databinding.NewsDigestItemBinding; -import com.gh.gamecenter.feature.viewholder.NewsDigestViewHolder; +import com.gh.gamecenter.core.utils.DisplayUtils; +import com.gh.gamecenter.core.utils.MtaHelper; +import com.gh.gamecenter.core.utils.NumberUtils; +import com.gh.gamecenter.core.utils.StringUtils; import com.gh.gamecenter.databinding.CommentHeadItemBinding; +import com.gh.gamecenter.feature.databinding.NewsDigestItemBinding; import com.gh.gamecenter.feature.entity.ArticleCommentParent; import com.gh.gamecenter.feature.entity.CommentEntity; import com.gh.gamecenter.feature.entity.ConcernEntity; import com.gh.gamecenter.feature.eventbus.EBDeleteComment; +import com.gh.gamecenter.feature.viewholder.NewsDigestViewHolder; import com.gh.gamecenter.manager.VisitManager; -import com.gh.gamecenter.common.retrofit.JSONObjectResponse; -import com.gh.gamecenter.common.retrofit.OkHttpCache; -import com.gh.gamecenter.common.retrofit.Response; +import com.gh.gamecenter.newsdetail.NewsDetailActivity; import com.gh.gamecenter.retrofit.RetrofitManager; import com.lightgame.adapter.BaseRecyclerAdapter; import com.lightgame.utils.Utils; diff --git a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt index c01d91ccdb..ec8bdd7ba4 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt @@ -57,7 +57,7 @@ class DetailViewHolder( val gameEntity: GameEntity, val isNewsDetail: Boolean, // 新闻详情不显示下载的游戏名, 只显示下载状态 entrance: String?, - name: String, + name: String?, title: String?, val traceEvent: ExposureEvent?, val isSupportDualButton: Boolean = false // 是否支持双下载按钮,不支持的时候跟普通列表意义选用优先级高的那个来显示 @@ -104,7 +104,7 @@ class DetailViewHolder( val localDownloadListener = OnDetailDownloadClickListener( mViewHolder = this, mEntrance = entrance, - mName = name, + mName = name ?: "", mTitle = title ?: "", mAsVGame = false, mShowDualDownloadButton = gameDownloadMode == GameEntity.GAME_DOWNLOAD_BUTTON_MODE_DUAL, @@ -114,7 +114,7 @@ class DetailViewHolder( val vGameDownloadListener = OnDetailDownloadClickListener( mViewHolder = this, mEntrance = entrance, - mName = name, + mName = name ?: "", mTitle = title ?: "", mAsVGame = true, mShowDualDownloadButton = gameDownloadMode == GameEntity.GAME_DOWNLOAD_BUTTON_MODE_DUAL, @@ -615,7 +615,6 @@ class DetailViewHolder( builder.addHandler(BrowserInstallHandler()) builder.addHandler(PackageCheckHandler()) builder.addHandler(DownloadDialogHelperHandler()) - builder.addHandler(CertificationHandler()) builder.addHandler(VersionNumberHandler()) builder.addHandler(LandPageAddressHandler()) builder.addHandler(OverseaDownloadHandler()) @@ -624,7 +623,6 @@ class DetailViewHolder( download(asVGame, isSubscribe as Boolean) } } else { - builder.addHandler(CertificationHandler()) builder.addHandler(VersionNumberHandler()) builder.setProcessEndCallback { _: Boolean?, _: Any? -> DownloadDialog.showDownloadDialog( diff --git a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/ViewHolder.kt b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/ViewHolder.kt index 1db46a235c..294909c3c7 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/ViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/ViewHolder.kt @@ -19,4 +19,7 @@ class CommonCollectionDetailTwoItemViewHolder(val binding: CommonCollectionDetai BaseRecyclerViewHolder(binding.root) class CommonCollectionImageTextItemViewHolder(val binding: CommonCollectionImageTextItemBinding) : + BaseRecyclerViewHolder(binding.root) + +class CommonCollectionDetailTwoItemHorizontalViewHolder(val binding: CommonCollectionDetailTwoItemHorizontalCustomBinding) : BaseRecyclerViewHolder(binding.root) \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/amway/AmwayAdapter.kt b/app/src/main/java/com/gh/gamecenter/amway/AmwayAdapter.kt index a2b7fd88c7..3e860dd334 100644 --- a/app/src/main/java/com/gh/gamecenter/amway/AmwayAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/amway/AmwayAdapter.kt @@ -272,12 +272,6 @@ class AmwayAdapter( } binding.commentContainer.setOnClickListener { - val exposureSource = if (useAlternativeLayout) { - arrayListOf(ExposureSource("新首页"), ExposureSource("安利墙")) - } else { - arrayListOf(ExposureSource("安利墙")) - }.toJson() - val intent = RatingReplyActivity.getIntent( context = context, gameId = amway.game.id, @@ -285,7 +279,7 @@ class AmwayAdapter( comment = amway.comment, commentId = amway.comment.id, showKeyboardIfReplyListIsEmpty = false, - exposureSource = exposureSource, + exposureSource = basicExposureSource.toJson(), entrance = viewModel.entrance ?: "", path = EntranceConsts.ENTRANCE_AMWAY ) diff --git a/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt b/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt index 6a391b3be1..aca54d13c7 100644 --- a/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt @@ -4,13 +4,14 @@ import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.View -import android.view.ViewGroup import android.view.ViewGroup.MarginLayoutParams import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.recyclerview.widget.RecyclerView import com.ethanhua.skeleton.Skeleton import com.gh.common.exposure.ExposureListener +import com.gh.common.iinterface.ISearchToolbarTab import com.gh.common.util.DialogUtils import com.gh.common.util.NewFlatLogUtils import com.gh.common.xapk.XapkInstaller @@ -35,7 +36,6 @@ import com.gh.gamecenter.databinding.FragmentAmwayBinding import com.gh.gamecenter.entity.RatingComment import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage -import com.gh.gamecenter.fragment.HomeSearchToolWrapperFragment import com.gh.gamecenter.gamedetail.rating.RatingFragment import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity @@ -54,9 +54,9 @@ class AmwayFragment : LazyListFragment() { private var mAdapter: AmwayAdapter? = null - private var mIsFromMainWrapper = false private var mUseAlternativeLayout = false private var mIsCollapsed = false + private var mTabIndex = -1 private val dataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { @@ -73,9 +73,9 @@ class AmwayFragment : LazyListFragment() { } override fun onCreate(savedInstanceState: Bundle?) { - mUseAlternativeLayout = arguments?.getBoolean(EntranceConsts.KEY_IS_HOME, false) ?: false - mIsFromMainWrapper = arguments?.getBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, false) ?: false + mTabIndex = arguments?.getInt(EntranceConsts.KEY_TAB_INDEX, -1) ?: -1 super.onCreate(savedInstanceState) + mUseAlternativeLayout = mIsFromTabWrapper } override fun getStubLayoutId() = R.layout.fragment_stub @@ -142,8 +142,8 @@ class AmwayFragment : LazyListFragment() { override fun provideListAdapter(): ListAdapter<*> { if (mAdapter == null) { val basicExposureSource = arrayListOf().apply { - arguments?.getParcelable(EntranceConsts.KEY_EXPOSURE_SOURCE)?.let { - add(it) + arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST)?.let { + addAll(it) } add(ExposureSource("安利墙", "")) } @@ -156,24 +156,28 @@ class AmwayFragment : LazyListFragment() { override fun isAutomaticLoad() = false private fun initDefaultLayout() { + val statusBarHeight = DisplayUtils.getStatusBarHeight(context?.resources) mDefaultBinding?.run { nightMaskView.goneIf(!mIsDarkModeOn) // toolbar 消费 fitsSystemWindows 避免在 collapsingToolbar 下面出现多出来的 padding // [https://stackoverflow.com/questions/48137666/viewgroup-inside-collapsingtoolbarlayout-show-extra-bottom-padding-when-set-fits] ViewCompat.setOnApplyWindowInsetsListener(appbar) { _, insets -> - (toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop - insets.consumeSystemWindowInsets() + (toolbar.layoutParams as MarginLayoutParams).topMargin = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top + WindowInsetsCompat.CONSUMED + } + if (mIsFromMainWrapper) { + (toolbar.layoutParams as MarginLayoutParams).topMargin = statusBarHeight } - val collapsingTrigger = 66F.dip2px() + DisplayUtils.getStatusBarHeight(context?.resources) + val collapsingTrigger = 66F.dip2px() + statusBarHeight toolbar.setNavigationOnClickListener { requireActivity().finish() } collapsingToolbar.scrimVisibleHeightTrigger = collapsingTrigger collapsingToolbar.scrimShownAction = { mIsCollapsed = it - DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && it) + updateStatusBar() if (it) { titleTv.alpha = 1F titleTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.text_black)) @@ -181,7 +185,7 @@ class AmwayFragment : LazyListFragment() { titleTv.setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) } - if (mIsFromMainWrapper) { + if (mIsFromMainWrapper || mIsFromTabWrapper) { toolbar.navigationIcon = null } else { toolbar.setNavigationIcon(if (it) R.drawable.ic_bar_back else R.drawable.ic_toolbar_back_white) @@ -207,11 +211,6 @@ class AmwayFragment : LazyListFragment() { mListRefresh?.isEnabled = absOffset <= 2 } - if (mIsFromMainWrapper) { - fab.layoutParams = (fab.layoutParams as MarginLayoutParams).apply { - setMargins(0, 0, 14F.dip2px(), 72F.dip2px()) - } - } fab.setOnClickListener { MtaHelper.onEvent("发表评论", "进入", "上墙") ifLogin("安利墙") { @@ -224,7 +223,7 @@ class AmwayFragment : LazyListFragment() { mListRefresh?.setProgressViewOffset( false, 0, - 118F.dip2px() + DisplayUtils.getStatusBarHeight(requireContext().resources) + 118F.dip2px() + statusBarHeight ) mSkeletonScreen = Skeleton.bind(mDefaultBinding?.skeletonPlaceholder).shimmer(false).load(R.layout.fragment_amway_skeleton) @@ -244,14 +243,7 @@ class AmwayFragment : LazyListFragment() { mAlternativeBinding?.listRv?.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) - val scrollY = recyclerView.computeVerticalScrollOffset() - mAlternativeBinding?.headerContainer?.translationY = -scrollY.toFloat() - if (parentFragment is HomeSearchToolWrapperFragment && isSupportVisible) { - (parentFragment as HomeSearchToolWrapperFragment).onScrollChanged( - mAlternativeBinding?.headerContainer?.measuredHeight ?: 0, - scrollY - ) - } + onScrollChanged() } }) @@ -274,7 +266,12 @@ class AmwayFragment : LazyListFragment() { mElapsedHelper.resetCounting() mElapsedHelper.resumeCounting() - if (mIsFromMainWrapper) { + updateStatusBar() + } + + private fun updateStatusBar() { + if (!mIsFromTabWrapper && isSupportVisible) { + DisplayUtils.transparentStatusBar(requireActivity()) DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && mIsCollapsed) } } @@ -330,17 +327,16 @@ class AmwayFragment : LazyListFragment() { } } - fun onScrollChanged() { - if (parentFragment is HomeSearchToolWrapperFragment) { - val scrollY = mAlternativeBinding?.listRv?.computeVerticalScrollOffset() - if (scrollY != null) { - mAlternativeBinding?.headerContainer?.translationY = -scrollY.toFloat() - if (parentFragment is HomeSearchToolWrapperFragment && isSupportVisible) { - (parentFragment as HomeSearchToolWrapperFragment).onScrollChanged( - mAlternativeBinding?.headerContainer?.measuredHeight ?: 0, - scrollY - ) - } + fun onScrollChanged(isDarkModeChanged: Boolean = false) { + val scrollY = mAlternativeBinding?.listRv?.computeVerticalScrollOffset() + if (scrollY != null) { + mAlternativeBinding?.headerContainer?.translationY = -scrollY.toFloat() + if (mTabIndex == (parentFragment as? ISearchToolbarTab)?.getCurrentTabIndex()) { + (parentFragment as ISearchToolbarTab).onScrollChanged( + mAlternativeBinding?.headerContainer?.measuredHeight ?: 0, + scrollY, + isDarkModeChanged + ) } } } @@ -355,15 +351,13 @@ class AmwayFragment : LazyListFragment() { mAlternativeBinding?.secondBackground?.goneIf(mIsDarkModeOn) mAlternativeBinding?.topBackgroundView?.goneIf(mIsDarkModeOn) mAlternativeBinding?.listRefresh?.setBackgroundColor(R.color.ui_background.toColor(requireContext())) - onScrollChanged() + onScrollChanged(true) } else { mDefaultBinding?.nightMaskView?.goneIf(!mIsDarkModeOn) mDefaultBinding?.appbar?.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) mDefaultBinding?.collapsingToolbar?.setContentScrimColor(R.color.ui_surface.toColor(requireContext())) mDefaultBinding?.listRefresh?.setBackgroundColor(R.color.ui_background.toColor(requireContext())) } - if (mIsFromMainWrapper) { - DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && mIsCollapsed) - } + updateStatusBar() } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Activity.kt b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Activity.kt index 4c4079e867..12a21b0f36 100644 --- a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Activity.kt +++ b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Activity.kt @@ -12,7 +12,6 @@ class CategoryV2Activity : DownloadToolbarActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setToolbarMenu(R.menu.menu_search) updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) } diff --git a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Fragment.kt b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Fragment.kt index 3874614a3f..3a3f658e53 100644 --- a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Fragment.kt +++ b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Fragment.kt @@ -24,13 +24,13 @@ import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.databinding.FragmentCategoryBinding import com.gh.gamecenter.entity.CategoryEntity import com.gh.gamecenter.entity.SidebarsEntity -import com.gh.gamecenter.fragment.HomeSearchToolWrapperViewModel +import com.gh.gamecenter.wrapper.SearchToolbarTabWrapperViewModel class CategoryV2Fragment : LazyFragment() { private var mBinding: FragmentCategoryBinding? = null private var mViewModel: CategoryV2ViewModel? = null - private var mHomeViewModel: HomeSearchToolWrapperViewModel? = null + private var mHomeViewModel: SearchToolbarTabWrapperViewModel? = null private var mEntity: SidebarsEntity? = null private var mSpecialCatalogFragment: SpecialCatalogFragment? = null private var mCategoryV2ListFragment: CategoryV2ListFragment? = null @@ -68,8 +68,10 @@ class CategoryV2Fragment : LazyFragment() { // 除了这里以外,下面还有一个判断是否为首页 tab 栏的赋值 mViewModel?.entrance = if (mEntrance.contains("首页")) "首页" else "板块" - if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true) { - mHomeViewModel = viewModelProviderFromParent() + + val multiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: "" + if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true && multiTabNavId.isNotEmpty()) { + mHomeViewModel = viewModelProviderFromParent(SearchToolbarTabWrapperViewModel.Factory(multiTabNavId, ""), multiTabNavId) mViewModel?.entrance = "首页Tab栏" } mViewModel?.logAppearance() @@ -79,7 +81,7 @@ class CategoryV2Fragment : LazyFragment() { override fun initRealView() { super.initRealView() - + initMenu(R.menu.menu_search) setNavigationTitle(mCategoryTitle) mBinding?.run { @@ -491,6 +493,7 @@ class CategoryV2Fragment : LazyFragment() { override fun onDarkModeChanged() { super.onDarkModeChanged() + getItemMenu(R.id.menu_search)?.setIcon(R.drawable.ic_column_search) mBinding?.categoryRv?.adapter?.run { mBinding?.categoryRv?.recycledViewPool?.clear() notifyItemRangeChanged(0, itemCount) diff --git a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt index 5def4b0595..e1474d6a46 100644 --- a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2ListAdapter.kt @@ -242,7 +242,7 @@ class CategoryV2ListAdapter( binding.run { gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F - BindingAdapters.setGameName(gameName, gameEntity, false, null) + BindingAdapters.setGameName(gameName, gameEntity, false) BindingAdapters.setGameTags(labelList, gameEntity) gameRating.setDrawableStart(if (gameEntity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable() else null) gameRating.text = if (gameEntity.commentCount > 3) { diff --git a/app/src/main/java/com/gh/gamecenter/collection/ArticleFragment.java b/app/src/main/java/com/gh/gamecenter/collection/ArticleFragment.java index e2cce7726b..162db65d26 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/ArticleFragment.java +++ b/app/src/main/java/com/gh/gamecenter/collection/ArticleFragment.java @@ -10,18 +10,18 @@ import androidx.recyclerview.widget.RecyclerView; import com.gh.common.util.CollectionUtils; import com.gh.common.util.DataCollectionUtils; -import com.gh.gamecenter.common.utils.DialogHelper; -import com.gh.gamecenter.common.constant.EntranceConsts; -import com.gh.gamecenter.common.view.CustomDividerItemDecoration; -import com.gh.gamecenter.NewsDetailActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.common.baselist.ListAdapter; import com.gh.gamecenter.common.baselist.ListFragment; import com.gh.gamecenter.common.baselist.LoadType; -import com.gh.gamecenter.feature.entity.NewsEntity; +import com.gh.gamecenter.common.constant.EntranceConsts; +import com.gh.gamecenter.common.utils.DialogHelper; +import com.gh.gamecenter.common.view.CustomDividerItemDecoration; import com.gh.gamecenter.eventbus.EBCollectionChanged; +import com.gh.gamecenter.feature.entity.NewsEntity; import com.gh.gamecenter.history.IBatchDelete; import com.gh.gamecenter.history.ManageOption; +import com.gh.gamecenter.newsdetail.NewsDetailActivity; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; diff --git a/app/src/main/java/com/gh/gamecenter/collection/ToolsFragment.java b/app/src/main/java/com/gh/gamecenter/collection/ToolsFragment.java index ffc24f91f5..6f8383e4fe 100644 --- a/app/src/main/java/com/gh/gamecenter/collection/ToolsFragment.java +++ b/app/src/main/java/com/gh/gamecenter/collection/ToolsFragment.java @@ -11,8 +11,6 @@ import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; import com.gh.common.util.CollectionUtils; -import com.gh.gamecenter.common.view.CustomDividerItemDecoration; -import com.gh.gamecenter.NewsDetailActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.WebActivity; import com.gh.gamecenter.common.baselist.ListAdapter; @@ -20,8 +18,10 @@ import com.gh.gamecenter.common.baselist.ListFragment; import com.gh.gamecenter.common.baselist.LoadType; import com.gh.gamecenter.common.baselist.NormalListViewModel; import com.gh.gamecenter.common.entity.ToolBoxEntity; +import com.gh.gamecenter.common.view.CustomDividerItemDecoration; import com.gh.gamecenter.eventbus.EBCollectionChanged; import com.gh.gamecenter.login.user.UserManager; +import com.gh.gamecenter.newsdetail.NewsDetailActivity; import com.gh.gamecenter.retrofit.RetrofitManager; import org.greenrobot.eventbus.Subscribe; diff --git a/app/src/main/java/com/gh/gamecenter/discovery/DiscoveryAdapter.kt b/app/src/main/java/com/gh/gamecenter/discovery/DiscoveryAdapter.kt index 6ae91ff9ee..e9b73530d5 100644 --- a/app/src/main/java/com/gh/gamecenter/discovery/DiscoveryAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/discovery/DiscoveryAdapter.kt @@ -14,7 +14,6 @@ import com.gh.common.util.HomeBottomBarHelper import com.gh.common.util.NewFlatLogUtils import com.gh.gamecenter.BlockActivity import com.gh.gamecenter.GameDetailActivity -import com.gh.gamecenter.MainActivity import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.GameViewHolder import com.gh.gamecenter.common.base.BaseRecyclerViewHolder @@ -33,15 +32,11 @@ import com.gh.gamecenter.discovery.DiscoveryFragment.Companion.INTERESTED_GAME_R import com.gh.gamecenter.discovery.interestedgame.InterestedGameActivity import com.gh.gamecenter.entity.DiscoveryGameCardLabel import com.gh.gamecenter.eventbus.EBDownloadStatus -import com.gh.gamecenter.eventbus.EBSkip 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.fragment.MainWrapperFragment -import com.gh.gamecenter.fragment.MainWrapperRepository import com.lightgame.download.DownloadEntity -import org.greenrobot.eventbus.EventBus class DiscoveryAdapter( context: Context, @@ -220,14 +215,8 @@ class DiscoveryAdapter( } } recommendIv.setOnClickListener { - val navBar = MainWrapperRepository.getInstance().getGameNavBarLiveData().value - if (navBar == null || mContext is DiscoveryActivity) { - val blockData = HomeBottomBarHelper.getDefaultGameBarData() - mContext.startActivity(BlockActivity.getIntent(mContext, blockData, mBaseExposureSource, mEntrance)) - } else { - EventBus.getDefault() - .post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_GAME)) - } + val blockData = HomeBottomBarHelper.getDefaultGameBarData() + mContext.startActivity(BlockActivity.getIntent(mContext, blockData, mBaseExposureSource, mEntrance)) NewFlatLogUtils.logDiscoverPageJumpGamesLibraries() } } @@ -319,7 +308,7 @@ class DiscoveryAdapter( gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F - BindingAdapters.setGameName(gameName, gameEntity, false, null) + BindingAdapters.setGameName(gameName, gameEntity, false) gameRating.setDrawableStart(if (gameEntity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable() else null) gameRating.text = if (gameEntity.commentCount > 3) { if (gameEntity.star == 10.0F) "10" else gameEntity.star.toString() diff --git a/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt b/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt index c84698befc..0b052fc8ba 100644 --- a/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt @@ -27,6 +27,7 @@ import com.gh.gamecenter.DownloadManagerActivity import com.gh.gamecenter.R import com.gh.gamecenter.common.base.fragment.BaseFragment_TabLayout import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.eventbus.EBMiPush import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.core.utils.DisplayUtils @@ -87,7 +88,16 @@ class DownloadFragment : BaseFragment_TabLayout() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - SensorsBridge.trackDownloadManagerEnter() + SensorsBridge.trackDownloadManagerEnter( + mEntrance, + arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_NAME, "") ?: "", + arguments?.getString(EntranceConsts.KEY_GAME_COLUMN_ID, "") ?: "", + arguments?.getString(EntranceConsts.KEY_GAME_COLUMN_NAME, "") ?: "", + arguments?.getString(EntranceConsts.KEY_CUSTOM_PAGE_ID, "") ?: "", + arguments?.getString(EntranceConsts.KEY_CUSTOM_PAGE_NAME, "") ?: "", + arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: "", + arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, "") ?: "" + ) mPermanentInactivePluggableApkList = HomePluggableHelper.getPermanentInactivePluggablePackage() } diff --git a/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java b/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java index b0a88bacf7..ec62756029 100644 --- a/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java +++ b/app/src/main/java/com/gh/gamecenter/download/GameDownloadFragment.java @@ -14,25 +14,26 @@ import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.gh.common.util.DirectUtils; import com.gh.common.xapk.XapkDialogHelper; -import com.gh.gamecenter.common.base.fragment.BaseFragment; -import com.gh.gamecenter.common.utils.DialogHelper; -import com.gh.gamecenter.common.constant.EntranceConsts; -import com.gh.gamecenter.common.utils.NetworkUtils; import com.gh.common.xapk.XapkInstaller; import com.gh.common.xapk.XapkUnzipStatus; import com.gh.download.DownloadManager; import com.gh.gamecenter.DownloadManagerActivity; import com.gh.gamecenter.MainActivity; 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.eventbus.EBMiPush; +import com.gh.gamecenter.common.utils.DialogHelper; import com.gh.gamecenter.common.utils.ExtensionsKt; +import com.gh.gamecenter.common.utils.NetworkUtils; import com.gh.gamecenter.core.utils.DisplayUtils; import com.gh.gamecenter.databinding.DownloadmanagerBinding; import com.gh.gamecenter.eventbus.EBDownloadChanged; -import com.gh.gamecenter.common.eventbus.EBMiPush; import com.gh.gamecenter.eventbus.EBPackage; import com.gh.gamecenter.eventbus.EBUISwitch; -import com.gh.gamecenter.fragment.MainWrapperFragment; +import com.gh.gamecenter.wrapper.MainWrapperFragment; import com.lightgame.download.DataWatcher; import com.lightgame.download.DownloadConfig; import com.lightgame.download.DownloadEntity; @@ -238,7 +239,7 @@ public class GameDownloadFragment extends BaseFragment implements View.OnClickLi mBinding.reuseNoneData.reuseNoneDataDescTv.setText(getString(R.string.game_no_data_desc)); mBinding.reuseNoneData.reuseResetLoadTv.setText("去首页看看"); mBinding.reuseNoneData.reuseResetLoadTv.setVisibility(View.VISIBLE); - mBinding.reuseNoneData.reuseResetLoadTv.setOnClickListener(v -> MainActivity.skipToMainActivity(getActivity(), MainWrapperFragment.INDEX_HOME)); + mBinding.reuseNoneData.reuseResetLoadTv.setOnClickListener(v -> DirectUtils.directToHomeDefaultTab(getActivity())); mBinding.downloadingHintContainer.setOnClickListener(v -> mBinding.downloadmanagerRvShow.scrollToPosition(adapter.getItemCount() - 1)); mBinding.downloadmanagerRvShow.setHasFixedSize(true); diff --git a/app/src/main/java/com/gh/gamecenter/download/InstalledGameViewModel.kt b/app/src/main/java/com/gh/gamecenter/download/InstalledGameViewModel.kt index 76070878c6..117efebb89 100644 --- a/app/src/main/java/com/gh/gamecenter/download/InstalledGameViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/download/InstalledGameViewModel.kt @@ -120,7 +120,6 @@ class InstalledGameViewModel(application: Application) : AndroidViewModel(applic for (entity in gameList) { if (entity.id == id) { val newEntity = entity.copy() - newEntity.isLibaoExists = entity.isLibaoExists // 下载管理不显示镜像游戏,不然会有奇怪的问题 if (entity.shouldUseMirrorInfo()) { diff --git a/app/src/main/java/com/gh/gamecenter/download/NewInstalledGameFragment.kt b/app/src/main/java/com/gh/gamecenter/download/NewInstalledGameFragment.kt index d0bf80da9b..fffd2b2b84 100644 --- a/app/src/main/java/com/gh/gamecenter/download/NewInstalledGameFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/download/NewInstalledGameFragment.kt @@ -8,6 +8,7 @@ import androidx.recyclerview.widget.DefaultItemAnimator import com.ethanhua.skeleton.Skeleton import com.ethanhua.skeleton.ViewSkeletonScreen import com.gh.common.exposure.ExposureListener +import com.gh.common.util.DirectUtils import com.gh.common.util.DownloadItemUtils import com.gh.download.DownloadManager import com.gh.gamecenter.MainActivity @@ -20,16 +21,15 @@ import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.view.FixLinearLayoutManager import com.gh.gamecenter.common.view.VerticalItemDecoration import com.gh.gamecenter.core.utils.DisplayUtils -import com.gh.gamecenter.core.utils.EmptyCallback import com.gh.gamecenter.databinding.FragmentMyGameBinding -import com.gh.gamecenter.feature.entity.GameEntity -import com.gh.gamecenter.feature.entity.GameInstall import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage -import com.gh.gamecenter.fragment.MainWrapperFragment +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.GameInstall import com.gh.gamecenter.manager.PackagesManager import com.gh.gamecenter.packagehelper.PackageRepository import com.gh.gamecenter.packagehelper.PackageViewModel +import com.gh.gamecenter.wrapper.MainWrapperFragment import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity import com.lightgame.download.DownloadStatus @@ -143,7 +143,7 @@ class NewInstalledGameFragment : ToolbarFragment() { mBinding.reuseNoneData.reuseNoneDataDescTv.text = getString(R.string.game_no_data_desc) mBinding.reuseNoneData.reuseResetLoadTv.text = "去首页看看" mBinding.reuseNoneData.reuseResetLoadTv.setOnClickListener { - MainActivity.skipToMainActivity(requireContext(), MainWrapperFragment.INDEX_HOME) + DirectUtils.directToHomeDefaultTab(requireContext()) } mBinding.run { @@ -163,7 +163,7 @@ class NewInstalledGameFragment : ToolbarFragment() { reuseNoneData.reuseNoneDataTv.text = getString(R.string.game_no_data) reuseNoneData.reuseNoneDataDescTv.text = getString(R.string.game_no_data_desc) reuseNoneData.reuseResetLoadTv.setOnClickListener { - MainActivity.skipToMainActivity(activity, MainWrapperFragment.INDEX_HOME) + DirectUtils.directToHomeDefaultTab(requireContext()) } } } diff --git a/app/src/main/java/com/gh/gamecenter/download/UpdatableGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameAdapter.kt index 66e86698eb..fb39ecc2cd 100644 --- a/app/src/main/java/com/gh/gamecenter/download/UpdatableGameAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameAdapter.kt @@ -10,18 +10,23 @@ import androidx.core.view.setPadding import androidx.fragment.app.FragmentActivity import androidx.recyclerview.widget.RecyclerView import com.gh.common.exposure.IExposable -import com.gh.common.util.* +import com.gh.common.util.DialogUtils +import com.gh.common.util.DirectUtils +import com.gh.common.util.PackageInstaller +import com.gh.common.util.PackageLauncher import com.gh.download.DownloadManager import com.gh.download.dialog.DownloadDialog import com.gh.gamecenter.DownloadManagerActivity import com.gh.gamecenter.R -import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.databinding.LayoutPopupContainerBinding +import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.common.utils.NewFlatLogUtils import com.gh.gamecenter.common.view.BugFixedPopupWindow import com.gh.gamecenter.core.utils.CurrentActivityHolder -import com.gh.gamecenter.databinding.* +import com.gh.gamecenter.databinding.ItemUpdatableGameBinding +import com.gh.gamecenter.databinding.ItemUpdatableGameHeaderBinding +import com.gh.gamecenter.databinding.ItemUpdatableOtherGameHintBinding +import com.gh.gamecenter.databinding.LayoutPopupOptionItemBinding import com.gh.gamecenter.entity.GameUpdateEntity import com.gh.gamecenter.eventbus.EBSkip import com.gh.gamecenter.feature.exposure.ExposureEvent @@ -188,6 +193,7 @@ class UpdatableGameAdapter(private var mViewModel: UpdatableGameViewModel) : DirectUtils.directToGameDetail( context = context, id = update.id, + name = update.name ?: "", entrance = mViewModel.entrance, traceEvent = update.exposureEvent ) diff --git a/app/src/main/java/com/gh/gamecenter/download/UpdatableGameFragment.kt b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameFragment.kt index e76230f4d9..325122bd3c 100644 --- a/app/src/main/java/com/gh/gamecenter/download/UpdatableGameFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/download/UpdatableGameFragment.kt @@ -2,6 +2,7 @@ package com.gh.gamecenter.download import android.view.View import com.gh.common.exposure.ExposureListener +import com.gh.common.util.DirectUtils import com.gh.download.DownloadManager import com.gh.gamecenter.MainActivity import com.gh.gamecenter.R @@ -14,9 +15,9 @@ import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.core.utils.EmptyCallback import com.gh.gamecenter.databinding.FragmentGameUpdatableBinding import com.gh.gamecenter.eventbus.EBDownloadStatus -import com.gh.gamecenter.fragment.MainWrapperFragment import com.gh.gamecenter.packagehelper.PackageRepository import com.gh.gamecenter.packagehelper.PackageViewModel +import com.gh.gamecenter.wrapper.MainWrapperFragment import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity import org.greenrobot.eventbus.Subscribe @@ -119,7 +120,7 @@ class UpdatableGameFragment : LazyFragment() { noDataContainer.reuseNoneDataDescTv.text = getString(R.string.game_no_data_desc) noDataContainer.reuseResetLoadTv.text = "去首页看看" noDataContainer.reuseResetLoadTv.setOnClickListener { - MainActivity.skipToMainActivity(activity, MainWrapperFragment.INDEX_HOME) + DirectUtils.directToHomeDefaultTab(requireContext()) } } } diff --git a/app/src/main/java/com/gh/gamecenter/entity/AmwayCommentEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/AmwayCommentEntity.kt index 4a0cba2049..74a3c8c517 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/AmwayCommentEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/AmwayCommentEntity.kt @@ -28,8 +28,6 @@ data class AmwayCommentEntity( var iconSubscript: String? = "", @SerializedName("name") private var mName: String, - @SerializedName("name_suffix") - var nameSuffix: String? = "", @SerializedName(value = "new_tag_style") var tag: List? = arrayListOf(), @SerializedName("new_star") @@ -48,7 +46,7 @@ data class AmwayCommentEntity( @IgnoredOnParcel val name: String? - get() = mName.removeSuffix(".") + (nameSuffix ?: "") + get() = mName.removeSuffix(".") fun toGameEntity(): GameEntity { val gameEntity = GameEntity() diff --git a/app/src/main/java/com/gh/gamecenter/entity/BottomTab.kt b/app/src/main/java/com/gh/gamecenter/entity/BottomTab.kt new file mode 100644 index 0000000000..ed32f2f834 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/BottomTab.kt @@ -0,0 +1,67 @@ +package com.gh.gamecenter.entity + +import android.os.Parcelable +import androidx.annotation.DrawableRes +import com.gh.common.util.ViewPagerFragmentHelper +import com.gh.gamecenter.common.entity.LinkEntity +import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.Parcelize + +@Parcelize +data class BottomTab( + @SerializedName("_id") + val id: String = "", + val name: String = "", + @SerializedName("icon_select") + val iconSelect: String = "", // 选中图片 + @SerializedName("icon_unselect") + val iconUnselect: String = "", // 未选中图片 + @DrawableRes + val iconSelector: Int = 0, // 本地字段 + @SerializedName("js_code") + val jsCode: String = "", // js代码 + val link: LinkEntity? = null, // 通用链接 + @SerializedName("search_style") + val searchStyle: SearchStyle = SearchStyle(), // 搜索样式 + @SerializedName("is_default_page") + var default: Boolean = false, // 是否为默认显示页 + var isTransparentStyle: Boolean = false // 本地字段,透明底部Tab +): Parcelable { + @Parcelize + data class SearchStyle( + @SerializedName("style_type") + var styleType: String = STYLE_TWO_LINES, // 样式类型(two_lines:搜索栏与顶部tab分为两行、apposition:搜索栏与顶部tab同一行) + @SerializedName("search_type") + var searchType: String = TYPE_HALO_GAME, // 搜索类型(halo_game:光环游戏、qq_mini_game:QQ小游戏、bbs:论坛) + ): Parcelable { + companion object { + const val STYLE_TWO_LINES = "two_lines" + const val STYLE_APPOSITION = "apposition" + const val TYPE_HALO_GAME = "halo_game" + const val TYPE_QQ_MINI_GAME = "qq_mini_game" + const val TYPE_BBS = "bbs" + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as BottomTab + // 只对比链接类型和id、搜索样式判断是否相同 + if (link?.type != other.link?.type) return false + if (link?.link == other.link?.link) return false + return searchStyle == other.searchStyle + } + + override fun hashCode(): Int { + var result = link?.type?.hashCode() ?: 0 + result = 31 * result + (link?.link?.hashCode() ?: 0) + result = 31 * result + searchStyle.hashCode() + // 除我的光环页面外,增加计算底部tab id的HashCode + if (link?.type != ViewPagerFragmentHelper.TYPE_MY_HALO) { + result = 31 * result + id.hashCode() + } + return result + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/CommonCollectionEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/CommonCollectionEntity.kt index 00091f28c1..4782fc953f 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/CommonCollectionEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/CommonCollectionEntity.kt @@ -11,13 +11,28 @@ data class CommonCollectionEntity( val type: String = "", val style: String = "", val more: Int = 0, + val layout: Int = -1, // 对应自定义页面的layout样式 @SerializedName("home_page_style") val homePageStyle: String = "horizontal_sliding", //首页样式 (横排滑动:horizontal_sliding 竖排一行两个:1-2) @SerializedName("vertical_line") val verticalLine: String = "", // 竖排时才有数据,代表竖排行数控制 @SerializedName("common_collection_content") val collectionList: MutableList = mutableListOf() -) +) { + val layoutChinese: String + get() = when (layout) { + 0 -> "轮播banner" + 1 -> "导航栏" + 2 -> "金刚区" + 3 -> "横向滑动banner" + 4 -> "双列banner" + 5 -> "横排竖式卡片" + 6 -> "双列竖式卡片" + 7 -> "竖式图文列表" + 8 -> "横排图文列表" + else -> "" + } +} @Parcelize data class CommonCollectionContentEntity( diff --git a/app/src/main/java/com/gh/gamecenter/entity/DataUnionEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/DataUnionEntity.kt new file mode 100644 index 0000000000..b9ba84ddf9 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/DataUnionEntity.kt @@ -0,0 +1,13 @@ +package com.gh.gamecenter.entity + +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.google.gson.annotations.SerializedName + +data class DataUnionEntity( + @SerializedName("bottom_tab") + val bottomTab: List = listOf(), // 底部tab + @SerializedName("multi_tab_nav") + val multiTabNav: MultiTabNav? = null, // 多tab导航页(当默认页为【多tab导航数据】时才有此字段数据) + @SerializedName("custom_page") + val customPage: CustomPageData? = null, // 自定义页面相关信息(当默认页为【自定义页面】时才有此字段数据】) +) \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/GameVideoInfo.kt b/app/src/main/java/com/gh/gamecenter/entity/GameVideoInfo.kt index 3a6aba1521..69b1a35eb1 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/GameVideoInfo.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/GameVideoInfo.kt @@ -9,8 +9,6 @@ class GameVideoInfo( val gameName: String = "", @SerializedName("game_icon") val gameIcon: String = "", - @SerializedName("name_suffix") - var nameSuffix: String? = null, @SerializedName("video_count") val videoCount: Int = 0, @SerializedName("vote_count") diff --git a/app/src/main/java/com/gh/gamecenter/entity/HistoryGameEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/HistoryGameEntity.kt index b192cc8f48..f19bb9fdd1 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/HistoryGameEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/HistoryGameEntity.kt @@ -43,8 +43,6 @@ data class HistoryGameEntity( gameEntity.subtitleStyle = subtitleStyle gameEntity.name = name gameEntity.tagStyle = tagStyle - gameEntity.isLibaoExists = isLibaoExist - gameEntity.setTag(tag) return gameEntity } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/HomeDataEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/HomeDataEntity.kt index c60d9a35e4..8edcd1ea59 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/HomeDataEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/HomeDataEntity.kt @@ -16,5 +16,5 @@ class HomeDataEntity( @SerializedName("home_sub_slide") val homeSubSlide: ArrayList = arrayListOf(), @SerializedName("home_push") - var homePush: HomePush? = null + var homePush: PullDownPush? = null ) diff --git a/app/src/main/java/com/gh/gamecenter/entity/HomeItemTestV2Entity.kt b/app/src/main/java/com/gh/gamecenter/entity/HomeItemTestV2Entity.kt index 685f420967..6cc75bba71 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/HomeItemTestV2Entity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/HomeItemTestV2Entity.kt @@ -34,9 +34,9 @@ data class HomeItemTestV2Entity( @SerializedName("start_id") val startId: String = "", @SerializedName("start_point") - val startPoint: Map = mapOf(), // key 时间轴名称,value 当前时间轴第一个游戏id + private val _startPoint: Map = mapOf(), // key 时间轴名称,value 当前时间轴第一个游戏id @SerializedName("data_count") - val dataCount: Map = mapOf(), //key 时间轴名称,value 当前时间轴所包含的游戏数量 + private val _dataCount: Map = mapOf(), //key 时间轴名称,value 当前时间轴所包含的游戏数量 @SerializedName("time_type") val timeType: String = "", @SerializedName("recommend_left_surplus_num") //首页推荐左侧剩余游戏数量 @@ -46,7 +46,39 @@ data class HomeItemTestV2Entity( // 用来判断是不是下拉刷新 @Expose(serialize = false, deserialize = false) var isRefresh: Boolean = true -) : Parcelable +) : Parcelable { + + val dataCount: List + get() { + val dataCountList = arrayListOf() + // 需要将 推荐 类型的数据放在第一位 + _dataCount.forEach { (key, value) -> + if (timeType == TIME_TYPE_RECOMMEND && key == TIME_TYPE_RECOMMEND) { + dataCountList.add(0, DataCount(key, value)) + } else { + dataCountList.add(DataCount(key, value)) + } + } + return dataCountList + } + + val startPoint: List + get() { + val startPointList = arrayListOf() + _startPoint.forEach { (key, value) -> + if (timeType == TIME_TYPE_RECOMMEND && key == TIME_TYPE_RECOMMEND) { + startPointList.add(0, StartPoint(key, value)) + } else { + startPointList.add(StartPoint(key, value)) + } + } + return startPointList + } + + companion object { + private const val TIME_TYPE_RECOMMEND = "recommend" + } +} @Keep @Entity @@ -100,3 +132,13 @@ data class GameDataWrapper( "gameName = ${gameData?.name})" } } + +data class DataCount( + val timeType: String, + val count: Int +) + +data class StartPoint( + val timeType: String, + val id: String +) diff --git a/app/src/main/java/com/gh/gamecenter/entity/HomePluggableFilterEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/HomePluggableFilterEntity.kt index 6aadbaa5f1..f31e600e61 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/HomePluggableFilterEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/HomePluggableFilterEntity.kt @@ -9,6 +9,6 @@ import androidx.room.PrimaryKey class HomePluggableFilterEntity( @PrimaryKey val pkgName: String, - val tag: String, // gameVersion or never + var tag: String, // gameVersion or never var active: Boolean // 是否可用 ) \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/HomeRecommend.kt b/app/src/main/java/com/gh/gamecenter/entity/HomeRecommend.kt index d93c22f395..f609055d05 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/HomeRecommend.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/HomeRecommend.kt @@ -1,5 +1,6 @@ package com.gh.gamecenter.entity +import com.gh.gamecenter.common.entity.CommunityEntity import com.gh.gamecenter.common.entity.Display import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.feature.exposure.ExposureEvent @@ -15,18 +16,26 @@ data class HomeRecommend( val icon: String = "", val name: String = "", val display: Display = Display(), + @SerializedName("image") + private val _image: String? = null, + @SerializedName("link_community") + private val community: CommunityEntity? = null, // 绑定的曝光实体 var exposureEvent: ExposureEvent? = null, ) { + val image: String + get() = _image ?: "" + fun transformLinkEntity(): LinkEntity { return LinkEntity( name = name, type = linkType, display = display, link = linkId, - text = linkText + text = linkText, + community = community ) } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/HomeSlide.kt b/app/src/main/java/com/gh/gamecenter/entity/HomeSlide.kt index 12a88f429c..ee4688db92 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/HomeSlide.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/HomeSlide.kt @@ -1,5 +1,6 @@ package com.gh.gamecenter.entity +import com.gh.gamecenter.common.entity.CommunityEntity import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.feature.entity.GameEntity import com.google.gson.annotations.SerializedName @@ -19,7 +20,9 @@ data class HomeSlide( val title: String = "", val text: String = "", @SerializedName("placeholder_color") - val placeholderColor: String = "#5C9599" + val placeholderColor: String = "#5C9599", + @SerializedName("link_community") + val community: CommunityEntity? = null ) { fun transformLinkEntity(): LinkEntity { @@ -27,7 +30,8 @@ data class HomeSlide( name = title, type = linkType, link = linkId, - text = linkText + text = linkText, + community = community ) } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/HomeSubSlide.kt b/app/src/main/java/com/gh/gamecenter/entity/HomeSubSlide.kt index 10a8f98fa0..dfd4fd6598 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/HomeSubSlide.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/HomeSubSlide.kt @@ -28,7 +28,9 @@ data class HomeSubSlide( val cardData: CardData = CardData(), val image: String = "", val title: String = "", - var sequence: Int = -1 // 本地字段 + // 本地字段 + var sequence: Int = -1, + var repeatCount: Int = -1 ) { fun toLinkEntity(): LinkEntity { return if (linkType.isNotEmpty()) { diff --git a/app/src/main/java/com/gh/gamecenter/entity/MultiTabNav.kt b/app/src/main/java/com/gh/gamecenter/entity/MultiTabNav.kt new file mode 100644 index 0000000000..f0b7c22538 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/MultiTabNav.kt @@ -0,0 +1,40 @@ +package com.gh.gamecenter.entity + +import android.graphics.Color +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.google.gson.annotations.SerializedName + +data class MultiTabNav( + @SerializedName("_id") + val id: String = "", + val title: String = "", + @SerializedName("link_multi_tab_nav") + val linkMultiTabNav: List = ArrayList() +) { + data class LinkMultiTabNav( + @SerializedName("_id") + val id: String = "", + val name: String = "", + val img: String = "", + @SerializedName("tab_show_img") + var showImgOnSelected: Boolean? = false, // 选中时是否显示图片 + @SerializedName("is_default_page") + var default: Boolean = false, // 是否为默认显示页 + val link: LinkEntity? = null, // 通用链接 + + // 本地字段 + var style: TabStyle = TabStyle(), + var showPullDownPush: Boolean = false, + var exposureEvent: ExposureEvent? = null, // 绑定的曝光实体 + ) { + data class TabStyle( + var useLightStyle: Boolean = false, + var primaryColor: Int = Color.WHITE, + var offsetRatio: Float = 0F, + var currentSelectColor: Int = 0, + var isSlideBackgroundColorEnable: Boolean = false, // 是否启用首页轮播图背景颜色 + var isSlideBackgroundColorShow: Boolean = false, // 是否正在显示首页轮播图背景颜色 + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt index 51839b56ba..6926dad8e2 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/PersonalHistoryEntity.kt @@ -235,8 +235,6 @@ data class PersonalHistoryEntity( val id: String = "", @SerializedName("name") private var mName: String = "", - @SerializedName("name_suffix") - val nameSuffix: String = "", val icon: String = "", @SerializedName(value = "new_tag_style") diff --git a/app/src/main/java/com/gh/gamecenter/entity/HomePush.kt b/app/src/main/java/com/gh/gamecenter/entity/PullDownPush.kt similarity index 64% rename from app/src/main/java/com/gh/gamecenter/entity/HomePush.kt rename to app/src/main/java/com/gh/gamecenter/entity/PullDownPush.kt index 01de6872c5..05c0f74a6b 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/HomePush.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/PullDownPush.kt @@ -3,7 +3,7 @@ package com.gh.gamecenter.entity import com.gh.gamecenter.feature.entity.GameEntity import com.google.gson.annotations.SerializedName -data class HomePush( +data class PullDownPush( @SerializedName("_id") val id: String = "", @SerializedName("pop_switch") @@ -13,6 +13,9 @@ data class HomePush( val imgUrl: String = "", val video: Video? = null, val game: GameEntity? = null, + var isSlideBackgroundColorEnable: Boolean = false, // 本地字段,是否启用首页轮播图背景颜色 + var customPageId: String = "", // 本地字段,自定义页面ID + var customPageName: String = "", // 本地字段,自定义页面名称 ) data class Video( diff --git a/app/src/main/java/com/gh/gamecenter/entity/SubjectData.kt b/app/src/main/java/com/gh/gamecenter/entity/SubjectData.kt index 46483f6a75..b748e232bc 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/SubjectData.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/SubjectData.kt @@ -2,6 +2,8 @@ package com.gh.gamecenter.entity import android.os.Parcelable import com.gh.gamecenter.feature.entity.GameSubjectData +import com.gh.gamecenter.home.custom.model.CustomPageItem +import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize @Parcelize @@ -15,8 +17,8 @@ class SubjectData( var filter: String = "", // Filter: 类型(分类) var tagType: String? = "", // 游戏Item 标签类型 var briefStyle: String = "", - var showSuffix: Boolean = true, var isQQMiniGame: Boolean = false, + var subjectStyle: String = "", var requireUpdateSetting: Boolean = false, // 多专题页面需要专题页面自行获取专题配置 var isAdData: Boolean = false, @@ -24,6 +26,10 @@ class SubjectData( var codeId: String = "" // 广告CODE_ID(本地字段),不为空时为广告专题 ) : Parcelable, Cloneable { + @IgnoredOnParcel + val subjectStyleChinese: String + get() = CustomPageItem.subjectTypeToComponentStyle[subjectStyle] ?: "" + fun deepCopy(): SubjectData { return super.clone() as SubjectData } @@ -34,7 +40,6 @@ class SubjectData( name = subjectName, isOrder = isOrder ?: false, briefStyle = briefStyle, - isShowSuffix = showSuffix, tag = tagType ) } diff --git a/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt index 02b7c6462c..eb585b5dfa 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/SubjectEntity.kt @@ -9,6 +9,8 @@ import com.gh.gamecenter.feature.entity.GameEntity import com.google.gson.annotations.SerializedName import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize +import kotlin.math.max +import kotlin.math.min /** * Created by LGT on 2016/7/1. @@ -32,7 +34,8 @@ data class SubjectEntity( @SerializedName("relation_column_id") var relatedColumnId: String? = null, var style: String? = "", // 值为 "top" 时表示此专题(合集)为排行榜 https://gitlab.ghzs.com/pm/halo-app-issues/issues/699 - val list: Int = 0, + @SerializedName("list") + private val _list: Int? = null, @SerializedName("common_collection_content") var commonCollectionList: MutableList? = null, @SerializedName("home_page_style") @@ -48,8 +51,6 @@ data class SubjectEntity( @SerializedName("show_name") var showName: Boolean = true, // 是否显示“专题名字”,true、false - @SerializedName("show_suffix") - var showSuffix: Boolean = true, // 是否显示”游戏后缀“,true、false @SerializedName("type_style") var typeStyle: String = "", // 横屏样式,不显示(default)、评分(star)、备注(remark) @SerializedName("brief_style") @@ -101,7 +102,10 @@ data class SubjectEntity( @SerializedName("is_qq_column") var isQQColumn: Boolean = false, - var explain: String = "" // 游戏单合集说明 + var explain: String = "", // 游戏单合集说明 + + @SerializedName("show_star") + private val _showStar: Boolean? = null ) : Parcelable { @IgnoredOnParcel @@ -111,10 +115,13 @@ data class SubjectEntity( mData = value } + val showStar: Boolean + get() = _showStar ?: false + fun getFilterName(): String { - if (!TextUtils.isEmpty(name) && !Config.isShowPlugin()) { - name = name?.replace("插件", "游戏") - } return if (name == null) "" else name!! } + + val list: Int + get() = max(min(_list ?: 0, data?.size ?: 0), 1) } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/SubjectSettingEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/SubjectSettingEntity.kt index 8fc7a3fec0..980b2071b9 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/SubjectSettingEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/SubjectSettingEntity.kt @@ -16,8 +16,6 @@ class SubjectSettingEntity( var filter: String = "", // rows: off/on var order: Boolean = false, // 是否显示序号 - @SerializedName("show_suffix") - var showSuffix: Boolean = true, @SerializedName("brief_style") var briefStyle: String = "", @@ -28,6 +26,8 @@ class SubjectSettingEntity( var filterOptions: List = ArrayList(), // 过滤选项,推荐、最新、评分、更新 @SerializedName("ad_icon_active") var adIconActive: Boolean = false, + @SerializedName("column_style") + var columnStyle: String = "", // 对应自定义页面的专题样式 ) : Parcelable { @Parcelize class TypeEntity( diff --git a/app/src/main/java/com/gh/gamecenter/entity/VideoEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/VideoEntity.kt index b44062980a..a4d7283e1d 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/VideoEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/VideoEntity.kt @@ -25,8 +25,6 @@ open class VideoEntity( val gameId: String = "", @SerializedName("game_name") private var mGameName: String = "", - @SerializedName("name_suffix") - var nameSuffix: String? = null, @SerializedName("game_icon") val gameIcon: String = "", @SerializedName("category_id") @@ -73,7 +71,7 @@ open class VideoEntity( @IgnoredOnParcel val gameName: String - get() = mGameName.removeSuffix(".") + (nameSuffix ?: "") + get() = mGameName.removeSuffix(".") fun getThumb(): String { val configProvider = ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider diff --git a/app/src/main/java/com/gh/gamecenter/eventbus/EBSkip.java b/app/src/main/java/com/gh/gamecenter/eventbus/EBSkip.java index c44408d178..f4878835a8 100644 --- a/app/src/main/java/com/gh/gamecenter/eventbus/EBSkip.java +++ b/app/src/main/java/com/gh/gamecenter/eventbus/EBSkip.java @@ -7,12 +7,18 @@ public class EBSkip { private String type; private int currentItem; + private String linkType; public EBSkip(String type, int currentItem) { this.type = type; this.currentItem = currentItem; } + public EBSkip(String type, String linkType) { + this.type = type; + this.linkType = linkType; + } + public String getType() { return type; } @@ -21,4 +27,8 @@ public class EBSkip { return currentItem; } + public String getLinkType() { + return linkType; + } + } diff --git a/app/src/main/java/com/gh/gamecenter/eventbus/EBUISwitch.java b/app/src/main/java/com/gh/gamecenter/eventbus/EBUISwitch.java index e92733b39e..8fcc198e55 100644 --- a/app/src/main/java/com/gh/gamecenter/eventbus/EBUISwitch.java +++ b/app/src/main/java/com/gh/gamecenter/eventbus/EBUISwitch.java @@ -4,12 +4,18 @@ public class EBUISwitch { private String from; private int position; + private String linkType; public EBUISwitch(String from, int position) { this.position = position; this.from = from; } + public EBUISwitch(String from, String linkType) { + this.linkType = linkType; + this.from = from; + } + public int getPosition() { return position; } @@ -26,4 +32,11 @@ public class EBUISwitch { this.from = from; } + public String getLinkType() { + return linkType; + } + + public void setLinkType(String linkType) { + this.linkType = linkType; + } } diff --git a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt index 411bb078fb..45a1c18d4b 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/detail/ForumArticleAskListFragment.kt @@ -16,12 +16,12 @@ import com.gh.gamecenter.common.utils.tryCatchInRelease import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.core.iinterface.IScrollable import com.gh.gamecenter.core.utils.MD5Utils -import com.gh.gamecenter.databinding.FragmentListBaseSkeletonBinding +import com.gh.gamecenter.databinding.FragmentForumArticleAskListBinding import com.gh.gamecenter.entity.ForumDetailEntity import com.gh.gamecenter.eventbus.EBDeleteDetail import com.gh.gamecenter.eventbus.EBUserFollow -import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper import com.gh.gamecenter.feature.entity.AnswerEntity +import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper import com.gh.gamecenter.video.detail.CustomManager import com.shuyu.gsyvideoplayer.video.base.GSYVideoView import org.greenrobot.eventbus.Subscribe @@ -32,7 +32,7 @@ class ForumArticleAskListFragment : LazyListFragment - (mBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop - insets.consumeSystemWindowInsets() + if (!mIsFromTabWrapper) { + ViewCompat.setOnApplyWindowInsetsListener(mBinding.forumAppbar) { _, insets -> + (mBinding.toolbar.layoutParams as MarginLayoutParams).topMargin = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top + WindowInsetsCompat.CONSUMED + } + } + if (mIsFromMainWrapper) { + (mBinding.toolbar.layoutParams as MarginLayoutParams).topMargin = DisplayUtils.getStatusBarHeight(requireContext().resources) } - mBinding.toolbar.setNavigationOnClickListener { NewLogUtils.logForumDetailEnterOrClick("click_forum_detail_return", mBbsId, mBbsType) requireActivity().finish() @@ -382,7 +347,7 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { false ) == true ) mEntrance else "论坛详情页点击" - com.gh.common.util.NewFlatLogUtils.logClickBBSDetailGameZoneTab( + NewFlatLogUtils.logClickBBSDetailGameZoneTab( mBbsId, entrance, mForumDetail?.zone?.link ?: "" @@ -440,6 +405,60 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { updateTabStyle(tab, true) } }) + + mViewModel?.answerLiveData?.observeNonNull(viewLifecycleOwner) { + insertDataToAllTab(it) + } + mViewModel?.forumDetail?.observe(this) { + mSkeleton.hide() + mBinding.reuseLoading.reuseLlLoading.visibility = View.GONE + if (it.status == Status.SUCCESS) { + mBinding.communityEdit.visibility = View.VISIBLE + mBinding.forumContainer.visibility = View.VISIBLE + mBinding.reuseNoConnection.reuseNoConnection.visibility = View.GONE + if (it.data != null) { + mForumDetail = it.data + mBbsType = if (it.data!!.type == "official_bbs") "综合论坛" else "游戏论坛" + val userId = UserManager.getInstance().userId + NewLogUtils.logForumDetailEnterOrClick( + "view_forum_detail", + mBbsId, + mBbsType, + userId, + entrance = if (mEntrance.contains("游戏详情")) "游戏详情页" else "论坛tab页" + ) + mBaseHandler.postDelayed({ + SensorsBridge.trackEvent( + "ViewForum", + "bbs_id", + mBbsId, + "forum_name", + mForumDetail?.name ?: "", + "bbs_type", + mBbsType + ) + }, 3000) + initUI() + } + } else { + mBinding.communityEdit.visibility = View.GONE + mBinding.forumContainer.visibility = View.GONE + if (it.exception != null && it.exception!!.code() == 404) { + mBinding.reuseNoneData.reuseNoneDataTv.text = "内容不见了~" + mBinding.reuseNoneData.reuseNoneDataDescTv.text = "先去看看其它的内容吧" + mBinding.reuseNoneData.reuseNoneDataIv.setImageResource(R.drawable.ic_data_load_exception) + mBinding.reuseNoneData.reuseNoneData.visibility = View.VISIBLE + mBinding.reuseNoConnection.reuseNoConnection.visibility = View.GONE + ToastUtils.showToast("内容可能已被删除") + } else { + mBinding.reuseNoneData.reuseNoneData.visibility = View.GONE + mBinding.reuseNoConnection.reuseNoConnection.visibility = View.VISIBLE + } + } + } + mViewModel?.statusEntity?.observeNonNull(this) { + mStatus = it + } } override fun onFragmentResume() { @@ -555,12 +574,6 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { } private fun initUI() { - if (mIsFromMainWrapper) { - mBinding.communityEdit.layoutParams = (mBinding.communityEdit.layoutParams as MarginLayoutParams).apply { - setMargins(0, 0, 12F.dip2px(), 74F.dip2px()) - } - mBinding.titleContainer.setPadding(21F.dip2px(), 0, 0, 0) - } mForumDetail?.apply { mBinding.forumTopMaskContainer.goneIf(background.isEmpty()) mBinding.forumDefaultBackground.goneIf(background.isNotEmpty()) @@ -579,8 +592,9 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { mBinding.forumThumbSmall.displayGameIcon(game.getIcon(), game.iconSubscript, game.iconFloat) mBinding.forumThumbBig.displayGameIcon(game.getIcon(), game.iconSubscript, game.iconFloat) } - if (mIsFromMainWrapper) { + if (mIsFromMainWrapper || mIsFromTabWrapper) { mBinding.toolbar.navigationIcon = null + mBinding.titleContainer.setPadding(21F.dip2px(), 0, 0, 0) } else { mBinding.toolbar.setNavigationIcon(if (!mIsDarkModeOn) R.drawable.ic_bar_back else R.drawable.ic_bar_back_light) } @@ -673,7 +687,7 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { ) else null ) mBinding.sectionMoreIv.setOnClickListener { - com.gh.common.util.NewFlatLogUtils.logClickMoreSection() + NewFlatLogUtils.logClickMoreSection() showSectionsPopupWindow() } @@ -724,7 +738,7 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { val location = IntArray(2) moderatorTv.getLocationOnScreen(location) moderatorGuideContainer.layoutParams = - (moderatorGuideContainer.layoutParams as ViewGroup.MarginLayoutParams).apply { + (moderatorGuideContainer.layoutParams as MarginLayoutParams).apply { leftMargin = location[0] - 16F.dip2px() } moderatorGuideContainer.alpha = 0F @@ -764,7 +778,7 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { ForumOrUserSearchActivity.getIntent( requireContext(), mBbsId, - "论坛详情", + ENTRANCE, SOURCE_ENTRANCE, mForumDetail?.name ?: "" ) @@ -918,7 +932,7 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { ) } mBinding.gameDetailTv.setOnClickListener { - com.gh.common.util.NewFlatLogUtils.logClickBBSDetailGameDetail() + NewFlatLogUtils.logClickBBSDetailGameDetail() GameDetailActivity.startGameDetailActivity( requireContext(), mForumDetail?.game?.id ?: "", "(论坛详情)" @@ -940,13 +954,18 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { if (isToolbarWhite) R.color.white else R.color.transparent ) ) - DisplayUtils.setStatusBarColor( - requireActivity(), - if (isToolbarWhite) R.color.white else R.color.transparent - ) + if (!mIsFromTabWrapper && isSupportVisible) { + DisplayUtils.setStatusBarColor( + requireActivity(), + if (isToolbarWhite) R.color.white else R.color.transparent + ) + DisplayUtils.setLightStatusBar(requireActivity(), isToolbarWhite) + } + if (isToolbarWhite) { - DisplayUtils.setLightStatusBar(requireActivity(), true) - if (!mIsFromMainWrapper) mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back) + if (!mIsFromMainWrapper && !mIsFromTabWrapper) { + mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back) + } mBinding.searchIv.setImageDrawable( ContextCompat.getDrawable( requireContext(), @@ -954,8 +973,9 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { ) ) } else { - DisplayUtils.setLightStatusBar(requireActivity(), false) - if (!mIsFromMainWrapper) mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) + if (!mIsFromMainWrapper && !mIsFromTabWrapper) { + mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) + } mBinding.searchIv.setImageDrawable( ContextCompat.getDrawable( requireContext(), @@ -980,7 +1000,9 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { requireActivity(), if (isToolbarWhite) R.color.black else R.color.transparent ) - if (!mIsFromMainWrapper) mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) + if (!mIsFromMainWrapper && !mIsFromTabWrapper) { + mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) + } mBinding.searchIv.setImageDrawable( ContextCompat.getDrawable( requireContext(), @@ -1335,5 +1357,6 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable { var TOP_CONTENT_TOP_MARGIN = 8F.dip2px() const val SOURCE_ENTRANCE = "论坛" + const val ENTRANCE = "论坛详情" } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/CommunityActivity.kt b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityActivity.kt new file mode 100644 index 0000000000..7f2c306516 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/forum/home/CommunityActivity.kt @@ -0,0 +1,28 @@ +package com.gh.gamecenter.forum.home + +import android.os.Bundle +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.activity.BaseActivity +import com.gh.gamecenter.common.constant.EntranceConsts.IS_DETAIL_PAGE +import com.gh.gamecenter.video.detail.HomeVideoFragment + +class CommunityActivity : BaseActivity() { + + override fun getLayoutId(): Int = R.layout.activity_community + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val extras = intent.extras + extras?.putBoolean(IS_DETAIL_PAGE, true) + val fragment = + supportFragmentManager.findFragmentByTag(CommunityHomeFragment::class.java.name) as? CommunityHomeFragment + ?: CommunityHomeFragment().with(extras) + + supportFragmentManager.beginTransaction() + .replace(R.id.layout_activity_content, fragment, CommunityHomeFragment::class.java.name) + .commitNowAllowingStateLoss() + + } +} \ No newline at end of file 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 e6f6b92a7e..0e51b0a975 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 @@ -21,12 +21,14 @@ import com.gh.common.browse.withLifecycle import com.gh.common.util.NewFlatLogUtils import com.gh.common.util.DirectUtils import com.gh.common.util.NewLogUtils +import com.gh.common.util.ViewPagerFragmentHelper import com.gh.gamecenter.R import com.gh.gamecenter.common.base.TrackableDialog import com.gh.gamecenter.common.base.adapter.FragmentAdapter 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.constant.EntranceConsts.IS_DETAIL_PAGE import com.gh.gamecenter.common.retrofit.ApiResponse import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.AvatarBorderView @@ -39,12 +41,9 @@ import com.gh.gamecenter.databinding.LayoutCommunityHomeVideoGuideBinding import com.gh.gamecenter.databinding.TabItemCommunityBinding import com.gh.gamecenter.eventbus.EBSkip import com.gh.gamecenter.eventbus.EBTypeChange -import com.gh.gamecenter.eventbus.EBUISwitch import com.gh.gamecenter.feature.entity.ArticleEntity import com.gh.gamecenter.feature.entity.ForumVideoEntity import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity -import com.gh.gamecenter.fragment.MainWrapperFragment -import com.gh.gamecenter.fragment.MainWrapperViewModel import com.gh.gamecenter.login.entity.UserInfoEntity import com.gh.gamecenter.login.user.UserRepository import com.gh.gamecenter.qa.article.detail.ArticleDetailWebCacheManager @@ -53,6 +52,7 @@ import com.gh.gamecenter.qa.draft.CommunityDraftWrapperActivity import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity import com.gh.gamecenter.qa.video.publish.VideoPublishActivity import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel +import com.gh.gamecenter.wrapper.MainWrapperViewModel import com.google.android.material.tabs.TabLayout import com.halo.assistant.HaloApp import org.greenrobot.eventbus.Subscribe @@ -70,7 +70,8 @@ class CommunityHomeFragment : LazyFragment() { private var mTabList = arrayListOf() private var mDefaultSelectedTab = -1 private var mNavigationBitmap: Bitmap? = null - private var mShowVideo = false + private var mShowVideo = true + private var mBottomTabId = "" private val browseTimer = BrowseTimer() .withLifecycle(this) @@ -88,7 +89,6 @@ class CommunityHomeFragment : LazyFragment() { override fun onFragmentFirstVisible() { - ArticleDetailWebCacheManager.init(requireContext().applicationContext) mViewModel = viewModelProvider() @@ -106,9 +106,7 @@ class CommunityHomeFragment : LazyFragment() { insertDataToRecommendTab(it) } - mMainWrapperViewModel?.videoNavBar?.observe(this) { - mShowVideo = it?.type != "video_stream" - mBinding?.searchContainer?.goneIf(mShowVideo) + mMainWrapperViewModel?.bottomTabListLiveData?.observe(this) { tabList -> mBinding?.videoAndSearchContainer?.goneIf(!mShowVideo) { val decorView = requireActivity().window.decorView as? FrameLayout @@ -139,27 +137,31 @@ class CommunityHomeFragment : LazyFragment() { } } + mMainWrapperViewModel?.bottomDoubleTabAction?.observe(viewLifecycleOwner) { + if (isSupportVisible && it.id == mBottomTabId) { + scrollToTop() + } + } + mBinding?.run { topBg.visibleIf(!mIsDarkModeOn) videoLottie.setOnClickListener { DirectUtils.directToLegacyVideoDetail(requireContext(), "", VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value, referer = "视频流-社区右上角", isHomeVideo = true) } - listOf(searchContainer, searchIconIv).forEach { - it.setOnClickListener { - NewLogUtils.logCommunitySearchClick() - NewLogUtils.logForumSearchEnter("社区搜索栏") - NewFlatLogUtils.logAccessToBbsSearch("社区", "") - requireContext().startActivity( - ForumOrUserSearchActivity.getIntent( - requireContext(), - "", - "论坛首页", - SOURCE_ENTRANCE, - SOURCE_ENTRANCE - ) + searchIconIv.setOnClickListener { + NewLogUtils.logCommunitySearchClick() + NewLogUtils.logForumSearchEnter("社区搜索栏") + NewFlatLogUtils.logAccessToBbsSearch("社区", "") + requireContext().startActivity( + ForumOrUserSearchActivity.getIntent( + requireContext(), + "", + "论坛首页", + SOURCE_ENTRANCE, + SOURCE_ENTRANCE ) - } + ) } communityEditBtn.setOnClickListener { @@ -183,12 +185,21 @@ class CommunityHomeFragment : LazyFragment() { } } }) + + val isDetailPage = arguments?.getBoolean(IS_DETAIL_PAGE) ?: false + ivBack.visibility = if (isDetailPage) View.VISIBLE else View.GONE + if (isDetailPage) { + ivBack.setOnClickListener { + activity?.finish() + } + } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) savedInstanceState?.let { mDefaultSelectedTab = it.getInt(LAST_SELECTED_POSITION) } + mBottomTabId = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_ID, "") ?: "" } override fun setUserVisibleHint(isVisibleToUser: Boolean) { @@ -207,7 +218,7 @@ class CommunityHomeFragment : LazyFragment() { override fun onFragmentResume() { super.onFragmentResume() - + DisplayUtils.transparentStatusBar(requireActivity()) DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn) NewLogUtils.logCommunityHomeEvent("view_community") } @@ -623,13 +634,10 @@ class CommunityHomeFragment : LazyFragment() { } } - @Subscribe(threadMode = ThreadMode.MAIN) - fun onEventMainThread(busNine: EBUISwitch) { - if (busNine.position == MainWrapperFragment.INDEX_BBS) { - if (mFragmentList.isEmpty()) return - (mFragmentList[0] as ForumArticleListFragment).scrollToTop() - setPutQuestionButtonStatus(View.VISIBLE) - } + private fun scrollToTop() { + if (mFragmentList.isEmpty()) return + (mFragmentList[0] as ForumArticleListFragment).scrollToTop() + setPutQuestionButtonStatus(View.VISIBLE) } @Subscribe(threadMode = ThreadMode.MAIN) @@ -709,8 +717,6 @@ class CommunityHomeFragment : LazyFragment() { } } } - searchTv.setHintTextColor(R.color.text_instance.toColor(requireContext())) - searchTv.setTextColor(R.color.text_instance.toColor(requireContext())) } } diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt index c34c06d5bb..1029838acc 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt @@ -2,14 +2,14 @@ package com.gh.gamecenter.forum.home import android.content.Context import android.view.ViewGroup -import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.common.util.CheckLoginUtils import com.gh.common.util.DirectUtils import com.gh.common.util.NewLogUtils -import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.InfoActivity +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.databinding.ForumWelfareItemBinding -import com.gh.gamecenter.toolbox.ToolBoxBlockActivity +import com.gh.gamecenter.toolbox.ToolBoxActivity import com.lightgame.adapter.BaseRecyclerAdapter class WelfaresAdapter( @@ -35,7 +35,7 @@ class WelfaresAdapter( when (entity.second) { "游戏工具箱" -> { NewLogUtils.logForumPageEvent("click_forum_toolbox") - mContext.startActivity(ToolBoxBlockActivity.getIntent(mContext, "(社区-论坛:工具箱)")) + mContext.startActivity(ToolBoxActivity.getIntent(mContext, "(社区-论坛:工具箱)")) } "礼包中心" -> { diff --git a/app/src/main/java/com/gh/gamecenter/forum/moderator/ModeratorListFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/moderator/ModeratorListFragment.kt index fcb21c3aa5..eb83cbae97 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/moderator/ModeratorListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/moderator/ModeratorListFragment.kt @@ -4,20 +4,20 @@ import android.os.Bundle import android.view.View import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager -import com.gh.gamecenter.common.constant.Constants -import com.gh.common.util.* -import com.gh.gamecenter.NewsDetailActivity +import com.gh.common.util.NewLogUtils import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.ToolbarFragment +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.ifLogin import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.utils.toDrawable import com.gh.gamecenter.common.utils.viewModelProvider -import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.databinding.FragmentModeratorListBinding import com.gh.gamecenter.entity.ApplyModeratorStatusEntity import com.gh.gamecenter.eventbus.EBUserFollow -import com.gh.gamecenter.common.base.fragment.ToolbarFragment +import com.gh.gamecenter.newsdetail.NewsDetailActivity import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode diff --git a/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchActivity.kt b/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchActivity.kt index 5557291bb3..0069bf55a9 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchActivity.kt @@ -5,7 +5,8 @@ import android.content.Intent import android.os.Bundle import android.text.TextUtils import android.view.View -import com.gh.common.util.* +import com.gh.common.util.NewFlatLogUtils +import com.gh.common.util.NewLogUtils import com.gh.gamecenter.DisplayType import com.gh.gamecenter.R import com.gh.gamecenter.SearchActivity @@ -15,6 +16,7 @@ import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.SensorsBridge import com.gh.gamecenter.common.utils.showKeyBoard import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.forum.detail.ForumDetailFragment import com.gh.gamecenter.search.SearchDefaultFragment import com.lightgame.utils.Util_System_Keyboard @@ -30,7 +32,7 @@ class ForumOrUserSearchActivity : SearchActivity() { mBbsId = intent.getStringExtra(EntranceConsts.KEY_BBS_ID) ?: "" mLocation = intent.getStringExtra(EntranceConsts.KEY_LOCATION) ?: "" mSourceEntrance = intent.getStringExtra(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: "" - searchEt.hint = if (mEntrance == "论坛首页") { + searchEt.hint = if (mEntrance != ForumDetailFragment.ENTRANCE) { "搜索论坛内容、用户" } else { "搜索此论坛中的内容" @@ -39,7 +41,7 @@ class ForumOrUserSearchActivity : SearchActivity() { searchBtn.setTextColor(R.color.text_theme.toColor(this)) searchBtn.setOnClickListener { val key = searchEt.text.toString().trim { it <= ' ' } - NewLogUtils.logForumSearchClick(key, if (mEntrance == "论坛首页") "社区搜索" else "论坛详情页搜索") + NewLogUtils.logForumSearchClick(key, if (mEntrance != ForumDetailFragment.ENTRANCE) "社区搜索" else "论坛详情页搜索") Util_System_Keyboard.hideSoftKeyboard(this) search(SearchType.MANUAL, null) } @@ -49,7 +51,7 @@ class ForumOrUserSearchActivity : SearchActivity() { super.initSearchBar() backBtn.setOnClickListener { val key = searchEt.text.toString().trim { it <= ' ' } - if (mEntrance == "论坛首页") { + if (mEntrance != ForumDetailFragment.ENTRANCE) { NewFlatLogUtils.logClickSearchReturn(key) } else { NewFlatLogUtils.logClickBbsSearchReturn(key, mBbsId) @@ -134,7 +136,7 @@ class ForumOrUserSearchActivity : SearchActivity() { val key = searchEt.text.toString().trim { it <= ' ' } if (key.isNotEmpty()) { val stayTime = (System.currentTimeMillis() - startPageTime) / 1000 - if (mEntrance == "论坛首页") { + if (mEntrance != ForumDetailFragment.ENTRANCE) { NewFlatLogUtils.logViewSearchList(stayTime, key) } else { NewFlatLogUtils.logViewBbsSearchList(stayTime, key, mBbsId) @@ -143,7 +145,7 @@ class ForumOrUserSearchActivity : SearchActivity() { } else -> { - if (mEntrance == "论坛首页") { + if (mEntrance != ForumDetailFragment.ENTRANCE) { val fragment = supportFragmentManager.findFragmentByTag(ForumOrUserSearchFragment::class.java.name) as? ForumOrUserSearchFragment ?: ForumOrUserSearchFragment() @@ -177,13 +179,25 @@ class ForumOrUserSearchActivity : SearchActivity() { bbsId: String, mEntrance: String, sourceEntrance: String, - location: String + location: String, + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + customPageId: String = "", + customPageName: String = "", + searchBoxPattern: String = "" ): Intent { val intent = Intent(context, ForumOrUserSearchActivity::class.java) intent.putExtra(EntranceConsts.KEY_BBS_ID, bbsId) intent.putExtra(EntranceConsts.KEY_ENTRANCE, mEntrance) intent.putExtra(EntranceConsts.KEY_SOURCE_ENTRANCE, sourceEntrance) intent.putExtra(EntranceConsts.KEY_LOCATION, location) + intent.putExtra(EntranceConsts.KEY_BOTTOM_TAB_NAME, bottomTab) + intent.putExtra(EntranceConsts.KEY_MULTI_TAB_NAV_ID, multiTabId) + intent.putExtra(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, multiTabName) + intent.putExtra(EntranceConsts.KEY_CUSTOM_PAGE_ID, customPageId) + intent.putExtra(EntranceConsts.KEY_CUSTOM_PAGE_NAME, customPageName) + intent.putExtra(EntranceConsts.KEY_SEARCH_BOX_PATTERN, searchBoxPattern) return intent } } diff --git a/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchDefaultFragment.kt b/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchDefaultFragment.kt index 40a4b2f76a..d6669e3650 100644 --- a/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchDefaultFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/forum/search/ForumOrUserSearchDefaultFragment.kt @@ -47,7 +47,7 @@ class ForumOrUserSearchDefaultFragment : SearchDefaultFragment() { mBinding = FragmentSearchDefaultBinding.bind(mCachedView) mBinding.hotTagHeadContainer.root.visibility = View.GONE mBinding.hotTagFlexContainer.visibility = View.GONE - if (mEntrance == "论坛首页") { + if (mEntrance == "论坛首页" || mEntrance == "搜索栏") { mBinding.hotHeadContainer.headTitle.text = "热门论坛" mViewModel.getForumSearchHotContent() } else { diff --git a/app/src/main/java/com/gh/gamecenter/fragment/HomeGameWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/fragment/HomeGameWrapperFragment.kt index f4c913e0bb..fb89dbe73a 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/HomeGameWrapperFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/HomeGameWrapperFragment.kt @@ -11,6 +11,7 @@ import com.gh.download.DownloadManager import com.gh.gamecenter.DownloadManagerActivity import com.gh.gamecenter.R import com.gh.gamecenter.SearchActivity +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.utils.goneIf @@ -88,7 +89,7 @@ class HomeGameWrapperFragment : HomeTabWrapperFragment() { startActivity(DownloadManagerActivity.getDownloadMangerIntent(requireContext(), "游戏库")) } mBinding?.menuDownloadCountHint?.typeface = - Typeface.createFromAsset(requireContext().assets, "fonts/d_din_bold_only_number.ttf") + Typeface.createFromAsset(requireContext().assets, Constants.DIN_FONT_PATH) } } diff --git a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperFragment.kt index 26ab950a26..f6519d7cf3 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperFragment.kt @@ -6,7 +6,6 @@ import android.view.View import android.view.ViewGroup import androidx.core.graphics.ColorUtils import com.gh.common.exposure.ExposureManager -import com.gh.common.prioritychain.AccelerateNotificationHandler import com.gh.common.util.DirectUtils import com.gh.common.util.DownloadItemUtils import com.gh.common.util.LogUtils @@ -28,7 +27,7 @@ import com.gh.gamecenter.core.utils.MD5Utils import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.core.utils.TimeElapsedHelper import com.gh.gamecenter.databinding.FragmentMainHomeWrapperBinding -import com.gh.gamecenter.entity.HomePush +import com.gh.gamecenter.entity.PullDownPush import com.gh.gamecenter.entity.SubjectRecommendEntity import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage @@ -38,6 +37,7 @@ import com.gh.gamecenter.feature.game.GameItemViewHolder import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment import com.gh.gamecenter.home.HomeFragment import com.gh.gamecenter.home.video.ScrollCalculatorHelper +import com.gh.gamecenter.wrapper.MainWrapperViewModel import com.google.android.material.appbar.AppBarLayout import com.halo.assistant.HaloApp import com.lightgame.download.DataWatcher @@ -75,7 +75,7 @@ class HomeSearchToolWrapperFragment : HomeTabWrapperFragment() { private var mTwoLevelOpenOffset = 0 private var mTwoLevelOpenCount = 0 private var mShowTwoLevelStartOffset = 0 - private var mHomePush: HomePush? = null + private var mHomePush: PullDownPush? = null private var mExposureEvent: ExposureEvent? = null private var mGameViewHolder: GameViewHolder? = null @@ -155,25 +155,6 @@ class HomeSearchToolWrapperFragment : HomeTabWrapperFragment() { setHomePush() } } - - mMainWrapperViewModel?.accelerateNotificationPopup?.observeNonNull(viewLifecycleOwner) { - if (parentFragment is MainWrapperFragment && isSupportVisible) { - val isHandlerQueueEmpty = (parentFragment as MainWrapperFragment).isPriorityChainHandlerQueueEmpty() - val accelerateSet = - HashSet(SPUtils.getStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET)) - if (isHandlerQueueEmpty && it.isNotEmpty() && !accelerateSet.contains(it[0].messageId)) { - AccelerateNotificationHandler.showAccelerateNotificationPopupWindow( - requireActivity(), - mMainWrapperViewModel, - mBaseHandler as BaseHandler, - it[0], - null - ) - accelerateSet.add(it[0].messageId) - SPUtils.setStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET, accelerateSet) - } - } - } } private fun initSmartRefresh() { @@ -609,7 +590,7 @@ class HomeSearchToolWrapperFragment : HomeTabWrapperFragment() { } mHomePush?.run { - val homePushSet = HashSet(SPUtils.getStringSet(Constants.SP_HOME_PUSH_POP_UP_SET)) + val homePushSet = HashSet(SPUtils.getStringSet(Constants.SP_PULL_DOWN_PUSH_POP_UP_SET)) if (popSwitch == "on" && !homePushSet.contains(id)) { // 自动弹出,5s后自动收起 twoLevelHeader.openTwoLevel(true) @@ -620,7 +601,7 @@ class HomeSearchToolWrapperFragment : HomeTabWrapperFragment() { toolbarBackground.animate().alpha(0F).duration = AUTO_OPEN_TWO_LEVEL_DURATION classicsHeader.visibility = View.INVISIBLE homePushSet.add(id) - SPUtils.setStringSet(Constants.SP_HOME_PUSH_POP_UP_SET, homePushSet) + SPUtils.setStringSet(Constants.SP_PULL_DOWN_PUSH_POP_UP_SET, homePushSet) mBaseHandler.postDelayed({ finishTwoLevel("自动收起") finishCallback.invoke() @@ -997,7 +978,7 @@ class HomeSearchToolWrapperFragment : HomeTabWrapperFragment() { } } } - if (reuse.type == Constants.FINISH_HOME_PUSH) { + if (reuse.type == Constants.FINISH_PULL_DOWN_PUSH) { if (!mHomePushClick) { mBaseHandler.postDelayed({ finishTwoLevel("跳转收起") diff --git a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt index 2ef3c4dee3..a66a43cfa5 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/HomeSearchToolWrapperViewModel.kt @@ -5,7 +5,6 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import com.gh.common.filter.RegionSettingHelper -import com.gh.common.prioritychain.FloatingWindowHandler import com.gh.gamecenter.BuildConfig import com.gh.gamecenter.R import com.gh.gamecenter.common.retrofit.BiResponse @@ -29,7 +28,6 @@ class HomeSearchToolWrapperViewModel(application: Application) : AndroidViewMode private var mHomeTabPosition: Int = -1 private var mHomeTab: SubjectRecommendEntity? = null - private var mFloatingWindowHandler: FloatingWindowHandler? = null var appBarOffset = 0 @@ -116,12 +114,4 @@ class HomeSearchToolWrapperViewModel(application: Application) : AndroidViewMode }) } - fun setFloatingWindowHandler(handler: FloatingWindowHandler) { - mFloatingWindowHandler = handler - } - - fun getFloatingWindowHandler(): FloatingWindowHandler? { - return mFloatingWindowHandler - } - } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/fragment/HomeToolbarWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/fragment/HomeToolbarWrapperFragment.kt index eae93531e4..113cad8d4c 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/HomeToolbarWrapperFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/HomeToolbarWrapperFragment.kt @@ -14,6 +14,7 @@ import androidx.appcompat.widget.Toolbar import androidx.constraintlayout.widget.ConstraintLayout import androidx.fragment.app.Fragment import com.facebook.drawee.view.SimpleDraweeView +import com.gh.common.util.ViewPagerFragmentHelper import com.gh.download.DownloadManager import com.gh.gamecenter.DownloadManagerActivity import com.gh.gamecenter.R @@ -79,7 +80,7 @@ class HomeToolbarWrapperFragment: LazyFragment(), ToolbarController { try { if (arguments != null) { - val className = requireArguments().getString(MainWrapperFragment.WRAPPER_FRAGMENT_NAME) + val className = requireArguments().getString(ViewPagerFragmentHelper.WRAPPER_FRAGMENT_NAME) mContentFragment = childFragmentManager.findFragmentById(R.id.wrapper_main_content) ?: Class.forName(className).newInstance() as Fragment mContentFragment!!.arguments = requireArguments().clone() as Bundle } @@ -125,7 +126,7 @@ class HomeToolbarWrapperFragment: LazyFragment(), ToolbarController { } val downloadMenuView = mActionMenuView?.menu?.findItem(R.id.menu_download)?.actionView mDownloadCountHint = downloadMenuView?.findViewById(R.id.menu_download_count_hint) - mDownloadCountHint?.typeface = Typeface.createFromAsset(requireActivity().assets, "fonts/d_din_bold_only_number.ttf") + mDownloadCountHint?.typeface = Typeface.createFromAsset(requireActivity().assets, Constants.DIN_FONT_PATH) } private fun updateDownloadCountHint(updateList: List?) { @@ -164,7 +165,7 @@ class HomeToolbarWrapperFragment: LazyFragment(), ToolbarController { } // 设置标题居中 - private fun setTitleCenter() { + override fun setTitleCenter() { if (mActionMenuView != null && mTitleContainer != null && mBackContainer != null) { mActionMenuView!!.post { val params = diff --git a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.kt deleted file mode 100644 index 1c62e50ea7..0000000000 --- a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperFragment.kt +++ /dev/null @@ -1,874 +0,0 @@ -package com.gh.gamecenter.fragment - -import android.graphics.PorterDuff -import android.graphics.drawable.Animatable -import android.net.Uri -import android.os.Bundle -import android.text.TextUtils -import android.view.View -import android.widget.CheckedTextView -import android.widget.ImageView -import androidx.annotation.ColorRes -import androidx.core.content.ContextCompat -import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider -import androidx.viewpager.widget.PagerAdapter -import com.airbnb.lottie.LottieAnimationView -import com.facebook.drawee.backends.pipeline.Fresco -import com.facebook.drawee.controller.BaseControllerListener -import com.facebook.drawee.interfaces.DraweeController -import com.facebook.drawee.view.SimpleDraweeView -import com.facebook.fresco.animation.drawable.AnimatedDrawable2 -import com.facebook.fresco.animation.drawable.BaseAnimationListener -import com.facebook.imagepipeline.image.ImageInfo -import com.gh.common.constant.Config -import com.gh.common.prioritychain.* -import com.gh.common.util.* -import com.gh.common.util.LogUtils -import com.gh.common.util.NewLogUtils -import com.gh.gamecenter.MainActivity -import com.gh.gamecenter.R -import com.gh.gamecenter.ShellActivity -import com.gh.gamecenter.adapter.MainFragmentPagerAdapter -import com.gh.gamecenter.amway.AmwayFragment -import com.gh.gamecenter.catalog.CatalogFragment -import com.gh.gamecenter.category.CategoryDirectoryFragment -import com.gh.gamecenter.category2.CategoryV2Fragment -import com.gh.gamecenter.common.base.fragment.BaseFragment_ViewPager_Checkable -import com.gh.gamecenter.common.base.fragment.ToolbarFragment -import com.gh.gamecenter.common.callback.OnDoubleTapListener -import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.common.constant.EntranceConsts -import com.gh.gamecenter.common.eventbus.EBReuse -import com.gh.gamecenter.common.exposure.ExposureSource -import com.gh.gamecenter.common.syncpage.SyncPageRepository -import com.gh.gamecenter.common.tracker.TrackerLogger -import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.core.utils.SPUtils -import com.gh.gamecenter.core.utils.SentryHelper -import com.gh.gamecenter.databinding.FragmentMainBinding -import com.gh.gamecenter.discovery.DiscoveryFragment -import com.gh.gamecenter.entity.HomeDataEntity -import com.gh.gamecenter.entity.SubjectData -import com.gh.gamecenter.entity.SubjectRecommendEntity -import com.gh.gamecenter.eventbus.EBSkip -import com.gh.gamecenter.eventbus.EBUISwitch -import com.gh.gamecenter.forum.detail.ForumDetailFragment -import com.gh.gamecenter.forum.home.CommunityHomeFragment -import com.gh.gamecenter.game.GameFragment -import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailFragment -import com.gh.gamecenter.game.commoncollection.detail.CommonCollectionDetailFragment -import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment -import com.gh.gamecenter.login.entity.UserInfoEntity -import com.gh.gamecenter.message.MessageUnreadRepository -import com.gh.gamecenter.message.MessageUnreadViewModel -import com.gh.gamecenter.personal.HaloPersonalFragment -import com.gh.gamecenter.pkg.PkgHelper -import com.gh.gamecenter.servers.GameServersPublishFragment -import com.gh.gamecenter.servers.GameServersTestFragment -import com.gh.gamecenter.subject.SubjectFragment -import com.gh.gamecenter.video.detail.HomeVideoFragment -import com.halo.assistant.HaloApp -import com.halo.assistant.fragment.WebFragment -import com.lightgame.listeners.OnBackPressedListener -import com.lightgame.view.CheckableLinearLayout -import com.lightgame.view.NoScrollableViewPager -import org.greenrobot.eventbus.EventBus -import org.greenrobot.eventbus.Subscribe -import org.greenrobot.eventbus.ThreadMode -import org.json.JSONObject - -class MainWrapperFragment : BaseFragment_ViewPager_Checkable(), OnBackPressedListener { - - private val mBinding by lazy { FragmentMainBinding.inflate(layoutInflater) } - private var mViewModel: MainWrapperViewModel? = null - private var mGameWrapperFragment: SearchToolWrapperFragment? = null - private var mHomeFragment: HomeSearchToolWrapperFragment? = null - private var mFourthFragment: ToolbarFragment? = null - private var mCommunityHomeFragment: CommunityHomeFragment? = null - private val mResAssets = arrayOf( - "lottie/tab_home.json", - "lottie/tab_game.json", - "lottie/tab_forum.json", - "lottie/tab_video.json", - "tab_mine.gif" - ) - private var mPopUpHomePush = false - - private val mPriorityChain by lazy { PriorityChain() } - - private var mVideoNavBarEntity: SubjectRecommendEntity? = null - - override fun getLayoutId(): Int = 0 - override fun getInflatedLayout() = mBinding.root - override fun getCheckableGroupId(): Int = R.id.lightgame_tab_container - override fun getViewPagerId(): Int = R.id.lightgame_tab_viewpager - override fun getCurrentItem() = mViewPager.currentItem - override fun getChildCount(): Int = 5 - override fun provideAdapter(): PagerAdapter = MainFragmentPagerAdapter(requireContext(), childFragmentManager, mFragmentsList as ArrayList) - - override fun initFragmentList(fragments: MutableList) { - mHomeFragment = HomeSearchToolWrapperFragment() - fragments.add(mHomeFragment!!) - initGameWrapperFragment() - fragments.add(mGameWrapperFragment!!) - mCommunityHomeFragment = CommunityHomeFragment() - mCommunityHomeFragment!!.arguments = arguments - fragments.add(mCommunityHomeFragment!!) - initFourthFragment(fragments) - if (mViewModel!!.shouldHideVideoTab()) { - mBinding.mainTabVideo.visibility = View.GONE - } - fragments.add(HaloPersonalFragment()) - } - - private fun initGameWrapperFragment() { - mGameWrapperFragment = SearchToolWrapperFragment() - val homeArgs = Bundle() - var entity = mViewModel!!.gameNavBar.value - if (entity == null) { - entity = HomeBottomBarHelper.getDefaultGameBarData() - } - putFragmentBundle(entity, homeArgs, true) - - if ("block" != entity.type) { - homeArgs.putParcelable( - EntranceConsts.KEY_EXPOSURE_SOURCE, - ExposureSource("游戏库", "") - ) - } - mGameWrapperFragment!!.arguments = homeArgs - } - - private fun initFourthFragment(fragments: MutableList) { - var entity = mViewModel!!.videoNavBar.value - if (entity == null) { - entity = HomeBottomBarHelper.getDefaultVideoData() - } - - val fourthFragment: Fragment? = when (entity.type) { - "video_stream" -> { - if (mFourthFragment !is HomeVideoFragment) { - val videoArgs = Bundle() - videoArgs.putBoolean(EntranceConsts.KEY_IS_HOME_VIDEO, true) - mFourthFragment = HomeVideoFragment() - mFourthFragment!!.arguments = videoArgs - mFourthFragment - } else { - null - } - } - - "top_game_comment" -> { - if (mFourthFragment !is AmwayFragment) { - val amwayArgs = Bundle() - amwayArgs.putBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, true) - mFourthFragment = AmwayFragment() - mFourthFragment!!.arguments = amwayArgs - mFourthFragment - } else { - null - } - } - - "community" -> { - if (mFourthFragment !is ForumDetailFragment) { - val forumArgs = Bundle() - forumArgs.putString(EntranceConsts.KEY_BBS_ID, entity.link) - forumArgs.putBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, true) - mFourthFragment = ForumDetailFragment() - mFourthFragment!!.arguments = forumArgs - mFourthFragment - } else { - null - } - } - - else -> { - // 新页面与旧页面类型不同或链接不同时才进行替换 - if (mFourthFragment !is HomeToolbarWrapperFragment || mVideoNavBarEntity?.type != entity.type || mVideoNavBarEntity?.link != entity.link) { - val homeArgs = Bundle() - putFragmentBundle(entity, homeArgs, false) - mFourthFragment = HomeToolbarWrapperFragment() - mFourthFragment!!.arguments = homeArgs - mFourthFragment - } else { - null - } - } - } - if (fourthFragment != null) { - if (mFragmentsList.size > 3) { - // 替换第4个Fragment - try { - (mAdapter as MainFragmentPagerAdapter).replaceFragment(3, fourthFragment) - } catch (e: Exception) { - SentryHelper.onEvent( - "REPLACE_FRAGMENT_CRASH", - "new_fragment_type", - entity.type, - "old_fragment_type", - mVideoNavBarEntity?.type, - "new_link", - entity.link, - "old_link", - mVideoNavBarEntity?.link - ) - } - } else { - fragments.add(fourthFragment) - } - mVideoNavBarEntity = entity - } - } - - private fun putFragmentBundle(entity: SubjectRecommendEntity, homeArgs: Bundle, isGameTab: Boolean) { - var className = HomeGameWrapperFragment::class.java.name - val entrance = if (isGameTab) "游戏库" else entity.linkTypeChinese - when (entity.type) { - "block" -> { - className = if (isGameTab) HomeGameWrapperFragment::class.java.name else GameFragment::class.java.name - if (isGameTab) { - homeArgs.putBoolean(EntranceConsts.KEY_SHOW_SEARCH_TOOLBAR, false) - } else { - homeArgs.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, true) - homeArgs.putBoolean(EntranceConsts.KEY_IS_FROM_HOME_TOOLBAR_WRAPPER, true) - } - homeArgs.putParcelable(EntranceConsts.KEY_BLOCK_DATA, entity) - } - - "column" -> { - className = SubjectFragment::class.java.name - homeArgs.putString(EntranceConsts.KEY_ENTRANCE, entrance) - homeArgs.putParcelable( - EntranceConsts.KEY_SUBJECT_DATA, - SubjectData(entity.link, entity.text, false, "", "", "", "", true, false, false) - ) - if (!isGameTab) { - homeArgs.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, true) - } - } - - "column_collection" -> { - className = ColumnCollectionDetailFragment::class.java.name - homeArgs.putString(EntranceConsts.KEY_ENTRANCE, entrance) - homeArgs.putString(EntranceConsts.KEY_COLLECTION_ID, entity.link) - homeArgs.putInt(EntranceConsts.KEY_POSITION, 0) - homeArgs.putString(EntranceConsts.KEY_COLUMNNAME, entity.text) - homeArgs.putBoolean(EntranceConsts.KEY_IS_COLUMN_COLLECTION, true) - } - - "server" -> { - className = GameServersPublishFragment::class.java.name - homeArgs.putString(EntranceConsts.KEY_ENTRANCE, entrance) - if (!isGameTab) { - homeArgs.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, true) - } - } - - "column_test" -> { - className = GameServersTestFragment::class.java.name - homeArgs.putString(GameServersTestFragment.TEST_COLUMN_ID, entity.link) - if (!isGameTab) { - homeArgs.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, true) - } - } - - "catalog" -> { - className = CatalogFragment::class.java.name - homeArgs.putString(EntranceConsts.KEY_CATALOG_ID, entity.link) - homeArgs.putString(EntranceConsts.KEY_CATALOG_TITLE, entity.text) - } - - "category" -> { - className = CategoryDirectoryFragment::class.java.name - homeArgs.putString(EntranceConsts.KEY_CATEGORY_ID, entity.link) - homeArgs.putString(EntranceConsts.KEY_CATEGORY_TITLE, entity.text) - } - - "category_v2" -> { - className = CategoryV2Fragment::class.java.name - homeArgs.putString(EntranceConsts.KEY_CATEGORY_ID, entity.link) - homeArgs.putString(EntranceConsts.KEY_CATEGORY_TITLE, entity.text) - if (!isGameTab) { - homeArgs.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, true) - } - } - - "common_collection" -> { - className = CommonCollectionDetailFragment::class.java.name - homeArgs.putString(EntranceConsts.KEY_ENTRANCE, entrance) - homeArgs.putString(EntranceConsts.KEY_COLLECTION_ID, entity.link) - homeArgs.putString(EntranceConsts.KEY_COLUMNNAME, entity.text) - } - - "game_list" -> { - className = GameCollectionSquareFragment::class.java.name - homeArgs.putString(EntranceConsts.KEY_ENTRANCE, entrance) - homeArgs.putInt(EntranceConsts.KEY_TAB_INDEX, 0) - homeArgs.putString(EntranceConsts.KEY_NAME, entity.name) - homeArgs.putBoolean(EntranceConsts.KEY_IS_FROM_HOME_TOOLBAR_WRAPPER, true) - } - - "web" -> { - className = WebFragment::class.java.name - homeArgs.putString(EntranceConsts.KEY_URL, entity.link) - homeArgs.putBoolean(WebFragment.KEY_OPEN_NATIVE_PAGE, false) - homeArgs.putBoolean(WebFragment.KEY_ENABLE_HORIZONTAL_SCROLL_DISPATCH, true) - if (entity.link != null && entity.link!!.contains("leave_web_page_handle_back_pressed=true")) { - homeArgs.putBoolean(WebFragment.KEY_LEAVE_WEB_PAGE_TO_HANDLE_BACK_PRESSED, true) - } - } - - "explore_column" -> { - className = DiscoveryFragment::class.java.name - homeArgs.putString(EntranceConsts.KEY_ENTRANCE, entrance) - } - } - homeArgs.putString(EntranceConsts.KEY_LOCATION, entrance) - homeArgs.putString(WRAPPER_FRAGMENT_NAME, className) - homeArgs.putBoolean(EntranceConsts.KEY_IS_HOME, true) - } - - override fun restoreFragments(): ArrayList = ArrayList() - - override fun onCreate(savedInstanceState: Bundle?) { - mViewModel = viewModelProviderFromParent(MainWrapperViewModel.Factory(HaloApp.getInstance())) - - super.onCreate(savedInstanceState) - - buildPriorityChain() - - mViewModel?.gameNavBar?.observe(this) { updateGameBarContent(it) } - - mViewModel?.videoNavBar?.observe(this) { updateVideoBarContent(it) } - - mBinding.viewShadow.visibility = if (mIsDarkModeOn) View.GONE else View.VISIBLE - - ViewModelProvider(this)[MessageUnreadViewModel::class.java] - .unreadMessageTotalLiveData.observe(this) { isShow: Boolean? -> - mBinding.mainIvMessageHint.goneIf(!isShow!!) - } - } - - /** - * 构建 PriorityChain,使用优先队列来决定当前需要显示哪一个弹窗 - * 正常弹窗顺序(不含首页自动下拉二楼),更新弹窗、隐私政策弹窗、消息通知权限弹窗、预约弹窗、插件化通知 popup、启动弹窗、右下悬浮窗 - * 特殊弹窗顺序(含首页自动下拉二楼,无需请求显示启动弹窗),更新弹窗、隐私政策弹窗、消息通知权限弹窗、预约弹窗、插件化通知 popup、首页自动下拉二楼、右下悬浮窗 - * 可以根据上述的顺序为弹窗预设优先级 - * 更新弹窗 (-100) - * 隐私政策弹窗 (-99) - * 消息通知权限弹窗 (0) - * 预约弹窗 (1) - * 插件化通知 popup (2) - * 首页自动下拉二楼 (3) - * 启动弹窗 (4) - * 首页右下悬浮窗 (5) - */ - private fun buildPriorityChain() { - val homeSearchToolWrapperViewModel: HomeSearchToolWrapperViewModel = viewModelProviderFromParent() - - val updateDialogHandler = UpdateDialogHandler(requireContext(), -100) - val privacyPolicyDialogHandler = PrivacyPolicyDialogHandler(-99) - val notificationPermissionDialogHandler = NotificationPermissionDialogHandler(0) - val reserveDialogHandler = ReserveDialogHandler(1) - val accelerateNotificationHandler = AccelerateNotificationHandler(2) - val homePushHandler = HomePushHandler(3) - val welcomeDialogHandler = WelcomeDialogHandler(4) - val floatingWindowHandler = FloatingWindowHandler(5) - - homeSearchToolWrapperViewModel.setFloatingWindowHandler(floatingWindowHandler) - - mPriorityChain.addHandler(updateDialogHandler) - mPriorityChain.addHandler(privacyPolicyDialogHandler) - mPriorityChain.addHandler(welcomeDialogHandler) - mPriorityChain.addHandler(reserveDialogHandler) - mPriorityChain.addHandler(notificationPermissionDialogHandler) - mPriorityChain.addHandler(accelerateNotificationHandler) - mPriorityChain.addHandler(homePushHandler) - mPriorityChain.addHandler(floatingWindowHandler) - - mViewModel?.privacyPolicyDialog?.observe(this) { - privacyPolicyDialogHandler.doPreProcess(requireActivity(), it) - } - - mViewModel?.reserveDialog?.observe(this) { - reserveDialogHandler.doPreProcess(this, it) - MessageUnreadRepository.loadMessageUnreadData() - } - - mViewModel?.accelerateNotificationPopup?.observe(this) { - accelerateNotificationHandler.doPreProcess( - requireActivity(), - mBaseHandler!! as BaseHandler, - it, - mViewModel!! - ) - } - - mViewModel?.floatingWindow?.observe(this) { windowEntityList -> - floatingWindowHandler.setData(windowEntityList ?: arrayListOf()) - } - - homeSearchToolWrapperViewModel.homeDataLiveData.observe(this) { homeDataEntity: HomeDataEntity? -> - val homePushSet = - SPUtils.getStringSet(Constants.SP_HOME_PUSH_POP_UP_SET) - mPopUpHomePush = homeDataEntity?.homePush != null - && "on" == homeDataEntity.homePush?.popSwitch - && !homePushSet.contains(homeDataEntity.homePush?.id) - - if (mPopUpHomePush) { - welcomeDialogHandler.doPreProcess(this, null) - } else { - welcomeDialogHandler.doPreProcess(this, mViewModel!!.welcomeDialog.value) - } - - homePushHandler.doPreProcess(mHomeFragment!!, mPopUpHomePush) - } - } - - fun showDialog() { - mPriorityChain.start() - } - - fun isPriorityChainHandlerQueueEmpty() = mPriorityChain.isHandlerQueueEmpty() - - private fun applyPkgConfig() { - val pkgLinkEntity = PkgHelper.getPkgConfig() - if (pkgLinkEntity != null) { - val bottomTab = pkgLinkEntity.homeBottomTab - if (!pkgLinkEntity.shouldStayAtMainActivity) { - // 不停留在首页,执行跳转,标记已用 - PkgHelper.markConfigUsed() - DirectUtils.directToLinkPage(requireContext(), pkgLinkEntity, "推广包配置", "首页") - } else if ("home" != bottomTab) { - // 停留首页,但选中底部 tab 不是首页的,执行选中,标记已用 - PkgHelper.markConfigUsed() - // TODO 根据具体 tab 来作为跳转的具体位置,避免硬编码 - var targetIndex = INDEX_HOME - when (bottomTab) { - "game_lib" -> targetIndex = INDEX_GAME - "community" -> targetIndex = INDEX_BBS - "video" -> targetIndex = INDEX_VIDEO - "gh" -> targetIndex = INDEX_PERSONAL - } - mViewPager.currentItem = targetIndex - onPageChanged(targetIndex) - changeColor(targetIndex) - } - } - } - - private fun updateGameBarContent(navBarEntity: SubjectRecommendEntity?) { - if (navBarEntity != null) { - mBinding.mainTabGame.visibility = View.VISIBLE - mBinding.mainTabGameName.text = navBarEntity.name - ImageUtils.picasso - .load(Uri.parse(navBarEntity.iconUnselect)) //.placeholder(R.drawable.ic_game_unselect) - .into(mBinding.mainTabGameIcon) - if (navBarEntity.default) currentItem = INDEX_GAME - } else { - mBinding.mainTabGame.visibility = View.GONE - } - } - - private fun updateVideoBarContent(navBarEntity: SubjectRecommendEntity?) { - mBinding.mainTabVideo.visibility = View.VISIBLE - if (navBarEntity != null) { - mBinding.mainTabVideoName.text = navBarEntity.name - ImageUtils.picasso - .load(Uri.parse(navBarEntity.iconUnselect)) - .placeholder(R.drawable.ic_video_unselect) - .into(mBinding.mainTabVideoIcon) - if (navBarEntity.default) currentItem = INDEX_VIDEO - initFourthFragment(mFragmentsList) - } else { - mBinding.mainTabVideo.visibility = View.GONE - } - } - - override fun onPause() { - super.onPause() - SyncPageRepository.clearSyncData() - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - if (mViewPager is NoScrollableViewPager) { - (mViewPager as NoScrollableViewPager).setScrollable(false) - } - - // 启动时即当选中第一个 tab - onPageChanged(INDEX_HOME) - changeColor(INDEX_HOME) - for (i in 0 until mCheckableGroup.childCount) { - val child = mCheckableGroup.getChildAt(i) - child.setOnTouchListener(object : OnDoubleTapListener(context) { - override fun onDoubleTap() { - EventBus.getDefault().post(EBUISwitch(EB_MAIN_SCROLL_TOP, i)) - } - }) - } - applyPkgConfig() - - updateRealNameErrorContainer() - } - - override fun handleOnClick(view: View): Boolean { - val toCheck = mCheckableGroup.indexOfChild(view) - playTabAnimation(toCheck) - changeColor(toCheck) - mHomeFragment?.finishTwoLevel("跳转收起") - val trackEvent = JSONObject() - val isFirstTime = SPUtils.getBoolean(Constants.SP_SENSORS_IS_FIRST_TIME_HOME_BOTTOM_TAB_SELECT, true) - tryWithDefaultCatch { - trackEvent.put("position", toCheck) - trackEvent.put("tab_content", getTabName(toCheck)) - trackEvent.put("\$is_first_time", isFirstTime) - } - SPUtils.setBoolean(Constants.SP_SENSORS_IS_FIRST_TIME_HOME_BOTTOM_TAB_SELECT, false) - SensorsBridge.trackEvent("HomeBottomTabSelect", trackEvent) - return super.handleOnClick(view) - } - - private fun getTabName(index: Int): String { - return when (index) { - 0 -> mBinding.mainTabHomeName.text.toString() - 1 -> mBinding.mainTabGameName.text.toString() - 2 -> mBinding.mainTabCommunityName.text.toString() - 3 -> mBinding.mainTabVideoName.text.toString() - 4 -> mBinding.mainTabMeName.text.toString() - else -> "" - } - } - - private fun changeColor(toCheck: Int) { - if (mFourthFragment is HomeVideoFragment && toCheck == INDEX_VIDEO) { - mBinding.viewShadow.visibility = View.GONE - mCheckableGroup.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.transparent)) - changeTabImageColor(R.color.text_A1A5B7, PorterDuff.Mode.SRC_ATOP, toCheck) - } else { - mBinding.viewShadow.visibility = if (mIsDarkModeOn) View.GONE else View.VISIBLE - mCheckableGroup.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.ui_surface)) - changeTabImageColor( - if (mIsDarkModeOn) R.color.text_A1A5B7 else R.color.text_50556B, - if (mIsDarkModeOn) PorterDuff.Mode.SRC_ATOP else PorterDuff.Mode.DST, - toCheck - ) - } - changeTabTextColor(toCheck) - } - - private fun playTabAnimation(toCheck: Int) { - when (toCheck) { - INDEX_HOME -> { - stopAnimation(mBinding.lottieGame, INDEX_GAME) - stopAnimation(mBinding.lottieVideo, INDEX_VIDEO) - stopAnimation(mBinding.lottieCommunity, INDEX_BBS) - stopAnimation(mBinding.lottieMine, INDEX_PERSONAL) - playAnimation(mBinding.lottieHome, toCheck) - } - - INDEX_GAME -> { - val value = mViewModel!!.gameNavBar.value - if (value != null && !TextUtils.isEmpty(value.animationCode)) { - stopAnimation(mBinding.lottieHome, INDEX_HOME) - stopAnimation(mBinding.lottieVideo, INDEX_VIDEO) - stopAnimation(mBinding.lottieCommunity, INDEX_BBS) - stopAnimation(mBinding.lottieMine, INDEX_PERSONAL) - playGameAnimation(value, toCheck) - } - } - - INDEX_BBS -> { - stopAnimation(mBinding.lottieHome, INDEX_HOME) - stopAnimation(mBinding.lottieGame, INDEX_GAME) - stopAnimation(mBinding.lottieVideo, INDEX_VIDEO) - stopAnimation(mBinding.lottieMine, INDEX_PERSONAL) - playAnimation(mBinding.lottieCommunity, toCheck) - } - - INDEX_VIDEO -> { - val value = mViewModel!!.videoNavBar.value - if (value != null && !TextUtils.isEmpty(value.animationCode)) { - stopAnimation(mBinding.lottieHome, INDEX_HOME) - stopAnimation(mBinding.lottieGame, INDEX_GAME) - stopAnimation(mBinding.lottieCommunity, INDEX_BBS) - stopAnimation(mBinding.lottieMine, INDEX_PERSONAL) - playVideoAnimation(value, toCheck) - } - } - - INDEX_PERSONAL -> { - stopAnimation(mBinding.lottieHome, INDEX_HOME) - stopAnimation(mBinding.lottieGame, INDEX_GAME) - stopAnimation(mBinding.lottieVideo, INDEX_VIDEO) - stopAnimation(mBinding.lottieCommunity, INDEX_BBS) - playAnimation(mBinding.lottieMine, toCheck) - } - - else -> {} - } - } - - private fun changeTabImageColor(@ColorRes colorRes: Int, mode: PorterDuff.Mode, toCheck: Int) { - for (i in 0 until mCheckableGroup.childCount) { - val checkableGroupChild = mCheckableGroup.getChildAt(i) as CheckableLinearLayout - val checkableView = checkableGroupChild.findCheckableImageView(checkableGroupChild) as? ImageView - if (checkableView != null) { - if (i != toCheck) { - checkableView.setColorFilter(ContextCompat.getColor(requireContext(), colorRes), mode) - } else { - checkableView.colorFilter = null - } - } - } - } - - private fun changeTabTextColor(toCheck: Int) { - for (i in 0 until mCheckableGroup.childCount) { - val checkableGroupChild = mCheckableGroup.getChildAt(i) as? CheckableLinearLayout - val checkableView = checkableGroupChild?.findCheckableTextView(checkableGroupChild) as? CheckedTextView - val isChecked = i == toCheck - if (checkableView != null) { - val unselectColor: Int = if (mFourthFragment is HomeVideoFragment && toCheck == INDEX_VIDEO) R.color.text_A1A5B7 else R.color.tab_text_unselect - checkableView.setTextColor( - ContextCompat.getColor(requireContext(), if (isChecked) R.color.text_theme else unselectColor) - ) - } - } - } - - private fun playGameAnimation(entity: SubjectRecommendEntity, position: Int) { - val checkableGroupChild = mCheckableGroup.getChildAt(position) as? CheckableLinearLayout - val checkableView = checkableGroupChild?.findCheckableImageView(checkableGroupChild) - if (checkableView != null) checkableView.visibility = View.INVISIBLE - mBinding.lottieGame.visibility = View.VISIBLE - mBinding.lottieGame.setAnimationFromJson(entity.animationCode, entity.iconSelect) - mBinding.lottieGame.playAnimation() - mBinding.lottieGame.doOnAnimationEnd { - mBinding.lottieGame.visibility = View.GONE - if (checkableView != null) checkableView.visibility = View.VISIBLE - } - } - - private fun playVideoAnimation(entity: SubjectRecommendEntity, position: Int) { - val checkableGroupChild = mCheckableGroup.getChildAt(position) as? CheckableLinearLayout - val checkableView = checkableGroupChild?.findCheckableImageView(checkableGroupChild) - if (checkableView != null) checkableView.visibility = View.INVISIBLE - mBinding.lottieVideo.visibility = View.VISIBLE - mBinding.lottieVideo.setAnimationFromJson(entity.animationCode, entity.iconSelect) - mBinding.lottieVideo.playAnimation() - mBinding.lottieVideo.doOnAnimationEnd { - mBinding.lottieVideo.visibility = View.GONE - if (checkableView != null) checkableView.visibility = View.VISIBLE - } - } - - private fun playAnimation(view: View, position: Int) { - val checkableGroupChild = mCheckableGroup.getChildAt(position) as CheckableLinearLayout - val checkableView = checkableGroupChild.findCheckableImageView(checkableGroupChild) - if (checkableView != null) checkableView.visibility = View.INVISIBLE - view.visibility = View.VISIBLE - if (view is LottieAnimationView) { - view.setAnimation(mResAssets[position]) - view.playAnimation() - view.doOnAnimationEnd { - view.visibility = View.GONE - checkableView?.visibility = View.VISIBLE - } - } else { - val lottieView = view as SimpleDraweeView - val uri = Uri.parse("asset:///" + mResAssets[position]) - val controller: DraweeController = Fresco.newDraweeControllerBuilder() - .setUri(uri) - .setAutoPlayAnimations(true) - .setControllerListener(object : BaseControllerListener() { - override fun onFinalImageSet(id: String, imageInfo: ImageInfo?, animatable: Animatable?) { - super.onFinalImageSet(id, imageInfo, animatable) - if (animatable != null) { - val animatedDrawable = animatable as AnimatedDrawable2 - animatedDrawable.setAnimationListener(object : BaseAnimationListener() { - override fun onAnimationStop(drawable: AnimatedDrawable2) { - super.onAnimationStop(drawable) - lottieView.visibility = View.GONE - checkableView?.visibility = View.VISIBLE - } - }) - } - } - }).build() - lottieView.controller = controller - } - } - - private fun stopAnimation(lottieView: View, position: Int) { - val checkableGroupChild = mCheckableGroup.getChildAt(position) as CheckableLinearLayout - val checkableView = checkableGroupChild.findCheckableImageView(checkableGroupChild) - checkableView?.visibility = View.VISIBLE - lottieView.visibility = View.GONE - } - - override fun onPageChanged(index: Int) { - super.onPageChanged(index) - var tabText = "" - when (index) { - INDEX_HOME -> { - tabText = "首页" - NewLogUtils.logBottomNavigationClick("首页", "", "", "", index) - } - - INDEX_GAME -> { - tabText = "游戏库" - var entity = mViewModel!!.gameNavBar.value - if (entity == null) { - entity = HomeBottomBarHelper.getDefaultGameBarData() - } - NewLogUtils.logBottomNavigationClick( - "游戏库", - (if (entity.type != null) entity.type else "")!!, - (if (entity.text != null) entity.text else "")!!, (if (entity.link != null) entity.link else "")!!, - index - ) - } - - INDEX_BBS -> { - tabText = "论坛" - LogUtils.uploadAccessBbsTab() - NewLogUtils.logBottomNavigationClick("社区", "", "", "", index) - } - - INDEX_VIDEO -> { - tabText = "视频" - var entity = mViewModel!!.videoNavBar.value - if (entity == null) { - entity = HomeBottomBarHelper.getDefaultVideoData() - } - SensorsBridge.trackAccessVideoStreaming("光环底部tab") - NewLogUtils.logCommunityHomeEvent("click_button_video_tab") - NewLogUtils.logBottomNavigationClick(entity.linkTypeChinese, entity.type ?: "", entity.text ?: "", entity.link ?: "", index) - } - - INDEX_PERSONAL -> { - tabText = "我的光环" - IntegralLogHelper.log("view_me", "我的光环") - NewLogUtils.logBottomNavigationClick("我的光环", "", "", "", index) - } - } - TrackerLogger.logHomeTabSelected(index, tabText) - val gameNavBarEntity = mViewModel!!.gameNavBar.value - if (gameNavBarEntity != null) { - if (index == INDEX_GAME) { - ImageUtils.picasso - .load(Uri.parse(gameNavBarEntity.iconSelect)) //.placeholder(R.drawable.ic_game_select) - .into(mBinding.mainTabGameIcon) - } else { - ImageUtils.picasso - .load(Uri.parse(gameNavBarEntity.iconUnselect)) //.placeholder(R.drawable.ic_game_unselect) - .into(mBinding.mainTabGameIcon) - } - } - - val videoNavBarEntity = mViewModel!!.videoNavBar.value - if (videoNavBarEntity != null) { - if (index == INDEX_VIDEO) { - ImageUtils.picasso - .load(Uri.parse(videoNavBarEntity.iconSelect)) - .placeholder(R.drawable.ic_video_select) - .into(mBinding.mainTabVideoIcon) - } else { - ImageUtils.picasso - .load(Uri.parse(videoNavBarEntity.iconUnselect)) - .placeholder(R.drawable.ic_video_unselect) - .into(mBinding.mainTabVideoIcon) - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onEventMainThread(skip: EBSkip) { - if (MainActivity.EB_SKIP_MAIN == skip.type) { - checkIndex(skip.currentItem) - changeColor(skip.currentItem) - mViewPager.setCurrentItem(skip.currentItem, false) - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onEventMainThread(reuse: EBReuse) { - if ("Refresh" == reuse.type) { - val settings = Config.getSettings() - if (settings != null && !settings.showCommunityEntrance()) { - mBinding.mainTabCommunity.visibility = View.GONE - } else { - mBinding.mainTabCommunity.visibility = View.VISIBLE - } - } else if (Constants.EB_REALNAME_RESULT == reuse.type) { - updateRealNameErrorContainer() - } - } - - private fun updateRealNameErrorContainer() { - val deviceCertificationInfoString = - SPUtils.getString(Constants.SP_DEVICE_CERTIFICATION_PREFIX + HaloApp.getInstance().gid) - - // 未点过关闭按钮并且处于实名认证失败状态的,显示提示 view - if (!SPUtils.getBoolean(Constants.SP_REALNAME_ERROR_HINT_IGNORED) - && !TextUtils.isEmpty(deviceCertificationInfoString) - ) { - val entity: UserInfoEntity? = deviceCertificationInfoString.toObject() - if (entity?.idCard != null && entity.idCard!!.status == 2) { - mBinding.realNameErrorContainer.visibility = View.VISIBLE - mBinding.realNameErrorCloseIv.enlargeTouchArea(100) - mBinding.realNameErrorCloseIv.setOnClickListener { - SPUtils.setBoolean(Constants.SP_REALNAME_ERROR_HINT_IGNORED, true) - mBinding.realNameErrorContainer.visibility = View.GONE - } - mBinding.realNameErrorContainer.setOnClickListener { - startActivity(ShellActivity.getIntent(requireActivity(), ShellActivity.Type.REAL_NAME_INFO, null)) - } - } else { - mBinding.realNameErrorContainer.visibility = View.GONE - } - } - } - - fun setCurrentItem(page: Int) { - mViewPager.setCurrentItem(page, false) - changeColor(page) - } - - override fun onHandleBackPressed(): Boolean { - if (mFourthFragment is HomeVideoFragment && (mFourthFragment as HomeVideoFragment).getCurrentFragment() != null) { - return (mFourthFragment!! as HomeVideoFragment).getCurrentFragment()!!.onHandleBackPressed() - } - if (mViewPager.currentItem == INDEX_VIDEO) { - return mFourthFragment!!.onBackPressed() - } - if (mViewPager.currentItem == INDEX_HOME) { - return mHomeFragment!!.onBackPressed() - } - if (mViewPager.currentItem == INDEX_GAME) { - return mGameWrapperFragment!!.onBackPressed() - } - return if (mViewPager.currentItem == INDEX_BBS) { - mCommunityHomeFragment!!.onBackPressed() - } else false - } - - override fun onDarkModeChanged() { - super.onDarkModeChanged() - changeColor(mViewPager.currentItem) - } - - companion object { - const val INDEX_HOME = 0 - const val INDEX_GAME = 1 - const val INDEX_BBS = 2 - const val INDEX_VIDEO = 3 - const val INDEX_PERSONAL = 4 - - const val EB_MAIN_SCROLL_TOP = "main_scroll_top" - const val WRAPPER_FRAGMENT_NAME = "WrapperFragmentName" - } -} diff --git a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperRepository.kt b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperRepository.kt deleted file mode 100644 index 275a9b2d7c..0000000000 --- a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperRepository.kt +++ /dev/null @@ -1,73 +0,0 @@ -package com.gh.gamecenter.fragment - -import android.annotation.SuppressLint -import androidx.lifecycle.MutableLiveData -import com.gh.common.util.HomeBottomBarHelper -import com.gh.gamecenter.BuildConfig -import com.gh.gamecenter.common.retrofit.BiResponse -import com.gh.gamecenter.core.utils.SingletonHolder -import com.gh.gamecenter.entity.SubjectRecommendEntity -import com.gh.gamecenter.retrofit.RetrofitManager -import com.halo.assistant.HaloApp -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers -import retrofit2.HttpException - -class MainWrapperRepository { - - private val mSensitiveApi = RetrofitManager.getInstance().api - - private val mGameNavBar = MutableLiveData() - private val mVideoNavBar = MutableLiveData() - - fun getGameNavBarLiveData() = mGameNavBar - fun getVideoNavBarLiveData() = mVideoNavBar - - @SuppressLint("CheckResult") - fun getHomeNavBar() { - mSensitiveApi.getHomeNavBarV2s(HaloApp.getInstance().channel, BuildConfig.VERSION_NAME) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse>() { - override fun onSuccess(data: List) { - var isVideoConfigurated = false - data.forEach { - if (it.position == TAB_GAME_POSITION) { - HomeBottomBarHelper.updateDefaultGameBarData(it) - mGameNavBar.postValue(it) - } else if (it.position == TAB_VIDEO_POSITION) { - isVideoConfigurated = true - HomeBottomBarHelper.updateDefaultVideoBarData(it) - mVideoNavBar.postValue(it) - } - } - // 没有配置底部tab第4位时默认展示视频tab - if (!isVideoConfigurated) { - val defaultVideoEntity = HomeBottomBarHelper.getDefaultVideoEntity() - HomeBottomBarHelper.updateDefaultVideoBarData(defaultVideoEntity) - mVideoNavBar.postValue(defaultVideoEntity) - } - } - - override fun onFailure(exception: Exception) { - if (exception is HttpException && exception.code() == 404) { - mGameNavBar.postValue(null) - HomeBottomBarHelper.updateDefaultGameBarData(SubjectRecommendEntity()) - - val defaultVideoEntity = HomeBottomBarHelper.getDefaultVideoEntity() - mVideoNavBar.postValue(defaultVideoEntity) - HomeBottomBarHelper.updateDefaultVideoBarData(defaultVideoEntity) - } else { - mGameNavBar.postValue(HomeBottomBarHelper.getDefaultGameBarData()) - mVideoNavBar.postValue(HomeBottomBarHelper.getDefaultVideoData()) - } - } - }) - } - - - companion object : SingletonHolder({ MainWrapperRepository() }) { - const val TAB_GAME_POSITION = 2 - const val TAB_VIDEO_POSITION = 4 - } -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperViewModel.kt b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperViewModel.kt deleted file mode 100644 index 30030d9cb0..0000000000 --- a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperViewModel.kt +++ /dev/null @@ -1,228 +0,0 @@ -package com.gh.gamecenter.fragment - -import android.annotation.SuppressLint -import android.app.Application -import android.preference.PreferenceManager -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import com.alibaba.android.arouter.launcher.ARouter -import com.gh.common.util.CheckLoginUtils -import com.gh.common.util.PackageUtils -import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.common.constant.RouteConsts -import com.gh.gamecenter.common.entity.SimpleGameEntity -import com.gh.gamecenter.common.retrofit.BiResponse -import com.gh.gamecenter.common.retrofit.Response -import com.gh.gamecenter.core.provider.IFloatingWindowProvider -import com.gh.gamecenter.core.utils.SPUtils -import com.gh.gamecenter.entity.DialogEntity -import com.gh.gamecenter.entity.SubjectRecommendEntity -import com.gh.gamecenter.feature.entity.GameEntity -import com.gh.gamecenter.feature.entity.WelcomeDialogEntity -import com.gh.gamecenter.floatingwindow.FloatingWindowEntity -import com.gh.gamecenter.login.user.UserManager -import com.gh.gamecenter.retrofit.RetrofitManager -import com.halo.assistant.HaloApp -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers -import okhttp3.MediaType -import okhttp3.RequestBody -import okhttp3.ResponseBody -import org.json.JSONObject -import retrofit2.HttpException - -class MainWrapperViewModel(application: Application, repository: MainWrapperRepository) : - AndroidViewModel(application) { - private val mApi = RetrofitManager.getInstance().api - private val mSensitiveApi = RetrofitManager.getInstance().api - - val gameNavBar: MutableLiveData = repository.getGameNavBarLiveData() - val videoNavBar: MutableLiveData = repository.getVideoNavBarLiveData() - - val welcomeDialog = MutableLiveData() - val reserveDialog = MutableLiveData?>() - val privacyPolicyDialog = MutableLiveData() - val accelerateNotificationPopup = MutableLiveData?>() - val floatingWindow = MutableLiveData?>() - - /** - * 请求各种弹窗的数据 - */ - fun requestAllDialogData() { - requestOpeningData() - requestReserveDialog() - requestAccelerateNotificationPopup() - requestFloatingWindowList() - } - - /** - * 获取弹窗 - */ - @SuppressLint("CheckResult") - private fun requestOpeningData() { - - val sp = PreferenceManager.getDefaultSharedPreferences(getApplication()) - - val lastId = SPUtils.getString(sp, Constants.SP_LAST_OPENING_ID, "") - val lastTime = SPUtils.getLong(sp, Constants.SP_LAST_OPENING_TIME, 0) - val openType = if (HaloApp.getInstance().isNewForThisVersion) "first" else "not_first_time" - - mSensitiveApi.getOpeningDialog(HaloApp.getInstance().channel, lastId, lastTime, openType) - .subscribeOn(Schedulers.io()) - .subscribe(object : BiResponse() { - override fun onSuccess(data: DialogEntity) { - val welcomeDialogEntity = data.welcomeDialog - val privacyPolicyDialogEntity = data.privacyPolicyDialog - - // 类型为游戏时判断是否本地已安装该游戏,已安装不弹弹窗 - if (welcomeDialogEntity != null) { - updateWelcomeDialogData(null, welcomeDialogEntity) - if (welcomeDialogEntity.type == "game") { - if (welcomeDialogEntity.packages == null) { - welcomeDialog.postValue(data.welcomeDialog) - } else { - for (packageName in data.welcomeDialog.packages!!) { - if (PackageUtils.isInstalled(getApplication(), packageName)) { - welcomeDialog.postValue(null) - return - } - } - welcomeDialog.postValue(data.welcomeDialog) - } - } else { - welcomeDialog.postValue(data.welcomeDialog) - } - } - - // 全新安装忽略隐私弹窗 - if (privacyPolicyDialogEntity != null) { - val id = privacyPolicyDialogEntity.id - val lastAcceptedId = SPUtils.getString(Constants.SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID, "") - if (HaloApp.getInstance().isBrandNewInstall) { - SPUtils.setString(Constants.SP_LAST_ACCEPTED_PRIVACY_DIALOG_ID, id) - privacyPolicyDialog.postValue(null) - } else { - if (id != lastAcceptedId) { - privacyPolicyDialog.postValue(privacyPolicyDialogEntity) - } else { - privacyPolicyDialog.postValue(null) - } - } - } else { - privacyPolicyDialog.postValue(null) - } - } - - override fun onFailure(exception: Exception) { - privacyPolicyDialog.postValue(null) - } - }) - } - - @SuppressLint("CheckResult") - private fun requestReserveDialog() { - if (CheckLoginUtils.isLogin()) { - mApi.getReserveDialog(UserManager.getInstance().userId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BiResponse>() { - override fun onSuccess(data: List) { - reserveDialog.postValue(data) - } - - override fun onFailure(exception: Exception) { - reserveDialog.postValue(null) - } - }) - } else { - reserveDialog.postValue(null) - } - } - - fun requestAccelerateNotificationPopup() { - if (CheckLoginUtils.isLogin()) { - mApi.getAccelerateNotificationPopup(UserManager.getInstance().userId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Response>() { - override fun onResponse(response: List?) { - super.onResponse(response) - accelerateNotificationPopup.postValue(response) - } - - override fun onFailure(e: HttpException?) { - super.onFailure(e) - accelerateNotificationPopup.postValue(null) - } - }) - } else { - accelerateNotificationPopup.postValue(null) - } - } - - private fun requestFloatingWindowList() { - val floatingWindowProvider = - ARouter.getInstance().build(RouteConsts.provider.floatingwindow) - .navigation() as? IFloatingWindowProvider - - floatingWindowProvider?.getFloatingWindowOnly { anies, throwable -> - if (anies != null) { - updateWelcomeDialogData(anies as ArrayList, null) - floatingWindow.postValue(anies) - } else if (throwable != null) { - floatingWindow.postValue(arrayListOf()) - } - } - } - - /** - * 更新启动弹窗是否需要显示消失动画的标记 - * 当启动弹窗绑定的 windowId 与右下角悬浮窗的第一个 window 的 id 一样时才显示消失动画 - */ - private fun updateWelcomeDialogData( - floatingWindowList: ArrayList?, - welcomeDialogEntity: WelcomeDialogEntity? - ) { - val finalFloatingWindowList = floatingWindowList ?: floatingWindow.value - val finalWelcomeDialogEntity = welcomeDialogEntity ?: welcomeDialog.value - - if (finalFloatingWindowList != null && finalWelcomeDialogEntity != null) { - if (finalFloatingWindowList.firstOrNull()?.id == finalWelcomeDialogEntity.floatingWindowId) { - finalWelcomeDialogEntity.shouldShowExitAnimation = true - } - } - } - - fun postMessageRead(messageId: String) { - val jsonObject = JSONObject() - jsonObject.put("type", "system_message") - val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString()) - RetrofitManager.getInstance().api.postMessageRead(UserManager.getInstance().userId, messageId, body) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Response() { - }) - } - - /** - * 特殊渠道隐藏视频 tab - */ - fun shouldHideVideoTab(): Boolean { - val channelInInteger = HaloApp.getInstance().channel.toIntOrNull() - return if (channelInInteger == null) false else channelInInteger in 1001..1050 - } - - class Factory( - private val mApplication: Application, - ) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T { - return MainWrapperViewModel(mApplication, MainWrapperRepository.getInstance()) as T - } - } - - companion object { - const val SHOULD_SHOW_OPENING_DIALOG = "show_opening_dialog" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/fragment/ReloadFragment.kt b/app/src/main/java/com/gh/gamecenter/fragment/ReloadFragment.kt new file mode 100644 index 0000000000..129e3a338e --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/fragment/ReloadFragment.kt @@ -0,0 +1,47 @@ +package com.gh.gamecenter.fragment + +import android.os.Bundle +import android.view.View +import com.gh.gamecenter.common.base.fragment.BaseLazyFragment +import com.gh.gamecenter.common.utils.observeNonNull +import com.gh.gamecenter.common.utils.viewModelProviderFromParent +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.databinding.FragmentReloadBinding +import com.gh.gamecenter.wrapper.MainWrapperRepository +import com.gh.gamecenter.wrapper.MainWrapperViewModel +import com.halo.assistant.HaloApp + +class ReloadFragment: BaseLazyFragment() { + + private val mBinding by lazy { FragmentReloadBinding.inflate(layoutInflater) } + private val mViewModel: MainWrapperViewModel by lazy { viewModelProviderFromParent(MainWrapperViewModel.Factory(HaloApp.getInstance())) } + + override fun getLayoutId(): Int = 0 + override fun getInflatedLayout(): View = mBinding.root + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mBinding.reuseLoading.root.visibility = View.VISIBLE + mBinding.reuseNoConnection.connectionReloadTv.setOnClickListener { + MainWrapperRepository.getInstance().getDataUnion() + mBinding.reuseNoConnection.root.visibility = View.GONE + mBinding.reuseLoading.root.visibility = View.VISIBLE + } + mViewModel.errorLiveData.observeNonNull(this) { + mBinding.reuseNoConnection.root.visibility = View.VISIBLE + mBinding.reuseLoading.root.visibility = View.GONE + } + } + + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() + mBaseHandler.post { DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn) } + } + + override fun onDarkModeChanged() { + super.onDarkModeChanged() + if (isSupportVisible) { + DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/fragment/SearchToolWrapperFragment.java b/app/src/main/java/com/gh/gamecenter/fragment/SearchToolWrapperFragment.java index 26f19fd4fd..4669dbbebe 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/SearchToolWrapperFragment.java +++ b/app/src/main/java/com/gh/gamecenter/fragment/SearchToolWrapperFragment.java @@ -8,6 +8,7 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import com.gh.common.constant.Config; +import com.gh.common.util.ViewPagerFragmentHelper; import com.gh.gamecenter.R; import com.gh.gamecenter.common.base.fragment.LazyFragment; import com.gh.gamecenter.common.base.fragment.ToolbarFragment; @@ -53,7 +54,7 @@ public class SearchToolWrapperFragment extends LazyFragment { try { Bundle arguments = getArguments(); if (arguments != null) { - String className = Objects.requireNonNull(arguments.getString(MainWrapperFragment.WRAPPER_FRAGMENT_NAME)); + String className = Objects.requireNonNull(arguments.getString(ViewPagerFragmentHelper.WRAPPER_FRAGMENT_NAME)); mContentFragment = contentFragment != null ? contentFragment : (Fragment) Class.forName(className).newInstance(); Bundle bundle = new Bundle(); diff --git a/app/src/main/java/com/gh/gamecenter/fragment/SearchToolbarFragment.java b/app/src/main/java/com/gh/gamecenter/fragment/SearchToolbarFragment.java index 7fa3803dca..6099ee439b 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/SearchToolbarFragment.java +++ b/app/src/main/java/com/gh/gamecenter/fragment/SearchToolbarFragment.java @@ -213,8 +213,8 @@ public class SearchToolbarFragment extends BaseLazyFragment implements View.OnCl new MessageUnreadViewModel.Factory(HaloApp.getInstance().getApplication())).get(MessageUnreadViewModel.class); mUnreadViewModel.getMessageCenterUnreadCountLiveData().observe(this, this::setMessageUnread); - mDownloadHintTv.setTypeface(Typeface.createFromAsset(requireContext().getAssets(), "fonts/d_din_bold_only_number.ttf")); - mMessageUnread.setTypeface(Typeface.createFromAsset(requireContext().getAssets(), "fonts/d_din_bold_only_number.ttf")); + mDownloadHintTv.setTypeface(Typeface.createFromAsset(requireContext().getAssets(), Constants.DIN_FONT_PATH)); + mMessageUnread.setTypeface(Typeface.createFromAsset(requireContext().getAssets(), Constants.DIN_FONT_PATH)); } private void setMessageUnread(int unreadCount) { diff --git a/app/src/main/java/com/gh/gamecenter/fragment/UpdateDialogFragment.kt b/app/src/main/java/com/gh/gamecenter/fragment/UpdateDialogFragment.kt new file mode 100644 index 0000000000..f9ca0a7041 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/fragment/UpdateDialogFragment.kt @@ -0,0 +1,333 @@ +package com.gh.gamecenter.fragment + +import android.annotation.SuppressLint +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import android.os.Bundle +import android.text.Html +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout +import com.gh.common.util.DirectUtils +import com.gh.common.util.PackageInstaller +import com.gh.download.DownloadManager +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.BaseDialogFragment +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.CustomLinkMovementMethod +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.databinding.AppUpdateHintDialogBinding +import com.gh.gamecenter.databinding.AppUpdatingDialogBinding +import com.gh.gamecenter.databinding.DialogUpdateBinding +import com.gh.gamecenter.entity.AppEntity +import com.gh.gamecenter.update.UpdateHelper +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus +import java.text.DecimalFormat +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.math.roundToInt + +/** + * 更新弹窗 DialogFragment + */ +class UpdateDialogFragment : BaseDialogFragment() { + + private var mUpdateEntity: AppEntity? = null + private var mDismissCallback: EmptyCallback? = null + private val mIsDismissByTouchInside = AtomicBoolean(false) + private val mBinding by lazy { DialogUpdateBinding.inflate(layoutInflater) } + + private var mIsDisplayingDownloadingStyle = false // 是否正在显示更新中样式 + + private val mDataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + if (downloadEntity.name.contains("光环助手")) { + if (mIsDisplayingDownloadingStyle) { + updateDownloadingView( + mBinding.updatingContainerView, + downloadEntity, + mUpdateEntity!! + ) + } else { + if (DownloadStatus.done == downloadEntity.status) { + updateUpdateHintView( + mBinding.updateHintContainerView, + mUpdateEntity!!, + UpdateHelper.isUpdateFileDownloaded(mUpdateEntity!!) + ) + } + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState != null) { + dismiss() + return + } + + DownloadManager.getInstance().addObserver(mDataWatcher) + + mUpdateEntity = arguments?.getParcelable(UPDATE_ENTITY) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + showUpdateHintStyle(requireContext(), mUpdateEntity!!) + + return mBinding.root + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return super.onCreateDialog(savedInstanceState).apply { + val cancelable = !mUpdateEntity!!.isForce + + setCancelable(cancelable) + setCanceledOnTouchOutside(cancelable) + this@UpdateDialogFragment.isCancelable = cancelable + } + } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + + if (!mIsDismissByTouchInside.get()) { + SensorsBridge.trackVersionUpdateDialogClick( + buttonName = "关闭弹窗", + keyDialogReminderTime = mUpdateEntity?.alert, + keyDialogClose = if (mUpdateEntity?.isForce == true) "关闭且强退" else "仅关闭" + ) + } + + mDismissCallback?.onCallback() + } + + override fun onDestroyView() { + super.onDestroyView() + + DownloadManager.getInstance().removeObserver(mDataWatcher) + } + + /** + * 显示更新弹窗(提示样式) + * @param context 上下文 + * @param updateEntity 更新实体 + */ + private fun showUpdateHintStyle(context: Context, updateEntity: AppEntity) { + mIsDisplayingDownloadingStyle = false + + val updateHintBinding = mBinding.updateHintContainerView + val updatingBinding = mBinding.updatingContainerView + + updatingBinding.root.visibility = View.GONE + updateHintBinding.root.visibility = View.VISIBLE + + val cancelUpdateTextView = updateHintBinding.cancel + val confirmTextView = updateHintBinding.confirm + + if (!TextUtils.isEmpty(updateEntity.spareLink)) { + val defaultText = "部分设备若无法更新安装,请前往官网进行下载安装:" + val externalTextTv = updateHintBinding.externalTextTv + val builder = SpanBuilder(defaultText + updateEntity.spareLink) + .click( + context, + defaultText.length, + defaultText.length + updateEntity.spareLink!!.length, + R.color.text_theme, + false + ) { + DirectUtils.directToExternalBrowser(context, updateEntity.spareLink!!) + } + .build() + externalTextTv.text = builder + externalTextTv.movementMethod = CustomLinkMovementMethod.getInstance() + externalTextTv.visibility = View.VISIBLE + } + + val updateFileIsDownloaded = UpdateHelper.isUpdateFileDownloaded(updateEntity) + + if (NetworkUtils.isWifiConnected(context)) { + if (!updateFileIsDownloaded) { + updateUpdateHintView(updateHintBinding, updateEntity, false) + UpdateHelper.createUpdate(updateEntity, true) + } else { + updateUpdateHintView(updateHintBinding, updateEntity, true) + } + } else { + updateUpdateHintView(updateHintBinding, updateEntity, updateFileIsDownloaded) + } + + updateHintBinding.desc.text = Html.fromHtml(updateEntity.content) + updateHintBinding.version.text = java.lang.String.format("版本%s更新日志:", updateEntity.version) + updateHintBinding.size.text = java.lang.String.format("大小 %s", updateEntity.size) + + cancelUpdateTextView.setOnClickListener { + val cancelText: String = cancelUpdateTextView.text.toString() + SensorsBridge.trackVersionUpdateDialogClick( + buttonName = cancelText, + keyDialogReminderTime = mUpdateEntity?.alert, + keyDialogClose = if (mUpdateEntity?.isForce == true) "关闭且强退" else "仅关闭" + ) + mIsDismissByTouchInside.set(true) + if (updateEntity.isForce) { + AppExecutor.uiExecutor.executeWithDelay({ UpdateHelper.exitApp() }, 500L) + } else { + dismiss() + } + } + + confirmTextView.setOnClickListener { + // 产品不接受显示下载完成文案从立即更新变为立即安装,所以文案为立即更新时一律不执行安装 + if (UpdateHelper.isUpdateFileDownloaded(updateEntity) && confirmTextView.text != "立即更新") { + DataLogUtils.uploadUpgradeLog(context, "install") //上传更新安装数据 + PackageInstaller.install(context, false, UpdateHelper.getUpdateDownloadPath(updateEntity), null) + } else { + showDownloadingStyle(updateEntity) + } + + val buttonName: String = confirmTextView.text.toString() + SensorsBridge.trackVersionUpdateDialogClick( + buttonName = buttonName, + keyDialogReminderTime = mUpdateEntity?.alert, + keyDialogClose = if (mUpdateEntity?.isForce == true) "关闭且强退" else "仅关闭" + ) + } + + SensorsBridge.trackVersionUpdateDialogShow( + keyDialogReminderTime = mUpdateEntity?.alert, + keyDialogClose = if (mUpdateEntity?.isForce == true) "关闭且强退" else "仅关闭" + ) + + DataLogUtils.uploadUpgradeLog(context, "notice") //上传更新通知弹窗数据 + } + + /** + * 更新更新提示的文案 + */ + private fun updateUpdateHintView( + binding: AppUpdateHintDialogBinding, + updateEntity: AppEntity, + isUpdateDownloaded: Boolean + ) { + val confirmText = if (isUpdateDownloaded) "立即安装" else "立即更新" + val cancelText = if (isUpdateDownloaded) { + if (updateEntity.isForce) "暂不安装,退出光环" else "暂不安装" + } else { + if (updateEntity.isForce) "暂不更新,退出光环" else "暂不更新" + } + + binding.confirm.text = confirmText + binding.downloadedHint.visibility = if (isUpdateDownloaded) View.VISIBLE else View.GONE + binding.cancel.text = cancelText + } + + /** + * 显示更新下载中弹窗 + */ + private fun showDownloadingStyle(updateEntity: AppEntity) { + mIsDisplayingDownloadingStyle = true + + val updatingBinding = mBinding.updatingContainerView + val updateHintBinding = mBinding.updateHintContainerView + + updatingBinding.root.visibility = View.VISIBLE + updateHintBinding.root.visibility = View.GONE + + updatingBinding.appTvCancel.setOnClickListener { + DownloadManager.getInstance().cancel(updateEntity.url) + if (updateEntity.isForce) { + UpdateHelper.exitApp() + } else { + dismiss() + } + } + + if (NetworkUtils.isMobileConnected(context)) { + ToastUtils.toast("当前使用移动数据进行下载") + } + + UpdateHelper.createUpdate(updateEntity, false) + } + + @SuppressLint("SetTextI18n") + private fun updateDownloadingView( + dialogBinding: AppUpdatingDialogBinding, + downloadEntity: DownloadEntity, + updateEntity: AppEntity + ) { + // 被取消任务的状态不用来更新页面 + if (DownloadStatus.cancel != downloadEntity.status) { + val size = (downloadEntity.progress / 1024) / 1024F + val df = DecimalFormat("0.00") + dialogBinding.size.text = (df.format(size) + "MB") + dialogBinding.remain.text = String.format( + "剩余%s", + SpeedUtils.getRemainSecondTime( + downloadEntity.size, + downloadEntity.progress, + downloadEntity.speed * 1024 + ) + ) + dialogBinding.progress.progress = (downloadEntity.percent * 10).roundToInt() + + val width = dialogBinding.progress.width + val marLeft = (downloadEntity.percent / 100 * width) + val anchorLp = dialogBinding.progressAnchor.layoutParams + if (anchorLp is ConstraintLayout.LayoutParams) { + anchorLp.leftMargin = marLeft.roundToInt() + dialogBinding.progressAnchor.layoutParams = anchorLp + } + + val fillingLp = dialogBinding.progressFilling.layoutParams + fillingLp.width = (marLeft + DisplayUtils.dip2px(5F)).toInt() + dialogBinding.progressFilling.layoutParams = fillingLp + + dialogBinding.percent.text = "${downloadEntity.percent} %" + } + + if (DownloadStatus.done == downloadEntity.status) { + DownloadManager.getInstance().cancel(downloadEntity.url, false, true, false) + try { + dismiss() + } catch (ignored: IllegalArgumentException) { + // do nothing + } + if (updateEntity.isForce) { + AppExecutor.uiExecutor.executeWithDelay({ UpdateHelper.exitApp() }, 1000L) + } + } else if (DownloadStatus.neterror == downloadEntity.status) { + ToastUtils.toast("网络错误,请稍后重试") + } else if (DownloadStatus.diskisfull == downloadEntity.status) { + ToastUtils.toast("磁盘已满,请清理后重试") + } else if (DownloadStatus.diskioerror == downloadEntity.status) { + ToastUtils.toast("磁盘 IO 异常,请稍后重试") + } else if (DownloadStatus.timeout == downloadEntity.status) { + ToastUtils.toast("请求超时,请稍后重试") + } else if (DownloadStatus.notfound == downloadEntity.status) { + ToastUtils.toast("下载链接异常,请稍后重试") + } else if (DownloadStatus.hijack == downloadEntity.status) { + ToastUtils.toast("网络劫持,请稍后重试") + } + } + + companion object { + const val UPDATE_ENTITY = "update_entity" + + fun newInstance(updateEntity: AppEntity, dismissCallback: EmptyCallback): UpdateDialogFragment { + val fragment = UpdateDialogFragment() + val bundle = Bundle() + bundle.putParcelable(UPDATE_ENTITY, updateEntity) + fragment.arguments = bundle + fragment.mDismissCallback = dismissCallback + return fragment + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/game/BaseGameViewModel.kt b/app/src/main/java/com/gh/gamecenter/game/BaseGameViewModel.kt index a89c31d72d..3c481c7567 100644 --- a/app/src/main/java/com/gh/gamecenter/game/BaseGameViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/game/BaseGameViewModel.kt @@ -4,7 +4,6 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData -import com.gh.common.util.GameSubstituteRepositoryHelper import com.gh.download.DownloadManager import com.gh.gamecenter.common.baselist.LoadStatus import com.gh.gamecenter.common.entity.ExposureEntity diff --git a/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt b/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt index 3cab9bb465..8976278cbe 100644 --- a/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt @@ -1,7 +1,6 @@ package com.gh.gamecenter.game import android.graphics.Color -import android.os.Bundle import android.view.View import androidx.core.content.ContextCompat import androidx.lifecycle.Observer @@ -32,13 +31,13 @@ import com.gh.gamecenter.eventbus.EBDiscoverChanged import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.eventbus.EBUISwitch -import com.gh.gamecenter.fragment.MainWrapperFragment import com.gh.gamecenter.game.data.GameItemData import com.gh.gamecenter.home.test_v2.HomeGameTestV2ViewModel import com.gh.gamecenter.home.test_v2.HomeItemGameTestV2ViewHolder import com.gh.gamecenter.home.test_v2.HomeItemGameTestV2ViewHolderWatcher import com.gh.gamecenter.home.test_v2.addViewHolderWatcher import com.gh.gamecenter.home.video.ScrollCalculatorHelper +import com.gh.gamecenter.wrapper.MainWrapperFragment import com.halo.assistant.HaloApp import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity @@ -58,8 +57,6 @@ open class GameFragment : LazyFragment() { private lateinit var mExposureListener: ExposureListener private lateinit var mScrollCalculatorHelper: ScrollCalculatorHelper - private var mIsFromHomeToolbarWrapper = false - private val dataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { mListAdapter.notifyItemByDownload(downloadEntity) @@ -76,11 +73,6 @@ open class GameFragment : LazyFragment() { override fun getRealLayoutId() = R.layout.fragment_game - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - mIsFromHomeToolbarWrapper = arguments?.getBoolean(EntranceConsts.KEY_IS_FROM_HOME_TOOLBAR_WRAPPER, false) ?: false - } - override fun onFragmentFirstVisible() { mViewModel = provideViewModel() mViewModel.entrance = mEntrance @@ -92,10 +84,6 @@ open class GameFragment : LazyFragment() { super.onFragmentFirstVisible() - if (mIsFromHomeToolbarWrapper) { - initMenu(R.menu.menu_download) - } - mViewModel.loadStatus.observe(this) { if (it != null) { mBinding.gameRefresh.isRefreshing = false @@ -333,18 +321,10 @@ open class GameFragment : LazyFragment() { if (::mListAdapter.isInitialized && "Refresh" == reuse.type) { mListAdapter.notifyDataSetChanged() } - (mViewModel as GameViewModel).refreshRecentVGameIfNeeded() - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onEventMainThread(busNine: EBUISwitch) { - if (::mLayoutManager.isInitialized && - MainWrapperFragment.EB_MAIN_SCROLL_TOP == busNine.from && - MainWrapperFragment.INDEX_GAME == busNine.position - ) { - mBinding.gameList.stopScroll() - mLayoutManager.scrollToPosition(0) + if(::mViewModel.isInitialized){ + (mViewModel as GameViewModel).refreshRecentVGameIfNeeded() } + } @Subscribe(threadMode = ThreadMode.MAIN) diff --git a/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt index e624dbcf6c..1f4868bce3 100644 --- a/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.kt @@ -11,7 +11,6 @@ import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ViewHolder -import com.gh.common.constant.Config import com.gh.common.databind.BindingAdapters import com.gh.common.exposure.IExposable import com.gh.common.util.* @@ -930,7 +929,7 @@ class GameFragmentAdapter( SensorsBridge.trackEvent("RecommendedLocationClick", json { "position" to clickedPosition - "Recommended_location_name" to if (Config.isShowPlugin()) entity.name else entity.nameNormal + "Recommended_location_name" to entity.name "location" to "版块" "block_name" to blockData?.text "block_id" to blockData?.link @@ -951,6 +950,7 @@ class GameFragmentAdapter( "", mViewModel.blockData?.link ?: "", mViewModel.blockData?.text ?: "", + "", exposureEvent ) @@ -1071,12 +1071,12 @@ class GameFragmentAdapter( source = listOf( ExposureSource( "推荐入口", - if (Config.isShowPlugin()) entity.name ?: "" else entity.nameNormal ?: "" + entity.name ?: "" ) ) ) event.payload.controlType = "推荐入口" - event.payload.controlName = if (Config.isShowPlugin()) entity.name else entity.nameNormal + event.payload.controlName = entity.name event.payload.controlLinkType = entity.type event.payload.controlLinkName = entity.text entity.exposureEvent = event @@ -1124,19 +1124,19 @@ class GameFragmentAdapter( ImageUtils.display(entranceIcon4, if (subjectDigestList.size > 3) subjectDigestList[3].icon else "") ImageUtils.display(entranceIcon5, if (subjectDigestList.size > 4) subjectDigestList[4].icon else "") entranceName1.text = if (subjectDigestList.isNotEmpty()) { - if (Config.isShowPlugin()) subjectDigestList[0].name else subjectDigestList[0].nameNormal + subjectDigestList[0].name } else "推荐入口" entranceName2.text = if (subjectDigestList.size > 1) { - if (Config.isShowPlugin()) subjectDigestList[1].name else subjectDigestList[1].nameNormal + subjectDigestList[1].name } else "推荐入口" entranceName3.text = if (subjectDigestList.size > 2) { - if (Config.isShowPlugin()) subjectDigestList[2].name else subjectDigestList[2].nameNormal + subjectDigestList[2].name } else "推荐入口" entranceName4.text = if (subjectDigestList.size > 3) { - if (Config.isShowPlugin()) subjectDigestList[3].name else subjectDigestList[3].nameNormal + subjectDigestList[3].name } else "推荐入口" entranceName5.text = if (subjectDigestList.size > 4) { - if (Config.isShowPlugin()) subjectDigestList[4].name else subjectDigestList[4].nameNormal + subjectDigestList[4].name } else "推荐入口" } @@ -1279,7 +1279,7 @@ class GameFragmentAdapter( ) } - holder.bindGameItem(gameEntity, subjectData.isShowSuffix, subjectData.briefStyle) + holder.bindGameItem(gameEntity, subjectData.briefStyle) holder.initServerType(gameEntity) runOnIoThread(true) { @@ -2071,4 +2071,4 @@ class GameFragmentAdapter( } -class GameAndPosition(val entity: GameEntity?, val position: Int, val index: Int = 0) +data class GameAndPosition(val entity: GameEntity?, val position: Int, val index: Int = 0) diff --git a/app/src/main/java/com/gh/gamecenter/game/GameViewModel.kt b/app/src/main/java/com/gh/gamecenter/game/GameViewModel.kt index 6ed6db1b5a..2ff1c9b7cc 100644 --- a/app/src/main/java/com/gh/gamecenter/game/GameViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/game/GameViewModel.kt @@ -73,10 +73,10 @@ class GameViewModel(application: Application, override var blockData: SubjectRec private var mSubjectGameIdList = hashSetOf() init { - if (blockData == null) { - // 读取默认板块 - blockData = HomeBottomBarHelper.getDefaultGameBarData() - } +// if (blockData == null) { +// // 读取默认板块 +// blockData = HomeBottomBarHelper.getDefaultGameBarData() +// } if (!blockData?.link.isNullOrEmpty()) { initData() @@ -722,8 +722,6 @@ class GameViewModel(application: Application, override var blockData: SubjectRec if (!data.isNullOrEmpty()) { for (game in data) { mSubjectGameIdList.add(game.id) - // 应用专题是否显示游戏名后缀的配置 - game.shouldShowNameSuffix = subjectEntity.showSuffix } } @@ -845,7 +843,6 @@ class GameViewModel(application: Application, override var blockData: SubjectRec position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, isOrder = subjectEntity.isOrder, briefStyle = subjectEntity.briefStyle, - isShowSuffix = subjectEntity.showSuffix ) addGamePositionAndPackage(game) @@ -877,8 +874,7 @@ class GameViewModel(application: Application, override var blockData: SubjectRec tag = subjectEntity.tag, position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, isOrder = subjectEntity.isOrder, - briefStyle = subjectEntity.briefStyle, - isShowSuffix = subjectEntity.showSuffix + briefStyle = subjectEntity.briefStyle ) game.outerSequence = index game.sequence = i @@ -909,7 +905,6 @@ class GameViewModel(application: Application, override var blockData: SubjectRec position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, isOrder = subjectEntity.isOrder, briefStyle = subjectEntity.briefStyle, - isShowSuffix = subjectEntity.showSuffix ) game.outerSequence = index game.sequence = i @@ -1065,7 +1060,6 @@ class GameViewModel(application: Application, override var blockData: SubjectRec position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, isOrder = subjectEntity.isOrder, briefStyle = subjectEntity.briefStyle, - isShowSuffix = subjectEntity.showSuffix ) val itemDataGame = GameItemData() itemDataGame.game = game diff --git a/app/src/main/java/com/gh/gamecenter/game/columncollection/detail/ColumnCollectionDetailAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/columncollection/detail/ColumnCollectionDetailAdapter.kt index c6c133426f..7cebe3aa7f 100644 --- a/app/src/main/java/com/gh/gamecenter/game/columncollection/detail/ColumnCollectionDetailAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/game/columncollection/detail/ColumnCollectionDetailAdapter.kt @@ -7,21 +7,21 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.StaggeredGridLayoutManager import com.gh.common.exposure.IExposable -import com.gh.gamecenter.common.constant.ItemViewType import com.gh.common.util.DirectUtils import com.gh.common.util.NewLogUtils -import com.gh.gamecenter.common.utils.ImageUtils -import com.gh.gamecenter.core.utils.MtaHelper -import com.gh.gamecenter.core.utils.PageSwitchDataHelper import com.gh.gamecenter.R -import com.gh.gamecenter.common.viewholder.FooterViewHolder import com.gh.gamecenter.adapter.viewholder.GameColumnCollectionItemViewHolder import com.gh.gamecenter.common.baselist.ListAdapter -import com.gh.gamecenter.databinding.GameColumnCollectionItemBinding +import com.gh.gamecenter.common.constant.ItemViewType import com.gh.gamecenter.common.entity.LinkEntity -import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.ImageUtils import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.viewholder.FooterViewHolder +import com.gh.gamecenter.core.utils.MtaHelper +import com.gh.gamecenter.core.utils.PageSwitchDataHelper +import com.gh.gamecenter.databinding.GameColumnCollectionItemBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent class ColumnCollectionDetailAdapter( context: Context, @@ -31,7 +31,7 @@ class ColumnCollectionDetailAdapter( private val mEntrance: String, private val mBlockId: String, private val mBlockName: String, - private val mLocation: String + private val mStyle: String ) : ListAdapter(context), IExposable { private var mExposureSparseArray = SparseArray() @@ -84,7 +84,7 @@ class ColumnCollectionDetailAdapter( mViewModel.columnCollection.value?.name ?: "", mViewModel.collectionId ) SensorsBridge.trackColumnCollectionClick( - location = mLocation, + location = "合集详情", blockName = mBlockName, blockId = mBlockId, columnCollectionName = mViewModel.columnCollection.value?.name ?: "", @@ -98,7 +98,10 @@ class ColumnCollectionDetailAdapter( val exposureEvent = ExposureEvent.createEventWithSourceConcat( null, mBasicExposureSourceList ?: arrayListOf(), - arrayListOf(ExposureSource("游戏专题合集", "${mViewModel.getGameColumnCollectionName()}+${mViewModel.collectionId}")) + arrayListOf( + ExposureSource("合集详情", ""), + ExposureSource("专题", "${data.name}+${mStyle}+${data.link}") + ) ).apply { payload.outerSequence = mTabIndex payload.sequence = position diff --git a/app/src/main/java/com/gh/gamecenter/game/columncollection/detail/ColumnCollectionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/game/columncollection/detail/ColumnCollectionDetailFragment.kt index 317d012e42..32c5463720 100644 --- a/app/src/main/java/com/gh/gamecenter/game/columncollection/detail/ColumnCollectionDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/game/columncollection/detail/ColumnCollectionDetailFragment.kt @@ -3,21 +3,16 @@ package com.gh.gamecenter.game.columncollection.detail import android.os.Bundle import android.view.View import androidx.lifecycle.ViewModelProviders -import androidx.recyclerview.widget.StaggeredGridLayoutManager -import com.ethanhua.skeleton.Skeleton -import com.gh.common.exposure.ExposureListener -import com.gh.gamecenter.common.constant.Constants -import com.gh.gamecenter.common.constant.EntranceConsts -import com.gh.gamecenter.common.utils.dip2px -import com.gh.gamecenter.common.utils.observeNonNull -import com.gh.gamecenter.common.utils.toColor -import com.gh.gamecenter.common.view.GridSpacingItemDecoration import com.gh.gamecenter.R import com.gh.gamecenter.common.baselist.LazyListFragment -import com.gh.gamecenter.databinding.FragmentColumnCollectionDetailBinding +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.constant.EntranceConsts.KEY_SHOW_SUBJECT_TAB import com.gh.gamecenter.common.entity.LinkEntity -import com.gh.gamecenter.entity.SubjectData import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.observeNonNull +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.databinding.FragmentColumnCollectionDetailBinding +import com.gh.gamecenter.entity.SubjectData import com.gh.gamecenter.subject.tab.SubjectTabFragment class ColumnCollectionDetailFragment : LazyListFragment() { @@ -25,8 +20,6 @@ class ColumnCollectionDetailFragment : LazyListFragment? = null @@ -34,6 +27,8 @@ class ColumnCollectionDetailFragment : LazyListFragment?) { - if (mListViewModel.columnCollection.value?.style != "top") { - super.onChanged(list) - } else { - if (list != null && list.isNotEmpty()) { - showSubjectTab(list) - } + if (list != null && list.isNotEmpty()) { + showSubjectTab(list) } } @@ -114,9 +74,11 @@ class ColumnCollectionDetailFragment : LazyListFragment? = null ): Intent { val bundle = Bundle() bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance) @@ -53,6 +55,7 @@ class CommonCollectionDetailActivity : ToolBarActivity() { bundle.putString(EntranceConsts.KEY_BLOCK_NAME, blockName) bundle.putString(EntranceConsts.KEY_COLLECTION_ID, collectionId) bundle.putString(EntranceConsts.KEY_LOCATION, "合集详情") + bundle.putParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST, exposureSourceList) return getTargetIntent( context, CommonCollectionDetailActivity::class.java, diff --git a/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CommonCollectionDetailAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CommonCollectionDetailAdapter.kt index c6b55d0ec1..abc3b8ac20 100644 --- a/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CommonCollectionDetailAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CommonCollectionDetailAdapter.kt @@ -5,9 +5,9 @@ import android.util.SparseArray import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.common.exposure.IExposable -import com.gh.common.util.* +import com.gh.common.util.DirectUtils +import com.gh.common.util.NewLogUtils import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.CommonCollectionDetailOneItemViewHolder import com.gh.gamecenter.adapter.viewholder.CommonCollectionDetailTwoItemViewHolder @@ -15,13 +15,14 @@ import com.gh.gamecenter.adapter.viewholder.CommonCollectionImageTextItemViewHol import com.gh.gamecenter.common.baselist.ListAdapter import com.gh.gamecenter.common.constant.ItemViewType import com.gh.gamecenter.common.exposure.ExposureSource -import com.gh.gamecenter.common.utils.goneIf -import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.common.utils.ImageUtils import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.common.viewholder.FooterViewHolder import com.gh.gamecenter.entity.CommonCollectionContentEntity import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent class CommonCollectionDetailAdapter( context: Context, @@ -32,7 +33,7 @@ class CommonCollectionDetailAdapter( val mLocation: String, val mTabIndex: Int, val mEntrance: String, - private val mBasicExposureSource: List? + var basicExposureSource: List? ) : ListAdapter(context), IExposable { private val mExposureEventSparseArray = SparseArray() @@ -117,29 +118,29 @@ class CommonCollectionDetailAdapter( ) } - if (linkEntity.type == "game") { - mExposureEventSparseArray.put( - position, - ExposureEvent.createEventWithSourceConcat( - GameEntity( - id = linkEntity.link, - name = linkEntity.name - ).also { - it.sequence = position - }, - basicSource = mBasicExposureSource ?: listOf(), - listOf( - ExposureSource( - "内容合集", - "${mViewModel.commonCollectionLiveData.value?.name}+${mViewModel.commonCollectionLiveData.value?.id}" - ), - ExposureSource("合集详情") - ), - ).apply { - this.payload.outerSequence = mTabIndex - } - ) + val commonCollectionEntity = mViewModel.commonCollectionLiveData.value + val gameEntity = if (linkEntity.type == "game") { + GameEntity(id = linkEntity.link, name = linkEntity.text) + } else { + GameEntity() } + mExposureEventSparseArray.put( + position, + ExposureEvent.createEventWithSourceConcat( + gameEntity.also { + it.sequence = position + }, + basicSource = basicExposureSource ?: listOf(), + listOf( + ExposureSource( + "通用内容合集", + "${commonCollectionEntity?.name ?: ""}+${commonCollectionEntity?.layoutChinese ?: ""}+${commonCollectionEntity?.id ?: ""}" + ), ExposureSource("合集详情") + ), + ).apply { + this.payload.outerSequence = mTabIndex + } + ) when (holder) { is CommonCollectionDetailOneItemViewHolder -> { diff --git a/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CommonCollectionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CommonCollectionDetailFragment.kt index 630620d8ac..172e7fb8e5 100644 --- a/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CommonCollectionDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CommonCollectionDetailFragment.kt @@ -14,14 +14,14 @@ import com.gh.gamecenter.common.baselist.LazyListFragment import com.gh.gamecenter.common.baselist.ListAdapter import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.common.view.FixGridLayoutManager import com.gh.gamecenter.common.view.GridSpacingItemDecoration import com.gh.gamecenter.common.view.VerticalItemDecoration import com.gh.gamecenter.databinding.FragmentListBaseSkeletonBinding -import com.gh.gamecenter.common.entity.LinkEntity -import com.gh.gamecenter.common.exposure.ExposureSource class CommonCollectionDetailFragment : LazyListFragment() { @@ -52,7 +52,7 @@ class CommonCollectionDetailFragment : LazyListFragment { if (mAdapter == null) { - val exposureEvent = + val exposureSourceList = requireArguments().getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) mAdapter = CommonCollectionDetailAdapter( requireContext(), @@ -63,7 +63,7 @@ class CommonCollectionDetailFragment : LazyListFragment { + val fragment = targetFragment as CustomCommonCollectionDetailFragment + return if (fragment.arguments != null) { + Pair( + fragment.requireArguments().getString(EntranceConsts.KEY_COLLECTION_ID) ?: "", + fragment.requireArguments().getString(EntranceConsts.KEY_BLOCK_ID) ?: "" + ) + } else { + super.getBusinessId() + } + } + + companion object { + + fun getIntent( + context: Context, + collectionId: String, + layout: Int, + entrance: String = "", + exposureSourceList: ArrayList? = null + ): Intent { + val bundle = Bundle() + bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance) + + bundle.putString(EntranceConsts.KEY_COLLECTION_ID, collectionId) + bundle.putString(EntranceConsts.KEY_LOCATION, "合集详情") + bundle.putInt(EntranceConsts.KEY_COMMON_CONTENT_COLLECTION_LAYOUT, layout) + bundle.putParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST, exposureSourceList) + return getTargetIntent( + context, + CustomCommonCollectionDetailActivity::class.java, + CustomCommonCollectionDetailFragment::class.java, + bundle + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CustomCommonCollectionDetailAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CustomCommonCollectionDetailAdapter.kt new file mode 100644 index 0000000000..67e71a3f74 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CustomCommonCollectionDetailAdapter.kt @@ -0,0 +1,205 @@ +package com.gh.gamecenter.game.commoncollection.detail + +import android.content.Context +import android.util.SparseArray +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.exposure.IExposable +import com.gh.common.util.DirectUtils +import com.gh.common.util.NewLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.CommonCollectionDetailOneItemViewHolder +import com.gh.gamecenter.adapter.viewholder.CommonCollectionDetailTwoItemHorizontalViewHolder +import com.gh.gamecenter.adapter.viewholder.CommonCollectionDetailTwoItemViewHolder +import com.gh.gamecenter.adapter.viewholder.CommonCollectionImageTextItemViewHolder +import com.gh.gamecenter.common.baselist.ListAdapter +import com.gh.gamecenter.common.constant.ItemViewType +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.common.viewholder.FooterViewHolder +import com.gh.gamecenter.entity.CommonCollectionContentEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_BANNER +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER + +class CustomCommonCollectionDetailAdapter( + context: Context, + val layout: Int, + var collectionStyle: String, + val mViewModel: CustomCommonCollectionDetailViewModel, + val mLocation: String, + val mTabIndex: Int, + val mEntrance: String, + private val mBasicExposureSourceList: List? +) : ListAdapter(context), IExposable { + + private val mExposureEventSparseArray = SparseArray() + + override fun areItemsTheSame( + oldItem: CommonCollectionContentEntity?, + newItem: CommonCollectionContentEntity? + ): Boolean { + return oldItem == newItem + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return if (viewType == ItemViewType.ITEM_BODY) { + when (collectionStyle) { + "1-1" -> CommonCollectionDetailOneItemViewHolder(parent.toBinding()) + "1-2" -> if (layout == COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER || + layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_BANNER + ) { + CommonCollectionDetailTwoItemHorizontalViewHolder(parent.toBinding()) + } else { + CommonCollectionDetailTwoItemViewHolder(parent.toBinding()) + } + + else -> CommonCollectionImageTextItemViewHolder(parent.toBinding()) + } + } else { + FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false)) + } + } + + override fun getItemViewType(position: Int): Int { + return when (position) { + itemCount - 1 -> ItemViewType.ITEM_FOOTER + else -> ItemViewType.ITEM_BODY + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is FooterViewHolder) { + holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver) + return + } + + val contentEntity = mEntityList[position] + val linkEntity = mEntityList[position].linkEntity + + val listener: (v: View) -> Unit = { + DirectUtils.directToLinkPage( + mContext, + linkEntity, + "通用链接合集详情", + "", + mExposureEventSparseArray.get(position) + ) + val commonLinkCollection = mViewModel.commonCollectionLiveData.value + NewLogUtils.logCommonCollectionClick( + commonLinkCollection?.id ?: "", + commonLinkCollection?.name ?: "", + "", + "", + mEntrance, + "合集详情", + linkEntity.title ?: "", + contentEntity.addedContent1 ?: "", + contentEntity.addedContent2 ?: "", + linkEntity.type ?: "", + linkEntity.text ?: "", + position + 1 + ) + NewLogUtils.logCommonCategoryDetailContentClick( + contentEntity?.title ?: "", + linkEntity.type ?: "", + linkEntity.link ?: "", + linkEntity.text ?: "", + commonLinkCollection?.name ?: "", + commonLinkCollection?.id ?: "" + ) + SensorsBridge.trackLinkContentCollectionClick( + location = mLocation, + blockName = "", + blockId = "", + linkContentCollectionId = commonLinkCollection?.id ?: "", + linkContentCollectionName = commonLinkCollection?.name ?: "", + position = position, + linkType = linkEntity.type ?: "", + linkId = linkEntity.link ?: "", + linkText = linkEntity.text ?: "", + text = "通用内容" + ) + } + + val commonCollectionEntity = mViewModel.commonCollectionLiveData.value + val gameEntity = if (linkEntity.type == "game") { + GameEntity(id = linkEntity.link, name = linkEntity.text) + } else { + GameEntity() + } + mExposureEventSparseArray.put( + position, + ExposureEvent.createEventWithSourceConcat( + gameEntity.also { + it.sequence = position + }, + basicSource = mBasicExposureSourceList ?: listOf(), + listOf( + ExposureSource( + "通用内容合集", + "${commonCollectionEntity?.name ?: ""}+${commonCollectionEntity?.layoutChinese ?: ""}+${commonCollectionEntity?.id ?: ""}" + ), ExposureSource("合集详情") + ), + ).apply { + this.payload.outerSequence = mTabIndex + } + ) + + when (holder) { + is CommonCollectionDetailOneItemViewHolder -> { + holder.binding.run { + ImageUtils.display(commonCollectionImage, contentEntity.image) + maskView.goneIf(contentEntity.title.isEmpty()) + titleTv.text = contentEntity.title + root.setOnClickListener(listener) + } + } + + is CommonCollectionDetailTwoItemViewHolder -> { + holder.binding.run { + ImageUtils.display(commonCollectionImage, contentEntity.image) + maskView.goneIf(contentEntity.title.isEmpty() && contentEntity.addedContent1.isNullOrEmpty()) + titleTv.text = contentEntity.title + desTv.text = contentEntity.addedContent1 + root.setOnClickListener(listener) + } + } + + is CommonCollectionDetailTwoItemHorizontalViewHolder -> { + holder.binding.run { + ImageUtils.display(commonCollectionImage, contentEntity.image) + maskView.goneIf(contentEntity.title.isEmpty()) + titleTv.text = contentEntity.title + root.setOnClickListener(listener) + } + } + + is CommonCollectionImageTextItemViewHolder -> { + holder.binding.run { + ImageUtils.display(commonCollectionImage, contentEntity.image) + linkTitleTv.text = contentEntity.title + linkDes1.text = contentEntity.addedContent1 + linkDes2.text = contentEntity.addedContent2 + + root.setOnClickListener(listener) + } + } + } + } + + override fun getItemCount(): Int { + return if (mEntityList.isNullOrEmpty()) 0 else mEntityList.size + FOOTER_ITEM_COUNT + } + + override fun getEventByPosition(pos: Int): ExposureEvent? { + return mExposureEventSparseArray[pos] + } + + override fun getEventListByPosition(pos: Int): List? = null +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CustomCommonCollectionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CustomCommonCollectionDetailFragment.kt new file mode 100644 index 0000000000..f05831a844 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CustomCommonCollectionDetailFragment.kt @@ -0,0 +1,172 @@ +package com.gh.gamecenter.game.commoncollection.detail + +import android.os.Bundle +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup +import androidx.recyclerview.widget.RecyclerView +import com.ethanhua.skeleton.Skeleton +import com.gh.common.exposure.ExposureListener +import com.gh.common.exposure.IExposable +import com.gh.common.util.NewLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.baselist.LazyListFragment +import com.gh.gamecenter.common.baselist.ListAdapter +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.viewModelProvider +import com.gh.gamecenter.common.view.FixGridLayoutManager +import com.gh.gamecenter.common.view.GridSpacingItemDecoration +import com.gh.gamecenter.common.view.VerticalItemDecoration +import com.gh.gamecenter.databinding.FragmentListBaseSkeletonBinding +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER + +class CustomCommonCollectionDetailFragment : LazyListFragment() { + + private var mAdapter: CustomCommonCollectionDetailAdapter? = null + private var mCollectionStyle: String = "" + private lateinit var mViewModel: CustomCommonCollectionDetailViewModel + private var mExposureListener: ExposureListener? = null + private lateinit var mBinding: FragmentListBaseSkeletonBinding + private var mBlockId = "" + private var mBlockName = "" + private var mLocation = "" + private var mTabIndexValue = 0 // 在首页的 tab 位置 + private var mLayout = COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER + + override fun getRealLayoutId(): Int = R.layout.fragment_list_base_skeleton + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mBlockId = requireArguments().getString(EntranceConsts.KEY_BLOCK_ID, "") + mBlockName = requireArguments().getString(EntranceConsts.KEY_BLOCK_NAME, "") + mLocation = requireArguments().getString(EntranceConsts.KEY_LOCATION, "") + mTabIndexValue = requireArguments().getInt(EntranceConsts.KEY_TAB_INDEX, -1) + mLayout = requireArguments().getInt(EntranceConsts.KEY_COMMON_CONTENT_COLLECTION_LAYOUT, mLayout) + } + + override fun onRealLayoutInflated(inflatedView: View) { + super.onRealLayoutInflated(inflatedView) + mBinding = FragmentListBaseSkeletonBinding.bind(inflatedView) + } + + override fun provideListAdapter(): ListAdapter<*> { + if (mAdapter == null) { + val exposureSourceList = + requireArguments().getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) + mAdapter = CustomCommonCollectionDetailAdapter( + requireContext(), + mLayout, + mCollectionStyle, + mViewModel, + mLocation, + mTabIndexValue, + mEntrance, + exposureSourceList + ) + } + return mAdapter!! + } + + override fun provideListViewModel(): CustomCommonCollectionDetailViewModel { + val collectionId = arguments?.getString(EntranceConsts.KEY_COLLECTION_ID) ?: "" + mViewModel = viewModelProvider(CustomCommonCollectionDetailViewModel.Factory(collectionId)) + return mViewModel + } + + override fun initRealView() { + super.initRealView() + mCachedView.setPadding(16f.dip2px(), 0, 16f.dip2px(), 0) + mReuseNoData?.findViewById(R.id.reuseNoneDataTv)?.text = "内容不见了~" + mReuseNoData?.findViewById(R.id.reuseNoneDataDescTv)?.text = "先去看看其它的内容吧" + mReuseNoData?.findViewById(R.id.reuseResetLoadTv)?.visibility = View.GONE + mReuseNoData?.findViewById(R.id.reuseNoneDataIv)?.setImageResource(R.drawable.ic_data_load_exception) + mSkeletonScreen = Skeleton.bind(mBinding.listSkeleton) + .shimmer(true) + .angle(Constants.SHIMMER_ANGLE) + .color(R.color.ui_skeleton_highlight) + .duration(Constants.SHIMMER_DURATION) + .maskWidth(Constants.MASK_WIDTH) + .gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH) + .load(R.layout.common_collection_detail_skeleton) + .show() + } + + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() + mExposureListener = ExposureListener(this, provideListAdapter() as IExposable) + mViewModel.commonCollectionLiveData.observe(viewLifecycleOwner) { + setNavigationTitle(it.name) + mCollectionStyle = it.style + mAdapter?.collectionStyle = mCollectionStyle + if (mCollectionStyle == "1-2") { + mLayoutManager = FixGridLayoutManager(requireContext(), 2).apply { + spanSizeLookup = object : SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + val itemCount = mAdapter?.itemCount ?: 0 + return if (position == itemCount - 1) { + 2 + } else { + 1 + } + } + } + } + mListRv?.layoutManager = mLayoutManager + } + mListRv?.removeItemDecorationAt(0) + mListRv?.addItemDecoration(itemDecoration) + } + mListViewModel.loadExceptionLiveData.observe(viewLifecycleOwner) { + if (it != null && it.code() == 404) { + toast("内容可能已被删除") + } + } + mListRv?.addOnScrollListener(mExposureListener!!) + mListRv?.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + val commonLinkCollection = mViewModel.commonCollectionLiveData.value + var position = mLayoutManager.findLastCompletelyVisibleItemPosition() + if (position < 0) position = mLayoutManager.findLastVisibleItemPosition() - 1 + if (position < 0 || position >= commonLinkCollection?.collectionList?.size ?: 0) return + val contentEntity = commonLinkCollection?.collectionList?.get(position) + val linkEntity = contentEntity?.linkEntity + NewLogUtils.logSlideCommonCollection( + commonLinkCollection?.id ?: "", + commonLinkCollection?.name ?: "", + mBlockId, + mBlockName, + mEntrance, + "合集详情", + linkEntity?.title ?: "", + contentEntity?.addedContent1 ?: "", + contentEntity?.addedContent2 ?: "", + position + 1 + ) + } + } + }) + } + + override fun getItemDecoration(): RecyclerView.ItemDecoration { + return if (mCollectionStyle == "1-2") { + GridSpacingItemDecoration(2, 8f.dip2px(), false, 16F.dip2px()) + } else { + VerticalItemDecoration(requireContext(), 16f, true, R.color.ui_surface) + } + } + + override fun onDarkModeChanged() { + super.onDarkModeChanged() + if ((mListRv?.itemDecorationCount ?: 0) > 0) { + mListRv?.removeItemDecorationAt(0) + mListRv?.addItemDecoration(itemDecoration) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CustomCommonCollectionDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CustomCommonCollectionDetailViewModel.kt new file mode 100644 index 0000000000..32f7971982 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/game/commoncollection/detail/CustomCommonCollectionDetailViewModel.kt @@ -0,0 +1,45 @@ +package com.gh.gamecenter.game.commoncollection.detail + +import android.app.Application +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.gh.gamecenter.common.baselist.ListViewModel +import com.gh.gamecenter.entity.CommonCollectionContentEntity +import com.gh.gamecenter.entity.CommonCollectionEntity +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import io.reactivex.Observable + +class CustomCommonCollectionDetailViewModel( + application: Application, + private val mCollectionId: String +) : ListViewModel(application) { + + val commonCollectionLiveData = MutableLiveData() + + override fun provideDataObservable(page: Int): Observable>? { + return if (page == 1) { + RetrofitManager.getInstance().api + .getCommonCollectionDetail(mCollectionId) + .map { + commonCollectionLiveData.postValue(it) + it.collectionList + } + } else { + RetrofitManager.getInstance().api + .getCommonCollectionDetail(mCollectionId, page) + } + } + + override fun mergeResultLiveData() { + mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) } + } + + class Factory(private val collectionId: String) : ViewModelProvider.NewInstanceFactory() { + + override fun create(modelClass: Class): T { + return CustomCommonCollectionDetailViewModel(HaloApp.getInstance().application, collectionId) as T + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt index 31bbef51fc..de854354da 100644 --- a/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/game/horizontal/GameHorizontalAdapter.kt @@ -11,10 +11,6 @@ import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.GameViewHolder import com.gh.gamecenter.common.base.GlobalActivityManager import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.common.utils.dip2px -import com.gh.gamecenter.common.utils.goneIf -import com.gh.gamecenter.common.utils.toBinding -import com.gh.gamecenter.common.utils.toPx import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.entity.SubjectEntity import com.gh.gamecenter.feature.entity.GameEntity @@ -127,6 +123,16 @@ class GameHorizontalAdapter( clickGameName = gameEntity.name ?: "", clickGameId = gameEntity.id ) + if (!gameEntity.isQQMiniGame()) { + SensorsBridge.trackColumnClick( + location = "游戏详情", + gameName = gameName, + gameId = gameId, + gameColumnName = mSubjectEntity.name ?: "", + gameColumnId = mSubjectEntity.id ?: "", + text = "游戏" + ) + } } if (gameEntity.isQQMiniGame()) { diff --git a/app/src/main/java/com/gh/gamecenter/game/rank/CustomRankGameItem.kt b/app/src/main/java/com/gh/gamecenter/game/rank/CustomRankGameItem.kt new file mode 100644 index 0000000000..a9431b8cb6 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/game/rank/CustomRankGameItem.kt @@ -0,0 +1,198 @@ +package com.gh.gamecenter.game.rank + +import android.content.Context +import android.text.TextUtils +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.view.setPadding +import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.TagStyleEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageData + +class CustomRankGameItem(val rankItemUi: RankGameItemUi) { + + fun updateGameItem( + ui: RankGameItemUi, + gameEntity: GameEntity, + exposureEventList: ArrayList?, + position: Int, + subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity?, + eventHelper: GameSubjectCollectionEventHelper, + createTrackData: () -> CustomPageTrackData + ) { + + // 为什么要用 post 呢? + // 因为普通用 recyclerView 来实现的纵向列表用的 notifyPositionChanged 是延时更新 UI 的 + // 如果不添加延时,刚开始下载任务时可能会出现奇怪的状态 "下载中 -> 下载 -> 下载中" + // 为了避免更改其它地方的逻辑,就这里先 post 延时处理了 + ui.root.post { + ui.run { + iconIv.displayGameIcon(gameEntity) + nameTv.text = gameEntity.name + + updateTag(tagContainer, gameEntity) + updateOrder(orderTv, position) + + val entrance = "(专题合集-排行榜:${subject?.title})" + val exposureEvent = exposureEventList?.getOrNull(position) + root.setOnClickListener { + eventHelper.navigateToGameDetailPage(position, gameEntity, subject) + } + + gameEntity.customPageTrackData = createTrackData() + DownloadItemUtils.setOnClickListener( + root.context, + downloadTv, + gameEntity, + position, + null, + entrance, + location = "专题合集-排行榜-${subject?.title}:${gameEntity.name}", + traceEvent = exposureEvent, + clickCallback = { + eventHelper.onDownloadButtonClick(position, gameEntity, subject) + }, + refreshCallback = { + DownloadItemUtils.updateItem(root.context, gameEntity, GameViewHolder(root).also { + it.gameDownloadBtn = downloadTv + it.gameDownloadTips = downloadTipsLottie + it.multiVersionDownloadTv = multiVersionDownloadTv + }) + }, + allStateClickCallback = null + ) + + DownloadItemUtils.updateItem(root.context, gameEntity, GameViewHolder(root).also { + it.gameDownloadBtn = downloadTv + it.gameDownloadTips = downloadTipsLottie + it.multiVersionDownloadTv = multiVersionDownloadTv + }) + + downloadTv.putWidgetBusinessName("专题合集") + } + } + } + + fun getGameTagView(context: Context, tagEntity: TagStyleEntity): TextView { + return TextView(context).apply { + updateGameTagView(this, tagEntity) + } + } + + private fun updateTag(tagContainer: LinearLayout, gameEntity: GameEntity) { + tagContainer.run { + for (i in 0 until childCount) { + getChildAt(i).visibility = View.GONE + } + + gameEntity.tagStyle.forEachIndexed { index, tagStyleEntity -> + if (index < childCount) { + updateGameTagView(getChildAt(index) as TextView, tagStyleEntity) + } else { + AppExecutor.lightWeightIoExecutor.execute { + val tagView = getGameTagView(context, tagStyleEntity) + AppExecutor.uiExecutor.execute { addView(tagView) } + } + } + if (index >= 2) return + } + } + } + + private fun updateOrder(orderTv: TextView, position: Int) { + orderTv.run { + visibility = View.VISIBLE + text = (position + 1).toString() + setPadding(0) + when (position + 1) { + 1 -> { + setTextColor(R.color.white.toColor()) + setBackgroundResource(R.drawable.subject_top_first) + textSize = 9F + setPadding(0, 2F.dip2px(), 0, 0) + layoutParams?.let { + it.height = 16F.dip2px() + it.width = 16F.dip2px() + if (it is ViewGroup.MarginLayoutParams) { + it.leftMargin = 8F.dip2px() + it.rightMargin = 8F.dip2px() + } + } + } + + 2 -> { + setTextColor(R.color.white.toColor()) + setBackgroundResource(R.drawable.subject_top_second) + textSize = 9F + setPadding(0, 2F.dip2px(), 0, 0) + layoutParams?.let { + it.height = 16F.dip2px() + it.width = 16F.dip2px() + if (it is ViewGroup.MarginLayoutParams) { + it.leftMargin = 8F.dip2px() + it.rightMargin = 8F.dip2px() + } + } + } + + 3 -> { + setTextColor(R.color.white.toColor()) + setBackgroundResource(R.drawable.subject_top_third) + textSize = 9F + setPadding(0, 2F.dip2px(), 0, 0) + layoutParams?.let { + it.height = 16F.dip2px() + it.width = 16F.dip2px() + if (it is ViewGroup.MarginLayoutParams) { + it.leftMargin = 8F.dip2px() + it.rightMargin = 8F.dip2px() + } + } + } + + else -> { + textSize = 16F + background = null + setTextColor(R.color.text_989898.toColor()) + layoutParams?.let { + it.height = 24F.dip2px() + it.width = 24F.dip2px() + if (it is ViewGroup.MarginLayoutParams) { + it.leftMargin = 4F.dip2px() + it.rightMargin = 4F.dip2px() + } else throwExceptionInDebug() + } + } + } + } + } + + private fun updateGameTagView(textView: TextView, tagEntity: TagStyleEntity) { + textView.run { + visibility = View.VISIBLE + + if (text == tagEntity.name) return + + isSingleLine = true + ellipsize = TextUtils.TruncateAt.END + includeFontPadding = false + textSize = 10F + text = tagEntity.name + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ).apply { rightMargin = 8F.dip2px() } + setTextColor(("#" + if (tagEntity.column?.isNotEmpty() == true) tagEntity.column else tagEntity.color).hexStringToIntColor()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/game/vertical/GameVerticalAdapter.kt b/app/src/main/java/com/gh/gamecenter/game/vertical/GameVerticalAdapter.kt index 662483f844..17c1bcf864 100644 --- a/app/src/main/java/com/gh/gamecenter/game/vertical/GameVerticalAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/game/vertical/GameVerticalAdapter.kt @@ -10,7 +10,6 @@ import com.gh.common.util.DownloadItemUtils import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.GameViewHolder import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.common.view.AsyncUi import com.gh.gamecenter.entity.SubjectEntity import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.GameSubjectData @@ -45,7 +44,7 @@ class GameVerticalAdapter( } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SimpleGameItemViewHolder { - return SimpleGameItemViewHolder(AsyncGameItemUi(parent.context).apply { inflate() }) + return SimpleGameItemViewHolder(GameItemUi(parent.context)) } override fun getItemCount(): Int { @@ -122,11 +121,7 @@ class GameVerticalAdapter( } } - class AsyncGameItemUi(context: Context) : AsyncUi(context) { - override fun provideUi(context: Context) = GameItemUi(context) - } - - class SimpleGameItemViewHolder(view: AsyncGameItemUi) : ViewHolder(view) { + class SimpleGameItemViewHolder(private val ui: GameItemUi) : ViewHolder(ui.root) { var placeholderGameViewHolder: GameViewHolder? = null fun bindSimpleGameItem( @@ -159,84 +154,81 @@ class GameVerticalAdapter( ViewGroup.LayoutParams(maxWidth - 1, height) } - (itemView as AsyncGameItemUi).bindWhenInflated { - (itemView as AsyncGameItemUi).ui!!.run { - gameNameTv.setTextColor(R.color.text_primary.toColor(context)) - serverTypeTv.setTextColor(R.color.primary_theme.toColor(context)) - downloadTv.background = R.drawable.download_button_normal_style.toDrawable(context) - gameDesTv.setTextColor(R.color.text_tertiary.toColor(context)) + with(ui) { + gameNameTv.setTextColor(R.color.text_primary.toColor(context)) + serverTypeTv.setTextColor(R.color.primary_theme.toColor(context)) + downloadTv.background = R.drawable.download_button_normal_style.toDrawable(context) + gameDesTv.setTextColor(R.color.text_tertiary.toColor(context)) - BindingAdapters.setGameName( - gameNameTv, - gameEntity, - false, - subjectData?.isShowSuffix - ) - BindingAdapters.setGame(iconIv, gameEntity) - BindingAdapters.setGameTags(gameTagContainer, gameEntity) - GameItemViewHolder.initServerType(gameNameTv, serverTypeTv, gameEntity) - gameDesTv.text = gameEntity.decoratedDes - GameItemViewHolder.initGameSubtitleAndAdLabel( - gameEntity, - gameSubtitleTv, - gameNameContainer, - gameNameTv, - gameEntity.adIconActive, - adLabelTv - ) + BindingAdapters.setGameName( + gameNameTv, + gameEntity, + false + ) + BindingAdapters.setGame(iconIv, gameEntity) + BindingAdapters.setGameTags(gameTagContainer, gameEntity) + GameItemViewHolder.initServerType(gameNameTv, serverTypeTv, gameEntity) + gameDesTv.text = gameEntity.decoratedDes + GameItemViewHolder.initGameSubtitleAndAdLabel( + gameEntity, + gameSubtitleTv, + gameNameContainer, + gameNameTv, + gameEntity.adIconActive, + adLabelTv + ) - var gameRatingPaddingEnd = 0 - var gameRatingDrawableStart: Drawable? = null - var gameRatingTextColor = R.color.primary_theme.toColor(context) - var gameRatingText = "" + var gameRatingPaddingEnd = 0 + var gameRatingDrawableStart: Drawable? = null + var gameRatingTextColor = R.color.primary_theme.toColor(context) + var gameRatingText = "" - gameRatingTv.textSize = if (gameEntity.commentCount > 3) 12F else 10F + gameRatingTv.textSize = if (gameEntity.commentCount > 3) 12F else 10F - if (gameEntity.commentCount > 3) { - gameRatingPaddingEnd = 8F.dip2px() - gameRatingDrawableStart = R.drawable.game_horizontal_rating.toDrawable() - gameRatingTextColor = R.color.text_theme.toColor(context) - gameRatingText = - if (gameEntity.star == 10.0F) "10" else gameEntity.star.toString() + if (gameEntity.commentCount > 3) { + gameRatingPaddingEnd = 8F.dip2px() + gameRatingDrawableStart = R.drawable.game_horizontal_rating.toDrawable() + gameRatingTextColor = R.color.text_theme.toColor(context) + gameRatingText = + if (gameEntity.star == 10.0F) "10" else gameEntity.star.toString() + } + + gameRatingTv.setDrawableStart(gameRatingDrawableStart) + gameRatingTv.setPadding(0, 0, gameRatingPaddingEnd, 0) + gameRatingTv.setTextColor(gameRatingTextColor) + gameRatingTv.text = gameRatingText + + // 没错,产品就把这个通用样式叫推荐榜单专题 + downloadTv.putWidgetBusinessName("推荐榜单专题") + + // Fuck this view holder + val tempViewHolder = + placeholderGameViewHolder ?: GameViewHolder(root).apply { + placeholderGameViewHolder = this } + tempViewHolder.gameDes = gameDesTv + tempViewHolder.gameDownloadBtn = downloadTv + tempViewHolder.multiVersionDownloadTv = multiVersionDownloadTv + tempViewHolder.gameDownloadTips = downloadTipsLottie + tempViewHolder.gameLabelList = gameTagContainer + tempViewHolder.recommendIv = recommendIv + tempViewHolder.recommendTv = recommendTv + tempViewHolder.recommendContainer = recommendConstraintLayout + tempViewHolder.gameServerType = serverTypeTv - gameRatingTv.setDrawableStart(gameRatingDrawableStart) - gameRatingTv.setPadding(0, 0, gameRatingPaddingEnd, 0) - gameRatingTv.setTextColor(gameRatingTextColor) - gameRatingTv.text = gameRatingText - - // 没错,产品就把这个通用样式叫推荐榜单专题 - downloadTv.putWidgetBusinessName("推荐榜单专题") - - // Fuck this view holder - val tempViewHolder = - placeholderGameViewHolder ?: GameViewHolder(this@run.root).apply { - placeholderGameViewHolder = this - } - tempViewHolder.gameDes = gameDesTv - tempViewHolder.gameDownloadBtn = downloadTv - tempViewHolder.multiVersionDownloadTv = multiVersionDownloadTv - tempViewHolder.gameDownloadTips = downloadTipsLottie - tempViewHolder.gameLabelList = gameTagContainer - tempViewHolder.recommendIv = recommendIv - tempViewHolder.recommendTv = recommendTv - tempViewHolder.recommendContainer = recommendConstraintLayout - tempViewHolder.gameServerType = serverTypeTv - - DownloadItemUtils.updateItem( - context, - gameEntity, - tempViewHolder, - subjectData?.briefStyle - ) + DownloadItemUtils.updateItem( + context, + gameEntity, + tempViewHolder, + subjectData?.briefStyle + ) DownloadItemUtils.setOnClickListener( context, downloadTv, gameEntity, position, adapter, entrance, location = location, traceEvent = gameEntity.exposureEvent ) - root.setPadding(paddingStart, 8F.dip2px(), paddingEnd, 8F.dip2px()) - } + root.setPadding(paddingStart, 8F.dip2px(), paddingEnd, 8F.dip2px()) } } } diff --git a/app/src/main/java/com/gh/gamecenter/game/vertical/SpanCountPagerSnapHelper.java b/app/src/main/java/com/gh/gamecenter/game/vertical/SpanCountPagerSnapHelper.java index 7575855fbb..d6115a8f78 100644 --- a/app/src/main/java/com/gh/gamecenter/game/vertical/SpanCountPagerSnapHelper.java +++ b/app/src/main/java/com/gh/gamecenter/game/vertical/SpanCountPagerSnapHelper.java @@ -11,7 +11,7 @@ import androidx.recyclerview.widget.RecyclerView; public class SpanCountPagerSnapHelper extends PagerSnapHelper { - private final int mSpanCount; + private int mSpanCount; private final boolean mAddOffsetToSolveWeirdBug; @Nullable @@ -30,6 +30,10 @@ public class SpanCountPagerSnapHelper extends PagerSnapHelper { return mSpanCount; } + public void setSpanCount(int spanCount) { + mSpanCount = spanCount; + } + @Override public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException { super.attachToRecyclerView(recyclerView); diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailAdapter.kt index 670f50a318..c3eb4d4552 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailAdapter.kt @@ -575,7 +575,7 @@ open class GameCollectionDetailAdapter( gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F - BindingAdapters.setGameName(gameName, gameEntity, false, null) + BindingAdapters.setGameName(gameName, gameEntity, false) BindingAdapters.setGameTags(labelList, gameEntity) gameRating.setDrawableStart( if (gameEntity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable(mContext) else null diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt index fe8ed20419..bb0170abb4 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/detail/GameCollectionDetailFragment.kt @@ -14,6 +14,7 @@ import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.RecyclerView import com.ethanhua.skeleton.Skeleton @@ -154,8 +155,8 @@ class GameCollectionDetailFragment : mBinding?.run { ViewCompat.setOnApplyWindowInsetsListener(appbar) { _, insets -> (toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = - insets.systemWindowInsetTop - insets.consumeSystemWindowInsets() + insets.getInsets(WindowInsetsCompat.Type.systemBars()).top + WindowInsetsCompat.CONSUMED } root.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListActivity.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListActivity.kt index 26ed0aafdc..ea16e2896f 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListActivity.kt @@ -3,7 +3,6 @@ package com.gh.gamecenter.gamecollection.hotlist import android.os.Bundle import com.gh.gamecenter.R import com.gh.gamecenter.common.base.activity.BaseActivity -import com.gh.gamecenter.core.utils.DisplayUtils class GameCollectionHotListActivity : BaseActivity() { override fun getLayoutId(): Int { @@ -13,8 +12,6 @@ class GameCollectionHotListActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - DisplayUtils.transparentStatusBar(this) - val containerFragment = supportFragmentManager.findFragmentByTag( GameCollectionHotListWrapperFragment::class.java.name ) diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListAdapter.kt index aef90ab021..fc65e9b4f1 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListAdapter.kt @@ -17,6 +17,7 @@ import com.gh.gamecenter.R import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.gamecenter.common.base.activity.BaseActivity import com.gh.gamecenter.common.baselist.ListAdapter +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.ItemViewType import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.json.json @@ -98,7 +99,7 @@ class GameCollectionHotListAdapter( ) } rankTv.goneIf(realPosition < 3 || mIsFromDetail) { - rankTv.typeface = Typeface.createFromAsset(mContext.assets, "fonts/d_din_bold_only_number.ttf") + rankTv.typeface = Typeface.createFromAsset(mContext.assets, Constants.DIN_FONT_PATH) rankTv.text = "${realPosition + 1}" } stampIv.goneIf(entity?.stamp.isNullOrEmpty()) @@ -139,15 +140,29 @@ class GameCollectionHotListAdapter( mGameCollectionListEntity?.name ?: "", "游戏" ) - SensorsBridge.trackEvent("ViewGameCollectHotRankTabClick", json { - "position" to mPosition - "tab_content" to mGameCollectionListEntity?.name - "game_list_collection_name" to mGameCollectionListEntity?.name - "game_list_collection_id" to mGameCollectionListEntity?.id - "text" to "游戏" - "game_name" to gameEntity.name - "game_id" to gameEntity.id - }) + if (mIsFromDetail) { + SensorsBridge.trackGameListCollectionClick( + location = "合集详情", + gameListCollectionId = mGameCollectionListEntity?.id ?: "", + gameListCollectionName = mGameCollectionListEntity?.name ?: "", + position = position, + text = "游戏", + otherParams = hashMapOf( + "game_name" to (gameEntity.name ?: ""), + "game_id" to gameEntity.id + ) + ) + } else { + SensorsBridge.trackEvent("ViewGameCollectHotRankTabClick", json { + "position" to mPosition + "tab_content" to mGameCollectionListEntity?.name + "game_list_collection_name" to mGameCollectionListEntity?.name + "game_list_collection_id" to mGameCollectionListEntity?.id + "text" to "游戏" + "game_name" to gameEntity.name + "game_id" to gameEntity.id + }) + } GameDetailActivity.startGameDetailActivity( mContext, gameEntity.id, @@ -172,14 +187,25 @@ class GameCollectionHotListAdapter( mGameCollectionListEntity?.name ?: "", "个人主页" ) - SensorsBridge.trackEvent("ViewGameCollectHotRankTabClick", json { - "position" to mPosition - "tab_content" to mGameCollectionListEntity?.name - "game_list_collection_name" to mGameCollectionListEntity?.name - "game_list_collection_id" to mGameCollectionListEntity?.id - "text" to "个人主页" - "mongold_id" to entity?.user?.id - }) + if (mIsFromDetail) { + SensorsBridge.trackGameListCollectionClick( + location = "合集详情", + gameListCollectionId = mGameCollectionListEntity?.id ?: "", + gameListCollectionName = mGameCollectionListEntity?.name ?: "", + position = position, + text = "个人主页", + otherParams = hashMapOf("mongold_id" to (entity?.user?.id ?: "")) + ) + } else { + SensorsBridge.trackEvent("ViewGameCollectHotRankTabClick", json { + "position" to mPosition + "tab_content" to mGameCollectionListEntity?.name + "game_list_collection_name" to mGameCollectionListEntity?.name + "game_list_collection_id" to mGameCollectionListEntity?.id + "text" to "个人主页" + "mongold_id" to entity?.user?.id + }) + } DirectUtils.directToHomeActivity(mContext, entity?.user?.id, 0, mEntrance, mPath) } } @@ -190,15 +216,29 @@ class GameCollectionHotListAdapter( mGameCollectionListEntity?.name ?: "", "游戏单" ) - SensorsBridge.trackEvent("ViewGameCollectHotRankTabClick", json { - "position" to mPosition - "tab_content" to mGameCollectionListEntity?.name - "game_list_collection_name" to mGameCollectionListEntity?.name - "game_list_collection_id" to mGameCollectionListEntity?.id - "text" to "游戏单" - "game_collect_title" to entity?.title - "game_collect_id" to entity?.id - }) + if (mIsFromDetail) { + SensorsBridge.trackGameListCollectionClick( + location = "合集详情", + gameListCollectionId = mGameCollectionListEntity?.id ?: "", + gameListCollectionName = mGameCollectionListEntity?.name ?: "", + position = position, + text = "游戏单", + otherParams = hashMapOf( + "game_collect_title" to (entity?.title ?: ""), + "game_collect_id" to (entity?.id ?: "") + ) + ) + } else { + SensorsBridge.trackEvent("ViewGameCollectHotRankTabClick", json { + "position" to mPosition + "tab_content" to mGameCollectionListEntity?.name + "game_list_collection_name" to mGameCollectionListEntity?.name + "game_list_collection_id" to mGameCollectionListEntity?.id + "text" to "游戏单" + "game_collect_title" to entity?.title + "game_collect_id" to entity?.id + }) + } DirectUtils.directToGameCollectionDetail( it.context, entity?.id ?: "", diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListFragment.kt index 2a57f4f2ed..df4f6e08c4 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionHotListFragment.kt @@ -21,7 +21,7 @@ import com.gh.gamecenter.entity.GameCollectionListEntity import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData class GameCollectionHotListFragment : ListFragment() { - private lateinit var mBasicExposureSource: List + private lateinit var mBasicExposureSourceList: ArrayList private lateinit var mExposureListener: ExposureListener private lateinit var mViewModel: GameCollectionHotListViewModel private var mAdapter: GameCollectionHotListAdapter? = null @@ -40,7 +40,7 @@ class GameCollectionHotListFragment : ListFragment() private var mAdapter: FragmentStateAdapter? = null + private var mUseAlternativeLayout = false private var mIsCollapsed = false - override fun getRealLayoutId(): Int = R.layout.fragment_game_collection_hot_list_wrapper + override fun getRealLayoutId(): Int = if (mUseAlternativeLayout) R.layout.fragment_game_collection_hot_list_wrapper_al else R.layout.fragment_game_collection_hot_list_wrapper + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mUseAlternativeLayout = mIsFromTabWrapper + } override fun onFragmentPause() { super.onFragmentPause() val stayTime = (System.currentTimeMillis() - startPageTime) / 1000 if (mTabEntityList.isNotEmpty() && stayTime >= 3) { + val viewPager = if (mUseAlternativeLayout) mAlternativeBinding.viewPager else mBinding.viewPager NewFlatLogUtils.logGameCollectionHotListExit( stayTime, - mTabEntityList.safelyGetInRelease(mBinding.viewPager.currentItem)?.name ?: "" + mTabEntityList.safelyGetInRelease(viewPager.currentItem)?.name ?: "" ) SensorsBridge.trackEvent("ViewGameCollectHotRank", "stay_length", stayTime.toString()) } } + override fun onFragmentResume() { + super.onFragmentResume() + updateStatusBar() + } + + private fun updateStatusBar() { + if (!mIsFromTabWrapper && isSupportVisible) { + DisplayUtils.transparentStatusBar(requireActivity()) + DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && mIsCollapsed) + } + } + override fun onFragmentFirstVisible() { super.onFragmentFirstVisible() mViewModel = viewModelProvider() @@ -61,7 +85,11 @@ class GameCollectionHotListWrapperFragment : LazyFragment() { initViewPager() initTabLayout() } else { - mBinding.reuseNoConnection.root.visibility = View.VISIBLE + if (mUseAlternativeLayout) { + mAlternativeBinding.reuseNoConnection.root.visibility = View.VISIBLE + } else { + mBinding.reuseNoConnection.root.visibility = View.VISIBLE + } } } @@ -71,63 +99,92 @@ class GameCollectionHotListWrapperFragment : LazyFragment() { override fun onRealLayoutInflated(inflatedView: View) { super.onRealLayoutInflated(inflatedView) - mBinding = FragmentGameCollectionHotListWrapperBinding.bind(inflatedView) + if (mUseAlternativeLayout) { + mAlternativeBinding = FragmentGameCollectionHotListWrapperAlBinding.bind(inflatedView) + } else { + mBinding = FragmentGameCollectionHotListWrapperBinding.bind(inflatedView) + } } override fun initRealView() { super.initRealView() - DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && mIsCollapsed) - changeToolbarStyle(mIsCollapsed) - mBinding.run { - ViewCompat.setOnApplyWindowInsetsListener(appbar) { _, insets -> - (toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = - insets.systemWindowInsetTop - insets.consumeSystemWindowInsets() - } - val collapsingTrigger = 66F.dip2px() + DisplayUtils.getStatusBarHeight(requireContext().resources) - - toolbar.setNavigationOnClickListener { requireActivity().finish() } - - collapsingToolbar.scrimVisibleHeightTrigger = collapsingTrigger - collapsingToolbar.scrimShownAction = { - if (mIsCollapsed != it) { - mIsCollapsed = it - DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && it) - changeToolbarStyle(it) + if (mUseAlternativeLayout) { + mAlternativeBinding.run { + tabContainer.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) + reuseNoConnection.connectionReloadTv.setOnClickListener { + reuseNoConnection.root.visibility = View.GONE + mViewModel.getGameCollectionHotListTab() } } - - appbar.addOnOffsetChangedListener { _, verticalOffset -> - val absOffset = abs(verticalOffset) - val currentFragment = childFragmentManager.findFragmentByTag("f${mBinding.viewPager.currentItem}") - if (currentFragment is GameCollectionPlayerCreationFragment) { - currentFragment.setListRefreshEnable(absOffset <= 2) - } else if (currentFragment is GameCollectionHotListFragment) { - currentFragment.setListRefreshEnable(absOffset <= 2) + } else { + updateStatusBar() + changeToolbarStyle(mIsCollapsed) + mBinding.run { + val statusBarHeight = DisplayUtils.getStatusBarHeight(requireContext().resources) + if (!mIsFromTabWrapper) { + ViewCompat.setOnApplyWindowInsetsListener(appbar) { _, insets -> + (toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = + insets.getInsets(WindowInsetsCompat.Type.systemBars()).top + WindowInsetsCompat.CONSUMED + } } - } + if (mIsFromMainWrapper) { + (toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = statusBarHeight + } + val collapsingTrigger = 66F.dip2px() + statusBarHeight - reuseNoConnection.connectionReloadTv.setOnClickListener { - reuseNoConnection.root.visibility = View.GONE - mViewModel.getGameCollectionHotListTab() + if (mIsFromMainWrapper) { + toolbar.navigationIcon = null + } else { + toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) + } + toolbar.setNavigationOnClickListener { requireActivity().finish() } + + collapsingToolbar.scrimVisibleHeightTrigger = collapsingTrigger + collapsingToolbar.scrimShownAction = { + if (mIsCollapsed != it) { + mIsCollapsed = it + updateStatusBar() + changeToolbarStyle(it) + } + } + + appbar.addOnOffsetChangedListener { _, verticalOffset -> + val absOffset = abs(verticalOffset) + val currentFragment = childFragmentManager.findFragmentByTag("f${mBinding.viewPager.currentItem}") + if (currentFragment is GameCollectionPlayerCreationFragment) { + currentFragment.setListRefreshEnable(absOffset <= 2) + } else if (currentFragment is GameCollectionHotListFragment) { + currentFragment.setListRefreshEnable(absOffset <= 2) + } + } + + reuseNoConnection.connectionReloadTv.setOnClickListener { + reuseNoConnection.root.visibility = View.GONE + mViewModel.getGameCollectionHotListTab() + } } } } private fun changeToolbarStyle(isCollapsed: Boolean) { if (isCollapsed) { + if (!mIsFromMainWrapper) { + mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back) + } mBinding.titleTv.alpha = 1F mBinding.titleTv.visibility = View.VISIBLE mBinding.titleTv.setTextColor(R.color.text_black.toColor(requireContext())) - mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back) mBinding.collapsingToolbar.setContentScrimColor(R.color.ui_surface.toColor(requireContext())) mBinding.tabContainer.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) mBinding.tabLayout.layoutParams = (mBinding.tabLayout.layoutParams as ConstraintLayout.LayoutParams).apply { topMargin = 12F.dip2px() } } else { + if (!mIsFromMainWrapper) { + mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) + } mBinding.titleTv.visibility = View.GONE - mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) mBinding.collapsingToolbar.setContentScrimColor(R.color.ui_background.toColor(requireContext())) mBinding.tabContainer.background = null mBinding.tabLayout.layoutParams = (mBinding.tabLayout.layoutParams as ConstraintLayout.LayoutParams).apply { @@ -137,13 +194,15 @@ class GameCollectionHotListWrapperFragment : LazyFragment() { } private fun initTabLayout() { - mBinding.tabLayout.run { - TabLayoutMediator(this, mBinding.viewPager) { tab, position -> + val tabLayout = if (mUseAlternativeLayout) mAlternativeBinding.tabLayout else mBinding.tabLayout + val viewPager = if (mUseAlternativeLayout) mAlternativeBinding.viewPager else mBinding.viewPager + tabLayout.run { + TabLayoutMediator(this, viewPager) { tab, position -> val tabEntity = mTabEntityList.safelyGetInRelease(position) val view = LayoutInflater.from(context).inflate(R.layout.game_collection_hot_list_tab_item, null) view.findViewById(R.id.tab_title)?.text = tabEntity?.name tab.customView = view - updateTabStyle(tab, position == mBinding.viewPager.currentItem) + updateTabStyle(tab, position == viewPager.currentItem) }.attach() val firstTabView = (getChildAt(0) as ViewGroup).getChildAt(0) firstTabView.layoutParams = (firstTabView.layoutParams as LinearLayout.LayoutParams).also { @@ -197,6 +256,7 @@ class GameCollectionHotListWrapperFragment : LazyFragment() { } private fun initViewPager() { + val exposureSourceList = arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) ?: arrayListOf() mAdapter = object : FragmentStateAdapter(this) { override fun getItemCount(): Int = mTabEntityList.size @@ -206,7 +266,8 @@ class GameCollectionHotListWrapperFragment : LazyFragment() { GameCollectionPlayerCreationFragment().with( bundleOf( EntranceConsts.KEY_ENTRANCE to mEntrance, - EntranceConsts.KEY_PATH to "游戏单热榜" + EntranceConsts.KEY_PATH to "游戏单热榜", + EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to ArrayList(exposureSourceList) ) ) } else { @@ -215,24 +276,40 @@ class GameCollectionHotListWrapperFragment : LazyFragment() { GameCollectionListEntity::class.java.simpleName to tabEntity, EntranceConsts.KEY_POSITION to position, EntranceConsts.KEY_ENTRANCE to mEntrance, - EntranceConsts.KEY_PATH to "游戏单热榜" + EntranceConsts.KEY_PATH to "游戏单热榜", + EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to ArrayList(exposureSourceList) ) ) } } } - mBinding.viewPager.adapter = mAdapter - mBinding.viewPager.isUserInputEnabled = false - mBinding.viewPager.offscreenPageLimit = 3 + if (mUseAlternativeLayout) { + mAlternativeBinding.viewPager.adapter = mAdapter + mAlternativeBinding.viewPager.isUserInputEnabled = false + mAlternativeBinding.viewPager.offscreenPageLimit = 3 + } else { + mBinding.viewPager.adapter = mAdapter + mBinding.viewPager.isUserInputEnabled = false + mBinding.viewPager.offscreenPageLimit = 3 + } } override fun onDarkModeChanged() { super.onDarkModeChanged() - DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && mIsCollapsed) - changeToolbarStyle(mIsCollapsed) - for (i in 0 until mBinding.tabLayout.tabCount) { - val tab = mBinding.tabLayout.getTabAt(i) + if (!::mBinding.isInitialized && !::mAlternativeBinding.isInitialized) { + return + } + if (!mUseAlternativeLayout) { + updateStatusBar() + changeToolbarStyle(mIsCollapsed) + } + + val tabCount = + if (mUseAlternativeLayout) mAlternativeBinding.tabLayout.tabCount else mBinding.tabLayout.tabCount + for (i in 0 until tabCount) { + val tab = + if (mUseAlternativeLayout) mAlternativeBinding.tabLayout.getTabAt(i) else mBinding.tabLayout.getTabAt(i) if (tab != null) { updateTabStyle(tab, tab.isSelected) } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionPlayerCreationAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionPlayerCreationAdapter.kt index a18d3c8679..a05547514f 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionPlayerCreationAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionPlayerCreationAdapter.kt @@ -11,6 +11,7 @@ import com.gh.common.util.NewFlatLogUtils import com.gh.gamecenter.R import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.gamecenter.common.baselist.ListAdapter +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.ItemViewType import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.common.exposure.ExposureSource @@ -29,6 +30,7 @@ class GameCollectionPlayerCreationAdapter( private val mViewModel: GameCollectionPlayerCreationViewModel, private val mEntrance: String, private val mPath: String, + private val mBasicExposureSourceList: ArrayList ) : ListAdapter(context) { private var mTipLinkEntity: LinkEntity? = null @@ -95,7 +97,7 @@ class GameCollectionPlayerCreationAdapter( ) } rankTv.goneIf(realPosition < 3) { - rankTv.typeface = Typeface.createFromAsset(mContext.assets, "fonts/d_din_bold_only_number.ttf") + rankTv.typeface = Typeface.createFromAsset(mContext.assets, Constants.DIN_FONT_PATH) rankTv.text = "${realPosition + 1}" } userNameTv.text = entity.user?.name @@ -211,7 +213,11 @@ class GameCollectionPlayerCreationAdapter( entity?.gameList?.id ?: "", mEntrance, mPath, - ExposureEvent.createEvent(null, listOf(ExposureSource("游戏单热榜-玩家创作榜"))) + ExposureEvent.createEventWithSourceConcat( + null, + mBasicExposureSourceList, + listOf(ExposureSource("游戏单热榜-玩家创作榜")) + ) ) } } diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionPlayerCreationFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionPlayerCreationFragment.kt index 19efe041d6..61c75e162c 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionPlayerCreationFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/hotlist/GameCollectionPlayerCreationFragment.kt @@ -27,7 +27,8 @@ class GameCollectionPlayerCreationFragment : requireContext(), provideListViewModel(), mEntrance, - arguments?.getString(EntranceConsts.KEY_PATH) ?: "" + arguments?.getString(EntranceConsts.KEY_PATH) ?: "", + arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) ?: arrayListOf() ) } return mAdapter as GameCollectionPlayerCreationAdapter diff --git a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt index 0405fae0a6..208b792e40 100644 --- a/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamecollection/square/GameCollectionSquareFragment.kt @@ -10,13 +10,16 @@ import android.view.Gravity import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.view.ViewGroup.MarginLayoutParams import android.view.animation.AnimationUtils import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.PagerSnapHelper import androidx.recyclerview.widget.RecyclerView @@ -67,7 +70,7 @@ import java.lang.ref.WeakReference import kotlin.math.abs class GameCollectionSquareFragment : LazyListFragment() { - private lateinit var mBasicExposureSource: ArrayList + private lateinit var mBasicExposureSourceList: ArrayList private lateinit var mViewModel: GameCollectionSquareViewModel private lateinit var mExposureListener: ExposureListener @@ -78,6 +81,8 @@ class GameCollectionSquareFragment : LazyListFragment() private var mUseAlternativeLayout = false - private var mIsFromHomeToolbarWrapper = false private var mIsCollapsed = false private var mIsLoadDone = false private var mForumName = "" private var mGameCollectionTitle = "" private var mGameCollectionId = "" + private var mIsHome = false override fun onCreate(savedInstanceState: Bundle?) { - mUseAlternativeLayout = arguments?.getBoolean(EntranceConsts.KEY_IS_HOME, false) ?: false - mIsFromHomeToolbarWrapper = arguments?.getBoolean(EntranceConsts.KEY_IS_FROM_HOME_TOOLBAR_WRAPPER, false) ?: false mForumName = arguments?.getString(EntranceConsts.KEY_FORUM_NAME, "") ?: "" mGameCollectionTitle = arguments?.getString(EntranceConsts.KEY_GAME_COLLECTION_TITLE, "") ?: "" mGameCollectionId = arguments?.getString(EntranceConsts.KEY_GAME_COLLECTION_ID, "") ?: "" + mIsHome = arguments?.getBoolean(EntranceConsts.KEY_IS_HOME, false) ?: false super.onCreate(savedInstanceState) + mUseAlternativeLayout = mIsHome || mIsFromTabWrapper } override fun getStubLayoutId() = R.layout.fragment_stub @@ -127,9 +132,8 @@ class GameCollectionSquareFragment : LazyListFragment(R.id.postFab)?.visibility = View.GONE - rootView?.findViewById(R.id.refreshFab)?.visibility = View.GONE + mPostFabView?.visibility = View.GONE + mRefreshFabView?.visibility = View.GONE mGuideContainer?.visibility = View.GONE } else { if (mExposureEventList.isNotEmpty()) ExposureManager.log(mExposureEventList) @@ -160,9 +164,8 @@ class GameCollectionSquareFragment : LazyListFragment(R.id.postFab)?.visibility = View.VISIBLE - rootView?.findViewById(R.id.refreshFab)?.visibility = View.VISIBLE + mPostFabView?.visibility = View.VISIBLE + mRefreshFabView?.visibility = View.VISIBLE if (mIsLoadDone) { mGuideContainer?.goneIf( !SPUtils.getBoolean( @@ -256,24 +259,12 @@ class GameCollectionSquareFragment : LazyListFragment { if (mAdapter == null) { val outerSequence = arguments?.getInt(EntranceConsts.KEY_TAB_INDEX) - val tabName = arguments?.getString(EntranceConsts.KEY_NAME) ?: "" - mBasicExposureSource = arrayListOf().apply { + mBasicExposureSourceList = arrayListOf().apply { val exposureSourceList = requireArguments().getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) if (exposureSourceList != null) { addAll(exposureSourceList) } - - if (mEntrance == "游戏库") { - add(ExposureSource("游戏库")) - add(ExposureSource("游戏单广场")) - } else { - add( - ExposureSource( - if (mUseAlternativeLayout) "顶部tab" else "游戏单广场", - if (mUseAlternativeLayout) tabName else "" - ) - ) - } + add(ExposureSource("游戏单广场")) } mAdapter = GameCollectionSquareAdapter( @@ -281,7 +272,7 @@ class GameCollectionSquareFragment : LazyListFragment (mDefaultBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = - insets.systemWindowInsetTop - insets.consumeSystemWindowInsets() + insets.getInsets(WindowInsetsCompat.Type.systemBars()).top + WindowInsetsCompat.CONSUMED } val collapsingTrigger = 66F.dip2px() + DisplayUtils.getStatusBarHeight(requireContext().resources) @@ -392,7 +383,7 @@ class GameCollectionSquareFragment : LazyListFragment { topMargin = headerHeight } + mViewModel.bannerList.observeNonNull(this) { if (it.isNotEmpty()) { bannerAdapter.submitList(it) @@ -461,7 +455,7 @@ class GameCollectionSquareFragment : LazyListFragment(R.id.wrapper_main_content) + val headerHeight = (DisplayUtils.getScreenWidth() - 40F.dip2px()) / 4 + 16F.dip2px() + mGuideContainer?.setPadding(0, headerHeight, 0, 0) + if (mIsFromMainWrapper || (!mIsHome && mIsFromTabWrapper)) { + if (mIsFromMainWrapper) setNavigationTitle("游戏单广场") + val rootView: FrameLayout? = parentFragment?.view?.findViewById(R.id.wrapper_main_content) + ?: parentFragment?.parentFragment?.view?.findViewById(R.id.wrapper_main_content) if (rootView != null) { - rootView.addView(postFabView, FrameLayout.LayoutParams(60F.dip2px(), 60F.dip2px()).also { + rootView.addView(mPostFabView, FrameLayout.LayoutParams(60F.dip2px(), 60F.dip2px()).also { it.gravity = Gravity.BOTTOM.xor(Gravity.END) it.setMargins(0, 0, 8F.dip2px(), 32F.dip2px()) }) - rootView.addView(refreshFabView, FrameLayout.LayoutParams(60F.dip2px(), 60F.dip2px()).also { + rootView.addView(mRefreshFabView, FrameLayout.LayoutParams(60F.dip2px(), 60F.dip2px()).also { it.gravity = Gravity.BOTTOM.xor(Gravity.END) it.setMargins(0, 0, 8F.dip2px(), 92F.dip2px()) }) - rootView.addView(mGuideContainer, FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)) + rootView.addView(mGuideContainer, FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT).also { + if (!mIsHome && mIsFromTabWrapper) { + it.setMargins(0, R.dimen.tab_layout_height.toPx(), 0, 0) + } + }) } } else { val rootView = parentFragment?.view?.findViewById(R.id.coordinator) if (rootView != null) { - rootView.addView(postFabView, CoordinatorLayout.LayoutParams(60F.dip2px(), 60F.dip2px()).also { + rootView.addView(mPostFabView, CoordinatorLayout.LayoutParams(60F.dip2px(), 60F.dip2px()).also { it.gravity = Gravity.BOTTOM.xor(Gravity.END) it.setMargins(0, 0, 8F.dip2px(), 32F.dip2px()) }) - rootView.addView(refreshFabView, CoordinatorLayout.LayoutParams(60F.dip2px(), 60F.dip2px()).also { + rootView.addView(mRefreshFabView, CoordinatorLayout.LayoutParams(60F.dip2px(), 60F.dip2px()).also { it.gravity = Gravity.BOTTOM.xor(Gravity.END) it.setMargins(0, 0, 8F.dip2px(), 92F.dip2px()) }) rootView.addView(mGuideContainer, CoordinatorLayout.LayoutParams(CoordinatorLayout.LayoutParams.MATCH_PARENT, CoordinatorLayout.LayoutParams.MATCH_PARENT).apply { behavior = AppBarLayout.ScrollingViewBehavior() - setMargins(0, 52F.dip2px(), 0, 0) + if (parentFragment?.view?.findViewById(R.id.tabContainer)?.isVisible == true) { + setMargins(0, 48F.dip2px(), 0, 0) + } }) } } @@ -610,11 +613,11 @@ class GameCollectionSquareFragment : LazyListFragment { - if (mGameEntity != null && mNewGameDetailEntity != null) { - GameDetailMoreDialog.showMoreDialog( - requireActivity() as AppCompatActivity, - mGameEntity, - mNewGameDetailEntity?.shortId ?: "", - mShowConcernOnMenu, - mNewGameDetailEntity!!.me?.isGameConcerned ?: false - ) - MtaHelper.onEvent("游戏详情_新", "更多按钮", mViewModel.game?.name ?: "") - } - } - - R.id.menu_search -> { - LogUtils.uploadSearchGame("access_to_search", "游戏详情", "", "") - val intent = SearchActivity.getIntent(requireContext(), false, "", "游戏详情", "游戏详情") - startActivity(intent) - } - } - } - } - - mMoreMenuItem?.setOnMenuItemClickListener(menuItemClickListener) - mSearchMenuItem?.setOnMenuItemClickListener(menuItemClickListener) - mDownloadMenuItem?.actionView?.setOnClickListener { - MtaHelper.onEvent("游戏详情_新", "下载管理图标", mViewModel.game?.name ?: "") - val intent = DownloadManagerActivity.getDownloadMangerIntent(requireContext(), mEntrance) - startActivity(intent) - } - - updateDownloadIcon() - } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) for (fragment in childFragmentManager.fragments) { @@ -441,7 +395,13 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + customPageTrackData = + arguments?.getParcelable(EntranceConsts.KEY_CUSTOM_PAGE_TRACK_DATA) as? CustomPageTrackData savedInstanceState?.let { mDestinationTab = it.getInt(LAST_SELECTED_POSITION) } + } + + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() mBinding.reuseNoneData.reuseNoneDataTv.text = "页面不见了" mBodyBinding.gamedetailVp.setScrollable(true) mBodyBinding.gamedetailVp.offscreenPageLimit = 5 @@ -575,6 +535,80 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { }) observeData() + + initMenu() + + mOrientationUtils = OrientationUtils(requireActivity(), mVideoBinding.player) + mOrientationUtils?.isEnable = false + setListener() + + // toolbar 消费 fitsSystemWindows 避免在 collapsingToolbar 下面出现多出来的 padding + // [https://stackoverflow.com/questions/48137666/viewgroup-inside-collapsingtoolbarlayout-show-extra-bottom-padding-when-set-fits] + if (!mIsFromTabWrapper) { + ViewCompat.setOnApplyWindowInsetsListener(mBodyBinding.gamedetailAppbar) { _, insets -> + (mBodyBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = + insets.getInsets(WindowInsetsCompat.Type.systemBars()).top + WindowInsetsCompat.CONSUMED + } + } + if (mIsFromMainWrapper) { + (mBodyBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = + DisplayUtils.getStatusBarHeight(requireContext().resources) + } + + updateDivider() + } + + private fun initMenu() { + mBodyBinding.run { + toolbar.inflateMenu(R.menu.menu_game_detail) + if (mIsFromMainWrapper || mIsFromTabWrapper) { + toolbar.navigationIcon = null + toolbarContainer.translationX = 16F.dip2px().toFloat() + } + toolbar.setNavigationOnClickListener { requireActivity().finish() } + mMoreMenuItem = toolbar.menu.findItem(R.id.menu_more) + mSearchMenuItem = toolbar.menu.findItem(R.id.menu_search) + mDownloadMenuItem = toolbar.menu.findItem(R.id.menu_download) + mDownloadMenuItem?.isVisible = Config.isShow() + } + + mDownloadMenuIcon = mDownloadMenuItem?.actionView?.findViewById(R.id.menu_download_iv) + + val menuItemClickListener = MenuItem.OnMenuItemClickListener { + consume { + when (it.itemId) { + R.id.menu_more -> { + if (mGameEntity != null && mNewGameDetailEntity != null) { + GameDetailMoreDialog.showMoreDialog( + requireActivity() as AppCompatActivity, + mGameEntity, + mNewGameDetailEntity?.shortId ?: "", + mShowConcernOnMenu, + mNewGameDetailEntity!!.me?.isGameConcerned ?: false + ) + MtaHelper.onEvent("游戏详情_新", "更多按钮", mViewModel.game?.name ?: "") + } + } + + R.id.menu_search -> { + LogUtils.uploadSearchGame("access_to_search", "游戏详情", "", "") + val intent = SearchActivity.getIntent(requireContext(), false, "", "游戏详情", "游戏详情") + startActivity(intent) + } + } + } + } + + mMoreMenuItem?.setOnMenuItemClickListener(menuItemClickListener) + mSearchMenuItem?.setOnMenuItemClickListener(menuItemClickListener) + mDownloadMenuItem?.actionView?.setOnClickListener { + MtaHelper.onEvent("游戏详情_新", "下载管理图标", mViewModel.game?.name ?: "") + val intent = DownloadManagerActivity.getDownloadMangerIntent(requireContext(), mEntrance) + startActivity(intent) + } + + updateDownloadIcon() } private fun initGameSubtitle( @@ -702,25 +736,6 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { return (width / textWidth).toInt() } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - mOrientationUtils = OrientationUtils(requireActivity(), mVideoBinding.player) - mOrientationUtils?.isEnable = false - setListener() - - // toolbar 消费 fitsSystemWindows 避免在 collapsingToolbar 下面出现多出来的 padding - // [https://stackoverflow.com/questions/48137666/viewgroup-inside-collapsingtoolbarlayout-show-extra-bottom-padding-when-set-fits] - ViewCompat.setOnApplyWindowInsetsListener(mBodyBinding.gamedetailAppbar) { _, insets -> - (mBodyBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop - insets.consumeSystemWindowInsets() - } - - updateDivider() - - mBodyBinding.toolbar.setNavigationOnClickListener { requireActivity().finish() } - } - @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(bean: EBScroll) { if (mGameEntity?.id == bean.id) { @@ -756,21 +771,48 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { DataLogUtils.uploadGameLog(context, mGameEntity!!.id, mGameEntity!!.name, mEntrance) - SensorsBridge.trackEventWithExposureSource( - "GameDetailPageShow", - mTraceEvent?.source, - "game_id", mGameEntity?.id ?: "", - "game_name", mGameEntity?.name ?: "", - "download_status", mGameEntity?.downloadStatusChinese ?: "", - "cloud_save_tab_status", if (data.showArchive) "开启" else "关闭", - "game_type", mGameEntity?.categoryChinese ?: "", - "page_name", getCurrentPageEntity().pageName, - "page_id", getCurrentPageEntity().pageId, - "page_business_id", getCurrentPageEntity().pageBusinessId, - "last_page_name", getLastPageEntity().pageName, - "last_page_id", getLastPageEntity().pageId, - "last_page_business_id", getLastPageEntity().pageBusinessId - ) + postDelayedRunnable({ + val buttonName = when { + mDownloadBinding.detailProgressbar.text.isNotBlank() -> mDownloadBinding.detailProgressbar.text + mDownloadBinding.overlayTv.isVisible && mDownloadBinding.overlayTv.text.isNotBlank() -> + mDownloadBinding.overlayTv.text.toString() + + mDownloadBinding.multiVersionDownloadTv.isVisible && mDownloadBinding.multiVersionDownloadTv.text.isNotBlank() -> + mDownloadBinding.multiVersionDownloadTv.text.toString() + + else -> "" + } + SensorsBridge.trackEventWithExposureSource( + "GameDetailPageShow", + mTraceEvent?.source, + "game_id", mGameEntity?.id ?: "", + "game_name", mGameEntity?.name ?: "", + "download_status", mGameEntity?.downloadStatusChinese ?: "", + "cloud_save_tab_status", if (data.showArchive) "开启" else "关闭", + "game_type", mGameEntity?.categoryChinese ?: "", + "page_name", getCurrentPageEntity().pageName, + "page_id", getCurrentPageEntity().pageId, + "page_business_id", getCurrentPageEntity().pageBusinessId, + "last_page_name", getLastPageEntity().pageName, + "last_page_id", getLastPageEntity().pageId, + "last_page_business_id", getLastPageEntity().pageBusinessId, + "button_name", buttonName, + "game_schema_type", mGameEntity?.gameBitChinese ?: "", + "download_type", mGameEntity?.downloadType ?: "", + "game_type", mGameEntity?.categoryChinese ?: "", + "bottom_tab", customPageTrackData?.pageLocation?.bottomTab ?: "", + "several_tab_page_id", customPageTrackData?.pageLocation?.severalTabPageId ?: "", + "several_tab_page_name", customPageTrackData?.pageLocation?.severalTabPageName ?: "", + "position", "${customPageTrackData?.pageLocation?.tabPosition ?: -1}", + "tab_content", customPageTrackData?.pageLocation?.tabContent ?: "", + "custom_page_id", customPageTrackData?.pageLocation?.pageId ?: "", + "custom_page_name", customPageTrackData?.pageLocation?.pageName ?: "", + "module_type", customPageTrackData?.moduleType ?: "", + "module_pattern", customPageTrackData?.modulePattern ?: "", + "link_content_id", customPageTrackData?.linkContentId ?: "", + "link_content_name", customPageTrackData?.linkContentName ?: "" + ) + }, 120) mNewGameDetailEntity = data @@ -1636,7 +1678,9 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { if (!mIsDarkModeOn && isToolbarWhite) R.color.black else R.color.white ) ) - mBodyBinding.toolbar.setNavigationIcon(if (!mIsDarkModeOn && isToolbarWhite) R.drawable.ic_bar_back else R.drawable.ic_bar_back_light) + if (!mIsFromMainWrapper && !mIsFromTabWrapper) { + mBodyBinding.toolbar.setNavigationIcon(if (!mIsDarkModeOn && isToolbarWhite) R.drawable.ic_bar_back else R.drawable.ic_bar_back_light) + } mBodyBinding.toolbar.post { if (context != null) { mBodyBinding.toolbar.setBackgroundColor( @@ -1646,16 +1690,19 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { ) } } - DisplayUtils.setStatusBarColor( - requireActivity(), - if (isToolbarWhite) R.color.ui_surface else R.color.transparent, - true - ) updateConcernMenuIcon(mNewGameDetailEntity?.me?.isGameConcerned ?: false) mMoreMenuItem?.setIcon(if (!mIsDarkModeOn && isToolbarWhite) R.drawable.ic_menu_more else R.drawable.ic_menu_more_light) mSearchMenuItem?.setIcon(if (!mIsDarkModeOn && isToolbarWhite) R.drawable.ic_column_search else R.drawable.ic_column_search_light) mDownloadMenuIcon?.setImageResource(if (!mIsDarkModeOn && isToolbarWhite) R.drawable.toolbar_download else R.drawable.toolbar_download_light) - DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && isToolbarWhite) + // 不在多Tab导航页内更新状态栏颜色 + if (!mIsFromTabWrapper && isSupportVisible) { + DisplayUtils.setStatusBarColor( + requireActivity(), + if (isToolbarWhite) R.color.ui_surface else R.color.transparent, + true + ) + DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && isToolbarWhite) + } } override fun onTouchEvent(event: MotionEvent) { @@ -1824,7 +1871,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { val downloadMenuView = mBodyBinding.toolbar.menu.findItem(R.id.menu_download).actionView mDownloadCountHint = downloadMenuView?.findViewById(R.id.menu_download_count_hint) mDownloadCountHint?.typeface = - Typeface.createFromAsset(requireContext().assets, "fonts/d_din_bold_only_number.ttf") + Typeface.createFromAsset(requireContext().assets, Constants.DIN_FONT_PATH) } private fun updateDownloadCountHint(updateList: List?) { @@ -1926,7 +1973,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { } private fun releaseVideo() { - if (mViewModel.displayTopVideo) { + if (::mViewModel.isInitialized && mViewModel.displayTopVideo) { mVideoBinding.player.release() mVideoBinding.player.disposableTimer() } @@ -2318,8 +2365,9 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { return super.onBackPressed() } - override fun onResume() { - super.onResume() + override fun onFragmentResume() { + super.onFragmentResume() + updateToolbarStyle(!mViewModel.displayTopVideo || mBodyBinding.gamedetailThumbSmall.visibility == View.VISIBLE) if (!mIsPauseTopVideo && mIsPlayingWhenPause) { resumeVideo() } @@ -2333,8 +2381,8 @@ class GameDetailFragment : ToolbarFragment(), IScrollable { controlInstallHint() } - override fun onPause() { - super.onPause() + override fun onFragmentPause() { + super.onFragmentPause() if (mViewModel.displayTopVideo) { mVideoBinding.player.postDelayed({ mVideoBinding.player.showFullPauseBitmap() diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt index ef700640ee..b26889c7cd 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescAdapter.kt @@ -22,7 +22,6 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.gh.common.DefaultUrlHandler -import com.gh.common.databind.BindingAdapters import com.gh.common.exposure.ExposureManager import com.gh.common.util.* import com.gh.common.util.LogUtils @@ -824,6 +823,18 @@ class DescAdapter( itemData.moreLink?.type ?: "", itemData.moreLink?.link ?: "" ) + SensorsBridge.trackColumnClick( + location = "游戏详情", + gameName = mGameName, + gameId = mViewModel.gameId ?: "", + gameColumnName = itemData.columnTitle, + gameColumnId = itemData.columnId, + linkText = itemData.moreLink?.text ?: "", + linkType = itemData.moreLink?.type ?: "", + linkId = itemData.moreLink?.link ?: "", + text = "右上角", + buttonType = itemData.displayHome + ) if (itemData.displayHome == "换一批") { headPb.visibility = View.VISIBLE moreTv.isEnabled = false @@ -852,6 +863,8 @@ class DescAdapter( layoutManager = LinearLayoutManager(mContext, RecyclerView.HORIZONTAL, false) if (adapter == null) { val subjectEntity = SubjectEntity().apply { + id = itemData.columnId + name = itemData.columnTitle data = columnGames } val exposureEventList = arrayListOf() diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescFragment.kt index b138ddab95..c2eaf01162 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/DescFragment.kt @@ -298,6 +298,8 @@ class DescFragment: LazyFragment(), IScrollable { override fun onDarkModeChanged() { super.onDarkModeChanged() + if (!::mBinding.isInitialized) return + mBinding.recyclerview.setBackgroundColor(R.color.ui_background.toColor(requireContext())) mBinding.recyclerview.recycledViewPool.clear() mAdapter?.let { it.notifyItemRangeChanged(0, it.itemCount) } @@ -310,6 +312,7 @@ class DescFragment: LazyFragment(), IScrollable { floatingWindowProvider?.getAndShowFloatingWindow( mGameEntity?.id ?: "", mGameEntity?.name ?: "", + "游戏详情", this, mBinding.recyclerview )?.let { diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/GameLibaoAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/GameLibaoAdapter.kt index e9ca50e0c9..b3ccda0625 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/GameLibaoAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/GameLibaoAdapter.kt @@ -8,7 +8,6 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.LibaoUtils import com.gh.common.util.NewLogUtils -import com.gh.gamecenter.LibaoDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.common.utils.copyTextAndToast import com.gh.gamecenter.common.utils.fromHtml @@ -18,6 +17,7 @@ import com.gh.gamecenter.core.utils.SpanBuilder import com.gh.gamecenter.databinding.ItemGameDetailMoreBinding import com.gh.gamecenter.databinding.ItemGameLibaoBinding import com.gh.gamecenter.feature.entity.LibaoEntity +import com.gh.gamecenter.libao.LibaoDetailActivity import com.gh.gamecenter.login.user.UserManager class GameLibaoAdapter( diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/GameRaidersAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/GameRaidersAdapter.kt index ad07edc9bc..e009511eeb 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/desc/GameRaidersAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/desc/GameRaidersAdapter.kt @@ -5,17 +5,19 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.gh.common.DefaultUrlHandler -import com.gh.common.util.* +import com.gh.common.util.DataCollectionUtils +import com.gh.common.util.DirectUtils import com.gh.common.util.LogUtils -import com.gh.gamecenter.NewsDetailActivity +import com.gh.common.util.NewsUtils import com.gh.gamecenter.R import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.core.utils.TimeUtils import com.gh.gamecenter.core.utils.MtaHelper +import com.gh.gamecenter.core.utils.TimeUtils import com.gh.gamecenter.databinding.ItemGameRaidersBinding import com.gh.gamecenter.databinding.ItemGameRaidersFixedTopBinding import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.NewsEntity +import com.gh.gamecenter.newsdetail.NewsDetailActivity class GameRaidersAdapter( val context: Context, diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/dialog/GameDetailMoreDialog.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/dialog/GameDetailMoreDialog.kt index 462e7778a1..cf84fdcada 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/dialog/GameDetailMoreDialog.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/dialog/GameDetailMoreDialog.kt @@ -122,13 +122,7 @@ class GameDetailMoreDialog : BaseDraggableDialogFragment() { private fun getShareUtils(): ShareUtils { val url = getShareUrl() val shareUtils = ShareUtils.getInstance(requireContext()) - var shareEntrance: ShareUtils.ShareEntrance = ShareUtils.ShareEntrance.game - val tagList = mGameEntity?.getTag() ?: arrayListOf() - for (s in tagList) { - if ("官方版" != s) { - shareEntrance = ShareUtils.ShareEntrance.plugin - } - } + val shareEntrance: ShareUtils.ShareEntrance = ShareUtils.ShareEntrance.game shareUtils.shareParamsDetail( requireActivity(), url, mGameEntity?.icon ?: "", mGameEntity?.name ?: "", diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/entity/DetailEntity.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/entity/DetailEntity.kt index f64464e793..5185880587 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/entity/DetailEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/entity/DetailEntity.kt @@ -200,8 +200,6 @@ class RelatedVersion( var gameId: String = "", @SerializedName("game_name") private var mGameName: String = "", - @SerializedName("name_suffix") - var nameSuffix: String? = "", @SerializedName("game_icon") var gameIcon: String = "", @SerializedName(value = "game_tag", alternate = arrayOf("new_game_tag")) @@ -210,5 +208,5 @@ class RelatedVersion( ) : Parcelable { @IgnoredOnParcel val gameName: String - get() = mGameName.removeSuffix(".") + (nameSuffix ?: "") + get() = mGameName.removeSuffix(".") } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/FuLiAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/FuLiAdapter.kt index 02be843a38..13ae72b162 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/FuLiAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/FuLiAdapter.kt @@ -11,7 +11,6 @@ import com.gh.common.util.DataCollectionUtils import com.gh.common.util.DirectUtils import com.gh.common.util.LogUtils import com.gh.gamecenter.GameNewsActivity -import com.gh.gamecenter.NewsDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.GameDetailNoticeViewHolder import com.gh.gamecenter.common.entity.SimpleGameEntity @@ -31,6 +30,7 @@ import com.gh.gamecenter.gamedetail.fuli.answer.GameDetailAnswerAdapter import com.gh.gamecenter.gamedetail.fuli.answer.GameDetailAnswerViewHolder import com.gh.gamecenter.gamedetail.fuli.tools.GameDetailToolsAdapter import com.gh.gamecenter.help.HelpAndFeedbackBridge +import com.gh.gamecenter.newsdetail.NewsDetailActivity import com.lightgame.adapter.BaseRecyclerAdapter class FuLiAdapter( diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/GameNewsAdapter.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/GameNewsAdapter.kt index 916e833332..ad1ef806a3 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/GameNewsAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/GameNewsAdapter.kt @@ -6,12 +6,12 @@ import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.gh.common.util.DataCollectionUtils import com.gh.common.util.NewsUtils +import com.gh.gamecenter.R import com.gh.gamecenter.common.utils.toBinding import com.gh.gamecenter.common.view.DrawableView -import com.gh.gamecenter.NewsDetailActivity -import com.gh.gamecenter.R import com.gh.gamecenter.databinding.ItemGameDetailNewsBinding import com.gh.gamecenter.feature.entity.NewsEntity +import com.gh.gamecenter.newsdetail.NewsDetailActivity class GameNewsAdapter( val context: Context, diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/tools/GameDetailToolsAdapter.java b/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/tools/GameDetailToolsAdapter.java index 532965f4d6..470f2a4608 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/tools/GameDetailToolsAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/fuli/tools/GameDetailToolsAdapter.java @@ -6,25 +6,25 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.gh.common.constant.Config; -import com.gh.gamecenter.core.utils.DisplayUtils; -import com.gh.gamecenter.common.utils.ImageUtils; -import com.gh.gamecenter.core.utils.MtaHelper; -import com.gh.gamecenter.NewsDetailActivity; -import com.gh.gamecenter.R; -import com.gh.gamecenter.WebActivity; -import com.gh.gamecenter.adapter.viewholder.ToolBoxViewHolder; -import com.gh.gamecenter.databinding.ToolboxItemBinding; -import com.gh.gamecenter.common.entity.ToolBoxEntity; -import com.lightgame.adapter.BaseRecyclerAdapter; - -import java.util.List; - import androidx.annotation.ColorRes; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; +import com.gh.common.constant.Config; +import com.gh.gamecenter.R; +import com.gh.gamecenter.WebActivity; +import com.gh.gamecenter.adapter.viewholder.ToolBoxViewHolder; +import com.gh.gamecenter.common.entity.ToolBoxEntity; +import com.gh.gamecenter.common.utils.ImageUtils; +import com.gh.gamecenter.core.utils.DisplayUtils; +import com.gh.gamecenter.core.utils.MtaHelper; +import com.gh.gamecenter.databinding.ToolboxItemBinding; +import com.gh.gamecenter.newsdetail.NewsDetailActivity; +import com.lightgame.adapter.BaseRecyclerAdapter; + +import java.util.List; + /** * Created by khy on 25/05/17. */ diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/myrating/MyRatingFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/myrating/MyRatingFragment.kt index 8795064908..5f32d092f1 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/myrating/MyRatingFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/myrating/MyRatingFragment.kt @@ -6,6 +6,7 @@ import android.os.Bundle import android.view.View import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.DirectUtils import com.gh.gamecenter.MainActivity import com.gh.gamecenter.R import com.gh.gamecenter.common.baselist.ListAdapter @@ -18,8 +19,8 @@ import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.databinding.FragmentListNodataSkipNewBinding import com.gh.gamecenter.entity.RatingComment -import com.gh.gamecenter.fragment.MainWrapperFragment import com.gh.gamecenter.personalhome.rating.MyRating +import com.gh.gamecenter.wrapper.MainWrapperFragment class MyRatingFragment : ListFragment() { @@ -41,7 +42,7 @@ class MyRatingFragment : ListFragment() { mBinding.reuseNoneData.reuseResetLoadTv.text = "去首页看看" mBinding.reuseNoneData.reuseResetLoadTv.visibility = View.VISIBLE mBinding.reuseNoneData.reuseResetLoadTv.setOnClickListener { - MainActivity.skipToMainActivity(requireContext(), MainWrapperFragment.INDEX_HOME) + DirectUtils.directToHomeDefaultTab(requireContext()) MtaHelper.onEvent("我的光环_新", "我的游戏评论", "去首页看看") } } diff --git a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt index 1801149595..567e7d8285 100644 --- a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt @@ -33,10 +33,8 @@ import com.gh.gamecenter.databinding.FragmentMainHomeBinding import com.gh.gamecenter.eventbus.EBDiscoverChanged import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage -import com.gh.gamecenter.eventbus.EBUISwitch import com.gh.gamecenter.fragment.HomeSearchToolWrapperFragment import com.gh.gamecenter.fragment.HomeSearchToolWrapperViewModel -import com.gh.gamecenter.fragment.MainWrapperFragment import com.gh.gamecenter.game.gallery.GameGallerySlideViewHolder import com.gh.gamecenter.home.slide.HomeSlideListAdapter import com.gh.gamecenter.home.test_v2.HomeGameTestV2ViewModel @@ -160,8 +158,6 @@ class HomeFragment : LazyFragment() { HomeItemGameTestV2ViewHolderWatcher() ) - mHomeSearchViewModel.getFloatingWindowHandler()?.setView(this, mBinding.gameList) - mBinding.gameList.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) @@ -200,7 +196,7 @@ class HomeFragment : LazyFragment() { fun setScrollEnabled(isScrollEnabled: Boolean) { if (::mAutomaticLayoutManager.isInitialized) { - mAutomaticLayoutManager.isScrollEnabled = isScrollEnabled + mAutomaticLayoutManager.isScrollVerticallyEnabled = isScrollEnabled } } @@ -366,17 +362,17 @@ class HomeFragment : LazyFragment() { mViewModel.refreshRecentVGameIfNeeded() } - @Subscribe(threadMode = ThreadMode.MAIN) - fun onEventMainThread(busNine: EBUISwitch) { - if (::mLayoutManager.isInitialized - && MainWrapperFragment.EB_MAIN_SCROLL_TOP == busNine.from - && MainWrapperFragment.INDEX_HOME == busNine.position - ) { - mBinding.gameList.stopScroll() - mLayoutManager.scrollToPosition(0) - mScrollCalculatorHelper.currentPlayer?.release() - } - } +// @Subscribe(threadMode = ThreadMode.MAIN) +// fun onEventMainThread(busNine: EBUISwitch) { +// if (::mLayoutManager.isInitialized +// && MainWrapperFragment.EB_MAIN_SCROLL_TOP == busNine.from +// && MainWrapperFragment.INDEX_HOME == busNine.position +// ) { +// mBinding.gameList.stopScroll() +// mLayoutManager.scrollToPosition(0) +// mScrollCalculatorHelper.currentPlayer?.release() +// } +// } @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(changed: EBDiscoverChanged) { diff --git a/app/src/main/java/com/gh/gamecenter/home/HomeViewModel.kt b/app/src/main/java/com/gh/gamecenter/home/HomeViewModel.kt index 563f55f5ad..a8bacd029e 100644 --- a/app/src/main/java/com/gh/gamecenter/home/HomeViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/home/HomeViewModel.kt @@ -23,7 +23,6 @@ import com.gh.gamecenter.entity.* import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.PluginLocation import com.gh.gamecenter.feature.utils.ApkActiveUtils -import com.gh.gamecenter.fragment.MainWrapperRepository import com.gh.gamecenter.game.rank.RankCollectionAdapter import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData import com.gh.gamecenter.home.test_v2.HomeGameTestV2DownloadStateUpdateHelper @@ -132,8 +131,6 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) { refreshRecentVGameIfNeeded() } - fun showRecentVGame() = MainWrapperRepository.getInstance().getVideoNavBarLiveData().value?.text?.contains("畅玩广场") == false - fun refreshRecentVGameIfNeeded() { if (VHelper.isVGameOn() && SPUtils.getBoolean(Constants.SP_HOME_VGAME_AREA_ENABLED, true) @@ -654,7 +651,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) { } // 最近在玩的畅玩游戏 - if (showRecentVGame() && mVGameList != null && !mVGameList.isNullOrEmpty()) { + if (mVGameList != null && !mVGameList.isNullOrEmpty()) { val item = HomeItemData() item.recentVGame = mVGameList mSnapshotItemList.add(item) @@ -736,8 +733,6 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) { // 这个 for 循环主要功能是用来标识替换已安装的游戏 for (game in data) { mSubjectGameIdList.add(game.id) - // 应用专题是否显示游戏名后缀的配置 - game.shouldShowNameSuffix = subjectEntity.showSuffix } subjectEntity.relatedColumnId?.let { diff --git a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt index 10a311c662..2157bf5079 100644 --- a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt +++ b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeFragmentAdapterAssistant.kt @@ -665,7 +665,7 @@ class LegacyHomeFragmentAdapterAssistant( holder.itemView.setPadding(16F.dip2px(), 8F.dip2px(), 16F.dip2px(), 8F.dip2px()) } - holder.bindGameItem(gameEntity, subjectData.isShowSuffix, subjectData.briefStyle) + holder.bindGameItem(gameEntity, subjectData.briefStyle) holder.initServerType(gameEntity) runOnIoThread(true) { @@ -923,15 +923,7 @@ class LegacyHomeFragmentAdapterAssistant( var adapter = holder.binding.recyclerView.adapter if (mPluginDisplayStatus == PluginDisplayStatus.DEFAULT) { - mPluginDisplayStatus = if (HaloApp.getInstance().isNewForThisVersion) { - if (pluginList!!.size > 2) { - PluginDisplayStatus.OPEN_TWO_AND_BUTTON - } else { - PluginDisplayStatus.OPEN - } - } else { - PluginDisplayStatus.CLOSE - } + mPluginDisplayStatus = HomePluggableHelper.getPluginDisplayedStyle(pluginList) } val exposureList = arrayListOf() diff --git a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeSubjectTransformer.kt b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeSubjectTransformer.kt index 1a1d79ebdb..d47df6c180 100644 --- a/app/src/main/java/com/gh/gamecenter/home/LegacyHomeSubjectTransformer.kt +++ b/app/src/main/java/com/gh/gamecenter/home/LegacyHomeSubjectTransformer.kt @@ -105,7 +105,6 @@ object LegacyHomeSubjectTransformer { position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, isOrder = subjectEntity.isOrder, briefStyle = subjectEntity.briefStyle, - isShowSuffix = subjectEntity.showSuffix ) game.outerSequence = blockPosition game.sequence = i @@ -139,7 +138,6 @@ object LegacyHomeSubjectTransformer { position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, isOrder = subjectEntity.isOrder, briefStyle = subjectEntity.briefStyle, - isShowSuffix = subjectEntity.showSuffix ) game.outerSequence = blockPosition game.sequence = i @@ -170,7 +168,6 @@ object LegacyHomeSubjectTransformer { position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, isOrder = subjectEntity.isOrder, briefStyle = subjectEntity.briefStyle, - isShowSuffix = subjectEntity.showSuffix ) game.outerSequence = blockPosition game.sequence = i @@ -250,7 +247,6 @@ object LegacyHomeSubjectTransformer { position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, isOrder = subjectEntity.isOrder, briefStyle = subjectEntity.briefStyle, - isShowSuffix = subjectEntity.showSuffix ) game.outerSequence = blockPosition game.sequence = i @@ -279,7 +275,6 @@ object LegacyHomeSubjectTransformer { position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, isOrder = subjectEntity.isOrder, briefStyle = subjectEntity.briefStyle, - isShowSuffix = subjectEntity.showSuffix ) val itemDataGame = newItemInstance() itemDataGame.blockPosition = blockPosition + 1 diff --git a/app/src/main/java/com/gh/gamecenter/home/PageConfigure.kt b/app/src/main/java/com/gh/gamecenter/home/PageConfigure.kt new file mode 100644 index 0000000000..f56883824a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/PageConfigure.kt @@ -0,0 +1,27 @@ +package com.gh.gamecenter.home + +import com.gh.gamecenter.common.exposure.ExposureSource + +class PageConfigure( + val pageId: String = "", + var refreshCount: Int = 0, + val entrance: String = "", + val transparentBackground: Boolean = false, + val exposureSourceList: ArrayList = arrayListOf(), + val isInSearchToolbarTabWrapperPage: Boolean = false +) { + + private val customExposureSource = ExposureSource(CUSTOM_PAGE_SOURCE, "") + + init { + exposureSourceList.add(customExposureSource) + } + + fun setCustomPageExposureSource(v: String) { + customExposureSource.v = v + } + + companion object { + private const val CUSTOM_PAGE_SOURCE = "自定义页面" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/BannerInRecyclerController.kt b/app/src/main/java/com/gh/gamecenter/home/custom/BannerInRecyclerController.kt new file mode 100644 index 0000000000..8cf7ed4d1c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/BannerInRecyclerController.kt @@ -0,0 +1,87 @@ +package com.gh.gamecenter.home.custom + +import android.os.Handler +import android.os.Looper +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment + +/** + * 自定义页面 + * 控制折叠轮播图轮播逻辑 + */ +class BannerInRecyclerController( + private val nextPage: () -> Unit +) : DefaultLifecycleObserver { + + private val handler = Handler(Looper.getMainLooper()) + + private var isAttachToWindow = false + + private var isActive = false + + private var isParentScrolling = false + + private val onScrollListener = object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + isParentScrolling = newState != RecyclerView.SCROLL_STATE_IDLE + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + start() + } else { + pause() + } + } + } + + fun onViewAttachedToWindow(parent: RecyclerView?) { + isAttachToWindow = true + parent?.addOnScrollListener(onScrollListener) + } + + fun onViewDetachedFromWindow(parent: RecyclerView?) { + isAttachToWindow = false + pause() + parent?.removeOnScrollListener(onScrollListener) + } + + fun start() { + if (isActive && !isParentScrolling) { + handler.removeCallbacksAndMessages(null) + nextToPage() + } + } + + fun pause() { + handler.removeCallbacksAndMessages(null) + } + + private fun destroy() { + handler.removeCallbacksAndMessages(null) + } + + override fun onResume(owner: LifecycleOwner) { + isActive = true + if (isAttachToWindow) { + start() + } + } + + override fun onPause(owner: LifecycleOwner) { + isActive = false + pause() + } + + override fun onDestroy(owner: LifecycleOwner) { + destroy() + } + + private fun nextToPage() { + handler.postDelayed(nextPageRunner, GameCollectionSquareFragment.BANNER_LOOP_TIME) + } + + private val nextPageRunner = Runnable { + nextPage() + nextToPage() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/CustomNewGameTestUseCase.kt b/app/src/main/java/com/gh/gamecenter/home/custom/CustomNewGameTestUseCase.kt new file mode 100644 index 0000000000..c5809327a2 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/CustomNewGameTestUseCase.kt @@ -0,0 +1,481 @@ +package com.gh.gamecenter.home.custom + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.gh.common.filter.RegionSettingHelper +import com.gh.gamecenter.BuildConfig +import com.gh.gamecenter.common.baselist.LoadStatus +import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.common.utils.observableToMain +import com.gh.gamecenter.core.utils.UrlFilterUtils +import com.gh.gamecenter.entity.* +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.test_v2.HomeGameTestV2DownloadStateUpdateHelper +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import com.lightgame.utils.Utils +import retrofit2.HttpException + +/** + * description : 自定义页面-新游开测 item 的分页加载 ViewModel + */ + +class CustomNewGameTestUseCase() : HomeGameTestV2DownloadStateUpdateHelper { + + companion object { + private const val TAG = "HomeGameTestV2ViewModel" + + //每页加载数据条数 + private const val PAGE_SIZE = 36 + + //提前多少加载数据 + private const val LOAD_PRE = 24 + + //每列条目数 + private const val COLUMN_COUNT = 3 + } + + override var mParentPosition: Int = 0 + override var mOnGameListAddCallback: (Int, List) -> Unit = { _, _ -> } + + //埋点上报的信息,别的板块使用修改这里 + var location: String = "首页" + var blockData: SubjectRecommendEntity? = null + + + private val mApi = RetrofitManager.getInstance().api + + /** + * 加载状态livedata + */ + private val mLoadStateLiveData = MutableLiveData() + + /** + * 加载数据返回livedata + */ + private val mDataLiveData = MutableLiveData>() + + /** + * 时间线节点事件通知livedata + */ + private val mTimePointLiveData = MutableLiveData() + + /** + * 游戏列表滑动到指定position位置通知 + */ + private val mGameListScrollLiveData = MutableLiveData() + + /** + * 时间轴数据 + */ + private var mTimePoint: List? = null + + /** + * 记录过滤掉的游戏 + */ + private val mFilterGameIdSet = mutableSetOf() + + /** + * 设置时间轴数据 + */ + fun setTimePoint(timePoint: List) { + this.mTimePoint = timePoint + mTimePointLiveData.value = timePoint.firstOrNull()?.timeType ?: "" + } + + /** + * 设置时间轴节点游戏总数 + * 用占位和空条目填充数据列表 + */ + fun setDataCount(dataCount: List) { + if (dataCount.isEmpty()) return + val rawCount = COLUMN_COUNT + val gameList = mutableListOf() + var index = 0 + dataCount.forEach { + val (timeType, count) = it + repeat(count) { + gameList.add(GameDataWrapper(index++, timeType = timeType, isPlaceHolder = true)) + } + val limit = rawCount - (count % rawCount) + repeat(limit % rawCount) { + gameList.add(GameDataWrapper(index++, timeType = timeType, isSpace = true)) + } + } + mDataLiveData.value = gameList + } + + /** + * 分页加载方向, + * action: + * left(左滑加载,即手指从右往左滑,也就是下一页)、 + * right(右滑加载,即手指从左往右滑,也就是上一页) + */ + enum class Action(val command: String) { + LEFT("left"), + RIGHT("right"); + + companion object { + fun getActionByCommand(command: String): Action { + return values().find { it.command == command } ?: LEFT + } + } + } + + /** + * 分页加载 + * @param isRefresh 是否是刷新 + * @param action 分页加载方向 + * @param pageId 分页id ,加载下一页为上一页最后一个id,加载上一页为下一页第一个id + * @param timeType 时间轴类型 time_type(recommend:推荐、xxxx-xx-xx:全部(比如:2023-01-01)) + * @param startId //时间轴id,起始点ID(与时间轴对应): + * start_id(【数据从“时间轴起始点”接口获取】,刚进入页面,或者用户自己“点击切换时间轴”的时候必填。 + * 如果是用户在数据列表里左右滑动则自动切换下一列的时候 则不用传,要用户主动切换时间轴的才传) + */ + private fun loadData( + isRefresh: Boolean = false, + action: Action, + pageId: String, + timeType: String, + loadTimeType: String, + startId: String = "", + limit: Int = PAGE_SIZE + ) { + Utils.log( + TAG, "loadData: isRefresh = $isRefresh, action = ${action.command}, pageId = $pageId," + + "timeType = $timeType, loadTimeType = $loadTimeType, startId = $startId, limit = $limit" + ) + if (isRefresh) { + mLoadStateLiveData.postValue(LoadStatus.INIT_LOADING) + } else { + mLoadStateLiveData.postValue(LoadStatus.LIST_LOADING) + } + val filterQuery = UrlFilterUtils.getFilterQuery( + "action", action.command, + "page_id", pageId, + "time_type", timeType, //时间轴类型必填:time_type(recommend:推荐、xxxx-xx-xx:全部(比如:2023-01-01)) + "load_time_type", loadTimeType, //下一个节点的 time_type,根据action 确定是左边的还是右边的 + "start_id", startId + ) + mApi.getHomeServerTestV2( + BuildConfig.VERSION_NAME, + HaloApp.getInstance().channel, + limit, + filterQuery + ).compose(observableToMain()) + .subscribe(object : Response() { + override fun onResponse(response: HomeItemTestV2Entity?) { + mLoadStateLiveData.postValue(LoadStatus.LIST_LOADED) + val data = response?.data ?: return + addGameList( + list = data, + timeType = response.timeType, + pageId = response.pageId, + action = Action.getActionByCommand(response.action), + isRefresh = false, + isSameTimeType = timeType == loadTimeType + ) + } + + override fun onFailure(e: HttpException?) { + mLoadStateLiveData.postValue(LoadStatus.INIT_FAILED) + } + }) + } + + fun getDataListLiveData(): LiveData> { + return mDataLiveData + } + + fun getLoadStateLiveData(): LiveData { + return mLoadStateLiveData + } + + fun getTimePointLiveData(): LiveData { + return mTimePointLiveData + } + + fun getGameListScrollLiveData(): LiveData { + return mGameListScrollLiveData + } + + private fun getCurrentData(): List { + return mDataLiveData.value ?: emptyList() + } + + /** + * 根据当前条目索引,向前和向后查找LOAD_PRE个条目,如果有占位条目,则加载数据 + */ + fun parseActionWithPosition(position: Int) { + val dataList = getCurrentData() + if (dataList.isEmpty()) return + //如果当前条目是占位条目,那就前后查找有数据的借力一下 + val currentItem = dataList[position] + if (currentItem.isPlaceHolder) { + //当前条目是占位条目,查找前后最近的有数据的条目 + for (i in position until dataList.size) { + val item = dataList[i] + if (item.gameData != null && item.index - position <= LOAD_PRE) { + parseActionWithPosition(item.index) + return + } + } + for (i in position downTo 0) { + val item = dataList[i] + if (item.gameData != null && position - item.index <= LOAD_PRE) { + parseActionWithPosition(item.index) + return + } + } + return + } + + //往前查找离position最近的占位条目 + val frontList = getCurrentData().subList(0, position + 1) + val findFrontNearPlaceHolder = frontList.lastOrNull { it.isPlaceHolder } + val frontPlaceHolderPosition = findFrontNearPlaceHolder?.index ?: -1 + if (findFrontNearPlaceHolder != null && position - frontPlaceHolderPosition <= LOAD_PRE) { + //position的前LOAD_PRE个条目列表 + val list = frontList.subList(frontPlaceHolderPosition, position + 1) + //查找holder后面的第一个有数据的条目 + val firstHasDataItem = list.firstOrNull { it.gameData != null } + if (firstHasDataItem != null && firstHasDataItem.index > frontPlaceHolderPosition) { + val pageId = firstHasDataItem.gameData?.id ?: return + loadData( + action = Action.RIGHT, + pageId = pageId, + timeType = firstHasDataItem.timeType, + loadTimeType = findFrontNearPlaceHolder.timeType + ) + } + } + //往后查找离position最近的占位条目 + val behindList = getCurrentData().subList(position, getCurrentData().size) + val findBehindPlaceHolder = behindList.firstOrNull { it.isPlaceHolder } + val behindPlaceHolderPosition = findBehindPlaceHolder?.index ?: -1 + if (findBehindPlaceHolder != null && behindPlaceHolderPosition - position <= LOAD_PRE) { + val list = behindList.subList(0, behindPlaceHolderPosition - position + 1) + //查找holder前面的第一个有数据的条目 + val lastHasDataItem = list.lastOrNull { it.gameData != null } + if (lastHasDataItem != null && lastHasDataItem.index < behindPlaceHolderPosition) { + val pageId = lastHasDataItem.gameData?.id ?: return + loadData( + action = Action.LEFT, + pageId = pageId, + timeType = lastHasDataItem.timeType, + loadTimeType = findBehindPlaceHolder.timeType + ) + } + } + } + + + //添加数据到列表 + fun addGameList( + list: List, + timeType: String, + pageId: String, + action: Action, + firstIndex: Int = 0, //首页推荐列表左侧剩余游戏数 + isRefresh: Boolean = false, + isSameTimeType: Boolean = false //当前时间轴类型与预加载时间轴类型是否相同 + ) { + val currentData = getCurrentData().toMutableList() + val gameList = getFilterGameAndTrimList(currentData, list) + if (currentData.size < gameList.size) { + //出现错误,没有获取到时间轴的游戏数量, 或者数量不正确 + Utils.log(TAG, "出现错误,没有获取到时间轴的游戏数量, 或者数量不正确currentData.size < gameList.size") + return + } + Utils.log( + TAG, "加载分页完成:action = $action, timeType = $timeType, pageId = $pageId, gameList = $gameList" + ) + //给数据分配位置 + var index = firstIndex + if (isRefresh) { + //初始化分配 + while (gameList.isNotEmpty()) { + val gameDataWrapper = gameList.removeFirst() + while (currentData.getOrNull(index)?.isPlaceHolder == false) { + index++ + } + if (index >= currentData.size) { + //出现错误,没有获取到时间轴的游戏数量, 或者数量不正确 + Utils.log(TAG, "isRefresh:出现错误,没有获取到时间轴的游戏数量, 或者数量不正确") + return + } + currentData[index] = gameDataWrapper.also { it.index = index } + } + } else { + //分页加载逻辑 + if (action == Action.RIGHT) { + //如果是向前加载,列表倒序一下 + gameList.reverse() + } + gameList.forEach { + addGameItemWithAction(currentData, it, action, isSameTimeType) + } + } + mDataLiveData.value = currentData.toMutableList() + //如果推荐定位不是第一个,向前加载一页 + if (firstIndex > 0) { + parseActionWithPosition(firstIndex) + } + } + + /** + * 根据条目timeType和action自动插入对应holder空位 + */ + private fun addGameItemWithAction( + dataList: MutableList, + gameDataWrapper: GameDataWrapper, + action: Action, + isSameTimeType: Boolean + ) { + val gameTimeType = gameDataWrapper.timeType + val firstLoadedRecommendPosition = + dataList.indexOfFirst { it.timeType == "recommend" && !it.isPlaceHolder && !it.isSpace } // 推荐类型第一个非占位的位置 + val lastLoadedRecommendPosition = + dataList.indexOfLast { it.timeType == "recommend" && !it.isPlaceHolder && !it.isSpace } // 推荐类型最后一个非占位的位置 + val shouldInsertLoadedRecommendFront = + gameTimeType == "recommend" && isSameTimeType && firstLoadedRecommendPosition > 0 // 推荐定位不是第一个时需要插入到已加载位置的前面 + val shouldInsertLoadedRecommendBehind = + gameTimeType == "recommend" && isSameTimeType && lastLoadedRecommendPosition > 0 // 推荐定位不是第一个时需要插入到已加载位置的后面 + val insertPosition = when (action) { + Action.LEFT -> if (shouldInsertLoadedRecommendBehind) lastLoadedRecommendPosition + 1 else dataList.indexOfFirst { it.timeType == gameTimeType && it.isPlaceHolder } + Action.RIGHT -> if (shouldInsertLoadedRecommendFront) firstLoadedRecommendPosition - 1 else dataList.indexOfLast { it.timeType == gameTimeType && it.isPlaceHolder } + } + val exist = dataList.find { it.timeType == gameTimeType && it.gameData?.id == gameDataWrapper.gameData?.id } + if (exist != null) { + Utils.log(TAG, "重复条目,不添加到列表: $exist") + return + } + if (insertPosition == -1) { + Utils.log(TAG, "出现错误,没有获取到时间轴的游戏数量, 或者数量不正确addGameItemWithAction") + return + } + dataList[insertPosition] = gameDataWrapper.also { it.index = insertPosition } + } + + /** + * 过滤被屏蔽的游戏并清理列表中的占位,以及重新调整空白条目 + * 返回屏蔽后的游戏列表 + */ + private fun getFilterGameAndTrimList( + dataList: MutableList, + list: List + ): MutableList { + val gameList = mutableListOf() + list.forEach { gameData -> + //记录被过滤的游戏id + val filterGameIdList = gameData.games.map { it.id }.toMutableList() + val gameTimeType = gameData.timeType + val size = gameData.games.size + //过滤被屏蔽的游戏 + val filterGames = RegionSettingHelper.filterGame(gameData.games) + val filterGameIds = filterGames.map { it.id } + filterGameIdList.removeAll(filterGameIds) //剩余的就是被过滤的游戏id + mFilterGameIdSet.addAll(filterGameIdList) + //主动过滤含有id的游戏 + filterGames.removeAll { mFilterGameIdSet.contains(it.id) } + //过滤掉了几个游戏,裁剪列表的占位holder,并调整空白条目 + val filterMinusCount = size - filterGames.size + trimDataWithTimeType(dataList, gameTimeType, filterMinusCount) + //注册到外部model,用于下载按钮状态更新 + notifyParentAddGameList(filterGames) + filterGames.forEach { gameEntity -> + gameList.add( + GameDataWrapper( + -1, //还未分配位置的数据 + timeType = gameTimeType, + gameData = gameEntity, + isPlaceHolder = false, + isSpace = false + ) + ) + } + } + return gameList + } + + /** + * 移除指定时间轴的count个数占位条目, + * 调整时间轴的空白条目数量,并把后续条目的索引前移 + */ + private fun trimDataWithTimeType( + dataList: MutableList, + timeType: String, + count: Int + ) { + if (count <= 0) return + Utils.log(TAG, "过滤掉了时间轴:$timeType 上 $count 个游戏。") + val listForTimeType = dataList.filter { it.timeType == timeType }.toMutableList() + if (listForTimeType.isEmpty()) return + //这个timeType还剩多少个holder占位条目 + val holderCount = listForTimeType.count { it.isPlaceHolder } + if (holderCount < count) return + //找到当前timeType的第一个条目索引,后续裁剪都在这个索引之后 + val timeTypeFirstIndex = listForTimeType.first().index + //先清除这个timeType的空白条目,后续裁剪holder后再补回来 + listForTimeType.removeAll { it.isSpace } + //删除指定个数holder + val delListForTimeType = listForTimeType.filter { it.isPlaceHolder }.take(count) + listForTimeType.removeAll(delListForTimeType) + //补充空白条目 + val rawCount = COLUMN_COUNT + val listForTimeTypeCount = listForTimeType.size + val spaceCount = rawCount - (listForTimeTypeCount % rawCount) + var index = timeTypeFirstIndex + listForTimeTypeCount + repeat(spaceCount % rawCount) { + listForTimeType.add(GameDataWrapper(index++, timeType = timeType, isSpace = true)) + } + //替换原数组中的timeType列表 + dataList.removeAll { it.timeType == timeType } + dataList.addAll(timeTypeFirstIndex, listForTimeType) + //重新给当前timeType以及后面的条目设置索引 + for (i in timeTypeFirstIndex until dataList.size) { + dataList[i].index = i + } + } + + /** + * 选中时间轴,如果列表内有相关数据则滑动到指定位置 + * @param timeType 时间轴类型 time_type(recommend:推荐、xxxx-xx-xx:全部(比如:2023-01-01)) + * 还需要做时间轴联动,列表滑动,时间轴也要跟着选中对应的 + */ + fun onTimePointSelected(timeType: String) { + mTimePointLiveData.postValue(timeType) + } + + /** + * 点击时间轴, 获取时间节点数据,或者移动列表到指定位置 + */ + fun onTimePointClick(timeType: String) { + val firstItemWithTimeType = getCurrentData().find { it.timeType == timeType } ?: return + val index = firstItemWithTimeType.index + if (index != -1) { + mGameListScrollLiveData.postValue(index) + mLoadStateLiveData.postValue(LoadStatus.INIT_LOADED) + } + val startId = mTimePoint?.find { it.timeType == timeType }?.id ?: "" + if (firstItemWithTimeType.isPlaceHolder) { + //加载当前页数据 , 不足limit 则用下一页补足 + val timeTypeList = mTimePoint?.map { it.timeType } + val currentTimeTypeIndex = timeTypeList?.indexOf(timeType) + var loadTimeType = "" + if (currentTimeTypeIndex != null && currentTimeTypeIndex != -1) { + val nextTimeType = timeTypeList.getOrNull(currentTimeTypeIndex + 1) + loadTimeType = nextTimeType ?: timeType + } + loadData( + isRefresh = false, + action = Action.LEFT, + pageId = startId, + timeType = timeType, + loadTimeType = loadTimeType, + startId = startId + ) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageExposureExt.kt b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageExposureExt.kt new file mode 100644 index 0000000000..cb9779d95a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageExposureExt.kt @@ -0,0 +1,61 @@ +package com.gh.gamecenter.home.custom + +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem + +fun createExposureEvent( + game: GameEntity?, + source: List, + base: List, + childPosition: Int, + position: Int +): ExposureEvent { + game?.sequence = childPosition + game?.outerSequence = position + val event = ExposureEvent.createEventWithSourceConcat( + gameEntity = game, + basicSource = base, + source = source + ) + game?.exposureEvent = event + return event +} + +fun fillExposureInSubjectCollection(item: CustomSubjectCollectionItem, base: List) { + + val eventList = arrayListOf() + runOnIoThread(true) { + item.data.data.forEachIndexed { index, customSubject -> + val source = if (item.isSubjectCollection) { + listOf( + ExposureSource("专题合集", "${item.data.name}+${item.componentStyle}+${item.data.id}"), + ExposureSource( + "专题", + "${customSubject.title}+${customSubject.styleChinese}+${customSubject.id}" + ) + ) + } else { + listOf( + ExposureSource("游戏单合集", "${item.data.name}+${item.componentStyle}+${item.data.id}"), + ExposureSource("游戏单", "${customSubject.title}+${customSubject.id}") + ) + } + customSubject.games.forEach { game -> + game.isAdData = customSubject.adIconActive + val event = createExposureEvent( + game, + source, + base, + index, + item.componentPosition + ) + eventList.add(event) + } + } + } + item.exposureEventList = eventList + +} diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageFragment.kt b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageFragment.kt new file mode 100644 index 0000000000..d4b08f0ba0 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageFragment.kt @@ -0,0 +1,726 @@ +package com.gh.gamecenter.home.custom + +import android.graphics.Color +import android.os.Build +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnScrollListener +import com.gh.common.exposure.ExposureListener +import com.gh.common.exposure.ExposureManager +import com.gh.common.iinterface.ISearchToolbarTab +import com.gh.common.iinterface.ISmartRefresh +import com.gh.common.iinterface.ISmartRefreshContent +import com.gh.common.iinterface.ISuperiorChain +import com.gh.common.prioritychain.CustomFloatingWindowHandler +import com.gh.common.prioritychain.PriorityChain +import com.gh.common.prioritychain.PullDownPushHandler +import com.gh.common.util.DefaultSearchHintHelper +import com.gh.common.util.DialogUtils +import com.gh.common.util.DirectUtils +import com.gh.common.util.NewFlatLogUtils +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus +import com.gh.download.DownloadManager +import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.GlobalActivityManager +import com.gh.gamecenter.common.base.fragment.LazyFragment +import com.gh.gamecenter.common.baselist.LoadStatus +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.eventbus.EBReuse +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.OffsetLinearLayoutManager +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.iinterface.IScrollable +import com.gh.gamecenter.core.utils.MD5Utils +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.databinding.FragmentCustomBinding +import com.gh.gamecenter.discovery.DiscoveryActivity +import com.gh.gamecenter.discovery.interestedgame.InterestedGameActivity +import com.gh.gamecenter.entity.BottomTab +import com.gh.gamecenter.entity.PullDownPush +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.ExposureType +import com.gh.gamecenter.game.commoncollection.detail.CustomCommonCollectionDetailActivity +import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity +import com.gh.gamecenter.home.PageConfigure +import com.gh.gamecenter.home.custom.adapter.CustomPageAdapter +import com.gh.gamecenter.home.custom.floatview.CustomFloatViewAnimationHelper +import com.gh.gamecenter.home.video.ScrollCalculatorHelper +import com.gh.gamecenter.livedata.EventObserver +import com.gh.gamecenter.wrapper.MainWrapperViewModel +import com.gh.gamecenter.wrapper.SearchToolbarTabWrapperFragment +import com.gh.gamecenter.wrapper.SearchToolbarTabWrapperViewModel +import com.halo.assistant.HaloApp +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode + +class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable { + + private var searchToolbarTabWrapperViewModel: SearchToolbarTabWrapperViewModel? = null + private var mainWrapperViewModel: MainWrapperViewModel? = null + + private val viewModel by viewModels() + + private lateinit var binding: FragmentCustomBinding + + private lateinit var adapter: CustomPageAdapter + + private lateinit var layoutManager: OffsetLinearLayoutManager + + private lateinit var scrollCalculatorHelper: ScrollCalculatorHelper + + private val priorityChain by lazy { PriorityChain() } + private var superiorChain: ISuperiorChain? = null + + private val floatViewManager = CustomFloatViewAnimationHelper() + + private var customPageId = "" + private var customPageName = "" + private var bottomTabId = "" + private var bottomTabName = "" + private var tabIndex = -1 + + private lateinit var pageLocation: PageLocation + + private var pullDownPushHandler: PullDownPushHandler? = null + + private val isInSearchToolbarTabWrapperPage: Boolean + get() = parentFragment is SearchToolbarTabWrapperFragment + + private val dataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + adapter.notifyDownload(downloadEntity) + + if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { + showUnzipFailureDialog(downloadEntity) + } + } + + override fun onDataInit(downloadEntity: DownloadEntity) { + adapter.notifyDownload(downloadEntity) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setNavigationTitle(arguments?.getString(EntranceConsts.KEY_NAVIGATION_TITLE, "") ?: "") + customPageId = arguments?.getString(EntranceConsts.KEY_CUSTOM_PAGE_ID, "") ?: "" + customPageName = arguments?.getString(EntranceConsts.KEY_CUSTOM_PAGE_NAME, "") ?: "" + bottomTabId = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_ID, "") ?: "" + bottomTabName = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_NAME, "") ?: "" + tabIndex = arguments?.getInt(EntranceConsts.KEY_TAB_INDEX, -1) ?: -1 + val tabName = arguments?.getString(EntranceConsts.KEY_TAB_NAME, "") ?: "" + val multiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: "" + val multiTabNavName = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, "") ?: "" + val noTabLinkId = arguments?.getParcelable(LinkEntity::class.java.simpleName)?.link ?: "" + + if (isInSearchToolbarTabWrapperPage) { + searchToolbarTabWrapperViewModel = + viewModelProviderFromParent( + SearchToolbarTabWrapperViewModel.Factory(multiTabNavId, noTabLinkId), + multiTabNavId + ) + } + if (bottomTabId.isNotEmpty()) { + mainWrapperViewModel = viewModelProviderFromParent(MainWrapperViewModel.Factory(HaloApp.getInstance())) + } + + val exposureSourceList = arrayListOf().apply { + arguments?.getParcelable(EntranceConsts.KEY_EXPOSURE_SOURCE)?.let { + add(it) + } + arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST)?.let { + addAll(it) + } + } + // todo transparentBackground的值待定 + val transparentBackground = true + val pageConfigure = + PageConfigure( + customPageId, + 0, + mEntrance, + transparentBackground, + exposureSourceList, + isInSearchToolbarTabWrapperPage + ) + pageLocation = PageLocation( + bottomTabName, + multiTabNavName, + multiTabNavId, + tabIndex, + tabName, + customPageName, + customPageId + ) + + viewModel.init(pageConfigure, searchToolbarTabWrapperViewModel, pageLocation) + } + + override fun getRealLayoutId() = R.layout.fragment_custom + + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() + if (!mIsFromMainWrapper && !mIsFromTabWrapper) { + initMenu(R.menu.menu_download) + } + binding.reuseSearchBar.root.setPadding(16F.dip2px(), 8F.dip2px(), 16F.dip2px(), 8F.dip2px()) + mainWrapperViewModel?.bottomDoubleTabAction?.observe(viewLifecycleOwner) { + if (isSupportVisible && it.id == bottomTabId) { + scrollToTop() + } + } + + with(viewModel) { + dataList.observe(viewLifecycleOwner) { + adapter.submitList(it) + } + + loadStatus.observe(viewLifecycleOwner) { (status, isPullToRefresh) -> + binding.gameRefresh.isRefreshing = false + + if (!isPullToRefresh) { + binding.gameList.goneIf(status == LoadStatus.INIT_LOADING || status == LoadStatus.INIT_FAILED || status == LoadStatus.INIT_EMPTY) + binding.reuseNoConnection.root.goneIf(status != LoadStatus.INIT_FAILED) + if (status == LoadStatus.INIT_LOADING) { + binding.reuseLoading.root.goneIf(false) + binding.root.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) + } else { + binding.reuseLoading.root.goneIf(true) + binding.root.setBackgroundColor(Color.TRANSPARENT) + } + binding.reuseNoData.root.goneIf(status != LoadStatus.INIT_EMPTY) + } else { + if (status == LoadStatus.INIT_LOADED) { + binding.gameList.visibleIf(true) + binding.reuseNoConnection.root.goneIf(true) + binding.reuseNoData.root.goneIf(true) + } + } + adapter.setLoadStatus(status) + if (status == LoadStatus.INIT_LOADED) { + AppExecutor.uiExecutor.executeWithDelay({ + scroll() + scrollCalculatorHelper.onScrollStateChanged(RecyclerView.SCROLL_STATE_IDLE) + }, 100) + } + if (status != LoadStatus.INIT_LOADING) { + (parentFragment as? ISmartRefresh)?.finishRefresh() + } + } + + changAppBarColorAction.observe(viewLifecycleOwner, EventObserver { + (parentFragment as? ISearchToolbarTab)?.changeAppBarColor(it, customPageId) + }) + + pullDownPushData.observe(viewLifecycleOwner) { + setSwipeRefreshEnabled(it == null) + setPullDownPush(it) + } + + pageSwitchData.observe(viewLifecycleOwner) { + // 当前自定义页面为二级页面时显示搜索栏 + if (!requireArguments().getBoolean(EntranceConsts.KEY_IS_HOME, false) && it.topSearch.isNotEmpty()) { + binding.reuseSearchBar.root.visibility = View.VISIBLE + binding.reuseSearchBar.etSearch.visibility = View.GONE + binding.reuseSearchBar.searchTv.visibility = View.VISIBLE + setSearchHint(it.topSearch) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + binding.reuseSearchBar.etSearch.focusable = View.NOT_FOCUSABLE + } + listOf(binding.reuseSearchBar.root, binding.reuseSearchBar.searchTv).forEach { view -> + view.setOnClickListener { _ -> + DirectUtils.directToSearch( + requireContext(), + it.topSearch, + binding.reuseSearchBar.searchTv.hint.toString(), + "搜索栏", + "自定义页面", + bottomTabName, + customPageId = customPageId, + customPageName = customPageName + ) + } + } + } + // 当前自定义页面为 底部Tab(非多Tab导航页) 关联的页面时更新搜索类型 + if (requireArguments().getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") + .isEmpty() && it.topSearch.isNotEmpty() + ) { + (parentFragment as? ISearchToolbarTab)?.setSearchStyle(BottomTab.SearchStyle(searchType = it.topSearch)) + } + } + + gameDetailDestination.observe(viewLifecycleOwner, EventObserver { (trackData, entrance, game) -> + if (game.isQQMiniGame()) { + GlobalActivityManager.currentActivity?.let { + NewFlatLogUtils.logQGameClick(game.qqMiniGameAppId, game.name) + DirectUtils.directToQGameById(it, game.qqMiniGameAppId) + } + } else { + GameDetailActivity.startGameDetailActivity( + requireContext(), + game.id, + entrance, + -1, + traceEvent = game.exposureEvent, + customPageTrackData = trackData + ) + } + + }) + + subjectDestination.observe(viewLifecycleOwner, EventObserver { subject -> + DirectUtils.directToSubject( + requireContext(), subject.id ?: "", subject.name, "自定义页面", null, subject.isQQColumn + ) + }) + + subjectDestinationWithCustom.observe(viewLifecycleOwner, EventObserver { (subject, isQqMini) -> + DirectUtils.directToSubject( + requireContext(), subject.id, subject.title, "自定义页面", null, isQqMini + ) + }) + + subjectCollectionDestination.observe(viewLifecycleOwner, EventObserver { (item, childPosition, subject) -> + val clickEvent = if (subject != null) { + ExposureEvent.createEventWithSourceConcat( + GameEntity(outerSequence = childPosition), pageConfigure.exposureSourceList, listOf( + ExposureSource("专题合集", "${item.data.name}+${item.componentStyle}+${item.data.id}"), + ExposureSource("专题", "${subject.title}+${subject.styleChinese}+${subject.id}") + ), null, ExposureType.CLICK + ) + } else null + clickEvent?.let { ExposureManager.log(it) } + DirectUtils.directToColumnCollection( + requireContext(), + item.data.id, + childPosition, + CustomPageTracker.buildSubjectCollectionEntrance(item), + subject?.title ?: "", + style = item.data.style, + exposureEvent = clickEvent, showSubjectTab = true + ) + }) + + gameListDetailDestination.observe(viewLifecycleOwner, EventObserver { (item, childPosition, subject) -> + val clickEvent = ExposureEvent.createEventWithSourceConcat( + GameEntity(outerSequence = childPosition), pageConfigure.exposureSourceList, listOf( + ExposureSource("游戏单合集", "${item.data.name}+${item.componentStyle}+${item.data.id}"), + ExposureSource("游戏单", "${subject.title}+${subject.id}") + ), null, ExposureType.CLICK + ) + ExposureManager.log(clickEvent) + DirectUtils.directToGameCollectionDetail( + requireContext(), + subject.id, + "", + "游戏单合集", + clickEvent + ) + }) + + userDestination.observe(viewLifecycleOwner, EventObserver { (userId, entrance, path) -> + DirectUtils.directToHomeActivity(requireContext(), userId, 0, entrance, path) + }) + + interestedGameDestination.observe(viewLifecycleOwner, EventObserver { + requireContext().startActivity( + InterestedGameActivity.getIntent( + requireContext(), + "${pageConfigure.entrance}-发现页卡片" + ) + ) + + }) + + discoverDestination.observe(viewLifecycleOwner, EventObserver { + requireContext().startActivity( + DiscoveryActivity.getIntent( + requireContext(), + "${pageConfigure.entrance}-发现页卡片" + ) + ) + }) + + linkDestination.observe(viewLifecycleOwner, EventObserver { data -> + DirectUtils.directToLinkPage(requireContext(), data.first, "自定义页面", "", data.second) + }) + + amwayDestination.observe(viewLifecycleOwner, EventObserver { + DirectUtils.directToAmway(requireContext(), null, "(游戏-专题:安利墙-全部)", "") + }) + + badgeWallDestination.observe(viewLifecycleOwner, EventObserver { + DirectUtils.directToBadgeWall( + requireContext(), + it.user.id, + it.user.name, + it.user.icon + ) + }) + + gameDetailDestinationOnAmway.observe( + viewLifecycleOwner, + EventObserver { (trackData, gameId, exposureEvent) -> + GameDetailActivity.startGameDetailActivity( + requireContext(), + gameId = gameId, + entrance = "自定义页面", + defaultTab = -1, + traceEvent = exposureEvent, + customPageTrackData = trackData + ) + }) + + ratingReplyDestination.observe(viewLifecycleOwner, EventObserver { (gameId, commentId) -> + val entrance = "安利墙" + val exposureSource = + arrayListOf( + ExposureSource("自定义页面", "${pageLocation.pageName}+${pageLocation.pageId}"), + ExposureSource("安利墙") + ).toJson() + val intent = RatingReplyActivity.getIntent( + context = requireContext(), + gameId = gameId, + commentId = commentId, + showKeyboardIfReplyListIsEmpty = false, + exposureSource = exposureSource, + entrance = entrance, + path = "" + ) + requireContext().startActivity(intent) + }) + + commonCollectionDestination.observe(viewLifecycleOwner, EventObserver { (commonCollection, collectionId) -> + val intent = CustomCommonCollectionDetailActivity.getIntent( + requireContext(), + collectionId, + commonCollection.layout, + "自定义页面", + ) + startActivity(intent) + }) + + gameListCollectionDestination.observe(viewLifecycleOwner, EventObserver { + DirectUtils.directToGameCollectionListDetail( + requireContext(), + it.data.id, + it.data.name, + it.data.explain, + "自定义页面" + ) + }) + + gameListSquareDestination.observe(viewLifecycleOwner, EventObserver { + DirectUtils.directToGameCollectionSquare( + requireContext(), + "版块内容列表", + "", + collectionName = it.data.name, + collectionId = it.data.id + ) + }) + } + + searchToolbarTabWrapperViewModel?.backgroundAlpha?.observe(viewLifecycleOwner) { + val viewHolder = binding.gameList.findViewHolderForAdapterPosition(0) + if (viewHolder is OnCustomPageRefreshStateListener) { + viewHolder.onBackgroundAlphaChanged(it) + } + } + + SensorsBridge.trackViewCustomPage( + pageLocation.bottomTab, + pageLocation.severalTabPageId, + pageLocation.severalTabPageName, + pageLocation.tabPosition, + pageLocation.tabContent, + pageLocation.pageId, + pageLocation.pageName + ) + + viewModel.loadData() + } + + private fun setSearchHint(searchType: String) { + when (searchType) { + BottomTab.SearchStyle.TYPE_HALO_GAME -> DefaultSearchHintHelper.setSearchHint(binding.reuseSearchBar.searchTv) + BottomTab.SearchStyle.TYPE_QQ_MINI_GAME -> binding.reuseSearchBar.searchTv.hint = "请输入小游戏关键词" + BottomTab.SearchStyle.TYPE_BBS -> binding.reuseSearchBar.searchTv.hint = "搜索论坛内容、用户" + } + } + + override fun initRealView() { + super.initRealView() + + binding = FragmentCustomBinding.bind(mCachedView) + + buildPriorityChain() + + scrollCalculatorHelper = ScrollCalculatorHelper(binding.gameList, R.id.autoVideoView, 0) + + adapter = CustomPageAdapter( + viewModel, + viewLifecycleOwner, + scrollCalculatorHelper, + ) + layoutManager = OffsetLinearLayoutManager(requireContext()) + binding.gameList.itemAnimator = null + binding.gameList.layoutManager = layoutManager + binding.gameList.adapter = adapter + + var listScrollHeight = 0 + binding.gameList.addOnScrollListener(object : OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + floatViewManager.onScrollChanged(newState != RecyclerView.SCROLL_STATE_IDLE) + scrollCalculatorHelper.onScrollStateChanged(newState) + } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + listScrollHeight += dy + onScrollChanged() + scroll() + } + }) + + val exposureListener = ExposureListener(this, adapter) + binding.gameList.addOnScrollListener(exposureListener) + + binding.reuseNoConnection.connectionReloadTv.setOnClickListener { + viewModel.loadFirst(false) + } + + binding.gameRefresh.setOnRefreshListener { + onRefresh() + } + } + + private fun buildPriorityChain() { + pullDownPushHandler = PullDownPushHandler(5) + val floatingWindowHandler = CustomFloatingWindowHandler(10) + + priorityChain.addHandler(pullDownPushHandler!!) + priorityChain.addHandler(floatingWindowHandler) + + viewModel.floatingWindows.observe(viewLifecycleOwner, EventObserver { + floatingWindowHandler.setData(it) + }) + + floatingWindowHandler.showFloatingAction.observe(viewLifecycleOwner, EventObserver { + binding.floatingView.setData(it, pageLocation, viewModel.pageConfigure.exposureSourceList) + floatViewManager.show(binding.floatingView) + }) + + binding.floatingView.setExpandListener { entity, hasAnimation -> + val dialog = CustomWelcomeDialogFragment.getInstance(entity, true, this, hasAnimation, pageLocation) + dialog.setDismissListener { + floatViewManager.resume() + } + dialog.show(childFragmentManager, CustomWelcomeDialogFragment.TAG) + } + + if (superiorChain != null) { + superiorChain?.registerInferiorChain(priorityChain) + } else { + priorityChain.start() + } + } + + private fun onScrollChanged(isDarkModeChanged: Boolean = false) { + if (parentFragment is ISearchToolbarTab && + (tabIndex == (parentFragment as ISearchToolbarTab).getCurrentTabIndex() || tabIndex == -1) && + ::layoutManager.isInitialized + ) { + val bannerViewHeight = layoutManager.heightMap[0] ?: 0 + (parentFragment as ISearchToolbarTab).onScrollChanged( + bannerViewHeight, + binding.gameList.computeVerticalScrollOffset(), + isDarkModeChanged + ) + } + } + + private fun scroll() { + val firstVisibleItem = layoutManager.findFirstVisibleItemPosition() + val lastVisibleItem = layoutManager.findLastVisibleItemPosition() + scrollCalculatorHelper.onScroll(firstVisibleItem, lastVisibleItem) + } + + override fun onFragmentPause() { + super.onFragmentPause() + pauseVideo() + + if (::adapter.isInitialized) { + DownloadManager.getInstance().removeObserver(dataWatcher) + } + + superiorChain?.unregisterInferiorChain(priorityChain) + } + + override fun onFragmentResume() { + super.onFragmentResume() + resumeVideo() + if (::adapter.isInitialized) { + DownloadManager.getInstance().addObserver(dataWatcher) + } + viewModel.pullDownPushData.value?.let { setPullDownPush(it) } + + superiorChain?.registerInferiorChain(priorityChain) + } + + private fun setPullDownPush(pullDownPush: PullDownPush?) { + (parentFragment as? ISmartRefresh)?.setPullDownPush(pullDownPush, pullDownPushHandler) + } + + override fun onDestroy() { + super.onDestroy() + if (::scrollCalculatorHelper.isInitialized) { + scrollCalculatorHelper.release() + } + } + + //下载被删除事件 + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(status: EBDownloadStatus) { + if (::adapter.isInitialized && "delete" == status.status) { + adapter.notifyDownloadDeleted(status) + } + } + + //安装、卸载事件 + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(busFour: EBPackage) { + if (::adapter.isInitialized) { + adapter.notifyInstalled(busFour) + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(reuse: EBReuse) { + if (::adapter.isInitialized && "Refresh" == reuse.type) { + setSearchHint(viewModel.pageSwitchData.value?.topSearch ?: "") + adapter.notifyDataSetChanged() + } + } + + fun setSuperiorChain(superiorChain: ISuperiorChain?): CustomPageFragment { + this.superiorChain = superiorChain + return this + } + + private fun pauseVideo() { + val currentPlayer = scrollCalculatorHelper.currentPlayer + if (currentPlayer != null) { + currentPlayer.resetDetailMask() + currentPlayer.onVideoPause() + val currentPosition = currentPlayer.getCurrentPosition() + + val videoUrl = currentPlayer.getUrl() + if (videoUrl.isNotEmpty()) { + ScrollCalculatorHelper.savePlaySchedule(MD5Utils.getContentMD5(videoUrl), currentPosition) + } + } + } + + private fun resumeVideo() { + val currentPlayer = scrollCalculatorHelper.currentPlayer + if (currentPlayer != null) { + val videoUrl = currentPlayer.getUrl() + if (videoUrl.isNotEmpty()) { + val position = ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(videoUrl)) + //这里必须要延迟操作,否则会白屏 + mBaseHandler.postDelayed({ + if (position != 0L) { + scrollCalculatorHelper.currentPlayer?.seekTo(position) + scrollCalculatorHelper.currentPlayer?.onVideoResume(false) + val topVideoVoiceStatus = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) + if (topVideoVoiceStatus) { + scrollCalculatorHelper.currentPlayer?.mute() + } else { + scrollCalculatorHelper.currentPlayer?.unMute() + } + } else { + scrollCalculatorHelper.currentPlayer?.release() + } + + }, 50) + } + } + } + + fun showUnzipFailureDialog(downloadEntity: DownloadEntity) { + val data = adapter.getGameEntityByPackage(downloadEntity.packageName) + for (gameAndPosition in data) { + val targetView = layoutManager.findViewByPosition(gameAndPosition.position) + if (targetView != null) { + if (targetView is RecyclerView) { + // todo 如果时竖向专题该怎么判断? + } else { + DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) + return + } + } + } + } + + override fun onDarkModeChanged() { + super.onDarkModeChanged() + if (::adapter.isInitialized && ::binding.isInitialized) { + binding.gameList.tryToClearRecycler() + binding.gameList.recycledViewPool.clear() + + adapter.notifyItemRangeChanged(0, adapter.itemCount) + binding.gameList.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) + binding.reuseSearchBar.searchTv.setHintTextColor(R.color.text_instance.toColor(requireContext())) + onScrollChanged(true) + } + } + + override fun setScrollEnabled(isScrollEnabled: Boolean) { + if (::layoutManager.isInitialized) { + layoutManager.isScrollVerticallyEnabled = isScrollEnabled + } + } + + override fun onRefresh() { + viewModel.onRefresh() + viewModel.loadFirst(true, true) + } + + override fun setSwipeRefreshEnabled(isSwipeRefreshEnabled: Boolean) { + if (::binding.isInitialized) { + binding.gameRefresh.isEnabled = isSwipeRefreshEnabled + } + } + + override fun scrollToTop() { + if (::binding.isInitialized) { + binding.gameList.stopScroll() + binding.gameList.scrollToPosition(0) + } + } + + fun getFloatingViewY(): Int = if (::binding.isInitialized) { + val locations = IntArray(2) + binding.floatingView.getLocationInWindow(locations) + locations[1] + } else { + 0 + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageTracker.kt b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageTracker.kt new file mode 100644 index 0000000000..6d8032983e --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageTracker.kt @@ -0,0 +1,123 @@ +package com.gh.gamecenter.home.custom + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.utils.SensorsBridge.KEY_GAME_COLLECT_ID +import com.gh.gamecenter.common.utils.SensorsBridge.KEY_GAME_COLLECT_TITLE +import com.gh.gamecenter.common.utils.SensorsBridge.KEY_GAME_ID +import com.gh.gamecenter.common.utils.SensorsBridge.KEY_GAME_NAME +import com.gh.gamecenter.common.utils.SensorsBridge.KEY_LINK_ID +import com.gh.gamecenter.common.utils.SensorsBridge.KEY_LINK_TEXT +import com.gh.gamecenter.common.utils.SensorsBridge.KEY_LINK_TYPE +import com.gh.gamecenter.common.utils.SensorsBridge.KEY_MONGOLD_ID +import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem + +class CustomPageTracker(val pageLocation: PageLocation) { + + fun trackGameListCollectionClickWithMore(item: CustomSubjectCollectionItem, link: LinkEntity) { + val otherParams = HashMap().apply { + put(KEY_LINK_ID, link.link ?: "") + put(KEY_LINK_TYPE, link.type ?: "") + put(KEY_LINK_TEXT, link.text ?: "") + } + trackGameListCollectionClick(item, "更多", otherParams) + } + + fun trackGameListCollectionClickWithGameListSquare(item: CustomSubjectCollectionItem) { + trackGameListCollectionClick(item, "游戏单广场") + } + + fun trackGameListCollectionClickWithUser(item: CustomSubjectCollectionItem, userId: String) { + val otherParams = hashMapOf( + KEY_MONGOLD_ID to userId + ) + trackGameListCollectionClick(item, "个人主页", otherParams) + } + + fun trackGameListCollectionClickWithGame(item: CustomSubjectCollectionItem, gameId: String, gameName: String?) { + val otherParams = hashMapOf( + KEY_GAME_ID to gameId, + KEY_GAME_NAME to (gameName ?: "") + ) + trackGameListCollectionClick(item, "游戏", otherParams) + } + + fun trackGameListCollectionClickWithGameCollection( + item: CustomSubjectCollectionItem, + gameCollectId: String, + gameCollectTitle: String + + ) { + val otherParams = hashMapOf( + KEY_GAME_COLLECT_ID to gameCollectId, + KEY_GAME_COLLECT_TITLE to gameCollectTitle + ) + trackGameListCollectionClick(item, "游戏单", otherParams) + } + + fun trackGameListCollectionClick( + item: CustomSubjectCollectionItem, + text: String, + otherParams: HashMap = hashMapOf() + ) { + SensorsBridge.trackGameListCollectionClick( + LOCATION_CUSTOM_PAGE, + item.data.id, + item.data.name, + text, + pageLocation.bottomTab, + pageLocation.severalTabPageId, + pageLocation.severalTabPageName, + pageLocation.tabPosition, + pageLocation.tabContent, + pageLocation.pageId, + pageLocation.pageName, + item.componentStyle, + otherParams + ) + } + + fun trackColumnCollectionClick( + item: CustomSubjectCollectionItem, + subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity?, + text: String + ) { + SensorsBridge.trackColumnCollectionClick( + LOCATION_CUSTOM_PAGE, + "", + "", + item.data.name, + item.data.id, + item.position, + subject?.title ?: "", + subject?.id ?: "", + text, + item.componentStyle, + pageLocation.bottomTab, + pageLocation.severalTabPageId, + pageLocation.severalTabPageName, + pageLocation.tabPosition, + pageLocation.tabContent, + pageLocation.pageId, + pageLocation.pageName + ) + } + + companion object { + + const val LOCATION_CUSTOM_PAGE = "自定义页面" + + fun buildGameSubjectEntrance(subjectName: String, childPosition: Int) = + "(游戏-专题:${subjectName}-列表[${childPosition + 1}])" + + fun buildSubjectCollectionEntrance(item: CustomSubjectCollectionItem): String { + return if (item.isSubjectCollection) { + "专题合集-${item.componentStyle}" + } else { + "" + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageViewModel.kt b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageViewModel.kt new file mode 100644 index 0000000000..7264ed6d4f --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageViewModel.kt @@ -0,0 +1,724 @@ +package com.gh.gamecenter.home.custom + +import android.app.Application +import androidx.collection.ArrayMap +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Observer +import com.gh.common.util.GameUtils +import com.gh.common.util.NewFlatLogUtils +import com.gh.common.util.NewLogUtils +import com.gh.gamecenter.common.baselist.LoadStatus +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.common.utils.observableToMain +import com.gh.gamecenter.core.utils.RandomUtils +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.entity.HomeSubSlide +import com.gh.gamecenter.entity.PullDownPush +import com.gh.gamecenter.entity.RatingComment +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.floatingwindow.FloatingWindowEntity +import com.gh.gamecenter.home.PageConfigure +import com.gh.gamecenter.home.custom.GamePositionAndPackageHelper.Companion.putGameWithPosition +import com.gh.gamecenter.home.custom.eventlistener.OnCustomPageEventListener +import com.gh.gamecenter.home.custom.model.* +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_COLLECTION_STYLE_REFRESH_ICONS_4_2 +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_LIST +import com.gh.gamecenter.livedata.Event +import com.gh.gamecenter.wrapper.SearchToolbarTabWrapperViewModel +import com.lightgame.utils.Utils +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import retrofit2.HttpException +import kotlin.collections.set + +class CustomPageViewModel( + application: Application +) : AndroidViewModel(application), OnCustomPageEventListener { + + private val compositeDisposable = CompositeDisposable() + + val newGameTestUserCase = CustomNewGameTestUseCase().apply { + setOnGameListAddCallback { position, gameEntities -> + gameEntities.forEach { + gamePositionAndPackageHelper.putGamePositionAndPackage(it, position) + } + } + } + + private val repository = CustomPageRepository.newInstance() + + val gamePositionAndPackageHelper = GamePositionAndPackageHelper() + + private var _pageConfigure = PageConfigure() + val pageConfigure: PageConfigure + get() = _pageConfigure + + private val _customPageData = MutableLiveData() + val customPageData: LiveData = _customPageData + + private val _dataList = MutableLiveData>() + val dataList: LiveData> = _dataList + + private val _loadStatus = MutableLiveData>() + val loadStatus: LiveData> = _loadStatus + + var refreshCount = 0 + + private val recentGamesObserver = Observer(::notifyItemChanged) + + private val pluginObserver = Observer(::notifyItemChanged) + + private val discoverObserver = Observer(::notifyItemChanged) + + val pullDownPushData = MutableLiveData() + val pageSwitchData = MutableLiveData() + + private var searchToolbarTabWrapperViewModel: SearchToolbarTabWrapperViewModel? = null + + private val subjectChangedMap: ArrayMap> = ArrayMap() + + var slideDiscoveryGamesPage = -1 + + private lateinit var _pageTracker: CustomPageTracker + + val pageTracker: CustomPageTracker + get() = _pageTracker + + val pageLocation: PageLocation + get() = pageTracker.pageLocation + + init { + repository.recentGamesItemLiveData.observeForever(recentGamesObserver) + repository.customPluginItemLiveData.observeForever(pluginObserver) + repository.customDiscoverItemLiveData.observeForever(discoverObserver) + } + + fun init( + pageConfigure: PageConfigure, + searchToolbarTabWrapperViewModel: SearchToolbarTabWrapperViewModel?, + pageLocation: PageLocation + ) { + this.searchToolbarTabWrapperViewModel = searchToolbarTabWrapperViewModel + _pageConfigure = pageConfigure + _pageTracker = CustomPageTracker(pageLocation) + } + + + fun loadData() { + loadFirst(false) + loadSuspendedWindow() + } + + /** + * 由于悬浮窗每次弹出都需要计数,这里当成一次性事件处理 + */ + private val _floatingWindows = MutableLiveData>>() + val floatingWindows: LiveData>> = _floatingWindows + + private fun loadSuspendedWindow() { + if (floatingWindows.value != null) { + return + } + repository.loadSuspendedWindow(pageConfigure.pageId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse>() { + override fun onSuccess(data: List) { + _floatingWindows.value = Event(data) + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + _floatingWindows.value = Event(emptyList()) + } + + }).addDisposable() + } + + fun loadFirst(isPullToRefresh: Boolean, forceLoad: Boolean = false) { + _loadStatus.value = LoadStatus.INIT_LOADING to isPullToRefresh + repository.loadFirstCustomPageData(pageConfigure.pageId, forceLoad) + .map { (custom, list) -> + Triple(custom, list, getPositionAndPackageMap(list)) + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse, HashMap>>() { + override fun onSuccess(data: Triple, HashMap>) { + val (custom, list, gameWithPositionMap) = data + + val isSlideBackgroundColorEnable = + custom.customsComponents.firstOrNull()?.linkCommonCollection?.layout == CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_BANNER + searchToolbarTabWrapperViewModel?.enableSlideBackgroundColor( + pageConfigure.pageId, + isSlideBackgroundColorEnable + ) + pullDownPushData.postValue(custom.linkPullDownPush?.apply { + this.isSlideBackgroundColorEnable = isSlideBackgroundColorEnable + this.customPageId = pageLocation.pageId + this.customPageName = pageLocation.pageName + this.game?.customPageTrackData = CustomPageTrackData(pageLocation, moduleType = "下拉推送") + }) + + gamePositionAndPackageHelper.putAll(gameWithPositionMap) + pageConfigure.setCustomPageExposureSource("${custom.title}+${pageConfigure.pageId}") + + _customPageData.value = custom + pageSwitchData.postValue(custom.pageSwitch) + _dataList.value = list + _loadStatus.value = if (list.isEmpty()) { + LoadStatus.INIT_EMPTY to isPullToRefresh + } else { + LoadStatus.INIT_LOADED to isPullToRefresh + } + + if (custom.pageSwitch.suspendedWindow) { + loadSuspendedWindow() + } + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + _loadStatus.value = LoadStatus.INIT_FAILED to isPullToRefresh + + } + + }).addDisposable() + + } + + override fun onRefresh() { + refreshCount++ + NewFlatLogUtils.logHomePagePullRefresh(refreshCount) + } + + override fun onLoadMore() { + val status = loadStatus.value?.first ?: LoadStatus.INIT + if (status == LoadStatus.LIST_LOADING || status == LoadStatus.INIT_LOADING || status == LoadStatus.LIST_OVER) { + return + } + _loadStatus.value = LoadStatus.LIST_LOADING to false + repository.loadNextCustomPageData(pageConfigure.pageId) + .map { + it to getPositionAndPackageMap(it) + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BiResponse, HashMap>>() { + + override fun onSuccess(data: Pair, HashMap>) { + val (list, gameWithPositionMap) = data + gamePositionAndPackageHelper.putAll(gameWithPositionMap) + _loadStatus.value = if (list.isEmpty()) { + LoadStatus.LIST_OVER to false + } else { + LoadStatus.LIST_LOADED to false + } + addData(list) + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + _loadStatus.value = LoadStatus.LIST_FAILED to false + } + }).addDisposable() + } + + + override fun onRetry() { + loadFirst(false) + } + + override fun onRotateRefresh(customSubjectCollectionItem: CustomSubjectCollectionItem) { + if (customSubjectCollectionItem.isLoadedEnd) { + notifyItemChanged(customSubjectCollectionItem.copy().apply { + showPage = customSubjectCollectionItem.showPage + 1 + isLoading = false + isLoadedEnd = true + isBackToStart = true + }) + } else { + repository.loadCollectionContent(customSubjectCollectionItem) + .map { + // 将新加载的游戏单添加到对应的游戏单合集中 + it to getPositionAndPackageMap(customSubjectCollectionItem, it.firstOrNull()?.games) + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : + BiResponse, HashMap>>() { + override fun onSuccess(data: Pair, HashMap>) { + val oldSubjectList = customSubjectCollectionItem.data.data + val (subjectList, gameWithPosition) = data + gamePositionAndPackageHelper.putAll(gameWithPosition) + + subjectList.ifEmpty { + // 已加载完毕 + if (customSubjectCollectionItem.showPage == 0 && oldSubjectList.firstOrNull()?.games.isNullOrEmpty()) { + // 首次加载的游戏被全部屏蔽且已加载完毕,移除该合集 + notifyItemRemoved(customSubjectCollectionItem) + } else { + notifyItemChanged(customSubjectCollectionItem.copy().apply { + showPage = customSubjectCollectionItem.showPage + 1 + isLoading = false + isLoadedEnd = true + isBackToStart = true + }) + } + return + } + val targetSubjectList = subjectList.filter { + when (customSubjectCollectionItem.data.style) { + COMPONENTS_COLLECTION_STYLE_REFRESH_ICONS_4_2 -> { + val needGameCount = customSubjectCollectionItem.data.styleSetting.rowsNum * 4 + it.games.size >= needGameCount + } + + COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_LIST -> it.games.size >= 3 + + else -> it.games.isNotEmpty() + } + } + targetSubjectList.ifEmpty { + customSubjectCollectionItem.loadPage++ + onRotateRefresh(customSubjectCollectionItem) + return + } + val newSubjectList = oldSubjectList + targetSubjectList + val newColumCollection = customSubjectCollectionItem.data.copy( + _data = newSubjectList + ) + val newItem = customSubjectCollectionItem.copy( + data = newColumCollection + ).apply { + showPage = customSubjectCollectionItem.showPage + 1 + loadPage = customSubjectCollectionItem.loadPage + 1 + isLoading = false + isLoadedEnd = false + } + notifyItemChanged(newItem) + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + Utils.toast(getApplication(), "网络异常") + val newItem = customSubjectCollectionItem.copy().apply { + isLoading = false + } + notifyItemChanged(newItem) + } + }).addDisposable() + } + } + + override fun onChangeABatch(subjectEntity: SubjectEntity, position: Int) { + val subjectId = subjectEntity.id ?: return + val gameList = subjectChangedMap[subjectId] + if (gameList != null) { + changeSubjectCustomPageItem(position, getRandomGameList(subjectEntity.data, ArrayList(gameList))) + } else { + repository.loadChangeSubjectGame(subjectEntity) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response>() { + override fun onResponse(response: List?) { + if (response != null) { + subjectChangedMap[subjectId] = response + onChangeABatch(subjectEntity, position) + } + } + + override fun onFailure(e: HttpException?) { + Utils.toast(getApplication(), "网络异常") + } + }) + } + } + + // 随机产生专题数据(换一换) + private fun getRandomGameList( + oldList: MutableList?, + sourceList: MutableList + ): MutableList { + val resultGameList = ArrayList() + val filterGameList = GameUtils.removeDuplicateData(oldList, sourceList)//排除重复 + val indexes = RandomUtils.getRandomArray(oldList?.size ?: 0, filterGameList!!.size) + for (index in indexes) { + val gameEntity = filterGameList[index] + resultGameList.add(gameEntity) + } + return resultGameList + } + + private fun changeSubjectCustomPageItem(position: Int, newGameList: MutableList) { + val oldData = _dataList.value as? MutableList + if (!oldData.isNullOrEmpty() && newGameList.isNotEmpty()) { + // 替换当前专题游戏数据 + val customPageItem = oldData.getOrNull(position) + if (customPageItem is CustomSubjectItem) { + val newCustomPageItem = customPageItem.copy(data = customPageItem.data.copy(mData = newGameList)) + oldData[position] = newCustomPageItem + gamePositionAndPackageHelper.putAll(getPositionAndPackageMap(newCustomPageItem, newGameList)) + } + + if (customPageItem is CustomSplitSubjectItem) { + + // 移除所有旧数据 + oldData.removeAll { + it.componentPosition == customPageItem.componentPosition + } + + // 构造新的 pageItem + val newItems = (newGameList.indices step customPageItem.step).mapIndexed { index, startPosition -> + val copySubject = customPageItem.data.copy(mData = newGameList) + customPageItem.copy( + data = copySubject, + _position = index + position, + _componentPosition = customPageItem.componentPosition, + step = customPageItem.step, + startChildPosition = startPosition + ).apply { + splitItemCount = newGameList.size + } + } + + gamePositionAndPackageHelper.putAll( + getPositionAndPackageMap(newItems) + ) + + // 将新的 items 添加到列表集合 + if (position >= oldData.size) { + oldData.addAll(newItems) + } else { + oldData.addAll(position, newItems) + } + + + // 修正集合中 item 的position 取值 + oldData.forEachIndexed { index, item -> + item.position = index + } + + } + // 通知更新 + _dataList.value = oldData + } + } + + private val _changeAppBarColorAction = MutableLiveData>() + val changAppBarColorAction: LiveData> = _changeAppBarColorAction + + override fun onChangeAppBarColor(color: Int) { + _changeAppBarColorAction.value = Event(color) + } + + private val _slideDiscoveryCardGames = MutableLiveData>() + val slideDiscoveryCardGames: LiveData> = _slideDiscoveryCardGames + + override fun loadSlideDiscoverCardGames(homeSubSlide: HomeSubSlide) { + if (slideDiscoveryGamesPage == homeSubSlide.repeatCount && _slideDiscoveryCardGames.value != null) { + _slideDiscoveryCardGames.postValue(ArrayList(_slideDiscoveryCardGames.value!!)) + } else { + repository.loadSlideDiscoverCardGames() + .compose(observableToMain()) + .subscribe(object : Response>() { + override fun onResponse(response: List?) { + response?.let { + SPUtils.setBoolean(Constants.SP_DISCOVER_FORCE_REFRESH, false) + _slideDiscoveryCardGames.postValue(it) + slideDiscoveryGamesPage = homeSubSlide.repeatCount + } + } + + override fun onFailure(e: HttpException?) { + Utils.toast(getApplication(), "网络异常") + } + }) + } + } + + private fun addData(newData: List) { + val oldData = _dataList.value + if (oldData.isNullOrEmpty()) { + _dataList.value = newData + } else { + _dataList.value = oldData + newData + } + } + + private fun notifyItemRemoved(removeItem: CustomPageItem) { + val oldData = _dataList.value ?: return + val newData = oldData.toMutableList() + if (removeItem.position >= 0 && removeItem.position < newData.size) { + newData.removeAt(removeItem.position) + // 更新position + for (i in removeItem.position until newData.size) { + newData[i].position-- + } + repository.notifyPositionChanged(-1) + gamePositionAndPackageHelper.clear() + gamePositionAndPackageHelper.putAll(getPositionAndPackageMap(newData)) + _dataList.value = newData + } + } + + private fun notifyItemChanged(newItem: CustomPageItem) { + gamePositionAndPackageHelper.putAll(getPositionAndPackageMap(listOf(newItem))) + val oldData = _dataList.value ?: return + val newData = oldData.toMutableList() + if (newItem.position >= 0 && newItem.position < newData.size) { + newData[newItem.position] = newItem + _dataList.value = newData + } + } + + private fun getPositionAndPackageMap(list: List): HashMap { + val hashMap = hashMapOf() + list.forEach { custom -> + if (custom is CustomRecentGamesItem) { + // 最近在玩 需要特殊处理 + custom.data.forEach { vGame -> + val packageName = vGame.downloadEntity.packageName + hashMap.put(packageName + custom.position, custom.position) + } + } else { + custom.showDownloadGames.forEach { + hashMap.putGameWithPosition(it, custom.position) + } + } + + } + return hashMap + } + + /** + * 临时屏蔽部分插件化游戏,避免直接修改数据源导致插件化区域整体刷新 + */ + fun temporarilyBlockGameInPluginItem(gameId: String) { + val pluginItem = _dataList.value?.find { it is CustomPluginItem } as? CustomPluginItem + pluginItem?.let { + val newItem = it.copy( + _link = it.link, + data = it.data.filterNot { gameEntity -> gameEntity.id == gameId }, + _position = it.position, + _componentPosition = it.componentPosition + ) + notifyItemChanged(newItem) + } + } + + private fun getPositionAndPackageMap( + customPageItem: CustomPageItem, + gameList: List? + ): HashMap { + val hashMap = hashMapOf() + if (customPageItem.shouldShowDownloadButton) { + gameList?.forEach { + hashMap.putGameWithPosition(it, customPageItem.position) + } + } + return hashMap + } + + private val _gameDetailDestination = MutableLiveData>>() + val gameDetailDestination: MutableLiveData>> = + _gameDetailDestination + + override fun navigateToGameDetailPageByGame( + item: CustomPageItem, + childPosition: Int, + game: GameEntity, + text: String + ) { + val linkContentName = if (item is CustomGameItem) item.linkColumn?.name ?: "" else item.link.text ?: "" + val linkContentId = if (item is CustomGameItem) item.linkColumn?.id ?: "" else item.link.link ?: "" + val trackData = CustomPageTrackData( + pageLocation, + item.componentName, + item.componentStyle, + linkContentName, + linkContentId + ) + _gameDetailDestination.value = Event(Triple(trackData, "", game)) + } + + override fun onDownloadButtonClick(item: CustomPageItem, childPosition: Int, game: GameEntity) { + + } + + private val _subjectDestination = MutableLiveData>() + val subjectDestination: LiveData> = _subjectDestination + override fun navigateSubjectPage(item: CustomPageItem, subject: SubjectEntity) { + _subjectDestination.value = Event(subject) + NewLogUtils.logColumnPictureClick( + "显示图集", subject.name ?: "", subject.id ?: "", subject.id ?: "", "column", + subject.name ?: "", "", "" + ) + } + + private val _subjectDestinationWithCustom = + MutableLiveData>>() + val subjectDestinationWithCustom: LiveData>> = + _subjectDestinationWithCustom + + override fun navigateSubjectDetailPage( + item: CustomSubjectCollectionItem, + subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity + ) { + _subjectDestinationWithCustom.value = Event(subject to item.data.isQqMini) + } + + private val _subjectCollectionDestination = + MutableLiveData>>() + val subjectCollectionDestination: LiveData>> = + _subjectCollectionDestination + + override fun navigateSubjectCollectionPage( + item: CustomSubjectCollectionItem, + childPosition: Int, + subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity? + ) { + _subjectCollectionDestination.value = Event(Triple(item, childPosition, subject)) + } + + private val _gameListDetailDestination = + MutableLiveData>>() + val gameListDetailDestination: LiveData>> = + _gameListDetailDestination + + override fun navigateGameListDetailPage( + item: CustomSubjectCollectionItem, + childPosition: Int, + subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity + ) { + _gameListDetailDestination.value = Event(Triple(item, childPosition, subject)) + } + + private val _userDestination = MutableLiveData>>() + val userDestination: LiveData>> = _userDestination + override fun navigateUserHomePage(item: CustomPageItem, userId: String) { + //todo 待定 + val entrance = "" + val path = "" + + when { + item is CustomSubjectCollectionItem -> pageTracker.trackGameListCollectionClickWithUser(item, userId) + } + _userDestination.value = Event(Triple(userId, entrance, path)) + } + + private val _interestedGameDestination = MutableLiveData>() + val interestedGameDestination: LiveData> = _interestedGameDestination + override fun navigateToInterestedGamePage(item: CustomPageItem) { + NewFlatLogUtils.logInterestedGameClick(pageConfigure.entrance, "") + _interestedGameDestination.value = Event(Unit) + } + + private val _discoverDestination = MutableLiveData>() + val discoverDestination: LiveData> = _discoverDestination + override fun navigateToDiscoveryPage(item: CustomPageItem) { + NewFlatLogUtils.logDiscoverDetailPageEnter(pageConfigure.entrance, "") + _discoverDestination.value = Event(Unit) + } + + private val _linkDestination = MutableLiveData>>() + val linkDestination: LiveData>> = _linkDestination + override fun navigateToLinkPage(item: CustomPageItem, link: LinkEntity, text: String, exposureEvent: ExposureEvent?) { + _linkDestination.value = Event(Pair(link, exposureEvent)) + } + + private val _amwayDestination = MutableLiveData>() + val amwayDestination: LiveData> = _amwayDestination + override fun navigateToAmway(item: CustomPageItem) { + _amwayDestination.value = Event(Unit) + } + + private val _badgeWallDestination = MutableLiveData>() + val badgeWallDestination: LiveData> = _badgeWallDestination + override fun navigateToBadgeWall(item: CustomPageItem, comment: RatingComment) { + _badgeWallDestination.value = Event(comment) + } + + private val _gameDetailDestinationOnAmway = MutableLiveData>>() + val gameDetailDestinationOnAmway: LiveData>> = + _gameDetailDestinationOnAmway + + override fun navigateToGameDetailPageById( + item: CustomPageItem, + gameId: String, + gameName: String?, + childPosition: Int + ) { + val linkContentName = if (item is CustomGameItem) item.linkColumn?.name ?: "" else item.link.text ?: "" + val linkContentId = if (item is CustomGameItem) item.linkColumn?.id ?: "" else item.link.link ?: "" + val trackData = CustomPageTrackData( + pageLocation, + item.componentName, + item.componentStyle, + linkContentName, + linkContentId + ) + _gameDetailDestinationOnAmway.value = + Event(Triple(trackData, gameId, item.exposureEventList.getOrNull(childPosition))) + } + + private val _ratingReplyDestination = MutableLiveData>>() + val ratingReplyDestination: LiveData>> = _ratingReplyDestination + override fun navigateToRatingReplyPage(item: CustomPageItem, gameId: String, commentId: String) { + _ratingReplyDestination.value = Event(gameId to commentId) + } + + private val _commonCollectionDestination = + MutableLiveData>>() + val commonCollectionDestination: LiveData>> = + _commonCollectionDestination + + override fun navigateToCommonCollectionDetailPage(item: CustomPageItem, link: LinkEntity) { + val data = when { + item is CustomCommonContentCollectionItem -> item.data + item is CustomSplitCommonContentCollectionItem -> item.data + else -> null + } + data?.let { + _commonCollectionDestination.value = Event(data to (link.link ?: "")) + } + } + + private val _gameListCollectionDestination = MutableLiveData>() + val gameListCollectionDestination: LiveData> = _gameListCollectionDestination + override fun navigateGameListCollectionDetailPage(item: CustomSubjectCollectionItem) { + _gameListCollectionDestination.value = Event(item) + } + + private val _gameListSquareDestination = MutableLiveData>() + val gameListSquareDestination: LiveData> = _gameListSquareDestination + override fun navigateToGameListSquare(item: CustomSubjectCollectionItem) { + _gameListSquareDestination.value = Event(item) + } + + private fun Disposable.addDisposable() { + compositeDisposable.add(this) + } + + override fun onCleared() { + super.onCleared() + compositeDisposable.clear() + repository.onClear() + repository.recentGamesItemLiveData.removeObserver(recentGamesObserver) + repository.customPluginItemLiveData.removeObserver(pluginObserver) + repository.customDiscoverItemLiveData.removeObserver(discoverObserver) + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/CustomWelcomeDialogFragment.kt b/app/src/main/java/com/gh/gamecenter/home/custom/CustomWelcomeDialogFragment.kt new file mode 100644 index 0000000000..88ec90535b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/CustomWelcomeDialogFragment.kt @@ -0,0 +1,423 @@ +package com.gh.gamecenter.home.custom + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.app.Dialog +import android.content.DialogInterface +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Build +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.Window +import android.view.animation.PathInterpolator +import android.widget.RelativeLayout +import com.alibaba.android.arouter.launcher.ARouter +import com.gh.common.exposure.ExposureManager +import com.gh.common.exposure.ExposureTraceUtils +import com.gh.common.util.DirectUtils +import com.gh.common.util.LogUtils +import com.gh.gamecenter.common.base.fragment.BaseDialogFragment +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.constant.RouteConsts +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.tryWithDefaultCatch +import com.gh.gamecenter.common.view.WrapContentDraweeView +import com.gh.gamecenter.core.provider.IFloatingWindowProvider +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.addListener +import com.gh.gamecenter.databinding.DialogWelcomeCustomBinding +import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.feature.entity.WelcomeDialogEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.ExposureType +import com.halo.assistant.HaloApp + +class CustomWelcomeDialogFragment : BaseDialogFragment() { + + private var mWelcomeEntity: WelcomeDialogEntity? = null + private var mPageLocation: PageLocation? = null + + private lateinit var mBinding: DialogWelcomeCustomBinding + + private var mShowEnterAnimation: Boolean = false + private var mShowExitAnimation: Boolean = false + + // 触发此 dialogFragment 的 fragment,可能为空 + private var mTriggerFragment: CustomPageFragment? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mWelcomeEntity = arguments?.getParcelable(TAG) + mPageLocation = arguments?.getParcelable(PageLocation::class.java.simpleName) + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + // the content + val root = RelativeLayout(activity) + root.layoutParams = + ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + + // creating the fullscreen dialog + val dialog = Dialog(requireActivity()) + dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) + dialog.setContentView(root) + if (mShowEnterAnimation) { + dialog.window?.setDimAmount(0F) + } else { + dialog.window?.setDimAmount(0.5F) + } + dialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + return dialog + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + mBinding = DialogWelcomeCustomBinding.inflate(inflater, container, false) + + mBinding.ivOpeningCover.setOnClickListener { + HaloApp.put(Constants.WELCOME_DIALOG_ID, mWelcomeEntity?.id) + HaloApp.put(Constants.WELCOME_DIALOG_LINK_TITLE, mWelcomeEntity?.text) + LogUtils.uploadWelcomeDialog( + "click", + mWelcomeEntity?.id, + mWelcomeEntity?.id, + mWelcomeEntity?.type, + mWelcomeEntity?.text + ) + SensorsBridge.trackSuspendedWindowClick( + "弹窗", + "跳转", + "自定义页面", + bottomTab = mPageLocation?.bottomTab ?: "", + multiTabId = mPageLocation?.severalTabPageId ?: "", + multiTabName = mPageLocation?.severalTabPageName ?: "", + position = mPageLocation?.tabPosition ?: -1, + tabContent = mPageLocation?.tabContent ?: "", + customPageId = mPageLocation?.pageId ?: "", + customPageName = mPageLocation?.pageName ?: "", + linkId = mWelcomeEntity?.link ?: "", + linkType = mWelcomeEntity?.type ?: "", + linkText = mWelcomeEntity?.text ?: "" + ) + val clickEvent = ExposureEvent.createEventWithSourceConcat( + null, + mWelcomeEntity?.traceEvent?.source ?: emptyList(), + listOf(ExposureSource("弹窗")), + eTrace = ExposureTraceUtils.appendTrace(mWelcomeEntity?.traceEvent), + event = ExposureType.CLICK + ) + if (mWelcomeEntity?.type != "game") { + ExposureManager.log(clickEvent) + } + + mWelcomeEntity?.let { + DirectUtils.directToLinkPage(requireContext(), it, EntranceConsts.ENTRANCE_WELCOME, "", clickEvent) + if (mShowEnterAnimation) { + val floatingWindowProvider = + ARouter.getInstance().build(RouteConsts.provider.floatingwindow) + .navigation() as? IFloatingWindowProvider + + floatingWindowProvider?.logWindowClicked( + action = "点击弹窗跳转页面", + windowId = mWelcomeEntity?.floatingWindowId ?: "", + source = "首页", + gameId = "", + gameName = "", + linkId = mWelcomeEntity?.link ?: "", + linkType = mWelcomeEntity?.type ?: "", + linkText = mWelcomeEntity?.text ?: "" + ) + } + } + + dismissAllowingStateLoss() + } + + mBinding.ivOpeningCover.setFixedWidth(300) + mBinding.ivOpeningCover.registerLoadingCallback(object : WrapContentDraweeView.LoadingCallback { + override fun loaded() { + mBinding.root.post { + if (mBinding.ivOpeningCover.measuredHeight > mBinding.root.resources.displayMetrics.heightPixels * .8) { + mBinding.ivCloseBackup.visibility = View.VISIBLE + } else { + mBinding.ivClose.visibility = View.VISIBLE + } + + if (mShowEnterAnimation) { + performAnimation(true) + } + } + + mBinding.ivOpeningCover.registerLoadingCallback(null) + } + }) + + mBinding.ivCloseBackup.setOnClickListener { + val floatingWindowProvider = + ARouter.getInstance().build(RouteConsts.provider.floatingwindow) + .navigation() as? IFloatingWindowProvider + + // 来源 fragment 是否不可见 + if (mTriggerFragment?.isCurrentlyVisible() == false) { + mShowExitAnimation = false + } else { + floatingWindowProvider?.showHiddenWindow(requireActivity()) + } + + if (mShowExitAnimation) { + dismissWithAnimation() + } else { + dismissAllowingStateLoss() + } + + SensorsBridge.trackSuspendedWindowClick( + "弹窗", + "关闭", + "自定义页面", + bottomTab = mPageLocation?.bottomTab ?: "", + multiTabId = mPageLocation?.severalTabPageId ?: "", + multiTabName = mPageLocation?.severalTabPageName ?: "", + position = mPageLocation?.tabPosition ?: -1, + tabContent = mPageLocation?.tabContent ?: "", + customPageId = mPageLocation?.pageId ?: "", + customPageName = mPageLocation?.pageName ?: "", + linkId = mWelcomeEntity?.link ?: "", + linkType = mWelcomeEntity?.type ?: "", + linkText = mWelcomeEntity?.text ?: "" + ) + } + mBinding.ivClose.setOnClickListener { mBinding.ivCloseBackup.performClick() } + ImageUtils.displayWithoutMemoryCache(mBinding.ivOpeningCover, mWelcomeEntity?.icon) + + // 弹起启动弹窗时,把右下角悬浮窗置为隐藏 + val floatingWindowProvider = + ARouter.getInstance().build(RouteConsts.provider.floatingwindow) + .navigation() as? IFloatingWindowProvider + + floatingWindowProvider?.hideWindow(requireActivity()) + + return mBinding.root + } + + override fun dismissAllowingStateLoss() { + tryWithDefaultCatch { + super.dismissAllowingStateLoss() + } + } + + override fun dismiss() { + tryWithDefaultCatch { + super.dismiss() + } + } + + private var onDismissListener: (() -> Unit)? = null + + fun setDismissListener(listener: () -> Unit) { + onDismissListener = listener + } + + /** + * 显示动画 + * @param isEnterAnimation 是否为进入动画 + */ + private fun performAnimation(isEnterAnimation: Boolean) { + // 贝塞尔曲线需要 Android 版本大于 Android 5.0 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + val screenWidth = DisplayUtils.getScreenWidth(requireActivity()) + val screenHeight = DisplayUtils.getScreenHeight(requireActivity()) + val statusBarHeight = DisplayUtils.getStatusBarHeight(requireContext().resources) + + // 最大宽度为 300dp + val maxWidth = 300F + val initialWidth = 1F + + val containerWidth = mBinding.container.width + val containerHeight = containerWidth / mBinding.ivOpeningCover.aspectRatio + 54F.dip2px() + + val location = IntArray(2) + mBinding.container.getLocationOnScreen(location) + + val xOffsetToTheScreen = location[0] + // 由于显示的时机问题,mBinding.container.getLocationOnScreen(location) 获取到的纵座标不一定是真实在屏幕的高度,这里手动计算 + val yOffsetToTheScreen = (screenHeight - containerHeight) / 2 + statusBarHeight + + val scaleRatio = initialWidth / maxWidth + // 获取动画原点的 X, Y 座标,由于 scale 会变化,所以需要结合 scale 进行计算 + val initialX = (screenWidth - 40F.dip2px() - xOffsetToTheScreen) - (containerWidth * (1 - scaleRatio)) / 2 + val dist = screenHeight - (mTriggerFragment?.getFloatingViewY() ?: 0) + val initialY = + (screenHeight - dist - yOffsetToTheScreen) - (containerHeight * (1 - scaleRatio)) / 2 + statusBarHeight + + val startX = if (isEnterAnimation) initialX else 0F + val startY = if (isEnterAnimation) initialY else 0F + + val startScale = if (isEnterAnimation) scaleRatio else 1F + val endScale = if (isEnterAnimation) 1F else scaleRatio + + val endX = if (isEnterAnimation) 0F else initialX + val endY = if (isEnterAnimation) 0F else initialY + + val enteringTranslationXInterpolator = PathInterpolator(0.4F, 1.6F, 0.4F, 1.24F) + val enteringTranslationYInterpolator = PathInterpolator(0.4F, 1.8F, 0.4F, 1.24F) + val exitingTranslationXInterpolator = PathInterpolator(0.6F, -0.24F, 0.6F, -0.6F) + val exitingTranslationYInterpolator = PathInterpolator(0.6F, -0.24F, 0.6F, -0.2F) + + val enteringScaleInterpolator = PathInterpolator(0.61F, 1F, 0.88F, 1F) + val exitingScaleInterpolator = PathInterpolator(0.12F, 0F, 0.39F, 0F) + + val translationXAnimator = + ObjectAnimator.ofFloat(mBinding.container, "translationX", startX, endX).apply { + interpolator = + if (isEnterAnimation) enteringTranslationXInterpolator else exitingTranslationXInterpolator + } + val translationYAnimator = + ObjectAnimator.ofFloat(mBinding.container, "translationY", startY, endY).apply { + interpolator = + if (isEnterAnimation) enteringTranslationYInterpolator else exitingTranslationYInterpolator + } + + val scaleXAnimator = ObjectAnimator.ofFloat(mBinding.container, "scaleX", startScale, endScale).apply { + addUpdateListener { + val dimAmount = it.animatedValue as Float / 2 + dialog?.window?.setDimAmount(dimAmount) + + // 避免与原有悬浮窗重叠,退出动画执行到 95% 进度后隐藏 + if (!isEnterAnimation && (it.animatedValue as Float) < 0.05F) { + mBinding.container.visibility = View.GONE + } else { + mBinding.container.visibility = View.VISIBLE + } + } + } + val scaleYAnimator = ObjectAnimator.ofFloat(mBinding.container, "scaleY", startScale, endScale) + val scaleAnimatorSet = AnimatorSet().apply { + playTogether(scaleXAnimator, scaleYAnimator) + interpolator = if (isEnterAnimation) enteringScaleInterpolator else exitingScaleInterpolator + } + + mBinding.root.pivotX = mBinding.root.width / 2F + mBinding.root.pivotY = mBinding.root.height / 2F + + dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + + val translationAnimatorSet = AnimatorSet().apply { + playTogether(translationXAnimator, translationYAnimator) + } + + AnimatorSet().apply { + playTogether( + scaleAnimatorSet, + translationAnimatorSet + ) + addListener( + onStart = { + mBinding.container.isClickable = false + mBinding.ivOpeningCover.isClickable = false + mBinding.ivClose.isClickable = false + }, + onEnd = { + if (!isEnterAnimation) { + dismissAllowingStateLoss() + } + mBinding.container.isClickable = true + mBinding.ivOpeningCover.isClickable = true + mBinding.ivClose.isClickable = true + } + ) + duration = ANIMATION_DURATION + }.start() + } + } + + private fun dismissWithAnimation() { + onDismissListener?.invoke() + performAnimation(false) + } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + + LogUtils.uploadWelcomeDialog( + "close", + mWelcomeEntity?.id, + mWelcomeEntity?.id, + mWelcomeEntity?.type, + mWelcomeEntity?.text + ) + + onDismissListener?.invoke() + } + + companion object { + const val TAG = "custom_welcome_dialog" + + const val ANIMATION_DURATION = 800L + + @JvmStatic + fun getInstance( + welcomeEntity: WelcomeDialogEntity?, + withAnimation: Boolean = false, + triggerFragment: CustomPageFragment? = null, + hasAnimation: Boolean, + pageLocation: PageLocation + ) = CustomWelcomeDialogFragment().apply { + arguments = Bundle().apply { + putParcelable(TAG, welcomeEntity) + putParcelable(PageLocation::class.java.simpleName, pageLocation) + } + LogUtils.uploadWelcomeDialog( + "show", + welcomeEntity?.id, + welcomeEntity?.id, + welcomeEntity?.type, + welcomeEntity?.text + ) + + mTriggerFragment = triggerFragment + + // 低于 Android 5.0 禁用动画,因为贝塞尔曲线需要 5.0+ 支持 + mShowEnterAnimation = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || !hasAnimation) { + false + } else { + withAnimation + } + + mShowExitAnimation = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + false + } else { + withAnimation || welcomeEntity?.shouldShowExitAnimation == true + } + + SensorsBridge.trackSuspendedWindowShow( + "弹窗", + "自定义页面", + bottomTab = pageLocation.bottomTab, + multiTabId = pageLocation.severalTabPageId, + multiTabName = pageLocation.severalTabPageName, + position = pageLocation.tabPosition, + tabContent = pageLocation.tabContent, + customPageId = pageLocation.pageId, + customPageName = pageLocation.pageName, + linkId = welcomeEntity?.link ?: "", + linkType = welcomeEntity?.type ?: "", + linkText = welcomeEntity?.text ?: "", + ) + ExposureManager.log( + ExposureEvent.createEventWithSourceConcat( + null, + welcomeEntity?.traceEvent?.source ?: emptyList(), + listOf(ExposureSource("弹窗")), + eTrace = ExposureTraceUtils.appendTrace(welcomeEntity?.traceEvent) + ) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/GamePositionAndPackageHelper.kt b/app/src/main/java/com/gh/gamecenter/home/custom/GamePositionAndPackageHelper.kt new file mode 100644 index 0000000000..1703c4efb2 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/GamePositionAndPackageHelper.kt @@ -0,0 +1,40 @@ +package com.gh.gamecenter.home.custom + +import com.gh.download.DownloadManager +import com.gh.gamecenter.feature.entity.GameEntity + +class GamePositionAndPackageHelper { + + private val _positionAndPackageMap = HashMap() + val positionAndPackageMap: HashMap = _positionAndPackageMap + + fun putGamePositionAndPackage(game: GameEntity, position: Int) { + var packages = "" + for (apkEntity in game.getApk()) { + packages += apkEntity.packageName + } + _positionAndPackageMap[packages + position] = position + game.gameLocation = GameEntity.GameLocation.INDEX + game.setEntryMap(DownloadManager.getInstance().getEntryMap(game.name)) + } + + fun putAll(hashMap: HashMap) { + _positionAndPackageMap.putAll(hashMap) + } + + fun clear() { + _positionAndPackageMap.clear() + } + + companion object { + + fun HashMap.putGameWithPosition(game: GameEntity, position: Int) { + var packages = "" + game.getApk().forEach { + packages += it.packageName + } + put(packages + position, position) + game.gameLocation = GameEntity.GameLocation.INDEX + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/IGameChangedNotifier.kt b/app/src/main/java/com/gh/gamecenter/home/custom/IGameChangedNotifier.kt new file mode 100644 index 0000000000..1cb707d6fb --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/IGameChangedNotifier.kt @@ -0,0 +1,26 @@ +package com.gh.gamecenter.home.custom + +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.lightgame.download.DownloadEntity + +/** + * 当游戏下载状态发生变化时,需要通知列表页刷新 + */ +interface IGameChangedNotifier { + + /** + * 下载 + */ + fun notifyDownload(download: DownloadEntity) + + /** + * 下载被删除 + */ + fun notifyDownloadDeleted(status: EBDownloadStatus) + + /** + * 安装/卸载 + */ + fun notifyInstalled(busFour: EBPackage) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/OnCustomPageRefreshStateListener.kt b/app/src/main/java/com/gh/gamecenter/home/custom/OnCustomPageRefreshStateListener.kt new file mode 100644 index 0000000000..af4fa7ca00 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/OnCustomPageRefreshStateListener.kt @@ -0,0 +1,6 @@ +package com.gh.gamecenter.home.custom + +interface OnCustomPageRefreshStateListener { + + fun onBackgroundAlphaChanged(alpha: Float) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomBaseChildAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomBaseChildAdapter.kt new file mode 100644 index 0000000000..c9ecb0f1d3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomBaseChildAdapter.kt @@ -0,0 +1,62 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.annotation.SuppressLint +import android.content.Context +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.common.utils.DarkModeUtils +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.lightgame.download.DownloadEntity + +abstract class CustomBaseChildAdapter( + val context: Context +) : RecyclerView.Adapter(), + IGameChangedNotifier { + + protected val _dataList = arrayListOf() + val dataList: List = _dataList + + private var darkMode = DarkModeUtils.isDarkModeOn(context) + + @SuppressLint("NotifyDataSetChanged") + fun submitList(data: List?) { + _dataList.clear() + data?.let(_dataList::addAll) + notifyDataSetChanged() + + if(darkMode != DarkModeUtils.isDarkModeOn(context)){ + darkMode = DarkModeUtils.isDarkModeOn(context) + } + } + + override fun getItemCount() = dataList.size + + open fun getItem(position: Int) = dataList[position] + + override fun notifyDownload(download: DownloadEntity) { + notifyItemWithGameChanged(download.gameId, download.packageName) + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + notifyItemWithGameChanged(status.gameId, status.packageName) + } + + override fun notifyInstalled(busFour: EBPackage) { + notifyItemWithGameChanged(busFour.gameId ?: "", busFour.packageName) + } + + private fun notifyItemWithGameChanged(gameId: String, packageName: String, block: ((GameEntity) -> Unit)? = null) { + dataList.filterIsInstance() + .forEachIndexed { index, item -> + val hasApk = item.getApk().any { + it.packageName == packageName + } + if (hasApk && item.id == gameId) { + block?.invoke(item) + notifyItemChanged(index) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomCommonCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomCommonCollectionAdapter.kt new file mode 100644 index 0000000000..7d4aa06ac3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomCommonCollectionAdapter.kt @@ -0,0 +1,113 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.databinding.CommonCollectionItemBinding +import com.gh.gamecenter.entity.CommonCollectionContentEntity +import com.gh.gamecenter.entity.ExposureLinkEntity +import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_VERTICAL_CARD + +class CustomCommonCollectionAdapter( + context: Context, + private val clickInvoke: (Int, CommonCollectionContentEntity) -> Unit, + private val exposureInvoke: (Int, ExposureLinkEntity) -> Unit +) : + CustomBaseChildAdapter( + context + ) { + + private lateinit var _data: CustomCommonContentCollectionItem + + fun setData(data: CustomCommonContentCollectionItem) { + this._data = data + submitList(data.data.data) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomCommonCollectionItemViewHolder { + return CustomCommonCollectionItemViewHolder(parent.toBinding()) + } + + override fun onBindViewHolder(holder: CustomCommonCollectionItemViewHolder, position: Int) { + val layout = _data.data.layout + val item = getItem(position) + var width = 0 + var height = 0 + when (layout) { + COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER -> { // 横向滑动banner + width = 244F.dip2px() + height = 122F.dip2px() + } + + COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_VERTICAL_CARD -> { // 横排竖式卡片 + width = 160F.dip2px() + height = 213F.dip2px() + } + + COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT -> { // 横排图文列表 + width = 160F.dip2px() + height = 80F.dip2px() + } + } + exposureInvoke.invoke(position, item.linkEntity) + + holder.binding.apply { + ImageUtils.display(commonCollectionImage, item.image) + maskView.goneIf(layout == COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT || (item.title.isEmpty() && item.addedContent1.isNullOrEmpty())) + titleTv.goneIf(layout == COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT) + titleTv.text = item.title + desTv.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_VERTICAL_CARD) + desTv.text = item.addedContent1 + linkTitleTv.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT) + linkTitleTv.text = item.title + linkDes1.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT) + linkDes1.text = item.addedContent1 + linkDes1.setTextColor(R.color.text_secondary.toColor(root.context)) + linkDes2.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT) + linkDes2.text = item.addedContent2 + linkDes2.setTextColor(R.color.text_tertiary.toColor(root.context)) + + val maskHeight = if (layout == COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER) { + 60f.dip2px() + } else { + 38F.dip2px() + } + val maskParams = maskView.layoutParams as ConstraintLayout.LayoutParams + maskParams.height = maskHeight + maskView.layoutParams = maskParams + + val imageParams = commonCollectionImage.layoutParams as ConstraintLayout.LayoutParams + imageParams.width = width + imageParams.height = height + commonCollectionImage.layoutParams = imageParams + + val rootParams = root.layoutParams as RecyclerView.LayoutParams + rootParams.width = width + rootParams.leftMargin = if (position == 0) 16F.dip2px() else 0 + rootParams.rightMargin = if (position == itemCount - 1) 16F.dip2px() else 0 + root.layoutParams = rootParams + + root.setOnClickListener { + clickInvoke(position, item) + } + } + } + + override fun getItemCount(): Int { + return dataList.size + } + + companion object { + const val spanCount = 2 + } + + class CustomCommonCollectionItemViewHolder(val binding: CommonCollectionItemBinding) : + BaseRecyclerViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomDiscoverCardGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomDiscoverCardGameAdapter.kt new file mode 100644 index 0000000000..58799c846a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomDiscoverCardGameAdapter.kt @@ -0,0 +1,209 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Context +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.RelativeLayout +import android.widget.TextView +import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.databinding.LayoutPopupDiscoveryDislikeBinding +import com.gh.gamecenter.discovery.DiscoveryAdapter.DiscoveryGameViewHolder +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper +import com.gh.gamecenter.retrofit.RetrofitManager +import com.google.android.flexbox.FlexboxLayout +import com.lightgame.download.DownloadEntity +import okhttp3.ResponseBody + +class CustomDiscoverCardGameAdapter( + context: Context, + private var entrance: String, + private val childEventHelper: OtherItemEventHelper, + private val createTrackData: () -> CustomPageTrackData +) : CustomBaseChildAdapter(context) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DiscoveryGameViewHolder { + return DiscoveryGameViewHolder(parent.toBinding()) + } + + override fun onBindViewHolder(holder: DiscoveryGameViewHolder, position: Int) { + val gameEntity = dataList[position] + val isEndOfRow = position >= if (itemCount % SPAN_COUNT == 0) { + itemCount - SPAN_COUNT + } else { + itemCount - itemCount % SPAN_COUNT + } + val screenWidth = context.resources.displayMetrics.widthPixels + if (!isEndOfRow) { + holder.itemView.layoutParams = ViewGroup.LayoutParams(screenWidth - 58F.dip2px(), 76F.dip2px()) + holder.itemView.setPadding(14F.dip2px(), 0, 0, 0) + } else { + holder.itemView.layoutParams = ViewGroup.LayoutParams(screenWidth - 44F.dip2px(), 76F.dip2px()) + holder.itemView.setPadding(14F.dip2px(), 0, 14F.dip2px(), 0) + } + DownloadItemUtils.updateItem(context, gameEntity, holder, "star") + holder.bindGameItem(gameEntity) + holder.itemView.setBackgroundColor(R.color.transparent.toColor(context)) + holder.itemView.setOnClickListener { + childEventHelper.navigateToGameDetailPageByGame(position, gameEntity) + } + holder.itemView.setOnLongClickListener { + showDislikeWindow(holder.itemView, gameEntity) + true + } + + gameEntity.customPageTrackData = createTrackData() + DownloadItemUtils.setOnClickListener( + context, + holder.binding.downloadBtn, + gameEntity, + position, + this, + StringUtils.buildString("(${entrance}", "-发现页卡片[", (position).toString(), "])"), + location = StringUtils.buildString(entrance, ":", gameEntity.name), + traceEvent = gameEntity.exposureEvent + ) { + childEventHelper.onDownloadButtonClick(position, gameEntity) + } + } + + @SuppressLint("CheckResult") + private fun showDislikeWindow(view: View, gameEntity: GameEntity) { + val decorView = (context as? Activity)?.window?.decorView as? FrameLayout + val binding = LayoutPopupDiscoveryDislikeBinding.inflate(LayoutInflater.from(context), decorView, true) + binding.reasonFlex.removeAllViews() + Constants.FEEDBACK_REASON_LIST.toList().forEach { + val popupItem = getPopupItem(it) + val params = + FlexboxLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) + binding.reasonFlex.addView(popupItem, params) + popupItem.setOnClickListener { + val index = dataList.indexOf(gameEntity) + if (index < 0) return@setOnClickListener + discoveryFeedback(gameEntity.id, popupItem.text.toString(), gameEntity.type ?: "") { + _dataList.removeAt(index) + notifyItemRemoved(index) + decorView?.removeView(binding.root) + ToastUtils.showToast("已根据你的偏好优化推荐机制~", Gravity.CENTER) + } + } + } + binding.root.setOnClickListener { + decorView?.removeView(it) + } + binding.contentView.visibility = View.INVISIBLE + binding.contentView.post { + val (windowPosition, isNeedShowUp) = getWindowPosition(view, binding.contentView, 36F.dip2px()) + (binding.contentView.layoutParams as RelativeLayout.LayoutParams).run { + topMargin = windowPosition[1] + binding.contentView.layoutParams = this + } + binding.anchorUpIv.goneIf(isNeedShowUp) + binding.anchorDownIv.goneIf(!isNeedShowUp) + binding.contentView.visibility = View.VISIBLE + } + } + + private fun getPopupItem(reason: String): TextView { + return TextView(context).apply { + height = 32F.dip2px() + text = reason + textSize = 12F + includeFontPadding = false + setTextColor(R.color.text_secondary.toColor(context)) + gravity = Gravity.CENTER + setPadding(12F.dip2px(), 0F.dip2px(), 12F.dip2px(), 0F.dip2px()) + background = R.drawable.bg_shape_space_radius_8.toDrawable(context) + } + } + + private fun getWindowPosition( + anchorView: View, + contentView: View, + distanceY: Int = 0 + ): Pair { + val windowPos = IntArray(2) + val anchorLoc = IntArray(2) + anchorView.getLocationInWindow(anchorLoc) + val anchorHeight = anchorView.height + val screenHeight = anchorView.context.resources.displayMetrics.heightPixels + contentView.measure(0, 0) + val contentViewWidth = contentView.width + val contentViewHeight = contentView.height + val isNeedShowUp = screenHeight - anchorLoc[1] - anchorHeight < contentViewHeight + windowPos[1] = if (isNeedShowUp) { + anchorLoc[1] - contentViewHeight + distanceY + } else { + anchorLoc[1] + anchorHeight - distanceY + } + windowPos[0] = (anchorLoc[0] - contentViewWidth + anchorView.width) / 2 + return Pair(windowPos, isNeedShowUp) + } + + @SuppressLint("CheckResult") + private fun discoveryFeedback(gameId: String, reason: String, type: String, callback: () -> Unit) { + val paramsMap = mapOf( + "reason" to reason, + "type" to type + ) + RetrofitManager.getInstance().api.discorveryFeedback(gameId, paramsMap.toRequestBody()) + .compose(singleToMain()) + .subscribe(object : BiResponse() { + + override fun onSuccess(data: ResponseBody) { + callback.invoke() + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + ToastUtils.showToast("反馈失败") + } + }) + } + + override fun notifyDownload(download: DownloadEntity) { + dataList.forEachIndexed { index, item -> + if (item.id == download.gameId) { + notifyItemChanged(index) + } + } + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + notifyChildItem(status.packageName) + } + + override fun notifyInstalled(busFour: EBPackage) { + notifyChildItem(busFour.packageName) + } + + + private fun notifyChildItem(packageName: String) { + dataList.forEachIndexed { position, gameEntity -> + gameEntity.getApk().forEach { apkEntity -> + if (apkEntity.packageName == packageName) { + notifyItemChanged(position) + return + } + } + } + } + + companion object { + const val SPAN_COUNT = 3 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomFoldSlideLargeImageItemAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomFoldSlideLargeImageItemAdapter.kt new file mode 100644 index 0000000000..58807285a3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomFoldSlideLargeImageItemAdapter.kt @@ -0,0 +1,323 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.Drawable +import android.graphics.drawable.GradientDrawable +import android.os.Handler +import android.os.Looper +import android.os.Message +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.databind.BindingAdapters +import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.view.AsyncCell +import com.gh.gamecenter.common.view.stacklayoutmanager2.StackLayoutManagerV2 +import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.databinding.RecyclerFoldSlideLargeImageItemBinding +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomSubjectItem +import com.lightgame.download.DownloadEntity +import splitties.views.backgroundColor + +class CustomFoldSlideLargeImageItemAdapter( + context: Context, + private val eventHelper: SubjectEventHelper, + private val createTrackData: () -> CustomPageTrackData, +) : CustomBaseChildAdapter(context) { + + private lateinit var _data: CustomSubjectItem + + private var _recyclerView: RecyclerView? = null + + fun setData(data: CustomSubjectItem) { + _data = data + val games = data.data.data + if (!games.isNullOrEmpty()) { + val list = ArrayList(games) + while (list.size <= 5) { + list.addAll(games) + } + submitList(list) + } + + } + + override fun getItemCount(): Int { + return if (dataList.isEmpty()) { + 0 + } else { + Int.MAX_VALUE + } + } + + override fun getItem(position: Int): GameEntity { + return dataList[position % (dataList.size)] + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageItemViewHolder { + return ImageItemViewHolder(eventHelper, parent.toBinding()) + } + + override fun onViewDetachedFromWindow(holder: ImageItemViewHolder) { + super.onViewDetachedFromWindow(holder) + holder.dismissBubble() + } + + override fun onBindViewHolder(holder: ImageItemViewHolder, position: Int) { + val realPosition = position % dataList.size + val game = getItem(realPosition) + holder.bindView(realPosition, game, _data, this, createTrackData) + holder.itemView.setOnClickListener { + eventHelper.navigateToGameDetailPage(realPosition, game) + } + + } + + + override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + _recyclerView = recyclerView + } + + override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { + _recyclerView = null + } + + override fun notifyDownload(download: DownloadEntity) { + forEach { position, game -> + if (game.id == download.gameId) { + notifyItemChanged(position, CustomPageAdapter.PAYLOAD_REFRESH_GAME_CHANGED) + return@forEach + } + } + + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + notifyChildItem(status.packageName) + } + + override fun notifyInstalled(busFour: EBPackage) { + notifyChildItem(busFour.packageName) + } + + private fun notifyChildItem(packageName: String) { + forEach { position, game -> + val hasApk = game.getApk().any { + it.packageName == packageName + } + if (hasApk) { + notifyItemChanged(position, CustomPageAdapter.PAYLOAD_REFRESH_GAME_CHANGED) + } + } + } + + private fun forEach(action: (Int, GameEntity) -> Unit) { + (_recyclerView?.layoutManager as? StackLayoutManagerV2)?.let { + val firstPosition = it.selectedPosition + val visibleCount = it.visibleCount + val endPosition = firstPosition + visibleCount - 1 + for (position in firstPosition..endPosition) { + val game = getItem(position) + action(position, game) + } + } + + } + + fun getInitPosition() = + if (dataList.isEmpty()) { + 0 + } else { + Int.MAX_VALUE / 2 - Int.MAX_VALUE % dataList.size + } + + class ImageItemViewHolder( + private val eventHelper: SubjectEventHelper, + val binding: RecyclerFoldSlideLargeImageItemBinding + ) : + RecyclerView.ViewHolder(binding.root) { + + private lateinit var item: GameEntity + + fun bindView( + childPosition: Int, + game: GameEntity, + data: CustomSubjectItem, + adapter: RecyclerView.Adapter, + createTrackData: () -> CustomPageTrackData + ) { + item = game + + ImageUtils.display(binding.poster, game.homeSetting.image) + binding.ivIcon.displayGameIcon(game) + binding.tvTitle.text = game.name + GameItemViewHolder.initGameSubtitleAndAdLabel( + game, + binding.tvSubTitle, + binding.llTitle, + binding.tvTitle, + data.data.adIconActive + ) + + binding.tvBubble.text = game.bubbleText + binding.gStar.goneIf(!(data.data.showStar && game.commentCount > 3)) { + binding.tvStar.text = if (game.star == 10.0F) "10" else game.star.toString() + } + BindingAdapters.setGameTags(binding.llLabel, game) + binding.cvContainer.setCardBackgroundColor(R.color.ui_surface.toColor(itemView.context)) + + try { + getBottomBackground(game.homeSetting.placeholderColor) + } catch (e: Exception) { + null + }?.let { (color, drawable) -> + binding.vGradient.background = drawable + binding.vBottomBackground.backgroundColor = color + } + + if (data.shouldShowDownloadButton) { + + binding.btnDownload.goneIf(false) + game.customPageTrackData = createTrackData() + DownloadItemUtils.setOnClickListener( + itemView.context, binding.btnDownload, game, bindingAdapterPosition, + adapter, + StringUtils.buildString( + "(游戏-专题:", + "subjectData.name", + "-列表[", + (bindingAdapterPosition + 1).toString(), + "])" + ), + location = StringUtils.buildString("游戏-专题-", "subjectData.name", ":", game.name), + traceEvent = data.exposureEvent + ) { + eventHelper.onDownloadButtonClick(childPosition, game) + } + DownloadItemUtils.updateItem( + itemView.context, + game, + CustomGameViewHolder(binding), + "" + ) + } else { + binding.btnDownload.goneIf(true) + } + + } + + private fun getBottomBackground(oColor: String): Pair { + val colorInt = Color.parseColor(oColor) + val red = Color.red(colorInt) + val green = Color.green(colorInt) + val blue = Color.blue(colorInt) + val hsv = FloatArray(3) + Color.RGBToHSV(red, green, blue, hsv) + val s = 0.3f + 0.3f * hsv[1] + val v = 0.3f + 0.3f * hsv[2] + val targetHsv = FloatArray(3) + targetHsv[0] = hsv[0] + targetHsv[1] = s + targetHsv[2] = v + val startColor = Color.HSVToColor(0, targetHsv) + val endColor = Color.HSVToColor(255, targetHsv) + + val drawable = GradientDrawable() + drawable.colors = intArrayOf(startColor, endColor) + drawable.orientation = GradientDrawable.Orientation.TOP_BOTTOM + return endColor to drawable + } + + private var valueAnimator: ValueAnimator? = null + + private val handler = object : Handler(Looper.getMainLooper()) { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + startBubbleAnimation(false) + } + } + + fun showBubble() { + if (item.bubbleText.isNotBlank() && !item.hasBubbleShowed) { + item.hasBubbleShowed = true + startBubbleAnimation(true) + } + } + + fun dismissBubble() { + // 取消气泡的显示动画(如果有的话) + valueAnimator?.cancel() + valueAnimator = null + // 取消气泡倒计时 + handler.removeCallbacksAndMessages(null) + binding.tvBubble.alpha = 0f + } + + private fun startBubbleAnimation(isShow: Boolean) { + valueAnimator = ValueAnimator.ofFloat(0f, 1f).apply { + duration = 300 + addUpdateListener { + val progress = it.animatedValue as Float + val scale = if (isShow) { + 0.4 * progress + 0.6f + } else { + 1 - progress * 0.4 + } + val alpha = if (isShow) { + progress + } else { + 1 - progress + } + binding.tvBubble.scaleX = scale.toFloat() + binding.tvBubble.scaleY = scale.toFloat() + binding.tvBubble.alpha = alpha + + if (isShow && progress == 1.0f) { + handler.sendEmptyMessageDelayed(0, BUBBLE_SHOW_DURATION) + } + } + start() + } + } + + companion object { + private const val BUBBLE_SHOW_DURATION = 4000L + } + } + + class FoldSlideLargeImageItemCell(context: Context) : AsyncCell(context) { + var binding: RecyclerFoldSlideLargeImageItemBinding? = null + + override val layoutId = R.layout.recycler_fold_slide_large_image_item + override val delayFirstTimeBindView = false + override fun createDataBindingView(view: View): View? { + binding = RecyclerFoldSlideLargeImageItemBinding.bind(view) + return view.rootView + } + } + + class CustomGameViewHolder(val binding: RecyclerFoldSlideLargeImageItemBinding) : + GameViewHolder(binding.root) { + + init { + gameDownloadBtn = binding.btnDownload + multiVersionDownloadTv = binding.multiVersionDownloadTv + gameDownloadTips = binding.downloadTipsLottie + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameHorizontalAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameHorizontalAdapter.kt new file mode 100644 index 0000000000..753326aa06 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameHorizontalAdapter.kt @@ -0,0 +1,203 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.* +import com.gh.common.util.NewFlatLogUtils +import com.gh.common.util.NewLogUtils +import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.base.GlobalActivityManager +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.game.horizontal.GameHorizontalSimpleItemViewHolder +import com.gh.gamecenter.home.PageConfigure +import com.gh.gamecenter.home.custom.model.CustomSubjectItem +import com.gh.gamecenter.home.custom.viewholder.CustomGameHorizontalItemViewHolder +import com.lightgame.download.DownloadEntity + +class CustomGameHorizontalAdapter( + context: Context, + private val pageConfigure: PageConfigure +) : CustomBaseChildAdapter(context) { + + var gameName = "" + var gameId = "" + var game: GameEntity? = null + + var path = "" + var exposureEventList: ArrayList? = null + private val mDefaultHorizontalPadding by lazy { R.dimen.game_detail_item_horizontal_padding.toPx() } + + private lateinit var _data: CustomSubjectItem + + private val subjectEntity: SubjectEntity + get() = _data.data + + private val type: GameHorizontalListType + get() = if (subjectEntity.isQQColumn) { + GameHorizontalListType.QGameSubjectHorizontalType + } else { + GameHorizontalListType.SubjectHorizontalType + } + + @SuppressLint("NotifyDataSetChanged") + fun setData(data: CustomSubjectItem) { + _data = data + super.submitList(data.data.data) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = + CustomGameHorizontalItemViewHolder(parent.toBinding()) + + override fun getItemCount(): Int { + val size = dataList.size + return if (type == GameHorizontalListType.GameDetailHorizontalType) { + size + } else if (type == GameHorizontalListType.QGameSubjectHorizontalType) { + dataList.size + } else { + when { + size < 4 -> size + size < 8 -> 4 + else -> 8 + } + } + + } + + override fun onBindViewHolder(holder: CustomGameHorizontalItemViewHolder, position: Int) { + val params = holder.binding.root.layoutParams as RecyclerView.LayoutParams + params.width = + if (gameName.isNotBlank()) RecyclerView.LayoutParams.WRAP_CONTENT else RecyclerView.LayoutParams.MATCH_PARENT + if (type == GameHorizontalListType.GameDetailHorizontalType) { + params.leftMargin = if (position == 0) mDefaultHorizontalPadding else 8F.dip2px() + params.rightMargin = if (position == itemCount - 1) mDefaultHorizontalPadding else 0F.dip2px() + } + holder.binding.root.layoutParams = params + + val gameEntity = getItem(position) + holder.binding.simpleGameContainer.run { + gameIcon.displayGameIcon(gameEntity) + GameHorizontalSimpleItemViewHolder.setHorizontalNameAndGravity(gameName, gameEntity.name) + downloadBtn.goneIf(!subjectEntity.showDownload) + gameName.maxLines = if (subjectEntity.showDownload) 1 else 2 + } + holder.bindGameHorizontalItem(gameEntity, subjectEntity, false, false) + val entranceResult: String + val locationResult: String + if (type == GameHorizontalListType.GameDetailHorizontalType) { + entranceResult = StringUtils.buildString( + pageConfigure.entrance, + "+(", + "游戏详情", + "[", + gameName, + "]:${path}[", + (position + 1).toString(), + "])" + ) + locationResult = StringUtils.buildString("游戏详情-", gameName, "-${path}", ":", gameEntity.name) + } else { + entranceResult = + StringUtils.buildString("(游戏-专题:", subjectEntity.name, "-列表[", (position + 1).toString(), "])") + locationResult = StringUtils.buildString("游戏-专题-", subjectEntity.name, ":", gameEntity.name) + } + holder.itemView.setOnClickListener { + if (type == GameHorizontalListType.GameDetailHorizontalType) { + DataCollectionUtils.uploadClick(context, path, "游戏详情", gameEntity.name) + NewLogUtils.logGameDetailPopularClick(gameName, gameId, "game", gameEntity.name ?: "") + SensorsBridge.trackGameDetailPagePopularClick( + gameId = gameName, + gameName = gameId, + pageName = GlobalActivityManager.getCurrentPageEntity().pageName, + pageId = GlobalActivityManager.getCurrentPageEntity().pageId, + pageBusinessId = GlobalActivityManager.getCurrentPageEntity().pageBusinessId, + lastPageName = GlobalActivityManager.getLastPageEntity().pageName, + lastPageId = GlobalActivityManager.getLastPageEntity().pageId, + lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId, + downloadStatus = game?.downloadStatusChinese ?: "", + gameType = game?.categoryChinese ?: "", + clickGameType = gameEntity.categoryChinese, + clickGameName = gameEntity.name ?: "", + clickGameId = gameEntity.id + ) + } + + if (gameEntity.isQQMiniGame()) { + NewFlatLogUtils.logQGameClick(gameEntity.qqMiniGameAppId, gameEntity.name) + GlobalActivityManager.currentActivity?.let { + DirectUtils.directToQGameById(it, gameEntity.qqMiniGameAppId) + } + } else { + GameDetailActivity.startGameDetailActivity( + context, + gameEntity.id, + entranceResult, + exposureEventList?.get(position) + ) + } + } + + if (subjectEntity.showDownload && type != GameHorizontalListType.GameDetailHorizontalType) { + DownloadItemUtils.setOnClickListener( + context, + holder.binding.simpleGameContainer.downloadBtn, + gameEntity, + position, + this, + entranceResult, + location = locationResult, + traceEvent = if (position < (exposureEventList?.size ?: 0)) exposureEventList!![position] else null + ) + + DownloadItemUtils.updateItem( + context, + gameEntity, + GameViewHolder(holder.binding.simpleGameContainer.root).apply { + gameDownloadBtn = holder.binding.simpleGameContainer.downloadBtn + gameDownloadTips = holder.binding.simpleGameContainer.downloadTipsLottie + multiVersionDownloadTv = holder.binding.simpleGameContainer.multiVersionDownloadTv + }, + ) + } + } + + fun notifyItemByDownload(downloadEntity: DownloadEntity?) { + if (downloadEntity == null) { + notifyDataSetChanged() + } else { + subjectEntity.data?.forEachIndexed { position, gameEntity -> + if (downloadEntity.gameId == gameEntity.id) { + notifyItemChanged(position) + return + } + } + } + } + + fun notifyChildItem(packageName: String) { + subjectEntity.data?.forEachIndexed { position, gameEntity -> + gameEntity.getApk().forEach { apkEntity -> + if (apkEntity.packageName == packageName) { + notifyItemChanged(position) + return + } + } + } + } + +} + +sealed class GameHorizontalListType { + object GameDetailHorizontalType : GameHorizontalListType() + object SubjectHorizontalType : GameHorizontalListType() + + object QGameSubjectHorizontalType : GameHorizontalListType() +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameHorizontalSlideAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameHorizontalSlideAdapter.kt new file mode 100644 index 0000000000..d1724b2022 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameHorizontalSlideAdapter.kt @@ -0,0 +1,135 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.ViewGroup +import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.game.horizontal.GameHorizontalSimpleItemViewHolder +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomSubjectItem +import com.gh.gamecenter.home.custom.viewholder.CustomGameHorizontalItemViewHolder +import com.lightgame.download.DownloadEntity + +class CustomGameHorizontalSlideAdapter( + context: Context, + private val eventHelper: SubjectEventHelper, + private val createTrackData: () -> CustomPageTrackData +) : CustomBaseChildAdapter(context) { + + var gameName = "" + var entrance = "" + private var _exposureEventList: List? = null + + private var isShowFirstLine = false + private var isShowSecondLine = false + + private lateinit var _data: CustomSubjectItem + + private val subjectEntity: SubjectEntity + get() = _data.data + + + fun setData(data: CustomSubjectItem, exposureEventList: List?) { + _data = data + _exposureEventList = exposureEventList + val dataList = data.data.data + dataList?.any { + if (!isShowFirstLine && it.assignRemark.firstLine.isNotEmpty()) { + isShowFirstLine = true + } + if (!isShowSecondLine && it.assignRemark.secondLine.isNotEmpty()) { + isShowSecondLine = true + } + isShowFirstLine && isShowSecondLine + } + submitList(dataList) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = + CustomGameHorizontalItemViewHolder(parent.toBinding()) + + override fun onBindViewHolder(holder: CustomGameHorizontalItemViewHolder, position: Int) { + holder.binding.remarkLine.setBackgroundColor(R.color.btn_gray_light.toColor(holder.binding.root.context)) + holder.binding.secondRemark.setTextColor(R.color.text_tertiary.toColor(holder.binding.root.context)) + holder.binding.simpleGameContainer.root.setPadding(8F.dip2px(), 0, 8F.dip2px(), 0) + holder.binding.root.layoutParams = + ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) + holder.binding.simpleGameContainer.downloadBtn.goneIf(!subjectEntity.showDownload) + holder.binding.simpleGameContainer.gameName.maxLines = if (subjectEntity.showDownload) 1 else 2 + val gameEntity = getItem(position) + holder.binding.simpleGameContainer.run { + gameName.setTextColor(R.color.text_primary.toColor(root.context)) + gameIcon.displayGameIcon(gameEntity) + GameHorizontalSimpleItemViewHolder.setHorizontalNameAndGravity(gameName, gameEntity.name) + } + holder.bindGameHorizontalItem(gameEntity, subjectEntity, isShowFirstLine, isShowSecondLine) + holder.itemView.setOnClickListener { + gameEntity.exposureEvent = _exposureEventList?.getOrNull(position) + eventHelper.navigateToGameDetailPage(position, gameEntity) + } + + if (subjectEntity.showDownload) { + gameEntity.customPageTrackData = createTrackData() + DownloadItemUtils.setOnClickListener( + context, + holder.binding.simpleGameContainer.downloadBtn, + gameEntity, + position, + this, + StringUtils.buildString("(游戏-专题:", subjectEntity.name, "-列表[", (position + 1).toString(), "])"), + location = StringUtils.buildString("游戏-专题-", subjectEntity.name, ":", gameEntity.name), + traceEvent = _exposureEventList?.getOrNull(position) + ) { + eventHelper.onDownloadButtonClick(position, gameEntity) + } + + DownloadItemUtils.updateItem( + context, + gameEntity, + GameViewHolder(holder.binding.simpleGameContainer.root).apply { + gameDownloadBtn = holder.binding.simpleGameContainer.downloadBtn + gameDownloadTips = holder.binding.simpleGameContainer.downloadTipsLottie + multiVersionDownloadTv = holder.binding.simpleGameContainer.multiVersionDownloadTv + }, + ) + + holder.binding.simpleGameContainer.downloadBtn.putWidgetBusinessName("横向专题列表") + } + } + + override fun notifyDownload(download: DownloadEntity) { + dataList.forEachIndexed { index, item -> + if (item.id == download.gameId) { + notifyItemChanged(index) + } + } + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + notifyChildItem(status.packageName) + } + + override fun notifyInstalled(busFour: EBPackage) { + notifyChildItem(busFour.packageName) + } + + private fun notifyChildItem(packageName: String) { + dataList.forEachIndexed { position, gameEntity -> + gameEntity.getApk().forEach { apkEntity -> + if (apkEntity.packageName == packageName) { + notifyItemChanged(position) + return + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameNavigationAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameNavigationAdapter.kt new file mode 100644 index 0000000000..7544dd0c90 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameNavigationAdapter.kt @@ -0,0 +1,124 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.exposure.ExposureManager +import com.gh.common.exposure.ExposureTraceUtils +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.entity.ExposureEntity +import com.gh.gamecenter.common.json.json +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.core.utils.PageSwitchDataHelper +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.databinding.ItemGameNavigationCustomBinding +import com.gh.gamecenter.entity.GameNavigationEntity +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.home.custom.eventlistener.CommonContentCollectionEventHelper + +class CustomGameNavigationAdapter( + context: Context, + private val eventHelper: CommonContentCollectionEventHelper, +) : CustomBaseChildAdapter(context) { + + private var recordMap = SPUtils.getMap(Constants.SP_GAME_NAVIGATION) + private var exposureEventList: List? = null + + fun setData(data: List, trackEventList: List?) { + exposureEventList = trackEventList + submitList(data) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameNavigationViewHolder { + return GameNavigationViewHolder(parent.toBinding()) + } + + override fun onBindViewHolder(holder: GameNavigationViewHolder, position: Int) { + + val entity = dataList[position] + ImageUtils.display(holder.binding.navigationView, entity.image) + holder.binding.navigationNameTv.text = entity.entryName + + if (entity.hint) { + val record = recordMap[entity.id] + if (record == null) { + holder.binding.redHintIv.visibility = View.VISIBLE + } else { + val split = record.split(divider)//时间戳|点击次数 + val updateTime = split[0].toLong() + val count = split[1].toInt() + //判断是否更新过 + if (updateTime == entity.time?.update) { + holder.binding.redHintIv.visibility = View.GONE + } else { + val currentTime = System.currentTimeMillis() / 1000 + holder.binding.redHintIv.goneIf(currentTime !in entity.hintStartTime..entity.hintEndTime || count >= 2) + } + } + } else { + holder.binding.redHintIv.visibility = View.GONE + } + + holder.itemView.setOnClickListener { + entity.linkEntity?.let { + val record = recordMap[entity.id] + if (record == null) { + recordMap[entity.id] = "${entity.time?.update}${divider}1" + } else { + val split = record.split(divider) + val updateTime = split[0].toLong() + var count = split[1].toInt() + recordMap[entity.id] = "${updateTime}${divider}${++count}" + } + SPUtils.setMap(Constants.SP_GAME_NAVIGATION, recordMap) + notifyItemChanged(position) + PageSwitchDataHelper.pushCurrentPageData( + hashMapOf( + Pair(PageSwitchDataHelper.PAGE_BUSINESS_TYPE, "板块-内容列表->导航栏"), + Pair(PageSwitchDataHelper.PAGE_BUSINESS_ID, "${""}->${entity.id}"), + Pair(PageSwitchDataHelper.PAGE_BUSINESS_NAME, "") + ) + ) + + SensorsBridge.trackEvent("NavigationClick", json { + "position" to position + "Navigation_name" to entity.entryName + "Navigation_id" to entity.id + "block_name" to "" + "block_id" to "" + }) + val exposureEvent = exposureEventList?.getOrNull(position) + if (exposureEvent != null) { + val clickEvent = ExposureEvent.createEvent( + GameEntity( + containerId = entity.id, + containerType = ExposureEntity.NAVIGATION_ID + ), + exposureEvent.source, + ExposureTraceUtils.appendTrace(exposureEvent), + ExposureType.CLICK + ) + if (it.type != "game") { + ExposureManager.log(clickEvent) + } + } + eventHelper.navigateToLinkPage(it, "导航栏", exposureEvent) + } + } + + } + + companion object { + const val divider = "|" + } + + class GameNavigationViewHolder(val binding: ItemGameNavigationCustomBinding) : + BaseRecyclerViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGamePluginAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGamePluginAdapter.kt new file mode 100644 index 0000000000..582db3ce4a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGamePluginAdapter.kt @@ -0,0 +1,216 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.Gravity +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.databind.BindingAdapters +import com.gh.common.util.DataCollectionUtils +import com.gh.common.util.DialogUtils +import com.gh.common.util.DownloadItemUtils.setOnClickListener +import com.gh.common.util.DownloadItemUtils.updateItem +import com.gh.common.util.HomePluggableHelper +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.constant.ItemViewType +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.MtaHelper.onEvent +import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.PluginLocation +import com.gh.gamecenter.feature.game.GameItemViewHolder.Companion.initGameSubtitleAndAdLabel +import com.gh.gamecenter.feature.utils.PlatformUtils +import com.gh.gamecenter.home.HomeDividerViewHolder +import com.gh.gamecenter.home.HomeFragmentAdapter +import com.gh.gamecenter.home.PluginDisplayStatus +import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.viewholder.CustomGameItemViewHolder +import com.gh.gamecenter.home.custom.viewholder.CustomGamePluggableViewHolder +import com.lightgame.download.DownloadEntity +import com.lightgame.utils.Utils + +/** + * Created by khy on 15/08/17. + * + * + * update on 27/12/19 + * link:https://gitlab.ghzs.com/pm/halo-app-issues/issues/730 (5) + */ +class CustomGamePluginAdapter( + private val context: Context, + private val viewModel: CustomPageViewModel, + private val childEventHelper: OtherItemEventHelper, + private val updateDisplayType: IUpdateDisplayType, + private val createTrackData: () -> CustomPageTrackData +) : RecyclerView.Adapter() { + + private var mPluginDisplayStatus = PluginDisplayStatus.CLOSE + private val dataList = arrayListOf() + + fun setData(data: List) { + dataList.clear() + dataList.addAll(data) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return if (viewType == ItemViewType.ITEM_BODY) { + CustomGamePluggableViewHolder(parent.toBinding()) + } else { + HomeDividerViewHolder(parent.toBinding()) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is CustomGamePluggableViewHolder) { + initGameNormalViewHolder(holder, position) + } else if (holder is HomeDividerViewHolder) { + holder.bindView(8f) + } + } + + override fun getItemViewType(position: Int): Int { + return if (mPluginDisplayStatus !== PluginDisplayStatus.OPEN) { + ItemViewType.ITEM_BODY + } else { + if (position != itemCount - 1) { + ItemViewType.ITEM_BODY + } else { + HomeFragmentAdapter.DIVIDER_ITEM + } + } + } + + private fun initGameNormalViewHolder(holder: CustomGamePluggableViewHolder, position: Int) { + val binding = holder.binding + val gameEntity = dataList[position] + + holder.initServerType(gameEntity) + binding.gameIconView.displayGameIcon(gameEntity) + binding.gameRating.textSize = + (if (gameEntity.commentCount > 3) 12 else 10).toFloat() + BindingAdapters.setGameName(binding.gameName, gameEntity, true) + BindingAdapters.setGameTags(binding.labelList, gameEntity) + binding.gameRating.setDrawableStart( + if (gameEntity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable() else null, + null, + null + ) + binding.gameRating.setPadding( + 0, + 0, + if (gameEntity.commentCount > 3) DisplayUtils.dip2px(8F) else 0, + 0 + ) + binding.gameRating.text = + if (gameEntity.commentCount > 3) (if (gameEntity.star.toDouble() == 10.0) "10" else gameEntity.star.toString()) else "" + binding.gameRating.setTextColor(if (gameEntity.commentCount > 3) R.color.text_theme.toColor() else R.color.primary_theme.toColor()) + binding.gameDes.text = gameEntity.decoratedDes + binding.recommendStar.rating = gameEntity.recommendStar.toFloat() + initGameSubtitleAndAdLabel( + gameEntity, + binding.gameSubtitleTv, + binding.gameNameContainer, + binding.gameName, + false, + null, + false, + null + ) + holder.itemView.setOnClickListener { v: View? -> + onEvent("首页_新", "点击", "插件化" + (position + 1) + "_" + gameEntity.name) + DataCollectionUtils.uploadClick( + context, + "插件化" + "-列表", + "游戏-专题", + gameEntity.name + ) + childEventHelper.navigateToGameDetailPageByGame(1, gameEntity) + } + + gameEntity.customPageTrackData = createTrackData() + setOnClickListener( + context, + binding.downloadBtn, + gameEntity, + 1, + this@CustomGamePluginAdapter, + StringUtils.buildString("(游戏-专题:", "插件化", "-列表[", 1.toString(), "])"), + location = StringUtils.buildString("游戏-专题-", "插件化", ":", gameEntity.name), + traceEvent = gameEntity.exposureEvent, + ) { + childEventHelper.onDownloadButtonClick(1, gameEntity) + } + updateItem( + context, + gameEntity, + CustomGameItemViewHolder.CustomGameViewHolder(holder.binding), + PluginLocation.only_index, + false, + null + ) + holder.binding.layoutTitle.root.goneIf(true) + holder.binding.neverRemind.goneIf(false) + holder.binding.neverRemind.setOnClickListener { v: View? -> + DialogUtils.showPluggableNeverRemindDialog( + context, String.format( + "%s - %s", + gameEntity.name, + PlatformUtils.getInstance(context).getPlatformName( + gameEntity.getApk()[0].getPlatform() + ) + ) + ) { + Utils.toast(context, "不再提醒设置成功") + HomePluggableHelper.setHomePluggableFilterData(gameEntity, true) + + viewModel.temporarilyBlockGameInPluginItem(gameId = gameEntity.id) + + updateDisplayType.update(PluginDisplayStatus.DEFAULT) + } + } + val layoutParams = holder.binding.downloadBtn.layoutParams + if (layoutParams is LinearLayout.LayoutParams) { + layoutParams.gravity = Gravity.TOP + layoutParams.topMargin = DisplayUtils.dip2px(4f) + } + HomePluggableHelper.setHomePluggableFilterData(gameEntity, false) + } + + override fun getItemCount(): Int { + return if (mPluginDisplayStatus === PluginDisplayStatus.OPEN) { + dataList.size + 1 // 显示底部线条 + } else if (mPluginDisplayStatus === PluginDisplayStatus.OPEN_AND_BUTTON) { + dataList.size + } else if (mPluginDisplayStatus === PluginDisplayStatus.OPEN_TWO_AND_BUTTON) { + if (dataList.size > 2) 2 else dataList.size + } else { + 0 + } + } + + fun notifyItemByDownload(downloadEntity: DownloadEntity) { + dataList.forEachIndexed { position, item -> + if (downloadEntity.name == item.name) { + notifyItemChanged(position) + } + } + } + + fun openList(pluginDisplayStatus: PluginDisplayStatus) { + if (mPluginDisplayStatus !== pluginDisplayStatus) { + mPluginDisplayStatus = pluginDisplayStatus + notifyDataSetChanged() + } else { + notifyItemRangeChanged(0, itemCount) + } + } + + interface IUpdateDisplayType { + fun update(status: PluginDisplayStatus) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameRefreshVerticalAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameRefreshVerticalAdapter.kt new file mode 100644 index 0000000000..2a1a6b5590 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameRefreshVerticalAdapter.kt @@ -0,0 +1,214 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.graphics.drawable.Drawable +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.gh.common.databind.BindingAdapters +import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.gh.gamecenter.game.vertical.GameItemUi +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem +import com.lightgame.download.DownloadEntity + +class CustomGameRefreshVerticalAdapter( + context: Context, + private var mTransparentBackground: Boolean = false, + private val eventHelper: GameSubjectCollectionEventHelper, + private val createTrackData: () -> CustomPageTrackData +) : CustomBaseChildAdapter(context) { + + private val mMaxWidth = context.resources.displayMetrics.widthPixels + + private var _spanCount: Int = 3 + private lateinit var _data: CustomSubjectCollectionItem + private lateinit var _subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity + + fun setData( + spanCount: Int, + data: CustomSubjectCollectionItem, + subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity + ) { + _spanCount = spanCount + _data = data + _subject = subject + super.submitList(subject.games) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return SimpleGameItemViewHolder(GameItemUi(parent.context)) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + if (holder is SimpleGameItemViewHolder) { + if (mTransparentBackground) holder.itemView.setBackgroundColor(R.color.transparent.toColor()) + + val gameEntity = dataList[position] + + holder.bindSimpleGameItem( + this, + gameEntity, + position, + _spanCount, + mMaxWidth, + eventHelper = eventHelper, + createTrackData = createTrackData + ) + + holder.itemView.setOnClickListener { + eventHelper.navigateToGameDetailPage(position % 3, gameEntity, _subject) + } + } + } + + override fun notifyDownload(download: DownloadEntity) { + super.notifyDownload(download) + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + notifyChildItem(status.packageName) + } + + override fun notifyInstalled(busFour: EBPackage) { + notifyChildItem(busFour.packageName) + } + + private fun notifyChildItem(packageName: String) { + dataList.forEachIndexed { position, game -> + val hasSamePackageName = game.getApk().any { + it.packageName == packageName + } + if (hasSamePackageName) { + notifyItemChanged(position) + return + } + } + + } + + inner class SimpleGameItemViewHolder(private val ui: GameItemUi) : ViewHolder(ui.root) { + var placeholderGameViewHolder: GameViewHolder? = null + + fun bindSimpleGameItem( + adapter: RecyclerView.Adapter, + gameEntity: GameEntity, + position: Int, + spanCount: Int, + maxWidth: Int, + entrance: String = "", + location: String = "", + eventHelper: GameSubjectCollectionEventHelper, + createTrackData: () -> CustomPageTrackData + ) { + val context = itemView.context + + val paddingStart = 16F.dip2px() + val isEndOfRow = position >= if (adapter.itemCount % spanCount == 0) { + adapter.itemCount - spanCount + } else { + adapter.itemCount - adapter.itemCount % spanCount + } + // 你可能会问为什么这下面会有一些,"加1减1" 的魔法。没错,这里的 "加1减1"就是为了规避 bug,做出的一些 UI 补偿 + // bug 的表现可以 checkout https://git.shanqu.cc/android/playground/-/tree/test-snap_adapter_culprit 代码,然后滑动到最后一列体验一下 + var paddingEnd = if (isEndOfRow) 16F.dip2px() else 0F.dip2px() + val height = 80F.dip2px() + + itemView.layoutParams = if (!isEndOfRow) { + paddingEnd += 1 + ViewGroup.LayoutParams(maxWidth - 24F.dip2px(), height) + } else { + ViewGroup.LayoutParams(maxWidth - 1, height) + } + with(ui) { + gameNameTv.setTextColor(R.color.text_primary.toColor(context)) + serverTypeTv.setTextColor(R.color.primary_theme.toColor(context)) + downloadTv.background = R.drawable.download_button_normal_style.toDrawable(context) + gameDesTv.setTextColor(R.color.text_tertiary.toColor(context)) + + BindingAdapters.setGameName( + gameNameTv, + gameEntity, + false + ) + BindingAdapters.setGame(iconIv, gameEntity) + BindingAdapters.setGameTags(gameTagContainer, gameEntity) + GameItemViewHolder.initServerType(gameNameTv, serverTypeTv, gameEntity) + gameDesTv.text = gameEntity.decoratedDes + GameItemViewHolder.initGameSubtitleAndAdLabel( + gameEntity, + gameSubtitleTv, + gameNameContainer, + gameNameTv, + gameEntity.adIconActive, + adLabelTv + ) + + var gameRatingPaddingEnd = 0 + var gameRatingDrawableStart: Drawable? = null + var gameRatingTextColor = R.color.primary_theme.toColor(context) + var gameRatingText = "" + + gameRatingTv.textSize = if (gameEntity.commentCount > 3) 12F else 10F + + if (gameEntity.commentCount > 3) { + gameRatingPaddingEnd = 8F.dip2px() + gameRatingDrawableStart = R.drawable.game_horizontal_rating.toDrawable() + gameRatingTextColor = R.color.text_theme.toColor(context) + gameRatingText = + if (gameEntity.star == 10.0F) "10" else gameEntity.star.toString() + } + + gameRatingTv.setDrawableStart(gameRatingDrawableStart) + gameRatingTv.setPadding(0, 0, gameRatingPaddingEnd, 0) + gameRatingTv.setTextColor(gameRatingTextColor) + gameRatingTv.text = gameRatingText + + // 没错,产品就把这个通用样式叫推荐榜单专题 + downloadTv.putWidgetBusinessName("推荐榜单专题") + + val tempViewHolder = + placeholderGameViewHolder ?: GameViewHolder(root).apply { + placeholderGameViewHolder = this + } + tempViewHolder.gameDes = gameDesTv + tempViewHolder.gameDownloadBtn = downloadTv + tempViewHolder.multiVersionDownloadTv = multiVersionDownloadTv + tempViewHolder.gameDownloadTips = downloadTipsLottie + tempViewHolder.gameLabelList = gameTagContainer + tempViewHolder.recommendIv = recommendIv + tempViewHolder.recommendTv = recommendTv + tempViewHolder.recommendContainer = recommendConstraintLayout + tempViewHolder.gameServerType = serverTypeTv + + DownloadItemUtils.updateItem( + context, + gameEntity, + tempViewHolder + ) + + gameEntity.customPageTrackData = createTrackData() + DownloadItemUtils.setOnClickListener( + context, downloadTv, gameEntity, position, + adapter, entrance, + location = location, + traceEvent = gameEntity.exposureEvent + ) { + eventHelper.onDownloadButtonClick(position, gameEntity, _subject) + } + + root.setPadding(paddingStart, 8F.dip2px(), paddingEnd, 8F.dip2px()) + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameVerticalAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameVerticalAdapter.kt new file mode 100644 index 0000000000..07332c46a7 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomGameVerticalAdapter.kt @@ -0,0 +1,207 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.graphics.drawable.Drawable +import android.os.Build +import android.view.ViewGroup +import android.view.ViewGroup.MarginLayoutParams +import androidx.core.content.ContextCompat +import androidx.core.view.marginBottom +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.gh.common.databind.BindingAdapters +import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.gh.gamecenter.game.vertical.GameItemUi +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.lightgame.download.DownloadEntity + +class CustomGameVerticalAdapter( + context: Context, + private var mTransparentBackground: Boolean = false, + private val createTrackData: () -> CustomPageTrackData, + private val eventHelper: SubjectEventHelper, +) : CustomBaseChildAdapter(context) { + + private val mMaxWidth = context.resources.displayMetrics.widthPixels + + private lateinit var _data: SubjectEntity + + fun setData(data: SubjectEntity) { + _data = data + super.submitList(data.data) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return SimpleGameItemViewHolder(GameItemUi(context)) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + if (holder is SimpleGameItemViewHolder) { + if (mTransparentBackground) holder.itemView.setBackgroundColor(R.color.transparent.toColor()) + + val gameEntity = dataList[position] + + holder.bindSimpleGameItem( + this, + gameEntity, + position, + _data.list, + mMaxWidth, + briefStyle = _data.briefStyle, + adIconActive = _data.adIconActive, + createTrackData = createTrackData + ) + + holder.itemView.setOnClickListener { + val childPosition = position % _data.list + eventHelper.navigateToGameDetailPage(childPosition, gameEntity) + } + } + } + + override fun notifyDownload(download: DownloadEntity) { + dataList.forEachIndexed { index, item -> + if (item.id == download.gameId) { + notifyItemChanged(index) + return + } + } + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + notifyChildItem(status.packageName) + } + + override fun notifyInstalled(busFour: EBPackage) { + notifyChildItem(busFour.packageName) + } + + private fun notifyChildItem(packageName: String) { + dataList.forEachIndexed { position, game -> + val hasSamePackageName = game.getApk().any { + it.packageName == packageName + } + if (hasSamePackageName) { + notifyItemChanged(position) + return + } + } + + } + + class SimpleGameItemViewHolder(private val ui: GameItemUi) : ViewHolder(ui.root) { + var placeholderGameViewHolder: GameViewHolder? = null + + fun bindSimpleGameItem( + adapter: RecyclerView.Adapter, + gameEntity: GameEntity, + position: Int, + spanCount: Int, + maxWidth: Int, + entrance: String = "", + location: String = "", + briefStyle: String, + adIconActive: Boolean, + createTrackData: () -> CustomPageTrackData + ) { + val context = itemView.context + + val paddingStart = 16F.dip2px() + val isEndOfRow = position >= if (adapter.itemCount % spanCount == 0) { + adapter.itemCount - spanCount + } else { + adapter.itemCount - adapter.itemCount % spanCount + } + // 你可能会问为什么这下面会有一些,"加1减1" 的魔法。没错,这里的 "加1减1"就是为了规避 bug,做出的一些 UI 补偿 + // bug 的表现可以 checkout https://git.shanqu.cc/android/playground/-/tree/test-snap_adapter_culprit 代码,然后滑动到最后一列体验一下 + var paddingEnd = if (isEndOfRow) 16F.dip2px() else 0F.dip2px() + val height = 80F.dip2px() + + itemView.layoutParams = if (!isEndOfRow) { + paddingEnd += 1 + ViewGroup.LayoutParams(maxWidth - 24F.dip2px(), height) + } else { + ViewGroup.LayoutParams(maxWidth - 1, height) + } + + with(ui) { + gameNameTv.setTextColor(R.color.text_primary.toColor(context)) + serverTypeTv.setTextColor(R.color.primary_theme.toColor(context)) + downloadTv.background = R.drawable.download_button_normal_style.toDrawable(context) + gameDesTv.setTextColor(R.color.text_tertiary.toColor(context)) + + BindingAdapters.setGameName( + gameNameTv, + gameEntity, + false + ) + BindingAdapters.setGame(iconIv, gameEntity) + BindingAdapters.setGameTags(gameTagContainer, gameEntity) + GameItemViewHolder.initServerType(gameNameTv, serverTypeTv, gameEntity) + + val styleResId = R.style.CustomPageGameRating + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + gameRatingTv.setTextAppearance(styleResId) + } else { + gameRatingTv.setTextAppearance(context, styleResId) + } + gameRatingTv.setPadding(0, 4f.dip2px(), 8f.dip2px(), 0) + gameRatingTv.setDrawableStart(R.drawable.game_horizontal_rating) + CustomViewExt.setGameRatting(gameRatingTv, briefStyle, gameEntity) + CustomViewExt.setDescription(gameDesTv, briefStyle, gameEntity) + GameItemViewHolder.initGameSubtitleAndAdLabel( + gameEntity, + gameSubtitleTv, + gameNameContainer, + gameNameTv, + adIconActive, + adLabelTv + ) + + // 没错,产品就把这个通用样式叫推荐榜单专题 + downloadTv.putWidgetBusinessName("推荐榜单专题") + + val tempViewHolder = + placeholderGameViewHolder ?: GameViewHolder(root).apply { + placeholderGameViewHolder = this + } + tempViewHolder.gameDes = gameDesTv + tempViewHolder.gameDownloadBtn = downloadTv + tempViewHolder.multiVersionDownloadTv = multiVersionDownloadTv + tempViewHolder.gameDownloadTips = downloadTipsLottie + tempViewHolder.gameLabelList = gameTagContainer + tempViewHolder.recommendIv = recommendIv + tempViewHolder.recommendTv = recommendTv + tempViewHolder.recommendContainer = recommendConstraintLayout + tempViewHolder.gameServerType = serverTypeTv + + DownloadItemUtils.updateItem( + context, + gameEntity, + tempViewHolder, + briefStyle + ) + + gameEntity.customPageTrackData = createTrackData() + DownloadItemUtils.setOnClickListener( + context, downloadTv, gameEntity, position, + adapter, entrance, + location = location, + traceEvent = gameEntity.exposureEvent + ) + + root.setPadding(paddingStart, root.paddingTop, paddingEnd, root.paddingBottom) + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeAmwayAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeAmwayAdapter.kt new file mode 100644 index 0000000000..00f634bf82 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeAmwayAdapter.kt @@ -0,0 +1,61 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import com.gh.common.util.DialogUtils +import com.gh.common.util.NewLogUtils +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.entity.AmwayCommentEntity +import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper +import com.gh.gamecenter.home.custom.viewholder.CustomHomeAmwayItemViewHolder + +class CustomHomeAmwayAdapter( + context: Context, + private val childEventHelper: OtherItemEventHelper, + private val mItemClick: (View, position: Int, AmwayCommentEntity) -> Unit +) : CustomBaseChildAdapter(context) { + + private val mMaxWidth by lazy { + context.resources.displayMetrics.widthPixels + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = + CustomHomeAmwayItemViewHolder(parent.toBinding()) + + override fun onBindViewHolder(holder: CustomHomeAmwayItemViewHolder, position: Int) { + holder.itemView.setPadding(16F.dip2px(), 0, 0, 12F.dip2px()) + holder.itemView.layoutParams?.width = + if (position == itemCount - 1) mMaxWidth - 4F.dip2px() else mMaxWidth - 60F.dip2px() + holder.binding.run { + val amway = dataList[position] + + holder.bindAmway(amway) + + all.goneIf(position != itemCount - 1) { + all.setOnClickListener { + NewLogUtils.logHomeShareWallButtonClick("卡片末尾") + childEventHelper.navigateToAmway() + } + } + + sdvUserBadge.goneIf(amway.comment.user.badge == null) { + ImageUtils.display(sdvUserBadge, amway.comment.user.badge!!.icon) + sdvUserBadge.setOnClickListener { + DialogUtils.showViewBadgeDialog(context, amway.comment.user.badge) { + childEventHelper.navigateToBadgeWall(amway.comment) + } + } + } + + userIconContainer.setOnClickListener { mItemClick.invoke(it, position, amway) } + userName.setOnClickListener { userIconContainer.performClick() } + + amwayBackground.setOnClickListener { mItemClick.invoke(it, position, amway) } + ratingBlock.setOnClickListener { mItemClick.invoke(it, position, amway) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionCarouselAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionCarouselAdapter.kt new file mode 100644 index 0000000000..0861a6e81a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionCarouselAdapter.kt @@ -0,0 +1,128 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.safelyGetInRelease +import com.gh.gamecenter.common.view.AsyncCell +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.databinding.HomeGameCollectionCardItemCustomBinding +import com.gh.gamecenter.home.PageConfigure +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem + +class CustomHomeGameCollectionCarouselAdapter( + context: Context, + private val pageConfigure: PageConfigure, + private val eventHelper: GameSubjectCollectionEventHelper +) : + CustomBaseChildAdapter( + context + ) { + + private val mPosterWidth = DisplayUtils.getScreenWidth() - 50F.dip2px() + + private lateinit var _data: CustomSubjectCollectionItem + + fun setData(item: CustomSubjectCollectionItem) { + _data = item + submitList(item.data.data) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = + HomeGameCollectionCardViewHolder(HomeGameCollectionCarouselItemCell(context).apply { inflate() }) + + override fun onBindViewHolder(holder: HomeGameCollectionCardViewHolder, position: Int) { + val item = getItem(position) + (holder.itemView as HomeGameCollectionCarouselItemCell).bindWhenInflated { + (holder.itemView as HomeGameCollectionCarouselItemCell).binding?.run { + holder.bindGameCollectionCard( + this, + item, + pageConfigure.entrance, + position + ) + + } + } + + } + + override fun getItemViewType(position: Int) = position + + class HomeGameCollectionCarouselItemCell(context: Context) : AsyncCell(context) { + var binding: HomeGameCollectionCardItemCustomBinding? = null + + override val layoutId = R.layout.home_game_collection_card_item_custom + override val delayFirstTimeBindView = false + override fun createDataBindingView(view: View): View? { + binding = HomeGameCollectionCardItemCustomBinding.bind(view) + return view.rootView + } + } + + inner class HomeGameCollectionCardViewHolder(cell: HomeGameCollectionCarouselItemCell) : + BaseRecyclerViewHolder(cell) { + fun bindGameCollectionCard( + binding: HomeGameCollectionCardItemCustomBinding, + itemData: CustomPageData.LinkColumnCollection.CustomSubjectEntity, + entrance: String, + itemPosition: Int + ) { + binding.run { + val context = root.context + root.layoutParams.width = mPosterWidth + + if (itemData.user.isValid) { + binding.userContainer.goneIf(false) + ImageUtils.display(userIv, itemData.user.icon) + userTv.text = itemData.user.name + userContainer.setOnClickListener { + eventHelper.onUserClick(itemData.user.id) + } + } else { + binding.userContainer.goneIf(true) + } + poster.setTag(ImageUtils.TAG_TARGET_WIDTH, mPosterWidth) + ImageUtils.display(poster, itemData.image) + + titleTv.text = itemData.title + itemData.games.take(3).forEachIndexed { index, game -> + when (index) { + 0 -> iconIvOne.displayGameIcon(game) + 1 -> iconIvTwo.displayGameIcon(game) + 2 -> iconIvThree.displayGameIcon(game) + } + } + itemData.count.game.let { + moreTv.goneIf(it < 4 || itemData.games.isEmpty()) + moreTv.text = "+${itemData.count.game - 3}" + } + + stampIv.goneIf(itemData.stamp.isEmpty()) + stampIv.setBackgroundResource(if (itemData.stamp == "official") R.drawable.ic_official else R.drawable.ic_chosen) + listOf(iconIvOne, iconIvTwo, iconIvThree).forEachIndexed { index, iconIv -> + iconIv.goneIf((itemData.games.size) < index + 1) { + val game = itemData.games.safelyGetInRelease(index) + iconIv.setOnClickListener { + game?.let { game -> + eventHelper.navigateToGameDetailPage(index, game, itemData) + } + } + } + } + + root.setOnClickListener { + eventHelper.navigateSubjectCollectionPage(itemPosition, itemData) + } + + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionSlideAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionSlideAdapter.kt new file mode 100644 index 0000000000..df08225c64 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameCollectionSlideAdapter.kt @@ -0,0 +1,300 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.util.SparseArray +import android.view.View +import android.view.ViewGroup +import androidx.core.util.forEach +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.util.DownloadItemUtils +import com.gh.common.util.NewFlatLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.AsyncCell +import com.gh.gamecenter.databinding.ItemHomeGameCollectionBigSlideCardCustomBinding +import com.gh.gamecenter.databinding.ItemHomeGameCollectionBigSlideCardGameBinding +import com.gh.gamecenter.databinding.ItemHomeGameCollectionSmallSlideCardCustomBinding +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem +import com.lightgame.download.DownloadEntity + +class CustomHomeGameCollectionSlideAdapter( + context: Context, + private var mIsBigSlide: Boolean, + private val entrance: String, + private val eventHelper: GameSubjectCollectionEventHelper, + private val createTrackData: () -> CustomPageTrackData +) : CustomBaseChildAdapter(context) { + + private val mBigSlideViewHolderList = SparseArray() + + private lateinit var data: CustomSubjectCollectionItem + + fun setData(data: CustomSubjectCollectionItem) { + this.data = data + super.submitList(data.data.data) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = + if (mIsBigSlide) { + HomeGameCollectionBigSlideCardViewHolder( + HomeGameCollectionBigSlideCardCell(context).apply { inflate() } + ).apply { + mBigSlideViewHolderList.put(mBigSlideViewHolderList.size(), this) + } + } else { + HomeGameCollectionSmallSlideCardViewHolder(parent.toBinding()) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val subject = getItem(position) + val games = subject.games + + // 大卡片 + if (holder is HomeGameCollectionBigSlideCardViewHolder) { + holder.bindView(subject) + (holder.itemView as HomeGameCollectionBigSlideCardCell).bindWhenInflated { + (holder.itemView as HomeGameCollectionBigSlideCardCell).binding?.run { + val params = root.layoutParams as ViewGroup.MarginLayoutParams + params.leftMargin = if (position == 0) 0 else (-24F).dip2px() + + if (subject.user.isValid) { + gUser.goneIf(false) + ImageUtils.display(userIv, subject.user.icon) + userTv.text = subject.user.name + listOf(userIv, userTv).forEach { + it.setOnClickListener { + eventHelper.onUserClick(subject.user.id) + } + } + } else { + gUser.goneIf(true) + } + ImageUtils.display(coverImage, subject.image, false) + + titleTv.text = subject.title + + listOf(gameItem1, gameItem2, gameItem3).forEachIndexed { index, binding -> + binding.root.goneIf(games.size < index + 1) { + val gameEntity = games[index] + holder.updateGameItem(binding, gameEntity, createTrackData) + } + } + + divider.setBackgroundColor(R.color.ui_divider.toColor(context)) + moreTv.setTextColor(R.color.text_tertiary.toColor(context)) + container.background = R.drawable.bg_item_game_test_v2.toDrawable(context) + + moreTv.text = "查看全部${subject.count.game}款游戏" + root.setOnClickListener { + eventHelper.navigateSubjectCollectionPage(position, subject) + } + } + } + } + + // 小卡片 + if (holder is HomeGameCollectionSmallSlideCardViewHolder) { + holder.binding.run { + val params = root.layoutParams as ViewGroup.MarginLayoutParams + params.leftMargin = if (position == 0) 0 else (-24F).dip2px() + + if (subject.user.isValid) { + gUser.goneIf(false) + userTv.setTextColor(R.color.text_secondary.toColor(context)) + userTv.text = subject.user.name + ImageUtils.display(userIv, subject.user.icon) + listOf(userIv, userTv).forEach { + it.setOnClickListener { + eventHelper.onUserClick(subject.user.id) + } + } + } else { + gUser.goneIf(true) + } + container.background = R.drawable.bg_item_game_test_v2.toDrawable(context) + titleTv.setTextColor(R.color.text_primary.toColor(context)) + + ImageUtils.display(coverImage, subject.image) + titleTv.text = subject.title + + subject.count.let { gameCountTv.text = "+${it.game - games.size}" } + var showGameCount = 0 + listOf(gameIcon1, gameIcon2, gameIcon3).forEachIndexed { index, gameIcon -> + gameIcon.goneIf(games.size < index + 1) { + val gameEntity = games[index] + gameIcon.displayGameIcon(gameEntity) + gameIcon.setBorderColor(R.color.ui_surface) + gameIcon.setOnClickListener { + eventHelper.navigateToGameDetailPage(index, gameEntity, subject) + } + showGameCount = index + 1 + } + } + + val moreCount = subject.count.game - showGameCount + gameCountTv.goneIf(moreCount <= 0) { + gameCountTv.text = "+$moreCount" + } + + root.setOnClickListener { + eventHelper.navigateSubjectCollectionPage(position, subject) + } + } + } + } + + override fun notifyDownload(download: DownloadEntity) { + mBigSlideViewHolderList.forEach { _, it -> + it.notifyDownload(download) + } + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + mBigSlideViewHolderList.forEach { _, it -> + it.notifyDownloadDeleted(status) + } + } + + override fun notifyInstalled(busFour: EBPackage) { + mBigSlideViewHolderList.forEach { _, it -> + it.notifyInstalled(busFour) + } + } + + private inner class HomeGameCollectionBigSlideCardCell(context: Context) : AsyncCell(context) { + var binding: ItemHomeGameCollectionBigSlideCardCustomBinding? = null + + override val layoutId = R.layout.item_home_game_collection_big_slide_card_custom + override val switchToWrapContentWhenInflated = true + override fun createDataBindingView(view: View): View? { + binding = ItemHomeGameCollectionBigSlideCardCustomBinding.bind(view) + return view.rootView + } + } + + inner class HomeGameCollectionBigSlideCardViewHolder(view: View) : BaseRecyclerViewHolder(view), + IGameChangedNotifier { + + private var subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = null + + fun bindView(subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity) { + this.subject = subject + } + + override fun notifyDownload(download: DownloadEntity) { + subject?.games?.forEachIndexed { index, item -> + val hasApk = item.getApk().any { + it.packageName == download.packageName + } + if (hasApk && item.id == download.gameId) { + item.getEntryMap()[download.platform] = download + (itemView as? HomeGameCollectionBigSlideCardCell)?.binding?.run { + when (index) { + 0 -> updateGameItem(gameItem1, item, createTrackData) + 1 -> updateGameItem(gameItem2, item, createTrackData) + 2 -> updateGameItem(gameItem3, item, createTrackData) + } + } + } + } + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + checkResetData() + } + + override fun notifyInstalled(busFour: EBPackage) { + checkResetData() + } + + fun checkResetData() { + val games = subject?.games ?: arrayListOf() + (itemView as HomeGameCollectionBigSlideCardCell).binding?.run { + listOf(gameItem1, gameItem2, gameItem3).forEachIndexed { index, binding -> + binding.root.goneIf(games.size < index + 1) { + val gameEntity = games[index] + updateGameItem(binding, gameEntity, createTrackData) + } + } + } + } + + fun updateGameItem( + binding: ItemHomeGameCollectionBigSlideCardGameBinding, + gameEntity: GameEntity, + createTrackData: () -> CustomPageTrackData + ) { + binding.root.visibility = View.VISIBLE + + binding.gameIconIv.displayGameIcon(gameEntity) + binding.gameNameTv.text = gameEntity.name + binding.gameNameTv.setTextColor(R.color.text_primary.toColor(context)) + binding.gameDesTv.text = gameEntity.brief + binding.gameDesTv.setTextColor(R.color.text_tertiary.toColor(context)) + + gameEntity.customPageTrackData = createTrackData() + DownloadItemUtils.setOnClickListener( + context, + binding.downloadBtn, + gameEntity, + bindingAdapterPosition, + null, + entrance, + location = "游戏单合集", + traceEvent = gameEntity.exposureEvent, + clickCallback = { + eventHelper.onDownloadButtonClick(bindingAdapterPosition, gameEntity, subject) + }, + refreshCallback = { + DownloadItemUtils.updateItem( + context, + gameEntity, + GameViewHolder(binding.root).apply { + gameDownloadBtn = binding.downloadBtn + gameDownloadTips = binding.downloadTipsLottie + multiVersionDownloadTv = binding.multiVersionDownloadTv + }, + true + ) + }, + allStateClickCallback = null + ) + + DownloadItemUtils.updateItem( + context, + gameEntity, + GameViewHolder(binding.root).apply { + gameDownloadBtn = binding.downloadBtn + gameDownloadTips = binding.downloadTipsLottie + multiVersionDownloadTv = binding.multiVersionDownloadTv + }, + false + ) + + binding.root.setOnClickListener { + NewFlatLogUtils.logGameListCollectionClick( + entrance, + "", + "", + subject?.title ?: "", + subject?.id ?: "", + "游戏" + ) + + eventHelper.navigateToGameDetailPage(bindingAdapterPosition, gameEntity, subject) + } + } + } + + class HomeGameCollectionSmallSlideCardViewHolder(val binding: ItemHomeGameCollectionSmallSlideCardCustomBinding) : + RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2GameListRvAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2GameListRvAdapter.kt new file mode 100644 index 0000000000..b90e22484d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2GameListRvAdapter.kt @@ -0,0 +1,156 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.entity.GameDataWrapper +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper +import com.gh.gamecenter.home.test_v2.HomeGameTestV2GameListPlaceHolderViewHolder +import com.gh.gamecenter.home.test_v2.HomeGameTestV2GameListSpaceViewHolder +import com.gh.gamecenter.home.test_v2.HomeGameTestV2GameListViewHolder +import com.lightgame.download.DownloadEntity + +/** + * @author : liujiarui + * date : 2023/3/6 + * description : 新游开测,游戏列表 adapter + */ +class CustomHomeGameTestV2GameListRvAdapter( + context: Context, + private val childEventHelper: OtherItemEventHelper, + private val createTrackData: () -> CustomPageTrackData, + private val exposureInvoke: (Int, GameEntity) -> ExposureEvent +) : CustomBaseChildAdapter(context) { + + private val mEntrance: String = "新游开测" + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return when (viewType) { + 0 -> HomeGameTestV2GameListSpaceViewHolder(parent.toBinding()) + 1 -> HomeGameTestV2GameListPlaceHolderViewHolder(parent.toBinding()) + else -> HomeGameTestV2GameListViewHolder(parent.toBinding()) + } + } + + override fun getItemViewType(position: Int): Int { + val data = dataList[position] + return when { + data.isSpace -> 0 + data.isPlaceHolder -> 1 + else -> 2 + } + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val data = dataList.getOrNull(position) ?: return + if (holder is HomeGameTestV2GameListViewHolder) { + val gameData = data.gameData ?: return + holder.bindGameItem(gameData) + initGameHolder(holder, gameData, position) + } else if (holder is HomeGameTestV2GameListPlaceHolderViewHolder) { + holder.bind() + } + } + + /** + * 初始化上报事件,下载按钮点击事件,游戏详情点击事件, + * 更新下载状态等 + */ + private fun initGameHolder( + viewHolder: HomeGameTestV2GameListViewHolder, + gameEntity: GameEntity, + position: Int + ) { + val exposureEvent = putExposureEvent(gameEntity, position) + //游戏下载事件上报 + gameEntity.customPageTrackData = createTrackData() + DownloadItemUtils.setOnClickListener( + context, + viewHolder.gameDownloadBtn, gameEntity, viewHolder.bindingAdapterPosition, this, + StringUtils.buildString(mEntrance, "+(新游开测[", position.toString(), "])"), + location = StringUtils.buildString("新游开测:", gameEntity.name), + traceEvent = exposureEvent + ) { + childEventHelper.onDownloadButtonClick(position, gameEntity) + } + + DownloadItemUtils.updateItem( + context, + gameEntity, + viewHolder, + false, + //"star&brief" //是否显示评分。不加这句不显示评分 + ) + + //游戏详情点击事件上报 + viewHolder.itemView.setOnClickListener { + childEventHelper.navigateToGameDetailPageByGame(position, gameEntity) + } + } + + fun notifyItemByDownload(downloadEntity: DownloadEntity?) { + if (downloadEntity == null) { + notifyDataSetChanged() + } else { + dataList.forEachIndexed { position, gameEntity -> + if (downloadEntity.gameId == gameEntity.gameData?.id) { + notifyItemChanged(position) + } + } + } + } + + override fun notifyDownload(download: DownloadEntity) { + dataList.forEachIndexed { index, item -> + if (item.gameData?.id == download.gameId) { + notifyItemChanged(index) + } + } + + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + notifyChildItem(status.packageName) + } + + override fun notifyInstalled(busFour: EBPackage) { + notifyChildItem(busFour.packageName) + } + + private fun notifyChildItem(packageName: String) { + dataList.forEachIndexed { position, gameEntity -> + gameEntity.gameData?.getApk()?.forEach { apkEntity -> + if (apkEntity.packageName == packageName) { + notifyItemChanged(position) + return + } + } + } + } + + private fun putExposureEvent( + gameEntity: GameEntity, + position: Int + ): ExposureEvent { + return exposureInvoke(position, gameEntity) + } + + /** + * 左右滑动曝光条目 + */ + fun exposureItem(firstItemPosition: Int) { + val gameList = dataList + for (position in firstItemPosition..firstItemPosition + 2) { + val gameEntity = gameList.getOrNull(position)?.gameData ?: continue + putExposureEvent(gameEntity, position) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2RecommendRvAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2RecommendRvAdapter.kt new file mode 100644 index 0000000000..9085978575 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2RecommendRvAdapter.kt @@ -0,0 +1,26 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.ViewGroup +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.entity.RecommendLabel +import com.gh.gamecenter.home.custom.viewholder.CustomHomeGameTestV2RecommendViewHolder + +/** + * @author : liujiarui + * date : 2023/3/6 + * description : 新游开测,推荐标签 + */ +class CustomHomeGameTestV2RecommendRvAdapter( + context: Context +) : CustomBaseChildAdapter(context) { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomHomeGameTestV2RecommendViewHolder { + return CustomHomeGameTestV2RecommendViewHolder(parent.toBinding()) + } + + override fun onBindViewHolder(holder: CustomHomeGameTestV2RecommendViewHolder, position: Int) { + val label = dataList.getOrElse(position) { RecommendLabel() } + holder.bind(label) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2TimePointRvAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2TimePointRvAdapter.kt new file mode 100644 index 0000000000..c32f720bfc --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeGameTestV2TimePointRvAdapter.kt @@ -0,0 +1,123 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.ViewGroup +import com.gh.common.util.NewFlatLogUtils +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.core.utils.TimeUtils +import com.gh.gamecenter.home.custom.CustomNewGameTestUseCase +import com.gh.gamecenter.home.custom.viewholder.CustomHomeGameTestV2TimePointViewHolder +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.* + +/** + * @author : liujiarui + * date : 2023/3/3 + * description : + */ +class CustomHomeGameTestV2TimePointRvAdapter( + context: Context, + private val useCase: CustomNewGameTestUseCase, +) : CustomBaseChildAdapter(context) { + + private var selectedPosition: Int = 0 + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomHomeGameTestV2TimePointViewHolder { + return CustomHomeGameTestV2TimePointViewHolder(parent.toBinding()) + } + + override fun onBindViewHolder(holder: CustomHomeGameTestV2TimePointViewHolder, position: Int) { + val text = dataList.getOrElse(position) { "" } + holder.binding.root.setOnClickListener { + val bindingAdapterPosition = holder.bindingAdapterPosition + if (bindingAdapterPosition == selectedPosition) return@setOnClickListener + selectPoint(bindingAdapterPosition, true) + useCase.onTimePointClick(text) + } + val isSelected = position == selectedPosition + val todayAndWeekText = if (isSelected) { + getShortDate(text) + } else { + getTodayAndWeekText(text) + } + holder.bind( + todayAndWeekText, + position == 0, + position == itemCount - 1, + isSelected + ) + } + + /** + * 把日期文本 yyyy-MM-dd 转换成 今天,周几,下周几格式 + */ + private fun getTodayAndWeekText(date: String): String { + val today = TimeUtils.getToday() + if (today == date) return "今天" + val list = TimeUtils.getCurrentAndNextWeek(pattern = "yyyy-MM-dd") + list.find { it.first == date }?.let { + return it.second + } ?: return getShortDate(date) + } + + /** + * 把日期文本 yyyy-MM-dd 转换成 MM.dd + */ + private fun getShortDate(date: String): String { + if (date == "recommend") return "推荐" + val format: DateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA) + return try { + val d = format.parse(date) ?: return date + SimpleDateFormat("MM.dd", Locale.CHINA).format(d) + } catch (e: Exception) { + date + } + } + + /** + * 时间轴日期转时间戳 + */ + private fun getDateTimeStamp(date: String): Long { + if (date == "recommend") return 0 + val format: DateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA) + return try { + val d = format.parse(date) ?: return 0 + d.time + } catch (e: Exception) { + 0 + } + } + + /** + * 点击时间轴节点事件 + */ + fun selectPoint(position: Int, isClick: Boolean = false) { + val lastSelectedPosition = selectedPosition + selectedPosition = position + notifyItemChanged(lastSelectedPosition) + notifyItemChanged(position) + //埋点上报 + val action = if (isClick) "手动点击" else "自动定位" + NewFlatLogUtils.logGameTestV2TimeLineClick( + action = action, + type = getType(), + time = getStartTime(), + sequence = getSequence() + ) + } + + fun getType(): String { + val text = dataList.getOrElse(selectedPosition) { "" } + return if (text == "recommend") "推荐" else "全部" + } + + fun getStartTime(): Long { + val text = dataList.getOrElse(selectedPosition) { "" } + return getDateTimeStamp(text) + } + + fun getSequence(): Int { + return selectedPosition + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeHorizontalSlideVideoAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeHorizontalSlideVideoAdapter.kt new file mode 100644 index 0000000000..90b5cc74dc --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeHorizontalSlideVideoAdapter.kt @@ -0,0 +1,94 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.ViewGroup +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.PageConfigure +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomSubjectItem +import com.gh.gamecenter.home.custom.viewholder.CustomHomeHorizontalSlideVideoItemViewHolder +import com.lightgame.download.DownloadEntity +import splitties.views.bottomPadding +import splitties.views.leftPadding +import splitties.views.rightPadding +import splitties.views.topPadding + +class CustomHomeHorizontalSlideVideoAdapter( + context: Context, + private val pageConfigure: PageConfigure, + private val eventHelper: SubjectEventHelper, + private val createTrackData: () -> CustomPageTrackData, + private val exposureClosure: (GameEntity, Int) -> ExposureEvent, + + ) : + CustomBaseChildAdapter(context) { + + private lateinit var item: CustomSubjectItem + + fun setNewData(item: CustomSubjectItem) { + this.item = item + submitList(item.data.data) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomHomeHorizontalSlideVideoItemViewHolder { + return CustomHomeHorizontalSlideVideoItemViewHolder(eventHelper, parent.toBinding()) + } + + override fun onBindViewHolder(holder: CustomHomeHorizontalSlideVideoItemViewHolder, position: Int) { + val gameEntity = dataList[position] + + val event = exposureClosure(gameEntity, position) + + holder.binding.root.setPadding( + 16F.dip2px(), + holder.binding.root.topPadding, + holder.binding.root.rightPadding, + holder.binding.root.bottomPadding + ) + + if (position == itemCount - 1) { + holder.binding.root.setPadding( + holder.binding.root.leftPadding, + holder.binding.root.topPadding, + 16F.dip2px(), + holder.binding.root.bottomPadding + ) + } + + holder.bindAttachGame(position, gameEntity, this, pageConfigure.entrance, event, createTrackData) + } + + + override fun notifyDownload(download: DownloadEntity) { + dataList.forEachIndexed { index, game -> + if (game.id == download.gameId) { + notifyItemChanged(index) + } + } + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + notifyChildItem(status.packageName) + } + + override fun notifyInstalled(busFour: EBPackage) { + notifyChildItem(busFour.packageName) + } + + private fun notifyChildItem(packageName: String) { + dataList.forEachIndexed { position, gameEntity -> + gameEntity.getApk().forEach { apkEntity -> + if (apkEntity.packageName == packageName) { + notifyItemChanged(position) + return + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecentVGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecentVGameAdapter.kt new file mode 100644 index 0000000000..56d83c018b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecentVGameAdapter.kt @@ -0,0 +1,160 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.download.DownloadManager +import com.gh.gamecenter.R +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.entity.IconFloat +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.ToastUtils +import com.gh.gamecenter.databinding.ItemHomeVgameRefactorBinding +import com.gh.gamecenter.manager.PackagesManager +import com.gh.vspace.VGameItemData +import com.gh.vspace.VHelper +import com.lightgame.download.DownloadEntity +import com.lightgame.download.DownloadStatus + +class CustomHomeRecentVGameAdapter( + context: Context +) : CustomBaseChildAdapter(context) { + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): HomeRecentVGameItemViewHolder { + return HomeRecentVGameItemViewHolder(parent.toBinding()) + } + + override fun onBindViewHolder(holder: HomeRecentVGameItemViewHolder, position: Int) { + (holder as? HomeRecentVGameItemViewHolder)?.bindView(dataList[position]) + } + + override fun getItemCount(): Int = dataList.size + + override fun notifyDownload(download: DownloadEntity) { + dataList.forEachIndexed { position, item -> + if (download.name == item.downloadEntity.name) { + item.downloadEntity = download + notifyItemChanged(position) + } + } + } + + class HomeRecentVGameItemViewHolder(private var binding: ItemHomeVgameRefactorBinding) : + RecyclerView.ViewHolder(binding.root) { + + fun bindView(entity: VGameItemData) { + if (binding.gameIconIv.getTag(R.string.app_name) != entity.downloadEntity.packageName) { + val iconFloat = IconFloat( + entity.downloadEntity.getMetaExtra(Constants.GAME_ICON_FLOAT_TOP_TEXT), + entity.downloadEntity.getMetaExtra(Constants.GAME_ICON_FLOAT_TOP_COLOR), + entity.downloadEntity.getMetaExtra(Constants.GAME_ICON_FLOAT_BOTTOM_TEXT) + ) + binding.gameIconIv.displayGameIcon( + entity.downloadEntity.getMetaExtra(Constants.RAW_GAME_ICON), + entity.downloadEntity.getMetaExtra(Constants.GAME_ICON_SUBSCRIPT), + iconFloat + ) + binding.gameIconIv.setTag(R.string.app_name, entity.downloadEntity.packageName) + } + + updateViewByStatus(binding, entity) + } + + private fun updateViewByStatus(binding: ItemHomeVgameRefactorBinding, itemData: VGameItemData) { + val downloadEntity = itemData.downloadEntity + + itemData.refresh() + + when (downloadEntity.status) { + DownloadStatus.done -> { + if (itemData.shouldShowUpdate) { + binding.root.setOnClickListener { + PackagesManager.getUpdateList() + .firstOrNull { it.id == downloadEntity.gameId } + ?.let { + VHelper.updateOrReDownload(downloadEntity, it) + } + } + } else { + binding.root.setOnClickListener { + if (!VHelper.isInstalled(downloadEntity.packageName)) { + NewFlatLogUtils.logGameInstall( + downloadEntity.gameId, + downloadEntity.name, + "主动安装" + ) + + SensorsBridge.trackInstallGameClick( + downloadEntity.gameId, + downloadEntity.name, + "主动安装" + ) + } + VHelper.installOrLaunch(binding.root.context, downloadEntity, "最近在玩-图标") + } + } + } + + DownloadStatus.subscribe, + DownloadStatus.waiting, + DownloadStatus.pause -> { + binding.progressBar.progressDrawable = + R.drawable.bg_home_vgame_progress_inactive.toDrawable() + + if (downloadEntity.status == DownloadStatus.waiting) { + binding.root.setOnClickListener { + ToastUtils.toast("最多只能同时下载三个任务,请稍等") + } + } else { + binding.root.setOnClickListener { + DownloadManager.getInstance().resume(downloadEntity, false) + } + } + } + + DownloadStatus.redirected, + DownloadStatus.downloading -> { + binding.progressBar.progressDrawable = + R.drawable.bg_home_vgame_progress_active.toDrawable() + binding.root.setOnClickListener { + DownloadManager.getInstance().pause(downloadEntity.url) + } + } + + DownloadStatus.cancel, + DownloadStatus.timeout, + DownloadStatus.neterror, + DownloadStatus.hijack, + DownloadStatus.uncertificated, + DownloadStatus.unqualified, + DownloadStatus.notfound, + DownloadStatus.diskisfull, + DownloadStatus.diskioerror, + DownloadStatus.unavailable, + DownloadStatus.overflow -> { + binding.root.setOnClickListener { + DownloadManager.getInstance().resume(downloadEntity, false) + } + } + + else -> { + binding.root.setOnClickListener { + ToastUtils.showToast(downloadEntity.status.toString()) + } + } + } + + binding.maskView.goneIf(!itemData.shouldShowMask) + binding.controlTv.goneIf(!itemData.shouldShowControlTv) + binding.progressBar.goneIf(!itemData.shouldShowProgressBar) + binding.dotView.goneIf(!itemData.shouldShowDot) + binding.updateHintIv.goneIf(!itemData.shouldShowUpdate) + + binding.controlTv.text = itemData.controlText + binding.progressBar.progress = downloadEntity.percent.toInt() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecommendItemGridAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecommendItemGridAdapter.kt new file mode 100644 index 0000000000..f84485aea9 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeRecommendItemGridAdapter.kt @@ -0,0 +1,120 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import com.gh.common.exposure.ExposureManager +import com.gh.common.exposure.ExposureTraceUtils +import com.gh.common.util.LogUtils +import com.gh.common.util.NewLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.utils.setDebouncedClickListener +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.core.utils.PageSwitchDataHelper +import com.gh.gamecenter.databinding.ItemHomeRecommendListItemCustomBinding +import com.gh.gamecenter.entity.HomeRecommend +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.ExposureType +import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper +import org.json.JSONException +import org.json.JSONObject + +/** + * @author : liujiarui + * date : 2023/3/23 + * description : + */ +class CustomHomeRecommendItemGridAdapter( + private val eventHelper: CommonContentCollectionEventHelper, +) : BaseAdapter() { + + private val dataList = arrayListOf() + private var exposureEventList: List? = null + + fun submitList(data: List, trackEventList: List?) { + exposureEventList = trackEventList + dataList.clear() + dataList.addAll(data) + notifyDataSetChanged() + } + + override fun getCount() = dataList.size + + override fun getItem(position: Int) = dataList[position] + + override fun getItemId(position: Int): Long { + return dataList[position].linkId.hashCode().toLong() + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val data = dataList.getOrNull(position) + val binding: ItemHomeRecommendListItemCustomBinding + if (convertView == null) { + binding = ItemHomeRecommendListItemCustomBinding + .inflate(LayoutInflater.from(parent.context), parent, false) + binding.root.tag = binding + } else { + binding = convertView.tag as ItemHomeRecommendListItemCustomBinding + } + if (data != null) { + ImageUtils.display(binding.iconIv, data.image) + binding.nameTv.text = data.name + binding.nameTv.setTextColor(R.color.text_primary.toColor(binding.root.context)) + } + initEvent(binding, position) + return binding.root + } + + private fun initEvent(binding: ItemHomeRecommendListItemCustomBinding, position: Int) { + //点击事件上报 + binding.root.setDebouncedClickListener { + val recommend = dataList[position] + + LogUtils.logRecommendClick( + "首页", + recommend.name, + recommend.linkType, + recommend.linkText, + recommend.linkId, + position + ) + + val trackEvent = JSONObject() + try { + trackEvent.put("position", position) + trackEvent.put("Recommended_location_name", recommend.name) + trackEvent.put("location", "首页") + } catch (e: JSONException) { + e.printStackTrace() + } + SensorsBridge.trackEvent("RecommendedLocationClick", trackEvent) + + if (recommend.linkType == "common_collection") { + NewLogUtils.logAccessToCommonCollectionDetail(recommend.linkId, recommend.linkText, "首页推荐入口") + } + PageSwitchDataHelper.pushCurrentPageData( + hashMapOf( + Pair( + PageSwitchDataHelper.PAGE_BUSINESS_TYPE, + "首页-推荐位" + ) + ) + ) + exposureEventList?.getOrNull(position)?.let { + val clickEvent = ExposureEvent( + payload = it.payload, + source = it.source, + eTrace = ExposureTraceUtils.appendTrace(it), + event = ExposureType.CLICK + ) + if (recommend.transformLinkEntity().type != "game") { + ExposureManager.log(clickEvent) + } + } + eventHelper.navigateToLinkPage(recommend.transformLinkEntity(), "金刚区", exposureEventList?.getOrNull(position)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeSlideListAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeSlideListAdapter.kt new file mode 100644 index 0000000000..7a38a50e38 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomHomeSlideListAdapter.kt @@ -0,0 +1,221 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.PagerSnapHelper +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.gh.common.databind.BindingAdapters +import com.gh.common.exposure.ExposureManager +import com.gh.common.exposure.ExposureTraceUtils +import com.gh.common.util.DirectUtils +import com.gh.gamecenter.common.utils.DataLogUtils +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.core.utils.MtaHelper +import com.gh.gamecenter.core.utils.PageSwitchDataHelper +import com.gh.gamecenter.entity.HomeSlide +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.home.custom.eventlistener.CommonContentCollectionEventHelper +import com.gh.gamecenter.home.custom.viewholder.CustomHomeSlideListItemViewHolder +import com.gh.gamecenter.home.custom.viewholder.CustomHomeSubSlideListItemViewHolder +import com.lightgame.download.DownloadEntity + +class CustomHomeSlideListAdapter( + context: Context, + val snapHelper: PagerSnapHelper, + private val hasCards: Boolean = false, + private val eventHelper: CommonContentCollectionEventHelper, + private val exposureInvoke: (Int, GameEntity?) -> ExposureEvent +) : CustomBaseChildAdapter(context) { + + private var recyclerView: RecyclerView? = null + + val dataCount: Int + get() = dataList.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return if (hasCards) { + CustomHomeSubSlideListItemViewHolder(parent.toBinding()) + } else { + CustomHomeSlideListItemViewHolder(parent.toBinding()) + } + } + + override fun getItemCount() = if (dataCount <= 1) dataCount else Int.MAX_VALUE + + override fun getItem(position: Int): HomeSlide { + return dataList[getDataPosition(position)] + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val actualPosition = getDataPosition(position) + if (actualPosition > dataList.size) { + holder.itemView.post { + notifyItemChanged(position) + } + return + } + + + val homeSlide = dataList[actualPosition] + + val game = homeSlide.linkGame + var exposureEvent: ExposureEvent? = null + if (homeSlide.linkType == "game") { + runOnIoThread(true) { + exposureEvent = exposureInvoke(actualPosition, game) + } + } else { + val event = exposureInvoke(actualPosition, game) + event.payload.controlType = "轮播图" + event.payload.controlName = homeSlide.title + event.payload.controlLinkName = homeSlide.linkText + event.payload.controlLinkType = homeSlide.linkType + exposureEvent = event + } + game?.exposureEvent = exposureEvent + + if (holder is CustomHomeSlideListItemViewHolder) { + holder.bindSlideListItem(homeSlide) + setOnClickListener( + holder.binding.root, + holder.binding.includeGame.root, + homeSlide, + actualPosition, + exposureEvent + ) + BindingAdapters.setGameTags(holder.binding.includeGame.gameTags, homeSlide.linkGame?.tagStyle, 3) + holder.binding.unknownBackground.goneIf(DirectUtils.directLinkTypes.contains(homeSlide.linkType)) + } + + if (holder is CustomHomeSubSlideListItemViewHolder) { + holder.bindSubSlideListItem(homeSlide) + setOnClickListener(holder.binding.root, holder.binding.gameGroup, homeSlide, actualPosition, exposureEvent) + } + } + + override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + this.recyclerView = recyclerView + } + + override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { + this.recyclerView = null + } + + private fun setOnClickListener( + rootView: View, + gameView: View, + homeSlide: HomeSlide, + actualPosition: Int, + exposureEvent: ExposureEvent? + ) { + rootView.setOnClickListener { + val actualPositionString = (actualPosition + 1).toString() + if (homeSlide.linkType == "video") { + MtaHelper.onEvent("首页_新", "轮播_点击", actualPositionString + "_视频详情") + } else if (homeSlide.linkType == "game") { + MtaHelper.onEvent("首页_新", "轮播_点击", actualPositionString + "_游戏详情") + } + + PageSwitchDataHelper.pushCurrentPageData( + hashMapOf( + Pair(PageSwitchDataHelper.PAGE_BUSINESS_TYPE, "首页轮播图") + ) + ) + + DataLogUtils.uploadLunbotuLog( + context, + homeSlide.linkType, + homeSlide.linkText, + homeSlide.title, + actualPositionString, + "新首页" + ) + exposureEvent?.let { + val clickEvent = ExposureEvent( + payload = it.payload, + source = it.source, + eTrace = ExposureTraceUtils.appendTrace(it), + event = ExposureType.CLICK + ) + if (homeSlide.transformLinkEntity().type != "game") { + ExposureManager.log(clickEvent) + } + } + eventHelper.navigateToLinkPage(homeSlide.transformLinkEntity(),"轮播图", exposureEvent) + } + gameView.setOnClickListener { + val actualPositionString = (actualPosition + 1).toString() + val linkGame = homeSlide.linkGame + DataLogUtils.uploadLunbotuLog( + context, + homeSlide.linkType, + homeSlide.linkText, + homeSlide.title, + actualPositionString, + "新首页" + ) + if (linkGame != null) { + eventHelper.navigateToGameDetailPage(actualPosition, linkGame,"轮播图") + } + } + } + + fun notifyItemByDownload(downloadEntity: DownloadEntity?) { + if (downloadEntity == null) { + notifyDataSetChanged() + } else { + val layoutManager = recyclerView?.layoutManager + if (layoutManager is LinearLayoutManager) { + val snapView = snapHelper.findSnapView(layoutManager) + if (snapView != null) { + val position = layoutManager.getPosition(snapView) + if (position != -1) { + for (index in getCheckPositions(getDataPosition(position))) { + if (index >= 0 && index < dataList.size) { + val game = dataList[index].linkGame + if (game != null && game.id == downloadEntity.gameId) { + notifyItemChanged(position) + } + } + } + } + } + } + } + } + + fun scrollToNextPage() { + recyclerView?.let { + val layoutManager = it.layoutManager + if (layoutManager is LinearLayoutManager) { + val snapView = snapHelper.findSnapView(layoutManager) + if (snapView != null) { + it.smoothScrollToPosition(layoutManager.getPosition(snapView) + 1) + } + } + } + } + + private fun getCheckPositions(actualPosition: Int): List { + return arrayListOf(actualPosition - 1, actualPosition, actualPosition + 1) + } + + fun getInitPosition() = if (dataCount > 1) { + Int.MAX_VALUE / 2 - (Int.MAX_VALUE / 2) % dataCount + } else { + dataCount + } + + fun getDataPosition(position: Int): Int { + return position % dataCount + } + + fun getActualSize() = dataList.size +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomIconMatrixAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomIconMatrixAdapter.kt new file mode 100644 index 0000000000..8537a55365 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomIconMatrixAdapter.kt @@ -0,0 +1,146 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.databinding.RecyclerChildIconMatrixCustomBinding +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.adapter.CustomPageAdapter.Companion.PAYLOAD_REFRESH_GAME_CHANGED +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem +import com.lightgame.download.DownloadEntity + +class CustomIconMatrixAdapter( + context: Context, + private val eventHelper: SubjectEventHelper, + private val createTrackData: () -> CustomPageTrackData +) : CustomBaseChildAdapter(context) { + + private var _data: CustomSplitSubjectItem? = null + + private var _exposureEventList: List? = null + + fun setData(data: CustomSplitSubjectItem, exposureEventList: List) { + _data = data + _exposureEventList = exposureEventList + val list = arrayListOf() + for (index in data.startChildPosition..data.endChildPosition) { + data.data.data?.getOrNull(index)?.let(list::add) + } + submitList(list) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomChildIconMatrixViewHolder = + CustomChildIconMatrixViewHolder(parent.toBinding()) + + override fun onBindViewHolder(holder: CustomChildIconMatrixViewHolder, position: Int, payloads: MutableList) { + if (payloads.isNotEmpty()) { + DownloadItemUtils.updateItem( + context, + getItem(position), + GameViewHolder(holder.binding.simpleGameContainer.root).apply { + gameDownloadBtn = holder.binding.simpleGameContainer.downloadBtn + gameDownloadTips = holder.binding.simpleGameContainer.downloadTipsLottie + multiVersionDownloadTv = holder.binding.simpleGameContainer.multiVersionDownloadTv + }, + ) + } else { + super.onBindViewHolder(holder, position, payloads) + } + } + + override fun onBindViewHolder(holder: CustomChildIconMatrixViewHolder, position: Int) { + _data?.data?.let { subject -> + holder.itemView.visibleIf(true) + val item = getItem(position) + with(holder.binding.simpleGameContainer) { + gameIcon.displayGameIcon(item) + downloadBtn.goneIf(!subject.showDownload) + com.gh.gamecenter.game.horizontal.GameHorizontalSimpleItemViewHolder.setHorizontalNameAndGravity( + gameName, + item.name + ) + gameName.maxLines = if (subject.showDownload) 1 else 2 + + } + val showStar = subject.showStar + && subject.data?.any { it.commentCount > 3 } ?: false + CustomViewExt.setGameRattingWithSubject(holder.binding.gameRating, showStar, item) + val entranceResult = + StringUtils.buildString("(游戏-专题:", subject.name, "-列表[", (position + 1).toString(), "])") + val locationResult = StringUtils.buildString("游戏-专题-", subject.name, ":", item.name) + + if (subject.showDownload) { + item.customPageTrackData = createTrackData() + DownloadItemUtils.setOnClickListener( + context, + holder.binding.simpleGameContainer.downloadBtn, + item, + position, + this, + entranceResult, + location = locationResult, + traceEvent = _exposureEventList?.getOrNull(position) + ) { + eventHelper.onDownloadButtonClick(position, item) + } + + DownloadItemUtils.updateItem( + context, + item, + GameViewHolder(holder.binding.simpleGameContainer.root).apply { + gameDownloadBtn = holder.binding.simpleGameContainer.downloadBtn + gameDownloadTips = holder.binding.simpleGameContainer.downloadTipsLottie + multiVersionDownloadTv = holder.binding.simpleGameContainer.multiVersionDownloadTv + }, + ) + } + + holder.itemView.setOnClickListener { + eventHelper.navigateToGameDetailPage(position, item) + } + } + } + + override fun notifyDownload(download: DownloadEntity) { + dataList.forEachIndexed { index, item -> + if (item.id == download.gameId) { + notifyItemChanged(index, PAYLOAD_REFRESH_GAME_CHANGED) + } + } + + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + notifyChildItem(status.packageName) + } + + override fun notifyInstalled(busFour: EBPackage) { + notifyChildItem(busFour.packageName) + } + + private fun notifyChildItem(packageName: String) { + dataList.forEachIndexed { index, item -> + val hasApk = item.getApk().any { + it.packageName == packageName + } + if (hasApk) { + notifyItemChanged(index, PAYLOAD_REFRESH_GAME_CHANGED) + } + } + } + + class CustomChildIconMatrixViewHolder( + val binding: RecyclerChildIconMatrixCustomBinding + ) : ViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomPageAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomPageAdapter.kt new file mode 100644 index 0000000000..db4b03b11b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomPageAdapter.kt @@ -0,0 +1,429 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.view.ViewGroup +import androidx.lifecycle.LifecycleOwner +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnScrollListener +import com.gh.common.exposure.IExposable +import com.gh.gamecenter.R +import com.gh.gamecenter.common.baselist.LoadStatus +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.game.GameAndPosition +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.model.CustomFooterItem +import com.gh.gamecenter.home.custom.model.CustomGameTestV2Item +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_AMWAY_WALL +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_LANE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_MATRIX +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_SLIDE_LIST +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_COMMON_BANNER +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_COMMON_BANNER_WITH_CARDS +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_CONTENT_CARD +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_DISCOVER +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_DOUBLE_ROW +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_FOOTER +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_GAME_FOLD_SLIDE_LARGE_IMAGE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_GAME_HOME_GAME_ITEM +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_GALLERY_SLIDE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_ICON_LANE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_STATIC_GALLERY +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_TIME_LINE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_GAME_ICON_MATRIX +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_GAME_LONG_LIST +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_GAME_VERTICAL_SLIDE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_HORIZONTAL_SLIDE_VIDEO_LIST +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_NAVIGATION +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_NEW_GAME_TEST +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_PLUGIN +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_RANK_COLLECTION +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_RECENT_PLAY +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_RECOMMEND +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_BANNER +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_HORIZONTAL_SLIDE_LARGE_CARD +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_HORIZONTAL_SLIDE_SMALL_CARD +import com.gh.gamecenter.home.custom.model.CustomRecentGamesItem +import com.gh.gamecenter.home.custom.viewholder.* +import com.gh.gamecenter.home.video.ScrollCalculatorHelper +import com.lightgame.download.DownloadEntity + +class CustomPageAdapter( + private val viewModel: CustomPageViewModel, + private val lifecycleOwner: LifecycleOwner, + private val scrollCalculatorHelper: ScrollCalculatorHelper, +) : ListAdapter(CALLBACK), IExposable, IGameChangedNotifier { + + private var loadStatus: LoadStatus? = null + + private var recyclerView: RecyclerView? = null + + override fun submitList(list: List?) { + val dataWithFooter = if (list.isNullOrEmpty()) { + ArrayList(list) + } else { + list + CustomFooterItem + } + super.submitList(dataWithFooter) + } + + fun setLoadStatus(status: LoadStatus) { + loadStatus = status + notifyItemChanged(itemCount - 1) + } + + override fun getItemViewType(position: Int): Int { + return getItem(position).itemType + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseCustomViewHolder = + when (viewType) { + CUSTOM_PAGE_ITEM_TYPE_DOUBLE_ROW -> CustomDoubleCardViewHolder(viewModel, parent.toBinding()) + CUSTOM_PAGE_ITEM_TYPE_HORIZONTAL_SLIDE_VIDEO_LIST -> + CustomHomeHorizontalSlideVideoListViewHolder( + viewModel, + scrollCalculatorHelper, + parent.toBinding() + ) + + CUSTOM_PAGE_ITEM_TYPE_GAME_LONG_LIST -> + CustomGameItemViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_GAME_VERTICAL_SLIDE -> + CustomGameVerticalSlideViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_GAME_ICON_MATRIX -> + CustomIconMatrixViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_TIME_LINE, CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_ICON_LANE -> + CustomGameHorizontalSlideListViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_STATIC_GALLERY -> + CustomGameGalleryViewHolder( + viewModel, + CustomGameGalleryViewHolder.GameGalleryItemCell(parent.context).apply { inflate() } + ) + + CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_GALLERY_SLIDE -> + CustomGameGallerySlideViewHolder(viewModel, parent.toBinding(), lifecycleOwner) + + CUSTOM_PAGE_ITEM_TYPE_GAME_FOLD_SLIDE_LARGE_IMAGE -> + CustomFoldSlideLargeImageViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_GAME_HOME_GAME_ITEM -> + CustomHomeGameItemViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_RANK_COLLECTION -> + CustomRankCollectionViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_HORIZONTAL_SLIDE_LARGE_CARD -> + CustomHomeGameCollectionSlideViewHolder(true, viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_HORIZONTAL_SLIDE_SMALL_CARD -> + CustomHomeGameCollectionSlideViewHolder(false, viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_BANNER -> { + CustomHomeGameCollectionCarouselViewHolder(viewModel, lifecycleOwner, parent.toBinding()) + } + + CUSTOM_PAGE_ITEM_TYPE_RECENT_PLAY -> { + CustomHomeRecentVGameViewHolder(true, viewModel, parent.toBinding()) + } + + CUSTOM_PAGE_ITEM_TYPE_PLUGIN -> { + CustomGamePluginViewHolder(true, viewModel, parent.toBinding()) + } + + CUSTOM_PAGE_ITEM_TYPE_NEW_GAME_TEST -> { + CustomHomeItemGameTestV2ViewHolder(viewModel, lifecycleOwner, parent.toBinding()) + } + + CUSTOM_PAGE_ITEM_TYPE_DISCOVER -> + CustomHomeDiscoverCardViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_CONTENT_CARD -> + CustomBigImageRecommendViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_AMWAY_WALL -> + CustomHomeAmwayListViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_COMMON_BANNER -> + CustomHomeSlideListViewHolder( + viewModel, + lifecycleOwner, + parent.toBinding() + ) + + CUSTOM_PAGE_ITEM_TYPE_COMMON_BANNER_WITH_CARDS -> + CustomHomeSlideWithCardsViewHolder( + viewModel, + lifecycleOwner, + parent.toBinding() + ) + + CUSTOM_PAGE_ITEM_TYPE_NAVIGATION -> + CustomNavigationViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_RECOMMEND -> + CustomHomeRecommendItemViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE -> + CustomCommonCollectionViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS -> + CustomCommonCollection12ViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_LANE -> + CustomRefreshIconViewHolder(parent.context, viewModel, false, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_MATRIX -> + CustomRefreshIconViewHolder(parent.context, viewModel, true, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_SLIDE_LIST -> + CustomGameCollectionRefreshVerticalSlideViewHolder(viewModel, parent.toBinding()) + + CUSTOM_PAGE_ITEM_TYPE_FOOTER -> CustomFooterViewHolder(viewModel, parent.toBinding()) + + else -> CustomDoubleCardViewHolder(viewModel, parent.toBinding()) + } + + override fun onBindViewHolder(holder: BaseCustomViewHolder, position: Int) = Unit + + override fun onBindViewHolder(holder: BaseCustomViewHolder, position: Int, payloads: MutableList) { + val item = getItem(position) + when (holder) { + is CustomDoubleCardViewHolder -> holder.bindView(item) + is CustomHomeHorizontalSlideVideoListViewHolder -> holder.bindView(item) + is CustomGameItemViewHolder -> { + holder.bindView(item, this) + } + + is CustomGameVerticalSlideViewHolder -> holder.bindView(item) + + is CustomIconMatrixViewHolder -> holder.bindView(item) + + is CustomGameHorizontalSlideListViewHolder -> holder.bindView(item) + + is CustomGameGalleryViewHolder -> holder.bindView(item) + + is CustomGameGallerySlideViewHolder -> holder.bindView(item) + + is CustomFoldSlideLargeImageViewHolder -> holder.bindView(item) + + is CustomHomeGameItemViewHolder -> holder.bindView(item, position, this) + + is CustomRankCollectionViewHolder -> holder.bindView(item) + + is CustomHomeGameCollectionSlideViewHolder -> holder.bindView(item) + + is CustomHomeGameCollectionCarouselViewHolder -> holder.bindView(item) + + is CustomHomeRecentVGameViewHolder -> holder.bindView(item) + + is CustomGamePluginViewHolder -> holder.bindView(item) { + notifyItemChanged(position) + } + + is CustomHomeItemGameTestV2ViewHolder -> holder.bindView(item, position) + + is CustomHomeDiscoverCardViewHolder -> { + holder.bindView(item, position) + } + + is CustomBigImageRecommendViewHolder -> holder.bindView(item) + + is CustomHomeAmwayListViewHolder -> holder.bindView(item) + + is CustomHomeSlideListViewHolder -> holder.bindView(item) + + is CustomHomeSlideWithCardsViewHolder -> holder.bindView(item) + + is CustomNavigationViewHolder -> holder.bindView(item) + + is CustomHomeRecommendItemViewHolder -> holder.bindView(item) + + is CustomCommonCollectionViewHolder -> holder.bindView(item) + + is CustomCommonCollection12ViewHolder -> holder.bindView(item) + + is CustomRefreshIconViewHolder -> holder.bindView(item) + + is CustomGameCollectionRefreshVerticalSlideViewHolder -> holder.bindView(item) + + is CustomFooterViewHolder -> holder.initFooterViewHolder( + loadStatus == LoadStatus.LIST_LOADING, + loadStatus == LoadStatus.LIST_FAILED, + loadStatus == LoadStatus.LIST_OVER, + R.string.load_over_with_click_hint + ) { + if (loadStatus == LoadStatus.LIST_OVER) { + recyclerView?.scrollToPosition(0) + } else if (loadStatus == LoadStatus.LIST_FAILED) { + viewModel.onLoadMore() + notifyItemChanged(itemCount - 1) + } + } + } + + } + + override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + super.onAttachedToRecyclerView(recyclerView) + this.recyclerView = recyclerView + recyclerView.addOnScrollListener(onLoadMoreListener) + } + + override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { + super.onDetachedFromRecyclerView(recyclerView) + recyclerView.removeOnScrollListener(onLoadMoreListener) + this.recyclerView = null + } + + override fun onViewAttachedToWindow(holder: BaseCustomViewHolder) { + holder.onViewAttach(recyclerView) + } + + override fun onViewDetachedFromWindow(holder: BaseCustomViewHolder) { + holder.onViewDetach(recyclerView) + } + + override fun getEventByPosition(pos: Int): ExposureEvent? { + return getItem(pos).exposureEvent + } + + override fun getEventListByPosition(pos: Int): List? { + return getItem(pos).exposureEventList + } + + override fun notifyDownload(download: DownloadEntity) { + val data = getGameEntityByPackage(download.packageName) + data.forEach { (game, position, _) -> + if (game != null && game.name == download.name) { + game.getEntryMap()[download.platform] = download + } + checkIfOnlyNotifyItemChanged(position) { + it.notifyDownload(download) + } + } + } + + /** + * 更新 游戏下载被删除 事件 + */ + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + val data = getGameEntityByPackage(status.packageName) + data.forEach { (game, position, _) -> + if (game != null && game.name == status.name) { + game.getEntryMap().remove(status.platform) + } + checkIfOnlyNotifyItemChanged(position) { + it.notifyDownloadDeleted(status) + } + } + + } + + override fun notifyInstalled(busFour: EBPackage) { + val data = getGameEntityByPackage(busFour.packageName) + data.forEach { (_, position, _) -> + checkIfOnlyNotifyItemChanged(position) { + it.notifyInstalled(busFour) + } + } + } + + private fun checkIfOnlyNotifyItemChanged(position: Int, block: (IGameChangedNotifier) -> Unit) { + val viewHolder = recyclerView?.findViewHolderForAdapterPosition(position) + if (viewHolder is BaseCustomViewHolder) { + if (viewHolder.onlyNotifyItemChanged) { + notifyItemChanged(position) + } else { + block(viewHolder) + } + } + } + + fun getGameEntityByPackage(packageName: String): List { + val positionMap = viewModel.gamePositionAndPackageHelper.positionAndPackageMap + return positionMap.entries + .filter { (key, position) -> + key.contains(packageName) && position < itemCount + } + .flatMap { (_, position) -> + when (val item = getItem(position)) { + is CustomRecentGamesItem -> { + item.data.map { + GameAndPosition(null, position) + } + } + + is CustomGameTestV2Item -> { + val positionList = ArrayList() + viewModel.newGameTestUserCase.getDataListLiveData().value?.forEach { gameDataWrapper -> + val gameEntity = gameDataWrapper.gameData + if (gameEntity != null) { + for (apkEntity in gameEntity.getApk()) { + if (apkEntity.packageName == packageName) { + positionList.add(GameAndPosition(gameEntity, position)) + } + } + } + } + positionList + } + + else -> { + item.showDownloadGames.map { game -> + GameAndPosition(game, position) + } + } + } + } + } + + + private val onLoadMoreListener = object : OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + val layoutManger = recyclerView.layoutManager + if (layoutManger is LinearLayoutManager) { + val lastVisibleItemPosition = layoutManger.findLastVisibleItemPosition() + if (lastVisibleItemPosition == itemCount - 1 && loadStatus != LoadStatus.LIST_OVER && loadStatus != LoadStatus.LIST_LOADING) { + viewModel.onLoadMore() + } + } + } + } + + } + + + companion object { + // 游戏单轮换刷新 + const val PAYLOAD_REFRESH_GAME_LIST = "payload_refresh_game_list" + + // 刷新游戏下载状态 + const val PAYLOAD_REFRESH_GAME_CHANGED = "payload_refresh_game_changed" + + private val CALLBACK = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: CustomPageItem, + newItem: CustomPageItem + ) = oldItem.areItemsTheSame(newItem) + + override fun areContentsTheSame( + oldItem: CustomPageItem, + newItem: CustomPageItem + ) = oldItem.areContentsTheSame(newItem) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRankCollectionAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRankCollectionAdapter.kt new file mode 100644 index 0000000000..ccfc1b403c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRankCollectionAdapter.kt @@ -0,0 +1,238 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.AsyncCell +import com.gh.gamecenter.databinding.RankCollectionItemCustomBinding +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.game.rank.CustomRankGameItem +import com.gh.gamecenter.game.rank.RankGameItemUi +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem +import com.lightgame.download.DownloadEntity + +class CustomRankCollectionAdapter( + context: Context, + private val eventHelper: GameSubjectCollectionEventHelper, + private val createTrackData: () -> CustomPageTrackData, +) : CustomBaseChildAdapter( + context +) { + + private val viewHolderList = arrayListOf() + + private var _data: CustomSubjectCollectionItem? = null + + fun setData(data: CustomSubjectCollectionItem) { + _data = data + super.submitList(data.data.data) + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): RankCollectionItemViewHolder { + return RankCollectionItemViewHolder(RankCollectionItemCell(context).apply { inflate() }) + } + + override fun onBindViewHolder(holder: RankCollectionItemViewHolder, position: Int) { + val subjectEntity = getItem(position) + holder.initPackageAndPosition(subjectEntity) + (holder.itemView as RankCollectionItemCell).bindWhenInflated { + (holder.itemView as RankCollectionItemCell).binding?.run { + val marginLeft = if (position == 0) 16F.dip2px() else 8F.dip2px() + val marginRight = if (position == itemCount - 1) 16F.dip2px() else 0 + root.layoutParams = (root.layoutParams as ViewGroup.MarginLayoutParams).apply { + leftMargin = marginLeft + rightMargin = marginRight + } + background.hierarchy?.setPlaceholderImage(R.drawable.bg_rank_list.toDrawable(this@CustomRankCollectionAdapter.context)) + if (subjectEntity.image.isNotEmpty() && !DarkModeUtils.isDarkModeOn(context)) { + ImageUtils.display(background, subjectEntity.image) + } else { + background.setImageURI("") + } + val title = if (subjectEntity.title.length > 12) { + "${subjectEntity.title.substring(0, 12)}..." + } else { + subjectEntity.title + } + rankTitle.text = title + rankTitle.setTextColor(R.color.text_primary.toColor(context)) + rankTitle.setOnClickListener { + eventHelper.navigateSubjectCollectionPage(position, subjectEntity) + } + rankTitleIndicator.setTextColor(R.color.text_primary.toColor(context)) + + rankListContainer.run { + val gameList = subjectEntity.games + + if (childCount == 0) { + for (i in 0..MAX_RANK_ITEM_COUNT) { + val gameItem = CustomRankGameItem(RankGameItemUi(context)) + holder.gameItemList.add(gameItem) + addView(gameItem.rankItemUi.root) + } + } + + // 更新 item + for (i in 0..MAX_RANK_ITEM_COUNT) { + if (i < gameList.size) { + with(holder.gameItemList[i]){ + rankItemUi.root.visibility = View.VISIBLE + updateGameItem( + holder.gameItemList[i].rankItemUi, + gameList[i], + _data?.exposureEventList, + i, + subjectEntity, + eventHelper, + createTrackData + ) + rankItemUi.orderTv.setTextColor(R.color.title.toColor(root.context)) + rankItemUi.nameTv.setTextColor(R.color.text_primary.toColor(root.context)) + } + } else { + // 数量变化,复用时隐藏多余的 + holder.gameItemList[i].rankItemUi.root.visibility = View.GONE + } + } + + + } + } + } + } + + override fun notifyDownload(download: DownloadEntity) { + viewHolderList.forEach { + it.notifyDownload(download) + } + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + viewHolderList.forEach { + it.notifyDownloadDeleted(status) + } + } + + override fun notifyInstalled(busFour: EBPackage) { + super.notifyInstalled(busFour) + viewHolderList.forEach { + it.notifyInstalled(busFour) + } + } + + override fun onViewAttachedToWindow(holder: RankCollectionItemViewHolder) { + super.onViewAttachedToWindow(holder) + viewHolderList.add(holder) + + } + + override fun onViewDetachedFromWindow(holder: RankCollectionItemViewHolder) { + super.onViewDetachedFromWindow(holder) + viewHolderList.remove(holder) + } + + companion object { + const val MAX_RANK_ITEM_COUNT = 5 + } + + private inner class RankCollectionItemCell(context: Context) : AsyncCell(context) { + var binding: RankCollectionItemCustomBinding? = null + + override val layoutId = R.layout.rank_collection_item_custom + override val switchToWrapContentWhenInflated = true + override fun createDataBindingView(view: View): View? { + binding = RankCollectionItemCustomBinding.bind(view) + return view.rootView + } + } + + inner class RankCollectionItemViewHolder(view: View) : + BaseRecyclerViewHolder(view), IGameChangedNotifier { + + val gameItemList = arrayListOf() + + private var subjectEntity: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = null + + fun initPackageAndPosition(subjectEntity: CustomPageData.LinkColumnCollection.CustomSubjectEntity) { + this.subjectEntity = subjectEntity + } + + override fun notifyDownload(download: DownloadEntity) { + notifyItemWithGameChanged(download.gameId, download.packageName) { + it.getEntryMap()[download.platform] = download + } + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + notifyItemWithGameChanged(status.gameId, status.packageName) { + it.getEntryMap().remove(status.platform) + } + } + + override fun notifyInstalled(busFour: EBPackage) { + checkResetData() + } + + private fun notifyItemWithGameChanged( + gameId: String, + packageName: String, + block: ((GameEntity) -> Unit)? = null + ) { + subjectEntity?.games?.forEachIndexed { index, item -> + val hasPackageName = item.getApk().any { + it.packageName == packageName + } + if (hasPackageName && item.id == gameId) { + block?.invoke(item) + gameItemList.safelyGetInRelease(index)?.run { + updateGameItem( + this.rankItemUi, + item, + _data?.exposureEventList, + index, + subjectEntity, + eventHelper, + createTrackData + ) + } + } + } + } + + private fun checkResetData(rankSubjectEntity: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = subjectEntity) { + rankSubjectEntity ?: return + subjectEntity = rankSubjectEntity + + rankSubjectEntity.games.forEachIndexed { index, item -> + // 因为卡片用了懒加载,所以存在页面还没添加便调用的情况,需特殊处理越界问题 + if (index >= gameItemList.size) { + return@forEachIndexed + } + + gameItemList.safelyGetInRelease(index)?.run { + updateGameItem( + this.rankItemUi, + item, + _data?.exposureEventList, + index, + rankSubjectEntity, + eventHelper, + createTrackData + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRefreshIconAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRefreshIconAdapter.kt new file mode 100644 index 0000000000..95c8d334af --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRefreshIconAdapter.kt @@ -0,0 +1,66 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.gh.common.util.NewFlatLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.databinding.RecyclerRefreshIconItemCustomBinding +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.game.horizontal.GameHorizontalSimpleItemViewHolder +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageData + +class CustomRefreshIconAdapter( + context: Context, + private val eventHelper: GameSubjectCollectionEventHelper, + private val exposureInvoke: (Int, GameEntity) -> Unit +) : CustomBaseChildAdapter(context) { + + private var collectionName = "" + private var collectionId = "" + private var subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = null + + fun setData(collectionName: String, collectionId: String, subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity, list: List) { + this.collectionName = collectionName + this.collectionId = collectionId + this.subject = subject + submitList(list) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RefreshIconChildViewHolder = + RefreshIconChildViewHolder(parent.toBinding()) + + override fun onBindViewHolder(holder: RefreshIconChildViewHolder, position: Int) { + val gameEntity = getItem(position) + + exposureInvoke(position, gameEntity) + + holder.binding.run { + val padR = if (position == itemCount - 1) 16F.dip2px() else 0 + root.setPadding(16F.dip2px(), 0, padR, 0) + gameName.setTextColor(R.color.text_primary.toColor(context)) + gameIcon.displayGameIcon(gameEntity) + GameHorizontalSimpleItemViewHolder.setHorizontalNameAndGravity(gameName, gameEntity.name) + + root.setOnClickListener { + NewFlatLogUtils.logGameListCollectionClick( + "", + "", + "", + collectionName, + collectionId, + "游戏" + ) + eventHelper.navigateToGameDetailPage(position, gameEntity, subject) + } + + } + + } + + class RefreshIconChildViewHolder(val binding: RecyclerRefreshIconItemCustomBinding) : ViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomViewExt.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomViewExt.kt new file mode 100644 index 0000000000..0f62f9b3f4 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomViewExt.kt @@ -0,0 +1,53 @@ +package com.gh.gamecenter.home.custom.adapter + +import android.widget.TextView +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.visibleIf +import com.gh.gamecenter.feature.entity.GameEntity + +object CustomViewExt { + + fun setGameRatting(tvRatting: TextView, briefStyle: String, game: GameEntity) { + tvRatting.goneIf(!(game.commentCount > 3 && briefStyle == "star&brief")) { + tvRatting.text = if (game.star == 10.0f) { + "10" + } else { + "${game.star}" + } + } + } + + fun setGameRattingWithSubject(tvRatting: TextView, showStar: Boolean, game: GameEntity) { + if (showStar) { + tvRatting.visibleIf(game.commentCount > 3) { + tvRatting.text = if (game.star == 10.0f) { + "10" + } else { + "${game.star}" + } + } + } else { + tvRatting.goneIf(true) + } + } + + fun setDescription(tvDesc: TextView, briefStyle: String, game: GameEntity) { + fun getBriefWithSize(): String { + val size = game.getApk().firstOrNull()?.size ?: "" + return if (size.isNotBlank()) { + "$size ${game.brief ?: ""}" + } else { + game.brief ?: "" + } + } + + val brief = when (briefStyle) { + "size&brief" -> getBriefWithSize() + "star&brief" -> game.brief + "recommend" -> game.columnRecommend?.text ?: getBriefWithSize() + + else -> getBriefWithSize() + } + tvDesc.text = brief + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/CommonContentCollectionEventHelper.kt b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/CommonContentCollectionEventHelper.kt new file mode 100644 index 0000000000..04e83f430c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/CommonContentCollectionEventHelper.kt @@ -0,0 +1,29 @@ +package com.gh.gamecenter.home.custom.eventlistener + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.tracker.CommonContentCollectionTracker + +class CommonContentCollectionEventHelper( + viewModel: CustomPageViewModel +) : CustomPageItemChildEventHelper(viewModel) { + + private val tracker = CommonContentCollectionTracker(viewModel.pageLocation) + + fun navigateToLinkPage(link: LinkEntity, text: String, exposureEvent: ExposureEvent?) { + tracker.trackLinkContentCollectionClick(_item, link, text) + viewModel.navigateToLinkPage(_item, link, text, exposureEvent) + } + + fun navigateToGameDetailPage(position: Int, game: GameEntity, text: String) { + tracker.trackLinkContentCollectionClick(_item, null, text) + viewModel.navigateToGameDetailPageByGame(_item, position, game, text) + } + + fun navigateToCommonCollectionDetailPage(link: LinkEntity) { + tracker.trackLinkContentCollectionClick(_item, link, "全部") + viewModel.navigateToCommonCollectionDetailPage(_item, link) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/CustomPageItemChildEventHelper.kt b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/CustomPageItemChildEventHelper.kt new file mode 100644 index 0000000000..da07e62d9f --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/CustomPageItemChildEventHelper.kt @@ -0,0 +1,14 @@ +package com.gh.gamecenter.home.custom.eventlistener + +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.model.CustomPageItem + +open class CustomPageItemChildEventHelper( + protected val viewModel: CustomPageViewModel +) { + + lateinit var _item: CustomPageItem + fun onBind(item: CustomPageItem) { + _item = item + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/GameSubjectCollectionEventHelper.kt b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/GameSubjectCollectionEventHelper.kt new file mode 100644 index 0000000000..227cafdf60 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/GameSubjectCollectionEventHelper.kt @@ -0,0 +1,139 @@ +package com.gh.gamecenter.home.custom.eventlistener + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem +import com.gh.gamecenter.home.custom.tracker.GameSubjectCollectionTracker + +// 专题合集/游戏单合集 点击事件处理 +class GameSubjectCollectionEventHelper( + viewModel: CustomPageViewModel +) : CustomPageItemChildEventHelper(viewModel) { + + private val tracker = GameSubjectCollectionTracker(viewModel.pageLocation) + + private fun isMatch(block: (CustomSubjectCollectionItem) -> Unit) { + (_item as? CustomSubjectCollectionItem)?.let(block) + } + + fun onUserClick(userId: String) { + isMatch { + tracker.trackGameListCollectionClickWithUser(it, userId) + viewModel.navigateUserHomePage(it, userId) + } + } + + fun navigateSubjectCollectionPage( + position: Int, + subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity + ) { + isMatch { + if (it.isSubjectCollection) { + tracker.trackColumnCollectionClick(it, subject, null, "游戏专题") + viewModel.navigateSubjectCollectionPage(it, position, subject) + } else { + tracker.trackGameListCollectionClickWithGameCollection(it, subject.id, subject.title) + viewModel.navigateGameListDetailPage(it, position, subject) + } + } + } + + fun navigateToGameDetailPage(childPosition: Int, game: GameEntity, subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = null) { + isMatch { + if (it.isSubjectCollection) { + tracker.trackColumnCollectionClick(it, subject, game, "游戏") + } else { + tracker.trackGameListCollectionClickWithGame(it, game.id, game.name ?: "") + } + viewModel.navigateToGameDetailPageByGame(it, childPosition, game) + } + } + + fun onDownloadButtonClick(childPosition: Int, gameEntity: GameEntity, subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = null) { + isMatch { + if (it.isSubjectCollection) { + tracker.trackColumnCollectionClick(it, subject, gameEntity, "按钮") + } else { + tracker.trackGameListCollectionClick(it, "按钮") + } + } + } + + fun navigateToLinkPage(link: LinkEntity) { + isMatch { + tracker.trackColumnCollectionClick(it, null, null, "更多", link) + viewModel.navigateToLinkPage(it, link, "", null) + } + } + + fun onRotateRefresh() { + isMatch { + if (it.isSubjectCollection) { + tracker.trackColumnCollectionClick(it, null, null, "刷新") + } else { + tracker.trackGameListCollectionClick(it, "刷新") + } + viewModel.onRotateRefresh(it) + } + + } + + fun onMoreClick(link: LinkEntity) { + isMatch { + if (it.isSubjectCollection) { + tracker.trackColumnCollectionClick(it, null, null, "更多", link) + } else { + tracker.trackGameListCollectionClickWithMore(it, link) + + } + viewModel.navigateToLinkPage(it, link, "更多", null) + } + } + + fun onAllClick() { + isMatch { + if (it.isSubjectCollection) { + tracker.trackColumnCollectionClick(it, null, null, "全部") + + val isRefreshStyle = it.data.style.contains("refresh") + if (isRefreshStyle) { + val showSubject = it.showSubject + if (showSubject != null) { + viewModel.navigateSubjectDetailPage(it, showSubject) + } + } else { + viewModel.navigateSubjectCollectionPage(it, -1, null) + } + } else { + val isRefreshStyle = it.data.style.contains("refresh") + if (isRefreshStyle) { + val subject = it.showSubject + if (subject != null) { + tracker.trackGameListCollectionClickWithGameCollection( + it, + it.showSubject?.id ?: "", + it.showSubject?.title ?: "" + ) + viewModel.navigateGameListDetailPage(it, -1, subject) + } else { + tracker.trackGameListCollectionClick(it, "全部") + viewModel.navigateGameListCollectionDetailPage(it) + } + + } else { + tracker.trackGameListCollectionClick(it, "全部") + viewModel.navigateGameListCollectionDetailPage(it) + } + } + } + } + + fun navigateGameListSquare() { + isMatch { + tracker.trackGameListCollectionClick(it, "游戏单广场") + viewModel.navigateToGameListSquare(it) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/OnCustomPageEventListener.kt b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/OnCustomPageEventListener.kt new file mode 100644 index 0000000000..4095e130e4 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/OnCustomPageEventListener.kt @@ -0,0 +1,130 @@ +package com.gh.gamecenter.home.custom.eventlistener + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.entity.HomeSubSlide +import com.gh.gamecenter.entity.RatingComment +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem + +interface OnCustomPageEventListener { + + /** + * 刷新 + */ + fun onRefresh() + + fun onLoadMore() + + fun onRetry() + + /** + * 轮换刷新(专题合集/游戏单合集) + */ + fun onRotateRefresh(customSubjectCollectionItem: CustomSubjectCollectionItem) + + /** + * 换一批 + */ + fun onChangeABatch(subjectEntity: SubjectEntity, position: Int) + + fun onChangeAppBarColor(color: Int) + + fun loadSlideDiscoverCardGames(homeSubSlide: HomeSubSlide) + + /** + * 通过Game进入游戏详情 + */ + fun navigateToGameDetailPageByGame( + item: CustomPageItem, + childPosition: Int, + game: GameEntity, + text: String = "游戏" + ) + + /** + * 点击游戏下载按钮 + */ + fun onDownloadButtonClick(item: CustomPageItem, childPosition: Int, game: GameEntity) + + /** + * 点击进入游戏专题 + */ + fun navigateSubjectPage(item: CustomPageItem, subject: SubjectEntity) + + /** + * 点击进入专题详情 + */ + fun navigateSubjectDetailPage(item: CustomSubjectCollectionItem, subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity) + + + /** + * 跳转至专题合集 + */ + fun navigateSubjectCollectionPage( + item: CustomSubjectCollectionItem, + childPosition: Int, + subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity? + ) + + /** + * 跳转至游戏单详情 + */ + fun navigateGameListDetailPage( + item: CustomSubjectCollectionItem, + childPosition: Int, + subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity + ) + + /** + * 跳转至用户主页 + */ + fun navigateUserHomePage(item: CustomPageItem, userId: String) + + /** + * 跳转至游戏偏好页 + */ + fun navigateToInterestedGamePage(item: CustomPageItem) + + /** + * 跳转至猜你喜欢 + */ + fun navigateToDiscoveryPage(item: CustomPageItem) + + /** + * 通过通用链接跳转页面 + */ + fun navigateToLinkPage(item: CustomPageItem, link: LinkEntity, text: String = "", exposureEvent: ExposureEvent?) + + /** + * 跳转至安利墙 + */ + fun navigateToAmway(item: CustomPageItem) + + /** + * 跳转至徽章墙 + */ + fun navigateToBadgeWall(item: CustomPageItem, comment: RatingComment) + + /** + * 通过 gameId 跳转至游戏详情页 + */ + fun navigateToGameDetailPageById(item: CustomPageItem, gameId: String, gameName: String?, childPosition: Int) + + fun navigateToRatingReplyPage(item: CustomPageItem, gameId: String, commentId: String) + + fun navigateToCommonCollectionDetailPage(item: CustomPageItem, link: LinkEntity) + + /** + * 跳转至游戏单合集详情 + */ + fun navigateGameListCollectionDetailPage(item: CustomSubjectCollectionItem) + + /** + * 跳转至游戏单广场 + */ + fun navigateToGameListSquare(item: CustomSubjectCollectionItem) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/OtherItemEventHelper.kt b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/OtherItemEventHelper.kt new file mode 100644 index 0000000000..6f865f2123 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/OtherItemEventHelper.kt @@ -0,0 +1,80 @@ +package com.gh.gamecenter.home.custom.eventlistener + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.entity.RatingComment +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem +import com.gh.gamecenter.home.custom.tracker.OtherItemTracker + +class OtherItemEventHelper( + viewModel: CustomPageViewModel +) : CustomPageItemChildEventHelper(viewModel) { + + private val tracker = OtherItemTracker(viewModel.pageLocation) + + fun navigateToGameDetailPageByGame(childPosition: Int, game: GameEntity) { + viewModel.navigateToGameDetailPageByGame(_item, childPosition, game) + } + + fun onDownloadButtonClick(childPosition: Int, game: GameEntity) { + viewModel.onDownloadButtonClick(_item, childPosition, game) + } + + fun navigateSubjectCollectionPage( + childPosition: Int, + subjectEntity: CustomPageData.LinkColumnCollection.CustomSubjectEntity, + ) { + val item = _item + if (item is CustomSubjectCollectionItem) { + if (item.isSubjectCollection) { + viewModel.navigateSubjectCollectionPage(item, childPosition, subjectEntity) + } else { + viewModel.navigateGameListDetailPage(item, childPosition, subjectEntity) + } + + } + } + + fun onUserClick(userId: String) { + viewModel.navigateUserHomePage(_item, userId) + } + + fun navigateToAmway() { + viewModel.navigateToAmway(_item) + } + + fun navigateToBadgeWall(comment: RatingComment) { + viewModel.navigateToBadgeWall(_item, comment) + } + + fun navigateToLinkPage(link: LinkEntity) { + viewModel.navigateToLinkPage(_item, link, "", null) + } + + fun navigateToGameDetailPageById(gameId: String, gameName: String?, childPosition: Int) { + viewModel.navigateToGameDetailPageById(_item, gameId, gameName, childPosition) + } + + fun directToLinkPage(linkEntity: LinkEntity) { + tracker.trackContentCardClick(_item, linkEntity) + viewModel.navigateToLinkPage(_item, linkEntity, "", null) + } + + fun navigateToRatingReplyPage(gameId: String, commentId: String) { + viewModel.navigateToRatingReplyPage(_item, gameId, commentId) + } + + fun navigateUserHomePage(userId: String) { + viewModel.navigateUserHomePage(_item, userId) + } + + fun navigateToInterestedGamePage() { + viewModel.navigateToInterestedGamePage(_item) + } + + fun navigateToDiscoveryPage() { + viewModel.navigateToDiscoveryPage(_item) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/SubjectEventHelper.kt b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/SubjectEventHelper.kt new file mode 100644 index 0000000000..354e1dfb2e --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/eventlistener/SubjectEventHelper.kt @@ -0,0 +1,52 @@ +package com.gh.gamecenter.home.custom.eventlistener + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.model.CustomSubjectItem +import com.gh.gamecenter.home.custom.tracker.SubjectTracker + +class SubjectEventHelper(viewModel: CustomPageViewModel) : CustomPageItemChildEventHelper(viewModel) { + + private val tracker = SubjectTracker(viewModel.pageLocation) + + fun navigateToGameDetailPage(position: Int, gameEntity: GameEntity) { + if (gameEntity.isQQMiniGame()) { + tracker.trackQQGameClick(_item, gameEntity) + } else { + tracker.trackColumnClick(_item, gameEntity, "游戏") + } + + viewModel.navigateToGameDetailPageByGame(_item, position, gameEntity) + } + + fun onDownloadButtonClick(childPosition: Int, gameEntity: GameEntity) { + tracker.trackColumnClick(_item, gameEntity, "按钮") + } + + fun navigateSubjectPage() { + tracker.trackColumnClick(_item, null, "游戏专题") + (_item as? CustomSubjectItem)?.data?.let { + viewModel.navigateSubjectPage(_item, it) + } + + } + + fun onChangeABatch(subject: SubjectEntity) { + tracker.trackColumnClick(_item, null, "右上角", "换一批") + viewModel.onChangeABatch(subject, _item.position) + } + + fun onMoreClick(link: LinkEntity) { + tracker.trackColumnClick(_item, null, "右上角", "更多", link) + viewModel.navigateToLinkPage(_item, link, "", null) + } + + fun onAllClick(subject: SubjectEntity) { + tracker.trackColumnClick(_item, null, "右上角", "全部") + viewModel.navigateSubjectPage(_item, subject) + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomFloatView.kt b/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomFloatView.kt new file mode 100644 index 0000000000..37957b75e7 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomFloatView.kt @@ -0,0 +1,109 @@ +package com.gh.gamecenter.home.custom.floatview + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.FrameLayout +import com.gh.common.exposure.ExposureManager +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.visibleIf +import com.gh.gamecenter.databinding.LayoutFloatingWindowCustomBinding +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.feature.entity.WelcomeDialogEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.floatingwindow.FloatingWindowEntity + +class CustomFloatView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + private val binding: LayoutFloatingWindowCustomBinding = + LayoutFloatingWindowCustomBinding.inflate(LayoutInflater.from(context), this, true) + + private val adapter by lazy { + CustomFloatingWindowViewAdapter(context, object : CustomFloatingWindowViewAdapter.Callback { + override fun onItemExpandCallback(entity: WelcomeDialogEntity) { + // 展开大图时,需要隐藏悬浮窗 + visibility = GONE + expandListener?.invoke(entity, true) + } + + override fun onItemDismissClickedCallback(position: Int) { + binding.horizontalScrollView.removeIndicator(position) + } + + override fun onDataListEmptyCallback() { + visibleIf(false) + } + + }) + } + + fun setData(entities: ArrayList, pageLocation: PageLocation, basicExposureSourceList: List) { + entities.forEachIndexed { index, entity -> + entity.link.traceEvent = ExposureEvent.createEventWithSourceConcat( + GameEntity(sequence = index), + basicExposureSourceList, + listOf(ExposureSource("右下悬浮窗", entity.id)) + ) + } + + adapter.submitList(entities) + adapter.pageLocation = pageLocation + binding.horizontalScrollView.setFixedWidthAndHeightForViewPager2(64F.dip2px(), 64F.dip2px()) + binding.horizontalScrollView.disableAutoScroll() + binding.horizontalScrollView.start(entities, false, adapter) { + val entity = entities.getOrNull(it) + val link = entity?.link + SensorsBridge.trackSuspendedWindowShow( + "收起", + "自定义页面", + bottomTab = pageLocation.bottomTab, + multiTabId = pageLocation.severalTabPageId, + multiTabName = pageLocation.severalTabPageName, + position = pageLocation.tabPosition, + tabContent = pageLocation.tabContent, + customPageId = pageLocation.pageId, + customPageName = pageLocation.pageName, + linkText = link?.text ?: "", + linkId = link?.link ?: "", + linkType = link?.type ?: "", + ) + entity?.link?.traceEvent?.let { exposureEvent -> ExposureManager.log(exposureEvent) } + } + + + // 是否需要自动展开大图 + entities.find { + it.autoExpandBigPopup + }?.let { + post { + it.link.icon = it.expandedImage + it.link.floatingWindowId = it.id + visibility = INVISIBLE + expandListener?.invoke(it.link, false) + } + } + + + } + + override fun setVisibility(visibility: Int) { + if (adapter.dataList.isEmpty()) { + super.setVisibility(GONE) + } else { + super.setVisibility(visibility) + } + } + + private var expandListener: ((WelcomeDialogEntity, Boolean) -> Unit)? = null + fun setExpandListener(listener: (WelcomeDialogEntity, hasAnimator: Boolean) -> Unit) { + expandListener = listener + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomFloatViewAnimationHelper.kt b/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomFloatViewAnimationHelper.kt new file mode 100644 index 0000000000..abfe007e8c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomFloatViewAnimationHelper.kt @@ -0,0 +1,137 @@ +package com.gh.gamecenter.home.custom.floatview + +import android.os.Handler +import android.os.Looper +import android.view.View +import androidx.annotation.IntDef +import androidx.core.view.ViewCompat +import androidx.core.view.ViewPropertyAnimatorListener +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.visibleIf + +/** + * 自定义页面左下角悬浮窗管理类 + * + */ +class CustomFloatViewAnimationHelper : DefaultLifecycleObserver { + + private var floatView: View? = null + + private var floatState: Int = VIEW_STATE_EXPAND + + private var isScrolling = false + + private val handler = Handler(Looper.getMainLooper()) + + fun show(floatView: View) { + this.floatView = floatView + floatView.visibleIf(true) + } + + fun dismiss() { + floatView?.visibleIf(false) + } + + fun resume() { + floatView?.visibleIf(true) + } + + // 页面滚动状态变化 + fun onScrollChanged(isScrolling: Boolean) { + this.isScrolling = isScrolling + handler.removeCallbacksAndMessages(null) + if (isScrolling) { + if (floatState == VIEW_STATE_EXPAND) { + // 如果悬浮窗处于展开状态,则立即执行收起动画 + collapse() + } + } else { + if (floatState == VIEW_STATE_COLLAPSE || floatState == VIEW_STATE_COLLAPSING) { + expandDelay() + } + } + } + + + private fun expandDelay() { + handler.postDelayed({ + expand() + }, EXPAND_DELAY) + } + + private fun expand() { + floatView?.startAnimation(true) + } + + private fun collapse() { + floatView?.startAnimation(false) + } + + private fun View.startAnimation(isExpand: Boolean) { + val startState = if (isExpand) VIEW_STATE_EXPANDING else VIEW_STATE_COLLAPSING + val endState = if (isExpand) VIEW_STATE_EXPAND else VIEW_STATE_COLLAPSE + val alpha = if (isExpand) 1F else 0.6F + val translationX = if (isExpand) 0F else (width + 8F.dip2px()) / 2F + + ViewCompat.animate(this) + .setListener(object : ViewPropertyAnimatorListener { + override fun onAnimationStart(view: View?) { + floatState = startState + } + + override fun onAnimationEnd(view: View?) { + floatState = endState + if (isExpand && isScrolling) { + // 执行展开动画之后,发现页面仍在滚动,则立马执行收起动画 + collapse() + } + } + + override fun onAnimationCancel(view: View?) = Unit + }) + .alpha(alpha) + .translationX(translationX) + .setDuration(SHRINK_AND_EXPAND_ANIMATION_DURATION) + .start() + } + + override fun onDestroy(owner: LifecycleOwner) { + super.onDestroy(owner) + handler.removeCallbacksAndMessages(null) + floatView?.animate()?.cancel() + floatView = null + } + + companion object { + + // 外界停止滚动 + const val SCROLL_STATE_IDLE = 0 + + // 外界正在滚动 + const val SCROLL_STATE_SCROLLING = 1 + + @IntDef(SCROLL_STATE_IDLE, SCROLL_STATE_SCROLLING) + @Retention(AnnotationRetention.SOURCE) + annotation class ScrollState + + // 悬浮的View当前的状态 + // 展开状态 + private const val VIEW_STATE_EXPAND = 0 + + // 正在执行动画 展开 ->收起 或 收起 -> 展开 + private const val VIEW_STATE_EXPANDING = 1 + + // 正在执行动画 收起 -> expand -> collapsing + private const val VIEW_STATE_COLLAPSING = 2 + + // 收起状态 + private const val VIEW_STATE_COLLAPSE = 3 + + + private const val SHRINK_AND_EXPAND_ANIMATION_DURATION = 300L + private const val EXPAND_DELAY = 1000L + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomFloatingWindowViewAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomFloatingWindowViewAdapter.kt new file mode 100644 index 0000000000..2653ec5fe8 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomFloatingWindowViewAdapter.kt @@ -0,0 +1,176 @@ +package com.gh.gamecenter.home.custom.floatview + +import android.content.Context +import android.net.Uri +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.alibaba.android.arouter.launcher.ARouter +import com.gh.common.exposure.ExposureManager +import com.gh.common.exposure.ExposureTraceUtils +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.constant.RouteConsts +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.WrapContentDraweeView +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.feature.entity.WelcomeDialogEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.ExposureType +import com.gh.gamecenter.feature.provider.ILinkDirectUtilsProvider +import com.gh.gamecenter.floatingwindow.FloatingWindowEntity +import com.gh.gamecenter.floatingwindow.databinding.LayoutFloatingWindowItemBinding +import com.gh.gamecenter.home.custom.adapter.CustomBaseChildAdapter + +class CustomFloatingWindowViewAdapter( + context: Context, + private var mCallback: Callback +) : CustomBaseChildAdapter(context) { + var pageLocation: PageLocation? = null + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): FloatingWindowItemViewHolder { + return FloatingWindowItemViewHolder(parent.toBinding()) + } + + override fun getItemCount(): Int = if (dataList.size <= 1) dataList.size else Int.MAX_VALUE + + override fun onBindViewHolder(holder: FloatingWindowItemViewHolder, position: Int) { + val realPosition = position % dataList.size + + val windowEntity = dataList[realPosition] + + val iv = holder.binding.imageIv + val closeView = holder.binding.closeIv + + if (windowEntity.pushType == "always") { + closeView.visibility = View.GONE + } else { + closeView.enlargeTouchArea(5) + closeView.setOnClickListener { + // 很丑陋,但暂时先这样处理 + _dataList.removeAt(realPosition) + notifyDataSetChanged() + mCallback.onItemDismissClickedCallback(realPosition) + + if (dataList.isEmpty()) { + mCallback.onDataListEmptyCallback() + } + + SensorsBridge.trackSuspendedWindowClick( + "收起", + "关闭", + "自定义页面", + bottomTab = pageLocation?.bottomTab ?: "", + multiTabId = pageLocation?.severalTabPageId ?: "", + multiTabName = pageLocation?.severalTabPageName ?: "", + position = pageLocation?.tabPosition ?: -1, + tabContent = pageLocation?.tabContent ?: "", + customPageId = pageLocation?.pageId ?: "", + customPageName = pageLocation?.pageName ?: "", + linkId = windowEntity.link.link ?: "", + linkType = windowEntity.link.type ?: "", + linkText = windowEntity.link.text ?: "", + ) + } + } + + // 限制最大宽度为 256dp,后续根据图片大小进行处理 + iv.setTag(ImageUtils.TAG_TARGET_WIDTH, 256F.dip2px()) + // 添加加载回调,避免网络差出现图片没有但是有关闭按钮的情况 + iv.registerLoadingCallback(object : WrapContentDraweeView.LoadingCallback { + override fun loaded() { + if (windowEntity.pushType != "always") { + closeView.visibility = View.VISIBLE + } + } + }) + iv.setMaxAndMinSizeLimit(64, 48) + iv.setSubsampleSize(4) + ImageUtils.display(iv, windowEntity.image) + + // 预加载需要展开的大图到本地,避免执行展开动画时图片为空 + if (windowEntity.expandable) { + ImageUtils.prefetchToDiskCache( + windowEntity.expandedImage) + } + + holder.binding.imageIv.setOnClickListener { + if (windowEntity.expandable) { + windowEntity.link.icon = windowEntity.expandedImage + + windowEntity.link.floatingWindowId = windowEntity.id + mCallback.onItemExpandCallback(windowEntity.link) + + SensorsBridge.trackSuspendedWindowClick( + "收起", + "展开", + "自定义页面", + bottomTab = pageLocation?.bottomTab ?: "", + multiTabId = pageLocation?.severalTabPageId ?: "", + multiTabName = pageLocation?.severalTabPageName ?: "", + position = pageLocation?.tabPosition ?: -1, + tabContent = pageLocation?.tabContent ?: "", + customPageId = pageLocation?.pageId ?: "", + customPageName = pageLocation?.pageName ?: "", + linkId = windowEntity.link.link ?: "", + linkType = windowEntity.link.type ?: "", + linkText = windowEntity.link.text ?: "", + ) + } else { + val directUtils = ARouter.getInstance().build(RouteConsts.provider.linkDirectUtils) + .navigation() as ILinkDirectUtilsProvider + if (windowEntity.link.type == EntranceConsts.HOST_WEB + || windowEntity.link.type == EntranceConsts.HOST_WEB_INURL + || windowEntity.link.type == EntranceConsts.HOST_WEB_AL + ) { + val uri = Uri.parse(windowEntity.link.link) + .buildUpon() + .appendQueryParameter(Constants.URL_QUERY_FROM_FLOATING_WINDOW, "true") + .build() + windowEntity.link.link = uri.toString() + } + val clickEvent = ExposureEvent.createEvent( + GameEntity().apply { sequence = position }, + source = windowEntity.link.traceEvent?.source ?: emptyList(), + event = ExposureType.CLICK, + eTrace = ExposureTraceUtils.appendTrace(windowEntity.link.traceEvent) + ) + if (windowEntity.link.type != "game") { + ExposureManager.log(clickEvent) + } + directUtils.directToLinkPage(it.context, windowEntity.link, "右下角悬浮窗", "", clickEvent) + SensorsBridge.trackSuspendedWindowClick( + "收起", + "跳转", + "自定义页面", + bottomTab = pageLocation?.bottomTab ?: "", + multiTabId = pageLocation?.severalTabPageId ?: "", + multiTabName = pageLocation?.severalTabPageName ?: "", + position = pageLocation?.tabPosition ?: -1, + tabContent = pageLocation?.tabContent ?: "", + customPageId = pageLocation?.pageId ?: "", + customPageName = pageLocation?.pageName ?: "", + linkId = windowEntity.link.link ?: "", + linkType = windowEntity.link.type ?: "", + linkText = windowEntity.link.text ?: "", + ) + } + } + } + + class FloatingWindowItemViewHolder(val binding: LayoutFloatingWindowItemBinding) : + RecyclerView.ViewHolder(binding.root) + + interface Callback { + fun onItemExpandCallback(entity: WelcomeDialogEntity) + + fun onItemDismissClickedCallback(position: Int) + + fun onDataListEmptyCallback() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomHorizontalScrollableView.kt b/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomHorizontalScrollableView.kt new file mode 100644 index 0000000000..95af19b361 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/floatview/CustomHorizontalScrollableView.kt @@ -0,0 +1,226 @@ +package com.gh.gamecenter.home.custom.floatview + +import android.content.Context +import android.util.AttributeSet +import android.view.* +import android.widget.LinearLayout +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.gh.gamecenter.common.R +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.rxTimer +import com.gh.gamecenter.core.utils.DisplayUtils +import io.reactivex.disposables.Disposable +import kotlin.math.abs + +/** + * 横向可滑动控件 + */ +class CustomHorizontalScrollableView : LinearLayout { + + private var mDataList = ArrayList() + private var currentPage = 0 + private var autoDurationTime = 6000L + private var mRxTimer: Disposable? = null + private var lastX: Float = 0F + private var lastY: Float = 0F + private var startX: Float = 0F + private var startY: Float = 0F + private val mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop + private lateinit var mIndicatorLayout: LinearLayout + private lateinit var mViewPager2: ViewPager2 + private var mWidth = ViewGroup.LayoutParams.WRAP_CONTENT + private var mHeight = ViewGroup.LayoutParams.WRAP_CONTENT + + private var mAutoScrollEnabled = true + + private var onPageSelectedAction: ((Int) -> Unit)? = null + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + init { + initViews() + } + + fun disableAutoScroll() { + mAutoScrollEnabled = false + } + + fun setFixedWidthAndHeightForViewPager2(widthInDp: Int, heightInDp: Int) { + mWidth = widthInDp + mHeight = heightInDp + mViewPager2.layoutParams.apply { + width = mWidth + height = mHeight + } + } + + private fun initViews() { + orientation = VERTICAL + mViewPager2 = ViewPager2(context).apply { + orientation = ViewPager2.ORIENTATION_HORIZONTAL + registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + currentPage = position + if (mDataList.isNotEmpty()) { + slideIndicator(currentPage % mDataList.size) + } + onPageSelectedAction?.invoke(position) + } + }) + } + val recyclerView = mViewPager2.getChildAt(0) as RecyclerView + recyclerView.overScrollMode = RecyclerView.OVER_SCROLL_NEVER + addView(mViewPager2) + mIndicatorLayout = LinearLayout(context).apply { + orientation = HORIZONTAL + gravity = Gravity.CENTER + val params = LayoutParams(LayoutParams.MATCH_PARENT, 12F.dip2px()) + params.topMargin = 4F.dip2px() + params.leftMargin = 6F.dip2px() + params.rightMargin = 6F.dip2px() + layoutParams = params + background = context.resources.getDrawable(R.drawable.background_shape_10p_black_radius_6) + } + addView(mIndicatorLayout) + } + + fun start( + datas: ArrayList<*>, + startFromCenter: Boolean, + newAdapter: RecyclerView.Adapter<*>, + onPageSelectedAction: ((Int) -> Unit)? = null + ) { + if (datas.isEmpty()) throw IllegalArgumentException("列表不能为空") + val adapter = mViewPager2.adapter + this.onPageSelectedAction = onPageSelectedAction + mDataList = datas as ArrayList + if (adapter == null) { + mViewPager2.adapter = newAdapter + } else { + adapter.notifyDataSetChanged() + } + currentPage = if (startFromCenter) getActualFirstPositionInCenter() else 0 + mViewPager2.setCurrentItem(currentPage, false) + if (mDataList.size >= 1) { + resetIndicator() + autoPlay() + } + } + + private fun autoPlay() { + if (mAutoScrollEnabled && mDataList.size > 1) { + if (mRxTimer != null && !mRxTimer!!.isDisposed) { + mRxTimer?.dispose() + mRxTimer = null + } + mRxTimer = rxTimer(autoDurationTime) { + slidePage() + } + } + } + + private fun slidePage() { + currentPage++ + mViewPager2.setCurrentItem(currentPage, true) + } + + fun resumePage() { + autoPlay() + } + + fun pausePage() { + if (mRxTimer != null && !mRxTimer!!.isDisposed) { + mRxTimer?.dispose() + mRxTimer = null + } + } + + fun resetIndicator() { + mIndicatorLayout.removeAllViews() + + if (mDataList.size <= 1) { + mIndicatorLayout.visibility = View.GONE + return + } + + mDataList.forEach { _ -> + val view = View(context).apply { + background = ContextCompat.getDrawable(context, R.drawable.selector_video_detail_ad_indicator) + val params = LayoutParams(DisplayUtils.dip2px(4F), DisplayUtils.dip2px(4F)) + params.leftMargin = DisplayUtils.dip2px(2F) + params.rightMargin = DisplayUtils.dip2px(2F) + layoutParams = params + } + mIndicatorLayout.addView(view) + } + + if (mDataList.isNotEmpty()) { + slideIndicator(currentPage % mDataList.size) + } + } + + private fun slideIndicator(position: Int) { + for (i in 0 until mIndicatorLayout.childCount) { + val childAt = mIndicatorLayout.getChildAt(i) + childAt.isSelected = i == position + } + } + + private fun getActualFirstPositionInCenter(): Int { + if (mDataList.size == 1) return 0 + var index = Int.MAX_VALUE / 2 + if (index % mDataList.size != 0) { + index -= (index % mDataList.size) + } + return index - 1 + } + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + val action = ev.action + if (action == MotionEvent.ACTION_DOWN) { + lastX = ev.rawX + startX = lastX + lastY = ev.rawY + startY = lastY + } else if (action == MotionEvent.ACTION_MOVE) { + lastX = ev.rawX + lastY = ev.rawY + val distanceX = abs(lastX - startX) + val distanceY = abs(lastY - startY) + val disallowIntercept: Boolean = if (mViewPager2.orientation == ViewPager2.ORIENTATION_HORIZONTAL) { + distanceX > mTouchSlop && distanceX > distanceY + } else { + distanceY > mTouchSlop && distanceY > distanceX + } + parent.requestDisallowInterceptTouchEvent(disallowIntercept) + } else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { + return abs(lastX - startX) > mTouchSlop || abs(lastY - startY) > mTouchSlop + } + return super.onInterceptTouchEvent(ev) + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + if (mRxTimer != null && !mRxTimer!!.isDisposed) { + mRxTimer?.dispose() + mRxTimer = null + } + } + + fun getAdDatas(): ArrayList { + return mDataList + } + + fun removeIndicator(position: Int) { + if (position < mDataList.size) { + mDataList.removeAt(position) + } + resetIndicator() + } + +} \ No newline at end of file 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 new file mode 100644 index 0000000000..a38fe3acfc --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt @@ -0,0 +1,437 @@ +package com.gh.gamecenter.home.custom.model + +import com.gh.common.filter.RegionSettingHelper +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.entity.* +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.floatingwindow.FloatingWindowEntity +import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.IgnoredOnParcel + +class CustomPageData( + @SerializedName("title") + private val _title: String? = null, + @SerializedName("page_switch") + private val _pageSwitch: PageSwitch? = null, + @SerializedName("link_suspended_window") + val linkSuspendedWindow: FloatingWindowEntity? = null, + @SerializedName("link_pull_down_push") + val linkPullDownPush: PullDownPush? = null, + @SerializedName("link_custom_page") + private val _customsComponents: List? = null +) { + + val title: String + get() = _title ?: "" + + val pageSwitch: PageSwitch + get() = _pageSwitch ?: PageSwitch() + + val customsComponents: List + get() = _customsComponents ?: emptyList() + + + data class PageSwitch( + @SerializedName("top_search") + private val _topSearch: String? = null, + @SerializedName("pull_down_push") + private val _pullDownPush: Boolean? = null, + @SerializedName("suspended_window") + private val _suspendedWindow: Boolean? = null + ) { + + val topSearch: String + get() = _topSearch ?: "" + + val pullDownPush: Boolean + get() = _pullDownPush ?: false + + val suspendedWindow: Boolean + get() = _suspendedWindow ?: false + } + + data class CustomsComponent( + @SerializedName("link") + private val _link: LinkEntity? = null, + @SerializedName("link_column") + val linkColumn: SubjectEntity? = null, + @SerializedName("link_game") + val game: GameEntity? = null, + @SerializedName("link_column_collection") + val linkColumnCollection: LinkColumnCollection? = null, + @SerializedName("link_game_list_collection") + val linkGameListCollection: LinkColumnCollection? = null, + @SerializedName("link_content_card") + val contentCard: ContentCard? = null, + @SerializedName("link_column_test_v2") + val homeItemTestV2: HomeItemTestV2Entity? = null, + @SerializedName("link_top_game_comment") + val linkTopGameComments: List? = null, + @SerializedName("link_qq_mini_game_column_detail") + val qqMiniGameColumn: SubjectEntity? = null, + @SerializedName("link_common_collection") + val linkCommonCollection: CommonContentCollection? = null + ) { + // 游戏专题 + val gameSubjectEntity: SubjectEntity? + get() = linkColumn ?: qqMiniGameColumn + + val link: LinkEntity + get() = _link ?: LinkEntity() + } + + data class LinkColumn( + @SerializedName("_id") + private val _id: String? = null, + @SerializedName("name") + private val _name: String? = null, + @SerializedName("show_name") + private val _showName: Boolean? = null, + @SerializedName("show_star") + private val _showStar: Boolean? = null, + @SerializedName("show_download") + private val _showDownload: Boolean? = null, + @SerializedName("order") + private val _order: Boolean? = null, + @SerializedName("more") + private val _more: Int? = null, + @SerializedName("list") + private val _list: Int? = null + ) + + data class LinkColumnCollection( + @SerializedName("_id") + private val _id: String? = null, + @SerializedName("name") + private val _name: String? = null, + @SerializedName("style") + private val _style: String? = null, + @SerializedName("style_setting") + private val _styleSetting: StyleSetting? = null, + @SerializedName("data") + private var _data: List? = null, + @SerializedName("game_platform") + private val _gamePlatform: String? = null, + @SerializedName("explain") + private val _explain: String? = null + ) { + + val id: String + get() = _id ?: "" + + val name: String + get() = _name ?: "" + + val style: String + get() = _style ?: "" + + val styleSetting: StyleSetting + get() = _styleSetting ?: StyleSetting() + + + var data: List + get() = _data ?: emptyList() + set(value) { + _data = value + } + + val gamePlatform: String + get() = _gamePlatform ?: "" + + val isQqMini: Boolean + get() = gamePlatform == "qq_mini" + + val explain: String + get() = _explain ?: "" + + data class StyleSetting( + @SerializedName("size") + private val _size: Int? = null, + @SerializedName("hide_name") + private val _hideName: Boolean? = null, + @SerializedName("right_top") + private val _rightTop: String? = null, + @SerializedName("right_top_link") + private val _rightTopLink: LinkEntity? = null, + @SerializedName("rows_num") + private val _rowsNum: Int? = null + ) { + + val size: Int + get() = _size ?: 0 + + val hideName: Boolean + get() = _hideName ?: false + + val rightTop: String + get() = _rightTop ?: "" + + val rightTopLink: LinkEntity + get() = _rightTopLink ?: LinkEntity() + + val rowsNum: Int + get() = _rowsNum ?: 0 + } + + /** + * 此实体包含了旧的专题合集与游戏单的所有字段 + */ + data class CustomSubjectEntity( + @SerializedName("_id") + private val _id: String? = null, + @SerializedName("_seq") + private val _seq: Int? = null, + @SerializedName("image") + private val _image: String? = null, + @SerializedName("title") + private val _title: String? = null, + @SerializedName("intro") + private val _intro: String? = null, + @SerializedName("ad_icon_active") + private val _adIconActive: Boolean? = null, + @SerializedName("games") + private var _games: List? = null, + @SerializedName("user") + private val _user: User? = null, + @SerializedName("count") + private val _count: Count? = null, + @SerializedName("time") + private val _time: Long? = null, + @SerializedName("stamp") + private val _stamp: String? = null, + @SerializedName("style") + private val _style: String? = null + ) { + + val id: String + get() = _id ?: "" + + val seq: Int + get() = _seq ?: 0 + + val image: String + get() = _image ?: "" + + val title: String + get() = _title ?: "" + + val intro: String + get() = _intro ?: "" + + val adIconActive: Boolean + get() = _adIconActive ?: false + + var games: List + get() = RegionSettingHelper.filterGame(_games) + set(value) { + _games = value + } + + val user: User + get() = _user ?: User() + + val count: Count + get() = _count ?: Count() + + val time: Long + get() = _time ?: 0 + + val stamp: String + get() = _stamp ?: "" + + val style: String + get() = _style ?: "" + + @IgnoredOnParcel + val styleChinese: String + get() = CustomPageItem.subjectTypeToComponentStyle[style] ?: "" + + data class User( + @SerializedName("_id") + private val _id: String? = null, + @SerializedName("name") + private val _name: String? = null, + @SerializedName("icon") + private val _icon: String? = null + ) { + + val id: String + get() = _id ?: "" + + val name: String + get() = _name ?: "" + + val icon: String + get() = _icon ?: "" + + val isValid: Boolean + get() = id.isNotBlank() + } + + data class Count( + @SerializedName("vote") + private val _vote: Int? = null, + @SerializedName("hot") + private val _hot: Int? = null, + @SerializedName("favorite") + private val _favorite: Int? = null, + @SerializedName("share") + private val _share: Int? = null, + @SerializedName("comment") + private val _comment: Int? = null, + @SerializedName("vote_comment") + private val _voteComment: Int? = null, + @SerializedName("real_hot") + private val _realHot: Int? = null, + @SerializedName("game") + private val _game: Int? = null, + @SerializedName("download") + private val _download: Int? = null, + @SerializedName("game_played") + private val _gamePlayed: Int? = null + ) { + + val vote: Int + get() = _vote ?: 0 + + val hot: Int + get() = _hot ?: 0 + + val favorite: Int + get() = _favorite ?: 0 + + val share: Int + get() = _share ?: 0 + + val comment: Int + get() = _comment ?: 0 + + val voteComment: Int + get() = _voteComment ?: 0 + + val realHot: Int + get() = _realHot ?: 0 + + val game: Int + get() = _game ?: 0 + + val download: Int + get() = _download ?: 0 + + val gamePlayed: Int + get() = _gamePlayed ?: 0 + } + } + } + + data class ContentCard( + @SerializedName("img") + private val _img: String? = null, + @SerializedName("first_line_recommend") + private val _firstLineRecommend: String? = null, + @SerializedName("second_line_recommend") + private val _secondLineRecommend: String? = null, + @SerializedName("recommend_tag") + private val _recommendTag: String? = null + ) { + val img: String + get() = _img ?: "" + + val firstLineRecommend: String + get() = _firstLineRecommend ?: "" + + val secondLineRecommend: String + get() = _secondLineRecommend ?: "" + + val recommendTag: String + get() = _recommendTag ?: "" + } + + data class CommonContentCollection( + @SerializedName("_id") + private val _id: String? = null, + @SerializedName("name") + private val _name: String? = null, + @SerializedName("detail_style") + private val _detailStyle: String? = null, + @SerializedName("layout") + private val _layout: Int? = null, + @SerializedName("primary_topic_count") + private val _primaryTopicCount: Int? = null, + @SerializedName("top_right_text") + private val _topRightText: String? = null, + @SerializedName("vertical_line") + private val _verticalLine: String? = null, + @SerializedName("slides") + private val _slides: Slides? = null, + @SerializedName("data") + private val _data: List? = null, + @SerializedName("navigations") + private val _navigations: List? = null, + @SerializedName("recommends") + private val _recommends: List? = null, + ) { + + val id: String + get() = _id ?: "" + + val name: String + get() = _name ?: "" + + val detailStyle: String + get() = _detailStyle ?: "" + + val layout: Int + get() = _layout ?: 0 + + val layoutChinese: String + get() = when (layout) { + 0 -> "轮播banner" + 1 -> "导航栏" + 2 -> "金刚区" + 3 -> "横向滑动banner" + 4 -> "双列banner" + 5 -> "横排竖式卡片" + 6 -> "双列竖式卡片" + 7 -> "竖式图文列表" + 8 -> "横排图文列表" + else -> "" + } + + val primaryTopicCount: Int + get() = _primaryTopicCount ?: 0 + + val topRightText: String + get() = _topRightText ?: "" + + val verticalLine: String + get() = _verticalLine ?: "" + + val slides: Slides + get() = _slides ?: Slides() + + val data: List + get() = _data ?: emptyList() + + val navigations: List + get() = _navigations ?: emptyList() + + val recommends: List + get() = _recommends ?: emptyList() + + data class Slides( + @SerializedName("slide") + private val _slide: List? = null, + @SerializedName("sub_slide") + private val _subSlide: List? = null + ) { + + val slide: List + get() = _slide ?: emptyList() + + val subSlide: List + get() = _subSlide ?: emptyList() + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageItem.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageItem.kt new file mode 100644 index 0000000000..1687f31632 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageItem.kt @@ -0,0 +1,729 @@ +package com.gh.gamecenter.home.custom.model + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.entity.AmwayCommentEntity +import com.gh.gamecenter.entity.DiscoveryCardEntity +import com.gh.gamecenter.entity.HomeItemTestV2Entity +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.vspace.VGameItemData + +abstract class CustomPageItem( + // 当前模块在数据列表中的位置(注意:非ui页面的位置,而是数据列表中的位置,因为某条数据可能会拆分成多个item) + val link: LinkEntity, + var position: Int, + val componentPosition: Int +) { + + abstract val itemType: Int + + val componentName: String + get() = componentTypeToName[link.type] ?: "" + + open val componentStyle: String + get() = "" + + /** + * 是否需要显示下载按钮 + */ + val shouldShowDownloadButton: Boolean + get() = shouldShowDownloadButtonItemTypes.contains(itemType) + || (checkShowDownloadButtonItemTypes.contains(itemType) && showDownload) + + protected open val showDownload: Boolean + get() = false + + + /** + * 当前模块中包含的游戏) + * Note:只有需要显示游戏下载按钮的模块需要调用此方法 + */ + val showDownloadGames: List + get() { + return if (shouldShowDownloadButton) { + games + } else { + emptyList() + } + } + + protected open val games: List + get() = emptyList() + + var exposureEvent: ExposureEvent? = null + + var exposureEventList: ArrayList = arrayListOf() + + fun areItemsTheSame(other: CustomPageItem): Boolean = + itemType == other.itemType + && doAreItemsTheSame(other) + + fun areContentsTheSame(other: CustomPageItem): Boolean = + itemType == other.itemType + && doAreContentsTheSames(other) + + open fun doAreItemsTheSame(other: CustomPageItem): Boolean = true + + open fun doAreContentsTheSames(other: CustomPageItem) = + this == other + + companion object { + const val CUSTOM_LINK_TYPE_GAME = "game" + const val CUSTOM_LINK_TYPE_COLUMN = "column" + const val CUSTOM_LINK_TYPE_ARTICLE = "article" + const val CUSTOM_LINK_TYPE_COMMUNITY_ARTICLE = "community_article" + const val CUSTOM_LINK_TYPE_VIDEO = "video" + const val CUSTOM_LINK_TYPE_QUESTION = "question" + const val CUSTOM_LINK_TYPE_PLUGIN_AREA = "plugin_area" + const val CUSTOM_LINK_TYPE_CWZS_RECENTLY_PLAYED = "cwzs_recently_played" + const val CUSTOM_LINK_TYPE_QQ_MINI_GAME_COLUMN_DETAIL = "qq_mini_game_column_detail" + const val CUSTOM_LINK_TYPE_HALO_RECOMMEND = "halo_recommend" + const val CUSTOM_LINK_TYPE_COLUMN_COLLECTION = "column_collection" // 专题合集 + const val CUSTOM_LINK_TYPE_GAME_LIST_COLLECTION = "game_list_collection" + const val CUSTOM_LINK_TYPE_COLUMN_TEST_V2 = "column_test_v2" + const val CUSTOM_LINK_TYPE_COMMON_COLLECTION = "common_collection" + const val CUSTOM_LINK_TYPE_TOP_GAME_COMMENT = "top_game_comment" + + // 游戏专题 + const val COMPONENTS_GAME_SUBJECT_TYPE_DOUBLE_CARD = "game_double_card" + const val COMPONENTS_GAME_SUBJECT_TYPE_GAME_VIDEO_HORIZONTAL_SLIDE = "game_video_horizontal_slide" + const val COMPONENTS_GAME_SUBJECT_TYPE_GAME_VERTICAL = "game_vertical" + const val COMPONENTS_SUBJECT_TYPE_GAME_VERTICAL_SLIDE = "game_vertical_slide" + const val COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL = "game_horizontal" + const val COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL_SLIDE = "game_horizontal_slide" + const val COMPONENTS_SUBJECT_TYPE_GAME_TIMELINE_HORIZONTAL_SLIDE = "game_timeline_horizontal_slide" + const val COMPONENTS_SUBJECT_TYPE_GALLERY = "gallery" + const val COMPONENTS_SUBJECT_TYPE_GALLERY_SLIDE = "gallery_slide" + const val COMPONENTS_SUBJECT_TYPE_GAME_FOLD_SLIDE = "game_fold_slide" + const val COMPONENTS_SUBJECT_TYPE_GAME = "game" + const val COMPONENTS_SUBJECT_TYPE_GAME_EXPAND = "game_expand" + + // 合集 + const val COMPONENTS_COLLECTION_STYLE_RANKING_LIST = "ranking-list" + const val COMPONENTS_COLLECTION_STYLE_X_SLIDE_CARD = "x-slide-card" + const val COMPONENTS_COLLECTION_STYLE_X_SLIDE_CARD_SMALL = "x-slide-card-small" + const val COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_LIST = "refresh-slide-list" + const val COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_ICON = "refresh-slide-icon" + const val COMPONENTS_COLLECTION_STYLE_REFRESH_ICONS_4_2 = "refresh-icons-4x2" + const val COMPONENTS_COLLECTION_STYLE_REFRESH_X = "refresh-x" + const val COMPONENTS_COLLECTION_STYLE_REFRESH_Y = "refresh-y" + const val COMPONENTS_COLLECTION_STYLE_CAROUSEL = "carousel" + + // 通用内用合集 + const val COMMON_CONTENT_COLLECTION_LAYOUT_BANNER = 0 + const val COMMON_CONTENT_COLLECTION_LAYOUT_NAVIGATION = 1 + const val COMMON_CONTENT_COLLECTION_LAYOUT_KING_KONG = 2 + const val COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER = 3 + const val COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_BANNER = 4 + const val COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_VERTICAL_CARD = 5 + const val COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD = 6 + const val COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT = 7 + const val COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT = 8 + + const val CUSTOM_PAGE_ITEM_TYPE_INVALID = -2 + const val CUSTOM_PAGE_ITEM_TYPE_FOOTER = -1 + const val CUSTOM_PAGE_ITEM_TYPE_DOUBLE_ROW = 1 + const val CUSTOM_PAGE_ITEM_TYPE_HORIZONTAL_SLIDE_VIDEO_LIST = 2 + const val CUSTOM_PAGE_ITEM_TYPE_GAME_VERTICAL_SLIDE = 3 + const val CUSTOM_PAGE_ITEM_TYPE_GAME_LONG_LIST = 4 + const val CUSTOM_PAGE_ITEM_TYPE_GAME_ICON_MATRIX = 5 + const val CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_TIME_LINE = 6 + const val CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_ICON_LANE = 7 + const val CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_STATIC_GALLERY = 8 + const val CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_GALLERY_SLIDE = 9 + const val CUSTOM_PAGE_ITEM_TYPE_GAME_FOLD_SLIDE_LARGE_IMAGE = 10 + const val CUSTOM_PAGE_ITEM_TYPE_GAME_HOME_GAME_ITEM = 11 + const val CUSTOM_PAGE_ITEM_TYPE_RANK_COLLECTION = 12 + const val CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_HORIZONTAL_SLIDE_LARGE_CARD = 13 + const val CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_HORIZONTAL_SLIDE_SMALL_CARD = 14 + const val CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_BANNER = 15 + const val CUSTOM_PAGE_ITEM_TYPE_RECENT_PLAY = 16 + const val CUSTOM_PAGE_ITEM_TYPE_PLUGIN = 17 + const val CUSTOM_PAGE_ITEM_TYPE_NEW_GAME_TEST = 18 + const val CUSTOM_PAGE_ITEM_TYPE_DISCOVER = 19 + const val CUSTOM_PAGE_ITEM_TYPE_CONTENT_CARD = 20 + const val CUSTOM_PAGE_ITEM_TYPE_AMWAY_WALL = 21 + const val CUSTOM_PAGE_ITEM_TYPE_COMMON_BANNER = 22 + const val CUSTOM_PAGE_ITEM_TYPE_COMMON_BANNER_WITH_CARDS = 23 + const val CUSTOM_PAGE_ITEM_TYPE_NAVIGATION = 24 + const val CUSTOM_PAGE_ITEM_TYPE_RECOMMEND = 25 + const val CUSTOM_PAGE_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE = 26 + const val CUSTOM_PAGE_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS = 27 + const val CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_MATRIX = 28 + const val CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_LANE = 29 + const val CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_SLIDE_LIST = 30 + + // 专题样式 to itemType + val subjectTypeMap: HashMap = hashMapOf( + COMPONENTS_GAME_SUBJECT_TYPE_DOUBLE_CARD to CUSTOM_PAGE_ITEM_TYPE_DOUBLE_ROW, + COMPONENTS_GAME_SUBJECT_TYPE_GAME_VIDEO_HORIZONTAL_SLIDE to CUSTOM_PAGE_ITEM_TYPE_HORIZONTAL_SLIDE_VIDEO_LIST, + COMPONENTS_GAME_SUBJECT_TYPE_GAME_VERTICAL to CUSTOM_PAGE_ITEM_TYPE_GAME_LONG_LIST, + COMPONENTS_SUBJECT_TYPE_GAME_VERTICAL_SLIDE to CUSTOM_PAGE_ITEM_TYPE_GAME_VERTICAL_SLIDE, + COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL to CUSTOM_PAGE_ITEM_TYPE_GAME_ICON_MATRIX, + COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL_SLIDE to CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_ICON_LANE, + COMPONENTS_SUBJECT_TYPE_GAME_TIMELINE_HORIZONTAL_SLIDE to CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_TIME_LINE, + COMPONENTS_SUBJECT_TYPE_GALLERY to CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_STATIC_GALLERY, + COMPONENTS_SUBJECT_TYPE_GALLERY_SLIDE to CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_GALLERY_SLIDE, + COMPONENTS_SUBJECT_TYPE_GAME_FOLD_SLIDE to CUSTOM_PAGE_ITEM_TYPE_GAME_FOLD_SLIDE_LARGE_IMAGE, + COMPONENTS_SUBJECT_TYPE_GAME to CUSTOM_PAGE_ITEM_TYPE_GAME_HOME_GAME_ITEM + ) + + // 专题样式 to 组件样式名称 + val subjectTypeToComponentStyle by lazy { + hashMapOf( + COMPONENTS_GAME_SUBJECT_TYPE_DOUBLE_CARD to "双列卡片", + COMPONENTS_GAME_SUBJECT_TYPE_GAME_VIDEO_HORIZONTAL_SLIDE to "视频横屏滚动", + COMPONENTS_GAME_SUBJECT_TYPE_GAME_VERTICAL to "长列表式", + COMPONENTS_SUBJECT_TYPE_GAME_VERTICAL_SLIDE to "滑动列表", + COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL to "图标矩阵", + COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL_SLIDE to "图标泳道", + COMPONENTS_SUBJECT_TYPE_GAME_TIMELINE_HORIZONTAL_SLIDE to "横向时间轴", + COMPONENTS_SUBJECT_TYPE_GALLERY to "静态图集", + COMPONENTS_SUBJECT_TYPE_GALLERY_SLIDE to "横向滚动图集", + COMPONENTS_SUBJECT_TYPE_GAME_FOLD_SLIDE to "折叠滑动大图", + COMPONENTS_SUBJECT_TYPE_GAME to "展开大图", + COMPONENTS_SUBJECT_TYPE_GAME_EXPAND to "展开大图" + ) + } + + // 专题合集 游戏单合集 样式 + val collectionTypeMap: HashMap = hashMapOf( + COMPONENTS_COLLECTION_STYLE_RANKING_LIST to CUSTOM_PAGE_ITEM_TYPE_RANK_COLLECTION, + COMPONENTS_COLLECTION_STYLE_X_SLIDE_CARD to CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_HORIZONTAL_SLIDE_LARGE_CARD, + COMPONENTS_COLLECTION_STYLE_X_SLIDE_CARD_SMALL to CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_HORIZONTAL_SLIDE_SMALL_CARD, + COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_LIST to CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_SLIDE_LIST, + COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_ICON to CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_LANE, + COMPONENTS_COLLECTION_STYLE_REFRESH_ICONS_4_2 to CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_MATRIX, + COMPONENTS_COLLECTION_STYLE_REFRESH_X to CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_LANE, + COMPONENTS_COLLECTION_STYLE_REFRESH_Y to CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_SLIDE_LIST, + COMPONENTS_COLLECTION_STYLE_CAROUSEL to CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_BANNER + ) + + // 专题合集 游戏单合集 样式 to 组件样式名称 + val collectionTypeToComponentName by lazy { + hashMapOf( + COMPONENTS_COLLECTION_STYLE_RANKING_LIST to "排行榜", + "top" to "排行榜", + COMPONENTS_COLLECTION_STYLE_X_SLIDE_CARD to "横排滑动大卡片", + COMPONENTS_COLLECTION_STYLE_X_SLIDE_CARD_SMALL to "横排滑动小卡片", + COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_LIST to "轮换刷新-滑动列表", + COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_ICON to "轮换刷新-图标泳道", + COMPONENTS_COLLECTION_STYLE_REFRESH_ICONS_4_2 to "轮换刷新-图标矩阵", + COMPONENTS_COLLECTION_STYLE_REFRESH_X to "轮换刷新-图标泳道", + COMPONENTS_COLLECTION_STYLE_REFRESH_Y to "轮换刷新-滑动列表", + COMPONENTS_COLLECTION_STYLE_CAROUSEL to "轮播图" + ) + } + + // 内容卡片组件样式 to 样式名称 + val contentCardTypeToComponentName by lazy { + hashMapOf( + CUSTOM_LINK_TYPE_ARTICLE to "文章", + CUSTOM_LINK_TYPE_COMMUNITY_ARTICLE to "帖子", + CUSTOM_LINK_TYPE_VIDEO to "视频贴", + CUSTOM_LINK_TYPE_QUESTION to "提问" + ) + } + + // 通用内容合集 样式 + val commonContentCollection: HashMap = hashMapOf( + COMMON_CONTENT_COLLECTION_LAYOUT_NAVIGATION to CUSTOM_PAGE_ITEM_TYPE_NAVIGATION, + COMMON_CONTENT_COLLECTION_LAYOUT_KING_KONG to CUSTOM_PAGE_ITEM_TYPE_RECOMMEND, + COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER to CUSTOM_PAGE_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE, + COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_BANNER to CUSTOM_PAGE_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS, + COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_VERTICAL_CARD to CUSTOM_PAGE_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE, + COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD to CUSTOM_PAGE_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS, + COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT to CUSTOM_PAGE_ITEM_TYPE_COMMON_VERTICAL_TWO_CARDS, + COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT to CUSTOM_PAGE_ITEM_TYPE_COMMON_HORIZONTAL_SLIDE + ) + + // 通用内容合集 样式 to 样式名称 + val commonContentLayoutToComponentName by lazy { + hashMapOf( + COMMON_CONTENT_COLLECTION_LAYOUT_BANNER to "轮播banner", + COMMON_CONTENT_COLLECTION_LAYOUT_NAVIGATION to "导航栏", + COMMON_CONTENT_COLLECTION_LAYOUT_KING_KONG to "金刚区", + COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_SLIDE_BANNER to "横向滑动banner", + COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_BANNER to "双列banner", + COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_VERTICAL_CARD to "横排竖式卡片", + COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD to "双列竖式卡片", + COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT to "竖式图文列表", + COMMON_CONTENT_COLLECTION_LAYOUT_HORIZONTAL_IMAGE_TEXT to "横排图文列表" + ) + + } + + val componentTypeToName by lazy { + hashMapOf( + CUSTOM_LINK_TYPE_COLUMN to "游戏专题", + CUSTOM_LINK_TYPE_GAME to "游戏专题", + CUSTOM_LINK_TYPE_ARTICLE to "内容卡片", + CUSTOM_LINK_TYPE_COMMUNITY_ARTICLE to "内容卡片", + CUSTOM_LINK_TYPE_VIDEO to "内容卡片", + CUSTOM_LINK_TYPE_QUESTION to "内容卡片", + CUSTOM_LINK_TYPE_PLUGIN_AREA to "插件化区域", + CUSTOM_LINK_TYPE_CWZS_RECENTLY_PLAYED to "最近在玩", + CUSTOM_LINK_TYPE_QQ_MINI_GAME_COLUMN_DETAIL to "QQ小游戏专题", + CUSTOM_LINK_TYPE_HALO_RECOMMEND to "光环推荐", + CUSTOM_LINK_TYPE_COLUMN_COLLECTION to "专题合集", + CUSTOM_LINK_TYPE_GAME_LIST_COLLECTION to "游戏单合集", + CUSTOM_LINK_TYPE_COLUMN_TEST_V2 to "新游开测", + CUSTOM_LINK_TYPE_COMMON_COLLECTION to "通用内容合集", + CUSTOM_LINK_TYPE_TOP_GAME_COMMENT to "安利墙" + ) + } + + + // 需要检测下载按钮是否显示的样式 + val checkShowDownloadButtonItemTypes = listOf( + CUSTOM_PAGE_ITEM_TYPE_HORIZONTAL_SLIDE_VIDEO_LIST, + CUSTOM_PAGE_ITEM_TYPE_GAME_VERTICAL_SLIDE, + CUSTOM_PAGE_ITEM_TYPE_GAME_ICON_MATRIX, + CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_TIME_LINE, + CUSTOM_PAGE_ITEM_TYPE_GAME_HORIZONTAL_ICON_LANE, + CUSTOM_PAGE_ITEM_TYPE_GAME_HOME_GAME_ITEM, + CUSTOM_PAGE_ITEM_TYPE_RANK_COLLECTION, + CUSTOM_PAGE_ITEM_TYPE_SUBJECT_COLLECTION_HORIZONTAL_SLIDE_LARGE_CARD, + CUSTOM_PAGE_ITEM_TYPE_PLUGIN, + CUSTOM_PAGE_ITEM_TYPE_NEW_GAME_TEST, + CUSTOM_PAGE_ITEM_TYPE_DISCOVER, + CUSTOM_PAGE_ITEM_TYPE_CONTENT_CARD, + CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_MATRIX, + CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_LANE, + CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_SLIDE_LIST, + CUSTOM_PAGE_ITEM_TYPE_GAME_FOLD_SLIDE_LARGE_IMAGE + ) + + // 一定会显示下载按钮的样式 + val shouldShowDownloadButtonItemTypes = listOf( + CUSTOM_PAGE_ITEM_TYPE_HORIZONTAL_SLIDE_VIDEO_LIST, + CUSTOM_PAGE_ITEM_TYPE_GAME_LONG_LIST, + CUSTOM_PAGE_ITEM_TYPE_GAME_VERTICAL_SLIDE, + CUSTOM_PAGE_ITEM_TYPE_PLUGIN, + CUSTOM_PAGE_ITEM_TYPE_RECENT_PLAY + ) + + + } +} + +// 游戏专题 +data class CustomSubjectItem( + private val _link: LinkEntity, + var data: SubjectEntity, + private val _position: Int, + private val _componentPosition: Int +) : CustomPageItem(_link, _position, _componentPosition) { + + override val itemType: Int + get() = subjectTypeMap[data.type] ?: -1 + + override val componentStyle: String + get() = subjectTypeToComponentStyle[data.type] ?: "" + + override val showDownload: Boolean + get() = data.showDownload + + override val games: List + get() = data.data ?: emptyList() + + // 临时变量,记录横向列表滚动的距离 + var scrolledOffset: Int = 0 + + val exposureSource: List + get() = listOf(ExposureSource("专题", "${data.name ?: ""}+$componentStyle+${data.id}")) + + override fun doAreItemsTheSame(other: CustomPageItem): Boolean { + return other is CustomSubjectItem + && data.id == other.data.id + } + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomSubjectItem + && data == other.data + && position == other.position + && componentPosition == other.componentPosition + } + +} + +// 拆分的 游戏专题 +data class CustomSplitSubjectItem( + private val _link: LinkEntity, + var data: SubjectEntity, + private val _position: Int, + private val _componentPosition: Int, + val step: Int, + val startChildPosition: Int +) : CustomPageItem(_link, _position, _componentPosition) { + + var splitItemCount: Int = -1 // 游戏专题拆分出来的子项数量 + + val endChildPosition: Int + get() { + val gameSize = data.data?.size ?: 0 + return if (startChildPosition + step <= gameSize) { + startChildPosition + step - 1 + } else { + gameSize - 1 + } + } + + override val itemType: Int + get() = subjectTypeMap[data.type] ?: -1 + + override val componentStyle: String + get() = subjectTypeToComponentStyle[data.type] ?: "" + + override val showDownload: Boolean + get() = data.showDownload + + override val games: List + get() = data.data + ?.filterIndexed { index, _ -> + index in startChildPosition until (startChildPosition + step) + } ?: emptyList() + + val isFirstLine: Boolean + get() = startChildPosition == 0 + + val isLastLine: Boolean + get() = (startChildPosition + step) >= (data.data?.size ?: 0) + + val exposureSource: List + get() = listOf(ExposureSource("专题", "${data.name ?: ""}+${componentStyle}+${data.id}")) + + override fun doAreItemsTheSame(other: CustomPageItem): Boolean { + return other is CustomSplitSubjectItem + && data.id == other.data.id + } + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomSplitSubjectItem + && startChildPosition == other.startChildPosition + && step == other.step + && (startChildPosition until startChildPosition + step).all { + data.data?.getOrNull(it) == other.data.data?.getOrNull(it) + } + } +} + +// 游戏专题:展开大图样式专用 +data class CustomGameItem( + private val _link: LinkEntity, + val data: GameEntity, + val linkColumn: SubjectEntity?, + val childPosition: Int, + private val _position: Int, // 当前 item 在 ui 中的位置 + private val _componentPosition: Int // 当前组件的位置 +) : CustomPageItem(_link, _position, _componentPosition) { + + override val itemType: Int + get() = CUSTOM_PAGE_ITEM_TYPE_GAME_HOME_GAME_ITEM + + override val componentStyle: String + get() = "展开大图" + + override val showDownload: Boolean + get() = data.homeSetting.downloadBtnSwitch == "on" + + override val games: List + get() = listOf(data) + + val isFirstLine: Boolean + get() = childPosition == 0 + + override fun doAreItemsTheSame(other: CustomPageItem): Boolean { + return other is CustomGameItem + && data.id == other.data.id + } + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomGameItem + && data == other.data + && linkColumn == other.linkColumn + && childPosition == other.childPosition + && position == other.position + && componentPosition == other.componentPosition + } +} + +// 专题合集 游戏单合集 +data class CustomSubjectCollectionItem( + private val _link: LinkEntity, + val data: CustomPageData.LinkColumnCollection, + private val _position: Int, + private val _componentPosition: Int +) : CustomPageItem(_link, _position, _componentPosition) { + + val isSubjectCollection: Boolean // 是否是专题合集(还有可能是游戏单合集) + get() = link.type == CUSTOM_LINK_TYPE_COLUMN_COLLECTION + + override val itemType: Int + get() = collectionTypeMap[data.style] ?: -1 + + override val componentStyle: String + get() = collectionTypeToComponentName[data.style] ?: "" + + override + val showDownload: Boolean + get() = true + + override val games: List + get() = if (showDownload) { + data.data.flatMap { + it.games + } + } else { + emptyList() + } + + // 临时变量:记录合集中,轮换刷新时当前显示的页码 + var showPage: Int = 0 + + // 临时变量:记录合集中,轮换刷新时当前加载的页码 + var loadPage: Int = 1 + + // 临时变量,记录点击轮换刷新时,是否正在loading + var isLoading: Boolean = false + + // 临时变量,数据是否已全部加载完毕 + var isLoadedEnd: Boolean = false + + var isBackToStart = true + + val showSubject: CustomPageData.LinkColumnCollection.CustomSubjectEntity? + get() = data.data.getOrNull(showPage % data.data.size) + + override fun doAreItemsTheSame(other: CustomPageItem): Boolean { + return other is CustomSubjectCollectionItem + && data.id == other.data.id + } + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomSubjectCollectionItem + && showPage == other.showPage + && isLoading == other.isLoading + && isLoadedEnd == other.isLoadedEnd + && showSubject == other.showSubject + } +} + +// 最近在玩 +data class CustomRecentGamesItem( + private val _link: LinkEntity, + val data: List, + private val _position: Int, + private val _componentPosition: Int +) : CustomPageItem(_link, _position, _componentPosition) { + + override val itemType: Int + get() = CUSTOM_PAGE_ITEM_TYPE_RECENT_PLAY + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomRecentGamesItem + && data == other.data + && position == other.position + && componentPosition == other.componentPosition + } +} + +// 插件化区域 +data class CustomPluginItem( + private val _link: LinkEntity, + val data: List, + private val _position: Int, + private val _componentPosition: Int +) : CustomPageItem(_link, _position, _componentPosition) { + + override val itemType: Int + get() = CUSTOM_PAGE_ITEM_TYPE_PLUGIN + + override val games: List + get() = data + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomPluginItem + && data == other.data + && position == other.position + && componentPosition == other.componentPosition + } +} + +// 新游开测 +data class CustomGameTestV2Item( + private val _link: LinkEntity, + val data: HomeItemTestV2Entity, + private val _position: Int, + private val _componentPosition: Int +) : CustomPageItem(_link, _position, _componentPosition) { + + override val itemType: Int + get() = CUSTOM_PAGE_ITEM_TYPE_NEW_GAME_TEST + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomGameTestV2Item + && data == other.data + && position == other.position + && componentPosition == other.componentPosition + } +} + +// 光环推荐 +data class CustomDiscoverCardItem( + private val _link: LinkEntity, + val data: DiscoveryCardEntity?, + private val _position: Int, + private val _componentPosition: Int +) : CustomPageItem(_link, _position, _componentPosition) { + + val spanCount = 3 + + override val itemType: Int + get() = CUSTOM_PAGE_ITEM_TYPE_DISCOVER + + override val showDownload: Boolean + get() = true + + override val games: List + get() = data?.games ?: emptyList() + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomDiscoverCardItem + && data == other.data + && position == other.position + && componentPosition == other.componentPosition + } +} + +// 安利墙 +data class CustomAmwayItem( + private val _link: LinkEntity, + val data: List, + private val _position: Int, + private val _componentPosition: Int +) : CustomPageItem(_link, _position, _componentPosition) { + + override val itemType: Int + get() = CUSTOM_PAGE_ITEM_TYPE_AMWAY_WALL + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomAmwayItem + && data == other.data + && position == other.position + && componentPosition == other.componentPosition + } +} + +// 内容卡片 +data class CustomContentCardItem( + private val _link: LinkEntity, + val data: CustomPageData.ContentCard, + private val _position: Int, + private val _componentPosition: Int +) : CustomPageItem(_link, _position, _componentPosition) { + + override val itemType: Int + get() = CUSTOM_PAGE_ITEM_TYPE_CONTENT_CARD + + override val componentStyle: String + get() = contentCardTypeToComponentName[link.type] ?: "" + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomContentCardItem + && data == other.data + && position == other.position + && componentPosition == other.componentPosition + } +} + +// 通用内容合集 +data class CustomCommonContentCollectionItem( + private val _link: LinkEntity, + val data: CustomPageData.CommonContentCollection, + private val _position: Int, + private val _componentPosition: Int +) : CustomPageItem(_link, _position, _componentPosition) { + + override val itemType: Int + get() = if (data.layout == COMMON_CONTENT_COLLECTION_LAYOUT_BANNER) { + // 轮播图右边卡片列表, 至少需要2个卡片才能显示 + if (data.slides.subSlide.size < 2) { + CUSTOM_PAGE_ITEM_TYPE_COMMON_BANNER + } else { + CUSTOM_PAGE_ITEM_TYPE_COMMON_BANNER_WITH_CARDS + } + } else { + commonContentCollection[data.layout] ?: CUSTOM_PAGE_ITEM_TYPE_INVALID + } + + override val componentStyle: String + get() = commonContentLayoutToComponentName[data.layout] ?: "" + + /** + * 临时变量 + * 当样式为 banner 时,记录当前banner滚动到的位置 + */ + var currentPosition = -1 + + override fun doAreItemsTheSame(other: CustomPageItem): Boolean { + return other is CustomCommonContentCollectionItem + && data.id == other.data.id + } + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomCommonContentCollectionItem + && data == other.data + && position == other.position + && componentPosition == other.componentPosition + } +} + +// 拆分的通用内用合集 +data class CustomSplitCommonContentCollectionItem( + private val _link: LinkEntity, + val data: CustomPageData.CommonContentCollection, + private val _position: Int, + private val _componentPosition: Int, + val leftPosition: Int, +) : CustomPageItem(_link, _position, _componentPosition) { + + override val itemType + get() = commonContentCollection[data.layout] ?: CUSTOM_PAGE_ITEM_TYPE_INVALID + + override val componentStyle: String + get() = commonContentLayoutToComponentName[data.layout] ?: "" + + val isFirstLine: Boolean + get() = leftPosition == 0 + + val isLastLine: Boolean + get() = leftPosition + 2 >= data.data.size + + override fun doAreItemsTheSame(other: CustomPageItem): Boolean { + return other is CustomSplitCommonContentCollectionItem + && data.id == other.data.id + } + + override fun doAreContentsTheSames(other: CustomPageItem): Boolean { + return other is CustomSplitCommonContentCollectionItem + && data == other.data + && data.data.getOrNull(leftPosition) == other.data.data.getOrNull(leftPosition) + && data.data.getOrNull(leftPosition + 1) == other.data.data.getOrNull(leftPosition + 1) + + } +} + +object CustomFooterItem : CustomPageItem(LinkEntity(), -1, -1) { + override val itemType: Int + get() = CUSTOM_PAGE_ITEM_TYPE_FOOTER +} + +object CustomInvalidItem : CustomPageItem(LinkEntity(), -2, -2) { + override val itemType: Int + get() = CUSTOM_PAGE_ITEM_TYPE_INVALID +} + + diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageLocalDataSource.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageLocalDataSource.kt new file mode 100644 index 0000000000..32ac5ef401 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageLocalDataSource.kt @@ -0,0 +1,116 @@ +package com.gh.gamecenter.home.custom.model + +import android.content.Context +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.floatingwindow.FloatingWindowEntity +import com.gh.gamecenter.floatingwindow.HaloApp +import java.text.SimpleDateFormat +import java.util.* + +class CustomPageLocalDataSource { + + private val floatingSp by lazy { + HaloApp.getInstance().getSharedPreferences(CUSTOM_FLOATING_WINDOW, Context.MODE_PRIVATE) + } + + private val bigPopSp by lazy { + HaloApp.getInstance().getSharedPreferences(CUSTOM_DIALOG_SHOW_BIG_IMAGE, Context.MODE_PRIVATE) + } + + fun filterWindows(rawWindowList: List): List { + // 过滤不需要显示的悬浮窗 + val targetList = rawWindowList.filter(::checkIfShowWindow) + // 找到第一个需要自动弹出来大图的悬浮窗 + targetList.find { + when (it.bigPopupNotice) { + BIG_POPUP_NOTICE_NEVER -> { + it.autoExpandBigPopup = false + } + + BIG_POPUP_NOTICE_ONCE -> { + val hasShow = bigPopSp.getBoolean(it.id, false) + if (hasShow) { + it.autoExpandBigPopup = false + } else { + it.autoExpandBigPopup = true + bigPopSp.edit().putBoolean(it.id, true).apply() + } + } + + BIG_POPUP_NOTICE_FOLLOW_PUSH_TYPE -> { + it.autoExpandBigPopup = true + } + } + it.autoExpandBigPopup + } + return targetList + } + + + private fun checkIfShowWindow(item: FloatingWindowEntity): Boolean { + val format = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA) + val currentTime = format.format(Calendar.getInstance().time) + return when (item.pushType) { + FLOATING_WINDOW_PUSH_TYPE_always -> { // 常驻显示 + // 清除之前的弹出记录 + SPUtils.setString(item.spKey, "") + true + } + + FLOATING_WINDOW_PUSH_TYPE_ONCE -> { // 只显示一次 + // 只显示一次 + val value = SPUtils.getString(floatingSp, item.spKey) + // value 为空,说明没有显示过,需要显示 + if (value.isEmpty()) { + SPUtils.setString(floatingSp, item.spKey, "$currentTime${DAY_AND_COUNT_SEPARATOR}1") + } + value.isEmpty() + } + + else -> { // 每天最多显示次数 + val value = SPUtils.getString(floatingSp, item.spKey) + if (value.isEmpty()) { + SPUtils.setString(floatingSp, item.spKey, "$currentTime${DAY_AND_COUNT_SEPARATOR}1") + true + } else { + val values = value.split(DAY_AND_COUNT_SEPARATOR) + val time = values.getOrNull(0) ?: "" + if (time == currentTime) { + val shouldShowCount = item.pushType.toIntOrNull() ?: 1 + val hasShowCount = values.getOrNull(1)?.toIntOrNull() ?: 0 + if (shouldShowCount > hasShowCount) { + SPUtils.setString( + floatingSp, + item.spKey, + "$time${DAY_AND_COUNT_SEPARATOR}${hasShowCount + 1}" + ) + } + shouldShowCount > hasShowCount + } else { + SPUtils.setString(floatingSp, item.spKey, "$currentTime${DAY_AND_COUNT_SEPARATOR}1") + true + } + } + } + } + + } + + private val FloatingWindowEntity.spKey: String + get() = id + + companion object { + private const val CUSTOM_FLOATING_WINDOW = "custom_floating_window" + + private const val FLOATING_WINDOW_PUSH_TYPE_ONCE = "once" + private const val FLOATING_WINDOW_PUSH_TYPE_always = "always" + + private const val DAY_AND_COUNT_SEPARATOR = "_" + + private const val BIG_POPUP_NOTICE_NEVER = "NEVER" + private const val BIG_POPUP_NOTICE_FOLLOW_PUSH_TYPE = "FOLLOW_PUSH_TYPE" + private const val BIG_POPUP_NOTICE_ONCE = "ONCE" + + private const val CUSTOM_DIALOG_SHOW_BIG_IMAGE = "custom_dialog_show_big_image" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRemoteDataSource.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRemoteDataSource.kt new file mode 100644 index 0000000000..663d65d545 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRemoteDataSource.kt @@ -0,0 +1,61 @@ +package com.gh.gamecenter.home.custom.model + +import com.gh.gamecenter.BuildConfig +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.floatingwindow.FloatingWindowEntity +import com.gh.gamecenter.retrofit.service.ApiService +import com.gh.gamecenter.wrapper.MainWrapperRepository +import com.halo.assistant.HaloApp +import io.reactivex.Observable +import io.reactivex.Single + +class CustomPageRemoteDataSource( + private val api: ApiService, + private val newApi: ApiService, + private val mainWrapperRepository: MainWrapperRepository +) { + fun loadSuspendedWindow(pageId: String): Single> { + return newApi.loadSuspendedWindow(pageId, BuildConfig.VERSION_NAME, HaloApp.getInstance().channel) + } + + fun loadCustomPageData(pageId: String, page: Int, forceLoad: Boolean = false): Single { + return if (page == 1 && + pageId == mainWrapperRepository.defaultCustomPageId && + mainWrapperRepository.customPageLiveData.value != null && + !forceLoad + ) { + Single.create { + it.onSuccess(mainWrapperRepository.customPageLiveData.value!!) + } + } else { + newApi.getCustomPageData(pageId, page) + } + } + + fun loadCollectionContent(item: CustomSubjectCollectionItem): Single> = + if (item.isSubjectCollection) { + newApi.getColumnsCollectionContents(item.data.id, "component", item.loadPage + 1, 1) + } else { + newApi.getGameCollectionContents(item.data.id, "component", item.loadPage + 1, 1) + } + + fun loadChangeSubjectGame(subjectEntity: SubjectEntity): Observable> = + if (subjectEntity.isQQColumn) { + newApi.getSubjectQGame(subjectEntity.id) + } else { + api.getSubjectGame(subjectEntity.id) + } + + fun loadSlideDiscoverCardGames(): Observable> { + val paramsMap = if (SPUtils.getBoolean(Constants.SP_DISCOVER_FORCE_REFRESH)) { + mapOf("page_size" to 3, "view" to "sub_slide", "refresh" to "true") + } else { + mapOf("page_size" to 3, "view" to "sub_slide") + } + return api.getDiscoveryGames(1, paramsMap) + .map { it.games } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRepository.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRepository.kt new file mode 100644 index 0000000000..3bbd7a7101 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRepository.kt @@ -0,0 +1,749 @@ +package com.gh.gamecenter.home.custom.model + +import android.graphics.Paint +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Observer +import com.gh.common.filter.RegionSettingHelper +import com.gh.common.util.GameSubstituteRepositoryHelper +import com.gh.common.util.HomePluggableHelper +import com.gh.download.DownloadManager +import com.gh.gamecenter.R +import com.gh.gamecenter.common.utils.TextHelper +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.sp2px +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.TimeUtils +import com.gh.gamecenter.entity.DiscoveryCardEntity +import com.gh.gamecenter.entity.GameUpdateEntity +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.PluginLocation +import com.gh.gamecenter.feature.entity.TagStyleEntity +import com.gh.gamecenter.feature.utils.ApkActiveUtils +import com.gh.gamecenter.floatingwindow.FloatingWindowEntity +import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_BANNER +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_BANNER +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_COLLECTION_STYLE_CAROUSEL +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_COLLECTION_STYLE_RANKING_LIST +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_COLLECTION_STYLE_REFRESH_ICONS_4_2 +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_LIST +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_COLLECTION_STYLE_X_SLIDE_CARD +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_COLLECTION_STYLE_X_SLIDE_CARD_SMALL +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_GAME_SUBJECT_TYPE_DOUBLE_CARD +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_GAME_SUBJECT_TYPE_GAME_VERTICAL +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_GAME_SUBJECT_TYPE_GAME_VIDEO_HORIZONTAL_SLIDE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_SUBJECT_TYPE_GALLERY +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_SUBJECT_TYPE_GALLERY_SLIDE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_SUBJECT_TYPE_GAME_FOLD_SLIDE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL_SLIDE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_SUBJECT_TYPE_GAME_TIMELINE_HORIZONTAL_SLIDE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_SUBJECT_TYPE_GAME_VERTICAL_SLIDE +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_COLUMN +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_GAME +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_HALO_RECOMMEND +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_QQ_MINI_GAME_COLUMN_DETAIL +import com.gh.gamecenter.packagehelper.PackageRepository +import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.gamecenter.wrapper.MainWrapperRepository +import com.gh.vspace.VHelper +import com.lightgame.download.DownloadEntity +import io.reactivex.Observable +import io.reactivex.Single +import io.reactivex.disposables.CompositeDisposable +import java.util.regex.Pattern + + +class CustomPageRepository private constructor( + private val remoteDataSource: CustomPageRemoteDataSource, + private val localDataSource: CustomPageLocalDataSource +) { + + private val pageInfo = PageInfo() + + private val compositeDisposable = CompositeDisposable() + + private val shareRepository = CustomPageShareRepository.instance + + private var recentItem: CustomRecentGamesItem? = null + + private var pluginItem: CustomPluginItem? = null + + private var discoverCardItem: CustomDiscoverCardItem? = null + + private var pluginGameList = arrayListOf() + + private var displayingGameIdSet = hashSetOf() // 当前正在显示的游戏 id 列表 + + private val _customPluginItemLiveData = MutableLiveData() + val customPluginItemLiveData: LiveData = _customPluginItemLiveData + + private val _recentGamesItemLiveData = MutableLiveData() + val recentGamesItemLiveData: LiveData = _recentGamesItemLiveData + + private val _customDiscoverItemLiveData = MutableLiveData() + val customDiscoverItemLiveData: LiveData = _customDiscoverItemLiveData + + private val gameUpdateObserver = Observer> { + refreshPluginIfNeed(it)?.let(_customPluginItemLiveData::setValue) + + } + + private val recentVaPlayedObserver = Observer { + refreshRecentVGameIfNeed()?.let(_recentGamesItemLiveData::setValue) + } + + private val downloadEntityObserver = Observer> { + refreshRecentVGameIfNeed()?.let(_recentGamesItemLiveData::setValue) + } + + private val discoveryCardObserver = Observer { + refreshDiscoverDataIfNeed(it)?.let(_customDiscoverItemLiveData::setValue) + } + + private var lastComponentType = "" + private var lastSubjectId = "" + private var gameChildPosition = 0 + + init { + PackageRepository.gameUpdateLiveData.observeForever(gameUpdateObserver) + PackageRepository.recentVaPlayedChanged.observeForever(recentVaPlayedObserver) + DownloadManager.getInstance().downloadEntityLiveData.observeForever(downloadEntityObserver) + shareRepository.discoverData.observeForever(discoveryCardObserver) + } + + private fun refreshPluginIfNeed(updateList: List): CustomPluginItem? { + val gameList = arrayListOf() + updateList.forEach { + if (it.isPluggable && it.indexPlugin == "open" && it.isShowPlugin(PluginLocation.only_index)) { + val game = it.transformGameEntity() + if (HomePluggableHelper.showHomePluggable(game)) { + game.setEntryMap(DownloadManager.getInstance().getEntryMap(game.name)) + gameList.add(game) + } + } + } + // 下载七天排序 + gameList.sortWith { o1, o2 -> o2.download - o1.download } + pluginGameList = gameList + return pluginItem?.let { plugin -> + pluginItem = CustomPluginItem(plugin.link, pluginGameList, plugin.position, plugin.componentPosition) + pluginItem + } + } + + private fun refreshRecentVGameIfNeed(): CustomRecentGamesItem? { + return recentItem?.let { + val entities = VHelper.getSortedRecentlyPlayedList() + recentItem = CustomRecentGamesItem(it.link, entities, it.position, it.componentPosition) + recentItem + } + } + + private fun refreshDiscoverDataIfNeed(discoveryCardEntity: DiscoveryCardEntity): CustomDiscoverCardItem? { + return discoverCardItem?.let { + discoverCardItem = CustomDiscoverCardItem(it.link, discoveryCardEntity, it.position, it.componentPosition) + discoverCardItem + } + } + + fun loadSuspendedWindow(pageId: String): Single> = + remoteDataSource.loadSuspendedWindow(pageId) + .map { + localDataSource.filterWindows(it) + } + + fun loadFirstCustomPageData( + pageId: String, + forceLoad: Boolean + ): Single>> { + pageInfo.reset() + lastComponentType = "" + lastSubjectId = "" + gameChildPosition = 0 + displayingGameIdSet.clear() + return remoteDataSource.loadCustomPageData(pageId, pageInfo.page, forceLoad) + .map { + it to transformRawDataIntoItemData(it, forceLoad) + } + } + + fun loadNextCustomPageData(pageId: String): Single> = + remoteDataSource.loadCustomPageData(pageId, pageInfo.page) + .map { + transformRawDataIntoItemData(it, true) + } + + private fun transformRawDataIntoItemData(data: CustomPageData, reloadDiscoverData: Boolean): List { + val list = arrayListOf() + data.customsComponents + .forEachIndexed { _, item -> + // 如果当前item不是展开大图,并且上一个item为展开大图,则说明展开大图组件已结束 + if (lastComponentType == CUSTOM_LINK_TYPE_GAME && item.link.type != CUSTOM_LINK_TYPE_GAME) { + pageInfo.componentPositionIncrement() + } + when { + item.game != null -> { + if (!RegionSettingHelper.shouldThisGameBeFiltered(item.game.id)) { + // 记录当前游戏 ID 正在显示中 + displayingGameIdSet.add(item.game.id) + + // 如果当前item组件id不等于上一个item的组件id,则说明这是展开大图的第一个item + if (lastSubjectId != item.linkColumn?.id) { + gameChildPosition = 0 + + + } + // 如果当前item为展开大图,并且上一个item也是展开大图,但是组件id不同,则说明这是一个新的并且连续的展开大图组件 + if (lastComponentType == item.link.type && lastSubjectId != item.linkColumn?.id) { + pageInfo.componentPositionIncrement() + } + item.game.gameLocation = GameEntity.GameLocation.INDEX + list.add( + CustomGameItem( + item.link, + item.game, + item.linkColumn, + gameChildPosition, + pageInfo.position, + pageInfo.componentPosition + ) + ) + gameChildPosition++ + pageInfo.positionIncrement() + + lastSubjectId = item.linkColumn?.id ?: "" + } + } + + item.link.type == CUSTOM_LINK_TYPE_COLUMN || item.link.type == CUSTOM_LINK_TYPE_QQ_MINI_GAME_COLUMN_DETAIL -> { + // 游戏专题 + val gameSubject = if (item.link.type == CUSTOM_LINK_TYPE_COLUMN) { + item.linkColumn?.data?.forEach { + it.gameLocation = GameEntity.GameLocation.INDEX + } + item.linkColumn + } else { + item.qqMiniGameColumn + } + + var isAdded = false + if (gameSubject != null && !gameSubject.data.isNullOrEmpty()) { + // 替换游戏 + substituteGameIfNeeded(gameSubject) + + // 记录已显示的游戏 id + for (game in gameSubject.data!!) { + displayingGameIdSet.add(game.id) + } + + if (gameSubject.tag == "update") { + // 优先显示更新标签 + gameSubject.data?.forEach { + it.tagStyle.clear() + it.tagStyle.add( + TagStyleEntity( + id = "local_generated", + name = TimeUtils.getFormatTime(it.updateTime, "MM-dd") + " 更新", + color = "1383EB", + background = "E8F3FF", + column = "1383EB" + ) + ) + } + } + + when (gameSubject.type) { + COMPONENTS_GAME_SUBJECT_TYPE_DOUBLE_CARD -> { + // 双向卡片 + val gameCount = gameSubject.data?.size ?: 0 + if (gameCount >= COMPONENT_TYPE_COLUMN_LIMIT_TWO_COUNT) { + val subjectItems = convertSplitSubjectItems(item, 2, false) + list.addAll(subjectItems) + isAdded = true + } + } + + COMPONENTS_GAME_SUBJECT_TYPE_GAME_VERTICAL -> { + // 长列表式 展开大图 + val subjectItems = convertSplitSubjectItems(item, 1, false) + list.addAll(subjectItems) + isAdded = true + } + + COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL -> { + // 图标矩阵 + val subjectItems = convertSplitSubjectItems(item, 4, true) + list.addAll(subjectItems) + isAdded = true + } + + else -> { + if (gameSubject.type == COMPONENTS_GAME_SUBJECT_TYPE_GAME_VIDEO_HORIZONTAL_SLIDE || + gameSubject.type == COMPONENTS_SUBJECT_TYPE_GAME_FOLD_SLIDE + ) { + val gameCount = gameSubject.data?.size ?: 0 + if (gameCount >= COMPONENT_TYPE_COLUMN_LIMIT_TWO_COUNT) { + list.add( + CustomSubjectItem( + item.link, + gameSubject, + pageInfo.position, + pageInfo.componentPosition + ) + ) + pageInfo.positionIncrement() + isAdded = true + } + + } else { + list.add( + CustomSubjectItem( + item.link, + gameSubject, + pageInfo.position, + pageInfo.componentPosition + ) + ) + pageInfo.positionIncrement() + isAdded = true + } + + } + } + if (isAdded) { + pageInfo.componentPositionIncrement() + } + } + } + + item.linkColumnCollection != null -> { + // 专题合集 + val linkColumnCollection = item.linkColumnCollection + linkColumnCollection.data = linkColumnCollection.data + .filter { + when (linkColumnCollection.style) { + COMPONENTS_COLLECTION_STYLE_REFRESH_SLIDE_LIST -> + it.games.size >= 4 + + COMPONENTS_COLLECTION_STYLE_REFRESH_ICONS_4_2 -> { + val needGameCount = linkColumnCollection.styleSetting.rowsNum * 4 + it.games.size >= needGameCount + } + + else -> it.games.isNotEmpty() + } + + } + val show = when (linkColumnCollection.style) { + COMPONENTS_COLLECTION_STYLE_RANKING_LIST, + COMPONENTS_COLLECTION_STYLE_X_SLIDE_CARD, + COMPONENTS_COLLECTION_STYLE_X_SLIDE_CARD_SMALL, + COMPONENTS_COLLECTION_STYLE_CAROUSEL -> linkColumnCollection.data.size > 1 + + else -> linkColumnCollection.data.isNotEmpty() + } + linkColumnCollection.data.forEach { + it.games.forEach { game -> + game.gameLocation = GameEntity.GameLocation.INDEX + } + } + if (show) { + list.add( + CustomSubjectCollectionItem( + item.link, + item.linkColumnCollection, + pageInfo.position, + pageInfo.componentPosition + ) + ) + pageInfo.positionIncrement() + pageInfo.componentPositionIncrement() + } + } + + item.linkGameListCollection != null -> { + // 游戏单合集 + val linkGameListCollection = item.linkGameListCollection + fun isCarouselStyle() = linkGameListCollection.style == COMPONENTS_COLLECTION_STYLE_CAROUSEL + linkGameListCollection.data = linkGameListCollection.data + .filter { + it.games.isNotEmpty() || isCarouselStyle() + } + // 如果是轮播图样式,需要不少于三个,才会展示 + val show = + !(isCarouselStyle() && linkGameListCollection.data.size < 3) + if (show && linkGameListCollection.data.isNotEmpty()) { + list.add( + CustomSubjectCollectionItem( + item.link, + item.linkGameListCollection, + pageInfo.position, + pageInfo.componentPosition + ) + ) + pageInfo.positionIncrement() + pageInfo.componentPositionIncrement() + } + + } + + item.linkCommonCollection != null -> { + val layout = item.linkCommonCollection.layout + if (layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_BANNER || + layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD || + layout == COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT + ) { + list.addAll(convertSplitCommonContentCollection(item)) + } else { + // 轮播图右边卡片列表, 至少需要2个卡片才能显示 + val subSlideList = item.linkCommonCollection.slides.subSlide + if (layout == COMMON_CONTENT_COLLECTION_LAYOUT_BANNER && subSlideList.size > 1) { + subSlideList.forEach { + if (it.cardData.games.isNotEmpty()) { + it.cardData.games = RegionSettingHelper.filterGame(it.cardData.games) + } + } + } + list.add( + CustomCommonContentCollectionItem( + item.link, + item.linkCommonCollection, + pageInfo.position, + pageInfo.componentPosition + ) + ) + pageInfo.positionIncrement() + } + pageInfo.componentPositionIncrement() + } + + item.link.type == "cwzs_recently_played" -> { + // 最近在玩 + val entities = VHelper.getSortedRecentlyPlayedList() + recentItem = + CustomRecentGamesItem(item.link, entities, pageInfo.position, pageInfo.componentPosition) + .also { + list.add(it) + pageInfo.positionIncrement() + pageInfo.componentPositionIncrement() + } + } + + item.link.type == "plugin_area" -> { + // 插件化区域 + pluginItem = + CustomPluginItem(item.link, pluginGameList, pageInfo.position, pageInfo.componentPosition) + .also { + list.add(it) + pageInfo.positionIncrement() + pageInfo.componentPositionIncrement() + } + + } + + item.homeItemTestV2 != null -> { + // 新游开测 + list.add( + CustomGameTestV2Item( + item.link, + item.homeItemTestV2, + pageInfo.position, + pageInfo.componentPosition + ) + ) + pageInfo.positionIncrement() + pageInfo.componentPositionIncrement() + } + + item.link.type == CUSTOM_LINK_TYPE_HALO_RECOMMEND -> { + // 光环推荐 + val discoverCardEntity = shareRepository.discoverData.value + discoverCardItem = + CustomDiscoverCardItem( + item.link, + discoverCardEntity, + pageInfo.position, + pageInfo.componentPosition + ) + .also { + list.add(it) + pageInfo.positionIncrement() + } + // 刷新光环推荐 + if (reloadDiscoverData || discoverCardEntity == null) { + shareRepository.loadDiscoverCard() + } + + pageInfo.componentPositionIncrement() + } + + item.contentCard != null -> { + // 内容卡片 + list.add( + CustomContentCardItem( + item.link, + item.contentCard, + pageInfo.position, + pageInfo.componentPosition + ) + ) + pageInfo.positionIncrement() + pageInfo.componentPositionIncrement() + } + + item.linkTopGameComments != null -> { + // 安利墙 + //如果评论预览的三行之中有至少有两行的字数均少于15个字,则该评论不在安利墙 展示,顺位由下一个游戏评论顶替(评论引导标签也需要加入字数计算 + + val comments = item.linkTopGameComments.filter { + val m = Pattern.compile(RatingEditActivity.LABEL_REGEX).matcher(it.comment.content) + if (m.find()) { + val contents = TextHelper.getCommentLabelSpannableStringBuilder( + it.comment.content, + R.color.text_theme + ) + val splits = contents.split("\n") + splits.take(3).filter { split -> + split.length >= AMWAY_COMMENT_MIN_LENGTH + }.size >= 2 + } else { + val content = it.comment.content + val tvWidth = DisplayUtils.getScreenWidth() - 108f.dip2px() + val paint = Paint() + paint.isLinearText + paint.textSize = 14f.sp2px().toFloat() + val resultList = arrayListOf() + val splits = content.split("\n") + splits.forEach { split -> + var itemContent = split + if (itemContent.length <= 15) { + resultList.add(itemContent) + } else { + while (itemContent.length > 15) { + val charCount = paint.breakText(itemContent, true, tvWidth.toFloat(), null) + val result = itemContent.substring(0, charCount) + resultList.add(result) + if (charCount < content.length) { + itemContent = itemContent.substring(charCount, itemContent.length) + } + if (resultList.size >= 3) { + return@forEach + } + } + } + } + resultList.filter { result -> + result.length >= AMWAY_COMMENT_MIN_LENGTH + }.size >= 2 + } + } + if (comments.isNotEmpty()) { + list.add( + CustomAmwayItem( + item.link, + comments, + pageInfo.position, + pageInfo.componentPosition + ) + ) + pageInfo.positionIncrement() + pageInfo.componentPositionIncrement() + } + } + + else -> { + CustomInvalidItem + } + } + lastComponentType = item.link.type ?: "" + } + + pageInfo.nextPage() + return list + } + + + private fun convertSplitSubjectItems( + item: CustomPageData.CustomsComponent, + stepNum: Int, + isMustFullyCover: Boolean + ): List { + val gameSubject = item.gameSubjectEntity ?: return emptyList() + val items = arrayListOf() + val games = gameSubject.data ?: emptyList() + (games.indices step stepNum).forEach { start -> + if (isMustFullyCover) { + if (start + stepNum - 1 < games.size) { + items.add( + CustomSplitSubjectItem( + item.link, + gameSubject, + pageInfo.position, + pageInfo.componentPosition, + stepNum, + start + ) + ) + pageInfo.positionIncrement() + } + } else { + items.add( + CustomSplitSubjectItem( + item.link, + gameSubject, + pageInfo.position, + pageInfo.componentPosition, + stepNum, + start + ) + ) + pageInfo.positionIncrement() + } + } + items.forEach { + if (it is CustomSplitSubjectItem) { + it.splitItemCount = items.size + } + } + return items + } + + private fun convertSplitCommonContentCollection(item: CustomPageData.CustomsComponent): List { + val linkCommonCollection = item.linkCommonCollection ?: return emptyList() + val data = linkCommonCollection.data + val items = arrayListOf() + (data.indices step 2).forEach { left -> + items.add( + CustomSplitCommonContentCollectionItem( + item.link, + linkCommonCollection, + pageInfo.position, + pageInfo.componentPosition, + left + ) + ) + pageInfo.positionIncrement() + } + return items + } + + fun loadCollectionContent(item: CustomSubjectCollectionItem): Single> = + remoteDataSource.loadCollectionContent(item) + .map { + it.apply { + firstOrNull()?.run { games = RegionSettingHelper.filterGame(games) } + } + } + + fun notifyPositionChanged(offset: Int) { + pageInfo.position += offset + } + + fun loadChangeSubjectGame(subjectEntity: SubjectEntity): Observable> = + remoteDataSource.loadChangeSubjectGame(subjectEntity) + .map(RegionSettingHelper.filterGame) + .map(ApkActiveUtils.filterMapperList) + + fun loadSlideDiscoverCardGames(): Observable> = + remoteDataSource.loadSlideDiscoverCardGames() + .map(RegionSettingHelper.filterGame) + + /** + * 部分样式的专题,当替换游戏功能启用时需要尝试替换游戏 + * + * 仅针对专题样式为“长列表式、滑动列表、图标矩阵、图标泳道、~~静态图集、横向滚动图集~~”生效 + * https://jira.shanqu.cc/browse/GHZS-3966 + */ + private fun substituteGameIfNeeded(gameSubject: SubjectEntity) { + if (!gameSubject.relatedColumnId.isNullOrEmpty() && gameSubject.data != null) { + when(gameSubject.type) { + COMPONENTS_GAME_SUBJECT_TYPE_GAME_VERTICAL, + COMPONENTS_SUBJECT_TYPE_GAME_VERTICAL_SLIDE, + COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL, + COMPONENTS_SUBJECT_TYPE_GAME_TIMELINE_HORIZONTAL_SLIDE, + COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL_SLIDE, + COMPONENTS_SUBJECT_TYPE_GALLERY, + COMPONENTS_SUBJECT_TYPE_GALLERY_SLIDE + -> { + GameSubstituteRepositoryHelper.replaceGames( + gameList = gameSubject.data!!, + displayingGameIdSet = displayingGameIdSet, + relatedCollectionId = gameSubject.relatedColumnId!! + ) + } + } + } + } + + + /** + * 分页信息 + * @param page 页码 + * @param position 当前数据在ui界面的最后一个位置 + * @param componentPosition 当前组件所在的位置 + */ + class PageInfo { + + private var _page: Int = 1 + val page: Int + get() = _page + + private var _position: Int = 0 + var position: Int + get() = _position + set(value) { + _position = value + } + + private var _componentPosition = 0 + val componentPosition: Int + get() = _componentPosition + + + fun nextPage() { + _page++ + } + + fun positionIncrement() { + _position++ + } + + fun componentPositionIncrement() { + _componentPosition++ + } + + fun reset() { + _page = 1 + _position = 0 + _componentPosition = 0 + } + } + + fun onClear() { + compositeDisposable.clear() + PackageRepository.gameUpdateLiveData.removeObserver(gameUpdateObserver) + PackageRepository.recentVaPlayedChanged.removeObserver(recentVaPlayedObserver) + shareRepository.discoverData.removeObserver(discoveryCardObserver) + DownloadManager.getInstance().downloadEntityLiveData.removeObserver(downloadEntityObserver) + } + + companion object { + + private const val COMPONENT_TYPE_COLUMN_LIMIT_TWO_COUNT = 2 + private const val AMWAY_COMMENT_MIN_LENGTH = 15 + + fun newInstance() = CustomPageRepository( + CustomPageRemoteDataSource( + RetrofitManager.getInstance().api, + RetrofitManager.getInstance().newApi, + MainWrapperRepository.getInstance() + ), + CustomPageLocalDataSource() + ) + } +} diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageShareRepository.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageShareRepository.kt new file mode 100644 index 0000000000..bd6323062c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageShareRepository.kt @@ -0,0 +1,117 @@ +package com.gh.gamecenter.home.custom.model + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.gh.common.filter.RegionSettingHelper +import com.gh.common.util.PackageUtils +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.utils.observableToMain +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.entity.DiscoveryCardEntity +import com.gh.gamecenter.entity.DiscoveryGameCardEntity +import com.gh.gamecenter.entity.DiscoveryGameCardLabel +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import io.reactivex.disposables.CompositeDisposable + +/** + * 由于多个自定义页面可能共用同一份数据,这里使用单例来全局保存多个页面共享的数据 + * 如:光环推荐 + */ +class CustomPageShareRepository private constructor() { + + private val apiService = RetrofitManager.getInstance().api + + private val compositeDisposable = CompositeDisposable() + + + private val _discoverData = MutableLiveData() + val discoverData: LiveData = _discoverData + + fun loadDiscoverCard() { + val paramsMap = if (SPUtils.getBoolean(Constants.SP_DISCOVER_FORCE_REFRESH)) { + mapOf("refresh" to "true") + } else { + mapOf() + } + apiService.getDiscoveryGames(1, paramsMap) + .zipWith(apiService.cardLabels) { t1, t2 -> + SPUtils.setBoolean(Constants.SP_DISCOVER_FORCE_REFRESH, false) + DiscoveryCardEntity( + RegionSettingHelper.filterGame(t1.filterDiscoverGames()), + filterDiscoveryLabel(t1, t2) + ) + } + .compose(observableToMain()) + .subscribe(_discoverData::setValue) { + /* no implement */ + } + .let(compositeDisposable::add) + + + } + + private fun DiscoveryGameCardEntity.filterDiscoverGames(): List { + val tempList = arrayListOf() + tempList.addAll(games) + // 删除已安装的游戏 + tempList.removeAll { + it.getApk().isNotEmpty() && + PackageUtils.isInstalled(HaloApp.getInstance().application, it.getApk()[0].packageName) + } + return tempList.take(18) + } + + // 过滤分组,现只需要"卡片一"分组 + private fun filterDiscoveryLabel( + discoveryGameCardEntity: DiscoveryGameCardEntity, + discoveryLabels: List + ): List { + + // 如果link_type为空需要在mUserTags/mGameTags中依次获取 + val gameTags = discoveryGameCardEntity.gameTags + val userTags = discoveryGameCardEntity.userTags + var gameTagIndex = 0 + var userTagIndex = 0 + val filteredLabels = discoveryLabels + .filter { + it.card == "卡片一" + } + filteredLabels.forEach { label -> + when { + userTagIndex in userTags.indices && label.type.isNullOrEmpty() -> { + val userTag = userTags[userTagIndex] + label.link = userTag.id + label.type = "tag" + label.linkText = userTag.name + label.title = userTag.name + userTagIndex++ + } + + gameTagIndex in gameTags.indices && label.type.isNullOrEmpty() -> { + val gameTag = gameTags[gameTagIndex] + label.link = gameTag.link + label.type = gameTag.type + label.linkText = gameTag.linkText + label.title = gameTag.linkText + gameTagIndex++ + } + } + + } + return filteredLabels + } + + // 这个方法要在用户退出首页时调用 + fun onClear() { + compositeDisposable.clear() + } + + companion object { + + val instance by lazy { + CustomPageShareRepository() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/tracker/CommonContentCollectionTracker.kt b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/CommonContentCollectionTracker.kt new file mode 100644 index 0000000000..ac1b8e9137 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/CommonContentCollectionTracker.kt @@ -0,0 +1,40 @@ +package com.gh.gamecenter.home.custom.tracker + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.home.custom.CustomPageTracker +import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSplitCommonContentCollectionItem + +class CommonContentCollectionTracker(private val pageLocation: PageLocation) { + + fun trackLinkContentCollectionClick(item: CustomPageItem, link: LinkEntity?, text: String) { + val (linkContentCollectionId, linkContentCollectionName) = when { + item is CustomCommonContentCollectionItem -> item.data.id to item.data.name + item is CustomSplitCommonContentCollectionItem -> item.data.id to item.data.name + else -> "" to "" + } + SensorsBridge.trackLinkContentCollectionClick( + CustomPageTracker.LOCATION_CUSTOM_PAGE, + "", + "", + linkContentCollectionName, + linkContentCollectionId, + item.position, + link?.type ?: "", + link?.link ?: "", + link?.text ?: "", + text, + item.componentStyle, + pageLocation.bottomTab, + pageLocation.severalTabPageId, + pageLocation.severalTabPageName, + pageLocation.tabPosition, + pageLocation.tabContent, + pageLocation.pageId, + pageLocation.pageName + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/tracker/GameSubjectCollectionTracker.kt b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/GameSubjectCollectionTracker.kt new file mode 100644 index 0000000000..f8f86b6ae3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/GameSubjectCollectionTracker.kt @@ -0,0 +1,105 @@ +package com.gh.gamecenter.home.custom.tracker + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.home.custom.CustomPageTracker +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem + +class GameSubjectCollectionTracker(private val pageLocation: PageLocation) { + + fun trackGameListCollectionClickWithUser(item: CustomSubjectCollectionItem, userId: String) { + val otherParams = hashMapOf( + SensorsBridge.KEY_MONGOLD_ID to userId + ) + trackGameListCollectionClick(item, "个人主页", otherParams) + } + + fun trackGameListCollectionClickWithGameCollection( + item: CustomSubjectCollectionItem, + gameCollectId: String, + gameCollectTitle: String + + ) { + val otherParams = hashMapOf( + SensorsBridge.KEY_GAME_COLLECT_ID to gameCollectId, + SensorsBridge.KEY_GAME_COLLECT_TITLE to gameCollectTitle + ) + trackGameListCollectionClick(item, "游戏单", otherParams) + } + + fun trackGameListCollectionClickWithGame(item: CustomSubjectCollectionItem, gameId: String, gameName: String?) { + val otherParams = hashMapOf( + SensorsBridge.KEY_GAME_ID to gameId, + SensorsBridge.KEY_GAME_NAME to (gameName ?: "") + ) + trackGameListCollectionClick(item, "游戏", otherParams) + } + + fun trackGameListCollectionClick( + item: CustomSubjectCollectionItem, + text: String, + otherParams: HashMap = hashMapOf() + ) { + SensorsBridge.trackGameListCollectionClick( + CustomPageTracker.LOCATION_CUSTOM_PAGE, + item.data.id, + item.data.name, + text, + pageLocation.bottomTab, + pageLocation.severalTabPageId, + pageLocation.severalTabPageName, + pageLocation.tabPosition, + pageLocation.tabContent, + pageLocation.pageId, + pageLocation.pageName, + item.componentStyle, + otherParams + ) + } + + + fun trackColumnCollectionClick( + item: CustomSubjectCollectionItem, + subject: CustomPageData.LinkColumnCollection.CustomSubjectEntity?, + game: GameEntity?, + text: String, + link: LinkEntity? = null + ) { + SensorsBridge.trackColumnCollectionClick( + CustomPageTracker.LOCATION_CUSTOM_PAGE, + "", + "", + item.data.name, + item.data.id, + item.position, + subject?.title ?: "", + subject?.id ?: "", + text, + item.componentStyle, + pageLocation.bottomTab, + pageLocation.severalTabPageId, + pageLocation.severalTabPageName, + pageLocation.tabPosition, + pageLocation.tabContent, + pageLocation.pageId, + pageLocation.pageName, + game?.id ?: "", + game?.name ?: "", + link?.type ?: "", + link?.link ?: "", + link?.text ?: "" + ) + } + + fun trackGameListCollectionClickWithMore(item: CustomSubjectCollectionItem, link: LinkEntity) { + val otherParams = HashMap().apply { + put(SensorsBridge.KEY_LINK_ID, link.link ?: "") + put(SensorsBridge.KEY_LINK_TYPE, link.type ?: "") + put(SensorsBridge.KEY_LINK_TEXT, link.text ?: "") + } + trackGameListCollectionClick(item, "更多", otherParams) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/tracker/OtherItemTracker.kt b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/OtherItemTracker.kt new file mode 100644 index 0000000000..2d44250d79 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/OtherItemTracker.kt @@ -0,0 +1,24 @@ +package com.gh.gamecenter.home.custom.tracker + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.home.custom.model.CustomPageItem + +class OtherItemTracker(private val pageLocation: PageLocation) { + + fun trackContentCardClick(item: CustomPageItem, link: LinkEntity) { + SensorsBridge.trackContentCardClick( + pageLocation.bottomTab, + pageLocation.severalTabPageId, + pageLocation.severalTabPageName, + pageLocation.tabPosition, + pageLocation.tabContent, + pageLocation.pageId, + pageLocation.pageName, + link.link ?: "", + link.type ?: "", + link.text ?: "" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/tracker/SubjectTracker.kt b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/SubjectTracker.kt new file mode 100644 index 0000000000..0de7ac7d28 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/tracker/SubjectTracker.kt @@ -0,0 +1,64 @@ +package com.gh.gamecenter.home.custom.tracker + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.utils.SensorsBridge +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem +import com.gh.gamecenter.home.custom.model.CustomSubjectItem + +class SubjectTracker(private val pageLocation: PageLocation) { + + fun trackColumnClick(item: CustomPageItem, game: GameEntity?, text: String, buttonType: String = "", link: LinkEntity? = null) { + val subject = when { + item is CustomSubjectItem -> item.data + item is CustomSplitSubjectItem -> item.data + else -> null + } + SensorsBridge.trackColumnClick( + pageLocation.bottomTab, + pageLocation.severalTabPageId, + pageLocation.severalTabPageName, + pageLocation.tabPosition, + pageLocation.tabContent, + pageLocation.pageId, + pageLocation.pageName, + link?.type ?: "", + link?.link ?: "", + link?.linkText ?: "", + subject?.id ?: "", + subject?.name ?: "", + game?.id ?: "", + game?.name ?: "", + "自定义页面", + item.componentStyle, + text, + buttonType + ) + } + + fun trackQQGameClick(item: CustomPageItem, game: GameEntity) { + val subject = when { + item is CustomSubjectItem -> item.data + item is CustomSplitSubjectItem -> item.data + else -> null + } + SensorsBridge.trackQQGameClick( + pageLocation.bottomTab, + pageLocation.severalTabPageId, + pageLocation.severalTabPageName, + pageLocation.tabPosition, + pageLocation.tabContent, + pageLocation.pageId, + pageLocation.pageName, + subject?.id ?: "", + subject?.name ?: "", + "自定义页面", + item.componentStyle, + "", + game.id, + game.name ?: "" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/BaseCustomViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/BaseCustomViewHolder.kt new file mode 100644 index 0000000000..b06bddd281 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/BaseCustomViewHolder.kt @@ -0,0 +1,322 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.view.View +import androidx.annotation.CallSuper +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.exposure.IExposable +import com.gh.common.util.DirectUtils +import com.gh.common.util.NewFlatLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.MtaHelper +import com.gh.gamecenter.databinding.LayoutTitleCustomBinding +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.PageLocation +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.PageConfigure +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.eventlistener.CustomPageItemChildEventHelper +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem +import com.gh.gamecenter.home.custom.model.CustomSubjectItem +import com.gh.gamecenter.subject.SubjectActivity +import com.lightgame.download.DownloadEntity + +abstract class BaseCustomViewHolder( + val viewModel: CustomPageViewModel, + itemView: View +) : RecyclerView.ViewHolder(itemView), IGameChangedNotifier, IExposable { + + protected val pageConfigure: PageConfigure + get() = viewModel.pageConfigure + + protected val pageLocation: PageLocation + get() = viewModel.pageLocation + + fun createTrackData(item: CustomPageItem) = + CustomPageTrackData( + viewModel.pageLocation, + item.componentName, + item.componentStyle, + item.link.text ?: "", + item.link.link ?: "" + ) + + protected var darkMode = DarkModeUtils.isDarkModeOn(itemView.context) + + open val gameChangedNotifier: IGameChangedNotifier? + get() = null + + open val onlyNotifyItemChanged: Boolean + get() = false + + abstract val childEventHelper: CustomPageItemChildEventHelper + + protected lateinit var _item: CustomPageItem + + @CallSuper + open fun bindView(item: CustomPageItem) { + _item = item + childEventHelper.onBind(item) + } + + override fun notifyDownload(download: DownloadEntity) { + gameChangedNotifier?.notifyDownload(download) + } + + override fun notifyDownloadDeleted(status: EBDownloadStatus) { + gameChangedNotifier?.notifyDownloadDeleted(status) + + } + + override fun notifyInstalled(busFour: EBPackage) { + gameChangedNotifier?.notifyInstalled(busFour) + } + + open fun onViewAttach(parent: RecyclerView?) = Unit + + open fun onViewDetach(parent: RecyclerView?) = Unit + + fun setSplitSubjectTitle( + titleBinding: LayoutTitleCustomBinding, + item: CustomSplitSubjectItem, + eventHelper: SubjectEventHelper, + titleType: String = TITLE_TYPE_REGULAR + ) { + if (item.isFirstLine) { + titleBinding.root.goneIf(false) + setSubjectTitle( + titleBinding, + CustomSubjectItem(item.link, item.data, item.position, item.componentPosition), + eventHelper, + titleType + ) + } else { + titleBinding.root.goneIf(true) + } + } + + // 专题标题 + fun setSubjectTitle( + titleBinding: LayoutTitleCustomBinding, + item: CustomSubjectItem, + eventHelper: SubjectEventHelper, + titleType: String = TITLE_TYPE_REGULAR + ) { + setTitleStyle(titleBinding) + val subject = item.data + if (subject.showName) { + titleBinding.clContainerTitle.goneIf(false) + with(titleBinding) { + tvTitle.text = subject.name ?: "" + tvRight.text = when (subject.home) { + HOME_CHANGE -> R.string.custom_home_text_change.toResString() + HOME_MORE -> R.string.custom_home_text_more.toResString() + HOME_HIDE -> "" + else -> R.string.custom_home_text_all.toResString() + } + val arrowRes = + if (subject.home == HOME_HIDE || subject.home == HOME_CHANGE) { + 0 + } else { + R.drawable.ic_home_head_arrow + } + tvRight.setCompoundDrawablesWithIntrinsicBounds(0, 0, arrowRes, 0) + + setTitlePadding(titleType, clContainerTitle) + + tvRight.setOnClickListener { + when (subject.home) { + HOME_CHANGE -> { + // 点击换一批 + eventHelper.onChangeABatch(subject) + } + + HOME_MORE -> { + // 点击更多 + subject.moreLink?.let { link -> + eventHelper.onMoreClick(link) + } + } + + else -> { + MtaHelper.onEvent("游戏专题", "全部", subject.name) + eventHelper.onAllClick(subject) + } + } + } + } + titleBinding.lavRefresh.goneIf(true) + titleBinding.gUser.goneIf(true) + } else { + titleBinding.clContainerTitle.goneIf(true) + } + } + + // 游戏单 + fun setSubjectCollectionTitle( + item: CustomSubjectCollectionItem, + titleBinding: LayoutTitleCustomBinding, + isDarkModeChanged: Boolean, + eventHelper: GameSubjectCollectionEventHelper, + showUser: Boolean = false, + titleType: String = TITLE_TYPE_REGULAR, + visibleCallback: (Boolean) -> Unit = {} + ) { + with(titleBinding) { + setTitleStyle(this) + if (item.data.styleSetting.hideName) { + titleBinding.clContainerTitle.goneIf(true) + visibleCallback(false) + } else { + val isRefreshStyle = item.data.style.contains("refresh") + lavRefresh.goneIf(!isRefreshStyle) + if (tvTitle.text.isEmpty()) { + lavRefresh.setAnimation( + if (DarkModeUtils.isDarkModeOn(itemView.context)) { + "lottie/icon_title_change_dark.json" + } else { + "lottie/icon_title_change_light.json" + } + ) + } + titleBinding.clContainerTitle.goneIf(false) + visibleCallback(true) + val subject = item.showSubject ?: return + checkAndSetDarkMode(isDarkModeChanged, this) + tvTitle.text = if (isRefreshStyle) subject.title else item.data.name + if (showUser && subject.user.isValid) { + gUser.goneIf(false) + ImageUtils.display(ivUser, subject.user.icon) + tvUserName.text = subject.user.name + gUser.setOnClickListener { + eventHelper.onUserClick(subject.user.id) + } + } else { + gUser.goneIf(true) + } + + setTitlePadding(titleType, clContainerTitle) + + val rightTopText = when (item.data.styleSetting.rightTop) { + RIGHT_TOP_ALL -> R.string.custom_home_text_all.toResString() + HOME_GAME_LIST_SQUARE -> R.string.custom_home_text_game_list_square.toResString() + RIGHT_TOP_LINK -> R.string.custom_home_text_more.toResString() + else -> "" + } + + tvRight.visibleIf(rightTopText.isNotBlank()) + tvRight.text = rightTopText + + val styleSetting = item.data.styleSetting + tvRight.setOnClickListener { + if (item.isSubjectCollection) { + // 专题合集 + // 更多 + if (styleSetting.rightTop == RIGHT_TOP_LINK) { + eventHelper.onMoreClick(styleSetting.rightTopLink) + } else { + // 全部 + eventHelper.onAllClick() + } + } else { + // 游戏单合集 + when (styleSetting.rightTop) { + RIGHT_TOP_LINK -> { // 更多 + eventHelper.onMoreClick(styleSetting.rightTopLink) + + } + + RIGHT_TOP_ALL -> { // 全部 + eventHelper.onAllClick() + } + + HOME_GAME_LIST_SQUARE -> { // 游戏单广场 + NewFlatLogUtils.logGameListCollectionClick( + "版块内容列表", + "", + "", + item.data.name, + item.data.id, + "游戏单广场" + ) + eventHelper.navigateGameListSquare() + } + } + } + } + + lavRefresh.setOnClickListener { + if (!lavRefresh.isAnimating) { + lavRefresh.playAnimation() + eventHelper.onRotateRefresh() + NewFlatLogUtils.logGameListCollectionClick( + pageConfigure.entrance, + "", + "", + item.data.name, + item.data.id, + "刷新" + ) + } + } + } + } + } + + private fun checkAndSetDarkMode(isDarkModeChanged: Boolean, titleBinding: LayoutTitleCustomBinding) { + if (isDarkModeChanged) { + titleBinding.run { + tvTitle.setTextColor(R.color.text_primary.toColor(root.context)) + tvUserName.setTextColor(R.color.text_tertiary.toColor(root.context)) + lavRefresh.cancelAnimation() + lavRefresh.setAnimation(if (DarkModeUtils.isDarkModeOn(root.context)) "lottie/icon_title_change_dark.json" else "lottie/icon_title_change_light.json") + lavRefresh.progress = 0F + } + + } + } + + private fun setTitlePadding(titleType: String, titleView: View) { + val paddingBottom = if (titleType == TITLE_TYPE_SMALL) { + 8f.dip2px() + } else { + 16f.dip2px() + } + titleView.setPadding(titleView.paddingLeft, titleView.paddingTop, titleView.paddingRight, paddingBottom) + } + + protected fun setTitleStyle(titleBinding: LayoutTitleCustomBinding) { + with(titleBinding) { + tvTitle.setTextColor(R.color.text_primary.toColor(itemView.context)) + } + } + + override fun getEventByPosition(pos: Int): ExposureEvent? { + return null + } + + override fun getEventListByPosition(pos: Int): List? { + return listOf() + } + + companion object { + + private const val HOME_CHANGE = "change" + private const val HOME_MORE = "more" + private const val HOME_GAME_LIST_SQUARE = "game_list_square" + private const val HOME_HIDE = "hide" + + private const val RIGHT_TOP_ALL = "all" + private const val RIGHT_TOP_LINK = "link" + + const val TITLE_TYPE_REGULAR = "title_type_regular" + const val TITLE_TYPE_SMALL = "title_type_small" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomBigImageRecommendViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomBigImageRecommendViewHolder.kt new file mode 100644 index 0000000000..94f6295971 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomBigImageRecommendViewHolder.kt @@ -0,0 +1,48 @@ +package com.gh.gamecenter.home.custom.viewholder + +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.databinding.BigImageRecommendItemCustomBinding +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper +import com.gh.gamecenter.home.custom.model.CustomContentCardItem +import com.gh.gamecenter.home.custom.model.CustomPageItem + +/** + * 内容卡片 + * 提问、帖子、视频帖、文章 + */ +class CustomBigImageRecommendViewHolder( + viewModel: CustomPageViewModel, + val binding: BigImageRecommendItemCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + OtherItemEventHelper(viewModel) + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomContentCardItem) { + val contentCard = item.data + binding.run { + ImageUtils.display(poster, contentCard.img, shouldAutoPlayAnimatedImage = false) + recommendTag.goneIf(contentCard.recommendTag.isEmpty()) + recommendTextTwo.goneIf(contentCard.secondLineRecommend.isEmpty()) + recommendTag.text = contentCard.recommendTag + recommendTextOne.text = contentCard.firstLineRecommend + recommendTextTwo.text = contentCard.secondLineRecommend + root.setOnClickListener { + val linkEntity = + LinkEntity( + link = item.link.link, + type = item.link.type, + text = item.link.text + ) + childEventHelper.directToLinkPage(linkEntity) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollection12ViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollection12ViewHolder.kt new file mode 100644 index 0000000000..26700910a2 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollection12ViewHolder.kt @@ -0,0 +1,222 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.view.View +import androidx.constraintlayout.widget.ConstraintLayout +import com.gh.common.exposure.ExposureManager +import com.gh.common.exposure.ExposureTraceUtils +import com.gh.common.util.NewLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.databinding.CommonCollection12ItemCustomBinding +import com.gh.gamecenter.databinding.CommonCollectionItemBinding +import com.gh.gamecenter.entity.CommonCollectionContentEntity +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.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT +import com.gh.gamecenter.home.custom.model.CustomSplitCommonContentCollectionItem + +/** + * 双列banner 双列竖式卡片 竖排图文列表 + * 通用链接合集(1-2样式) + */ +class CustomCommonCollection12ViewHolder( + viewModel: CustomPageViewModel, + val binding: CommonCollection12ItemCustomBinding +) : + BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + CommonContentCollectionEventHelper(viewModel) + } + + private val clickInvoke = { childPosition: Int, contentEntity: CommonCollectionContentEntity -> + val item = _item as CustomSplitCommonContentCollectionItem + val collection = item.data + val linkEntity = contentEntity.linkEntity + NewLogUtils.logCommonCollectionClick( + collection.id, + collection.name, + "blockData?.link", + "blockData?.name", + "板块内容列表", + "合集首页", + linkEntity.title ?: "", + contentEntity.addedContent1 ?: "", + contentEntity.addedContent2 ?: "", + linkEntity.type ?: "", + linkEntity.text ?: "", + childPosition + 1 + ) + NewLogUtils.logCommonCategoryHomeContentClick( + contentEntity.title, + linkEntity.type ?: "", + linkEntity.link ?: "", + linkEntity.text ?: "", + collection.name, + collection.id, + "板块", + "blockData?.name" + ) + item.exposureEventList.getOrNull(childPosition)?.let { + val clickEvent = ExposureEvent( + payload = it.payload, + source = it.source, + eTrace = ExposureTraceUtils.appendTrace(it), + event = ExposureType.CLICK + ) + if (linkEntity.type != "game") { + ExposureManager.log(clickEvent) + } + } + childEventHelper.navigateToLinkPage(linkEntity, "内容卡片", item.exposureEventList.getOrNull(childPosition)) + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSplitCommonContentCollectionItem) { + + item.exposureEventList.clear() + item.data.data.forEachIndexed { index, contentEntity -> + val gameEntity = if (contentEntity.linkEntity.type == "game") { + GameEntity(id = contentEntity.linkEntity.link, name = contentEntity.linkEntity.text) + } else { + GameEntity() + } + val event = createExposureEvent( + gameEntity.also { + it.sequence = index + it.outerSequence = item.position + }, + listOf(ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}")), + pageConfigure.exposureSourceList, + index, + item.componentPosition + + ).also { contentEntity.linkEntity.exposureEvent = it } + item.exposureEventList.add(event) + } + + binding.layoutTitle.root.goneIf(!item.isFirstLine) { + setTitleStyle(binding.layoutTitle) + } + + CustomCommonCollectionViewHolder.setTitle(item.data, binding.layoutTitle) { + childEventHelper.navigateToCommonCollectionDetailPage(item.link) + } + val collection = item.data + val data = collection.data + + val width = (DisplayUtils.getScreenWidth() - 16F.dip2px() * 2 - 8F.dip2px()) / 2 + val height = if (collection.layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD) { + width * 4 / 3 + } else { + width / 2 + } + + val leftBinding = CommonCollectionItemBinding.bind(binding.leftView.root) + val rightBinding = CommonCollectionItemBinding.bind(binding.rightView.root) + + + bindSubView( + data[item.leftPosition], + leftBinding, + collection.layout, + width, + height, + item.leftPosition, + clickInvoke + ) + + val rightPosition = item.leftPosition + 1 + if (rightPosition < data.size) { + binding.rightView.root.visibility = View.VISIBLE + bindSubView( + data[rightPosition], + rightBinding, + collection.layout, + width, + height, + rightPosition, + clickInvoke + ) + } else { + binding.rightView.root.visibility = View.INVISIBLE + + val imageParams = binding.rightView.commonCollectionImage.layoutParams as ConstraintLayout.LayoutParams + imageParams.width = width + imageParams.height = 1 + binding.rightView.commonCollectionImage.layoutParams = imageParams + } + + val _paddingBottom = + if (item.isLastLine || collection.layout == COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) { + 16f.dip2px() + } else { + 8f.dip2px() + } + with(binding.llContainer) { + setPadding(paddingLeft, paddingTop, paddingRight, _paddingBottom) + } + } + } + + private fun bindSubView( + contentEntity: CommonCollectionContentEntity, + subBinding: CommonCollectionItemBinding, + layout: Int, + width: Int, + height: Int, + position: Int, + clickClosure: (position: Int, contentEntity: CommonCollectionContentEntity) -> Unit + ) { + subBinding.run { + ImageUtils.display(commonCollectionImage, contentEntity.image) + maskView.goneIf(layout == COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT || (contentEntity.title.isEmpty() && contentEntity.addedContent1.isNullOrEmpty())) + titleTv.goneIf(layout == COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) { + titleTv.text = contentEntity.title + } + desTv.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD) { + desTv.text = contentEntity.addedContent1 + } + linkTitleTv.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) { + linkTitleTv.text = contentEntity.title + } + linkDes1.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) { + linkDes1.text = contentEntity.addedContent1 + linkDes1.setTextColor(R.color.text_secondary.toColor(root.context)) + } + linkDes2.goneIf(layout != COMMON_CONTENT_COLLECTION_LAYOUT_VERTICAL_IMAGE_TEXT) { + linkDes2.text = contentEntity.addedContent2 + linkDes2.setTextColor(R.color.text_tertiary.toColor(root.context)) + } + + val maskHeight = if (layout == COMMON_CONTENT_COLLECTION_LAYOUT_DOUBLE_ROW_VERTICAL_CARD) { + 60f.dip2px() + } else { + 38f.dip2px() + } + val maskParams = maskView.layoutParams as ConstraintLayout.LayoutParams + maskParams.height = maskHeight + maskView.layoutParams = maskParams + + val imageParams = commonCollectionImage.layoutParams as ConstraintLayout.LayoutParams + imageParams.width = width + imageParams.height = height + commonCollectionImage.layoutParams = imageParams + + root.setOnClickListener { clickClosure.invoke(position, contentEntity) } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollectionViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollectionViewHolder.kt new file mode 100644 index 0000000000..e633cf9487 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomCommonCollectionViewHolder.kt @@ -0,0 +1,167 @@ +package com.gh.gamecenter.home.custom.viewholder + +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.exposure.ExposureManager +import com.gh.common.exposure.ExposureTraceUtils +import com.gh.common.util.NewLogUtils +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.view.SpacingItemDecoration +import com.gh.gamecenter.databinding.CommonCollectionListCustomBinding +import com.gh.gamecenter.databinding.LayoutTitleCustomBinding +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.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.adapter.CustomCommonCollectionAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomPageItem + +/** + * 通用内容合集-横排滑动Banner + * 通用内容合集-横排竖式卡片 + * 通用内容合集-横排图文列表 + * 通用链接合集 + */ +class CustomCommonCollectionViewHolder( + viewModel: CustomPageViewModel, + val binding: CommonCollectionListCustomBinding, +) : BaseCustomViewHolder(viewModel, binding.root) { + + private val layoutManager by lazy { + LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false) + } + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + CommonContentCollectionEventHelper(viewModel) + } + + private val adapter by lazy { + CustomCommonCollectionAdapter(itemView.context, { childPosition, entity -> + val item = _item as CustomCommonContentCollectionItem + val subject = item.data + val linkEntity = entity.linkEntity + + NewLogUtils.logCommonCollectionClick( + subject.id, + subject.name, + "blockData?.link", + "blockData?.name", + "板块内容列表", + "合集首页", + linkEntity.title ?: "", + entity.addedContent1 ?: "", + entity.addedContent2 ?: "", + linkEntity.type ?: "", + linkEntity.text ?: "", + childPosition + 1 + ) + NewLogUtils.logCommonCategoryHomeContentClick( + entity.title, + linkEntity.type ?: "", + linkEntity.link ?: "", + linkEntity.text ?: "", + subject.name, + subject.id, + "板块", + "blockData?.name" + ) + linkEntity.exposureEvent?.let { + val clickEvent = ExposureEvent( + payload = it.payload, + source = it.source, + eTrace = ExposureTraceUtils.appendTrace(it), + event = ExposureType.CLICK + ) + if (linkEntity.type != "game") { + ExposureManager.log(clickEvent) + } + } + childEventHelper.navigateToLinkPage( + linkEntity, + "内容卡片", + (_item as CustomCommonContentCollectionItem).exposureEventList.getOrNull(childPosition) + ) + }) { childPosition, link -> + val item = _item as CustomCommonContentCollectionItem + val gameEntity = if (link.type == "game") GameEntity(id = link.link, name = link.text) else GameEntity() + _item.exposureEventList.add( + createExposureEvent( + gameEntity.also { + it.sequence = childPosition + it.outerSequence = _item.position + }, + listOf(ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}")), + pageConfigure.exposureSourceList, + childPosition, + _item.componentPosition + ).also { link.exposureEvent = it } + ) + } + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomCommonContentCollectionItem) { + item.exposureEventList.clear() + + setTitleStyle(binding.layoutTitle) + setTitle(item.data, binding.layoutTitle) { + childEventHelper.navigateToCommonCollectionDetailPage(item.link) + } + if (binding.collectionList.adapter == null) { + binding.collectionList.addItemDecoration( + SpacingItemDecoration( + notDecorateTheFirstItem = true, + left = 8f.dip2px() + ) + ) + binding.collectionList.layoutManager = layoutManager + binding.collectionList.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + var position = layoutManager.findLastCompletelyVisibleItemPosition() + if (position == -1) position = layoutManager.findLastVisibleItemPosition() - 1 + if (position < 0) return + } + } + }) + binding.collectionList.adapter = adapter + } + adapter.setData(item) + } + + } + + companion object { + + fun setTitle( + data: CustomPageData.CommonContentCollection, + layoutTitle: LayoutTitleCustomBinding, + trackInvoke: () -> Unit + ) { + with(layoutTitle) { + lavRefresh.goneIf(true) + gUser.goneIf(true) + tvTitle.text = data.name + + if (data.topRightText.isEmpty()) { + tvRight.goneIf(true) + } else { + tvRight.goneIf(false) + tvRight.text = data.topRightText + tvRight.setOnClickListener { + trackInvoke() + } + } + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomDoubleCardViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomDoubleCardViewHolder.kt new file mode 100644 index 0000000000..ef7b5611b5 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomDoubleCardViewHolder.kt @@ -0,0 +1,135 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.view.View +import android.view.ViewGroup.MarginLayoutParams +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.display +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.databinding.GameDoubleCardItemAlCustomBinding +import com.gh.gamecenter.databinding.GameDoubleCardItemCustomBinding +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.CustomPageItemChildEventHelper +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem + +/** + * + * 游戏专题-双列卡片-v2 + */ +class CustomDoubleCardViewHolder( + viewModel: CustomPageViewModel, + val binding: GameDoubleCardItemAlCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + private val mPosterWidth = (DisplayUtils.getScreenWidth() - 40F.dip2px()) / 2 + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + SubjectEventHelper(viewModel) + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSplitSubjectItem) { + + val gameList = item.data.data ?: return + + fillExposureList(item) + + setSplitSubjectTitle(binding.layoutTitle, item, childEventHelper) + + val topMargin = if (item.isFirstLine) { + 0 + } else { + 8f.dip2px() + } + val bottomMargin = if (item.isLastLine) { + 16f.dip2px() + } else { + 0 + } + (itemView.layoutParams as? MarginLayoutParams)?.let { + it.topMargin = topMargin + it.bottomMargin = bottomMargin + itemView.layoutParams = it + } + + + val leftPosition = item.startChildPosition + + val rightPosition = leftPosition + 1 + + val hasLeft = gameList.size > leftPosition + if (hasLeft) { + bindSubView( + item, + gameList[leftPosition], + binding.leftCardView, + leftPosition + ) + } + + val hasRight = gameList.size > rightPosition + if (hasRight) { + binding.rightCardView.posterCardView.visibility = View.VISIBLE + bindSubView( + item, + gameList[rightPosition], + binding.rightCardView, + rightPosition + ) + + } else { + binding.rightCardView.posterCardView.visibility = View.INVISIBLE + } + } + } + + private fun bindSubView( + item: CustomSplitSubjectItem, + gameEntity: GameEntity, + subBinding: GameDoubleCardItemCustomBinding, + position: Int + ) { + subBinding.run { + poster.setTag(ImageUtils.TAG_TARGET_WIDTH, mPosterWidth) + poster.post { poster.display(gameEntity.columnImage) } + gameName.text = gameEntity.name + brief.text = + if (gameEntity.columnRecommend?.text.isNullOrBlank()) gameEntity.brief + else gameEntity.columnRecommend!!.text + GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitle) + + root.setOnClickListener { + childEventHelper.navigateToGameDetailPage(position, gameEntity) + } + } + } + + private fun fillExposureList(item: CustomSplitSubjectItem) { + val eventList = arrayListOf() + runOnIoThread(true) { + for (i in item.startChildPosition..item.endChildPosition) { + val game = item.data.data?.getOrNull(i) ?: break + val event = createExposureEvent( + game, + item.exposureSource, + pageConfigure.exposureSourceList, + i, + item.componentPosition + ) + game.exposureEvent = event + eventList.add(event) + } + } + item.exposureEventList = eventList + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomFoldSlideLargeImageViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomFoldSlideLargeImageViewHolder.kt new file mode 100644 index 0000000000..723fd0526e --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomFoldSlideLargeImageViewHolder.kt @@ -0,0 +1,161 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.animation.TimeInterpolator +import android.view.View +import androidx.cardview.widget.CardView +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnScrollListener +import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE +import com.gh.gamecenter.R +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.view.stacklayoutmanager2.StackAnimation2 +import com.gh.gamecenter.common.view.stacklayoutmanager2.StackLayoutManagerV2 +import com.gh.gamecenter.databinding.RecyclerFoldSlideLargeImageCustomBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.adapter.CustomFoldSlideLargeImageItemAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSubjectItem + +/** + * 折叠滑动大图 + */ +class CustomFoldSlideLargeImageViewHolder( + viewModel: CustomPageViewModel, + val binding: RecyclerFoldSlideLargeImageCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + SubjectEventHelper(viewModel) + } + + private val layoutManager by lazy { + StackLayoutManagerV2(214, 310, CustomFoldSlideLargeImageLayoutAnimation()) + } + + private val adapter by lazy { + CustomFoldSlideLargeImageItemAdapter(itemView.context, childEventHelper) { + createTrackData(_item) + } + } + + override val gameChangedNotifier: IGameChangedNotifier? + get() = adapter + + private var topViewHolder: CustomFoldSlideLargeImageItemAdapter.ImageItemViewHolder? = null + + private val onScrollListener = object : OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + if (newState == SCROLL_STATE_IDLE) { + showBubble(layoutManager.selectedPosition) + } + } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (dy == 0) { + showBubble(layoutManager.selectedPosition) + } + } + } + + private var isInit = true + + init { + binding.recyclerView.isNestedScrollingEnabled = false + layoutManager.setOnItemSelectedListener(object : StackLayoutManagerV2.OnStackListener { + override fun onSelectedPositionChanged(position: Int) { + showBubble(position) + } + + override fun onScrollStateChanged(isScrolling: Boolean) = Unit + + }) + } + + private fun showBubble(position: Int) { + val viewHolder = binding.recyclerView.findViewHolderForAdapterPosition(position) + if (topViewHolder != viewHolder) { + topViewHolder?.dismissBubble() + } + if (viewHolder is CustomFoldSlideLargeImageItemAdapter.ImageItemViewHolder) { + topViewHolder = viewHolder + viewHolder.showBubble() + } + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSubjectItem) { + + setSubjectTitle(binding.layoutTitle, item, childEventHelper) + + fillExposureEventList(item) + + if (binding.recyclerView.adapter == null) { + binding.recyclerView.layoutManager = layoutManager + binding.recyclerView.adapter = adapter + } + adapter.setData(item) + if (isInit) { + isInit = false + binding.recyclerView.scrollToPosition(adapter.getInitPosition()) + } + } + } + + private fun fillExposureEventList(item: CustomSubjectItem) { + val eventList = arrayListOf() + item.data.data?.forEachIndexed { index, game -> + val event = createExposureEvent( + game, + item.exposureSource, + pageConfigure.exposureSourceList, + index, + item.componentPosition + ) + eventList.add(event) + } + item.exposureEventList = eventList + } + + override fun onViewAttach(parent: RecyclerView?) { + parent?.addOnScrollListener(onScrollListener) + } + + override fun onViewDetach(parent: RecyclerView?) { + parent?.removeOnScrollListener(onScrollListener) + // 取消气泡与倒计时 + topViewHolder?.dismissBubble() + } + + class CustomFoldSlideLargeImageLayoutAnimation : StackAnimation2 { + private val alphaInterceptor = TimeInterpolator { x -> + x * x * x * x + } + + override fun layoutAnimations(child: View, layoutPosition: Int, layoutManager: StackLayoutManagerV2) { + // 相邻卡片间透明度差距变化 + val alphaSpace = 1f / layoutManager.visibleCount + val alpha: Float + var scale = 1f + if (layoutPosition == layoutManager.selectedPosition) { + alpha = 1 - alphaInterceptor.getInterpolation(layoutManager.scrollPercent) + } else { + alpha = + 1 - (layoutPosition - layoutManager.selectedPosition) * alphaSpace + alphaSpace * layoutManager.scrollPercent + scale = + 1 - (layoutPosition - layoutManager.selectedPosition) * 0.1f + 0.1f * layoutManager.scrollPercent + } + child.findViewById(R.id.cl_container)?.alpha = alpha + child.pivotY = child.measuredHeight / 2f + child.pivotX = child.width.toFloat() + child.scaleX = scale + child.scaleY = scale + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomFooterViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomFooterViewHolder.kt new file mode 100644 index 0000000000..19a094ed9b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomFooterViewHolder.kt @@ -0,0 +1,160 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.view.View +import android.widget.ProgressBar +import android.widget.TextView +import androidx.annotation.StringRes +import com.gh.gamecenter.common.R +import com.gh.gamecenter.common.base.activity.BaseActivity +import com.gh.gamecenter.common.baselist.ListViewModel +import com.gh.gamecenter.common.baselist.LoadType +import com.gh.gamecenter.common.databinding.RefreshFooterviewBinding +import com.gh.gamecenter.home.PageConfigure +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.eventlistener.CustomPageItemChildEventHelper +import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper + +class CustomFooterViewHolder( + viewModel: CustomPageViewModel, + val binding: RefreshFooterviewBinding, +) : BaseCustomViewHolder(viewModel, binding.root) { + + val loading: ProgressBar + val hint: TextView + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + OtherItemEventHelper(viewModel) + } + + init { + loading = itemView.findViewById(R.id.footerview_loading) + hint = itemView.findViewById(R.id.footerview_hint) + } + + // 减去recyclerView item间隔(个别列表间隔很大) + fun initItemPadding() { + itemView.setPadding(0, 0, 0, 0) + } + + fun initFooterViewHolder( + isNetworkError: Boolean, + isOver: Boolean, + @StringRes loadOverHint: Int + ) { + if (isNetworkError) { + loading.visibility = View.GONE + hint.setText(R.string.loading_failed_retry) + } else if (isOver) { + loading.visibility = View.GONE + hint.setText(loadOverHint) + } else { + loading.visibility = View.VISIBLE + hint.setText(R.string.loading) + } + } + + fun initFooterViewHolder( + viewModel: ListViewModel<*, *>, + isLoading: Boolean, + isNetworkError: Boolean, + isOver: Boolean + ) { + initFooterViewHolder( + isLoading, + isNetworkError, + isOver, + R.string.load_over_hint + ) { v: View? -> if (isNetworkError) viewModel.load(LoadType.RETRY) } + } + + fun bindFooterDefaultEmpty( + viewModel: ListViewModel<*, *>, + isLoading: Boolean, + isNetworkError: Boolean, + isOver: Boolean + ) { + if (isNetworkError) { + loading.visibility = View.GONE + hint.visibility = View.VISIBLE + hint.setText(R.string.loading_failed_retry) + } else if (isOver) { + loading.visibility = View.GONE + hint.visibility = View.VISIBLE + hint.setText(R.string.load_over_hint) + } else if (isLoading) { + loading.visibility = View.VISIBLE + hint.visibility = View.VISIBLE + hint.setText(R.string.loading) + } else { + loading.visibility = View.GONE + hint.visibility = View.GONE + } + itemView.setOnClickListener { v: View? -> if (isNetworkError) viewModel.load(LoadType.RETRY) } + } + + @JvmOverloads + fun initFooterViewHolder( + isLoading: Boolean, + isNetworkError: Boolean, + isOver: Boolean, + @StringRes loadOverHint: Int = R.string.load_over_hint + ) { + BaseActivity.updateStaticView(itemView, ArrayList()) + if (isNetworkError) { + loading.visibility = View.GONE + hint.setText(R.string.loading_failed_retry) + } else if (isOver) { + loading.visibility = View.GONE + hint.setText(loadOverHint) + } else if (isLoading) { + loading.visibility = View.VISIBLE + hint.setText(R.string.loading) + } else { + loading.visibility = View.GONE + hint.setText(R.string.loading_more_hint) + } + } + + fun initFooterViewHolder( + isLoading: Boolean, + mIsNetworkError: Boolean, + mIsOver: Boolean, + onClickListener: View.OnClickListener? + ) { + initFooterViewHolder( + isLoading, + mIsNetworkError, + mIsOver, + R.string.load_over_hint, + onClickListener + ) + } + + fun initFooterViewHolder( + isLoading: Boolean, + mIsNetworkError: Boolean, + mIsOver: Boolean, + @StringRes loadOverHint: Int, + onClickListener: View.OnClickListener? + ) { + if (mIsNetworkError) { + loading.visibility = View.GONE + hint.setText(R.string.loading_failed_retry) + itemView.isClickable = true + itemView.setOnClickListener(onClickListener) + } else if (mIsOver) { + loading.visibility = View.GONE + hint.setText(loadOverHint) + itemView.isClickable = true + itemView.setOnClickListener(onClickListener) + } else if (isLoading) { + loading.visibility = View.VISIBLE + hint.setText(R.string.loading) + itemView.isClickable = false + } else { + loading.visibility = View.GONE + hint.setText(R.string.loading_more_hint) + itemView.isClickable = false + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameCollectionRefreshVerticalSlideViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameCollectionRefreshVerticalSlideViewHolder.kt new file mode 100644 index 0000000000..9e2ab680a0 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameCollectionRefreshVerticalSlideViewHolder.kt @@ -0,0 +1,168 @@ +package com.gh.gamecenter.home.custom.viewholder + +import androidx.core.view.children +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.DarkModeUtils +import com.gh.gamecenter.databinding.GameVerticalSlideItemCustomBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.game.vertical.OnPagerSnapScrollListener +import com.gh.gamecenter.game.vertical.SpanCountPagerSnapHelper +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.adapter.CustomGameRefreshVerticalAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem + +/** + * 专题合集:轮换刷新:滑动列表 + * 游戏单合集:轮换刷新-竖排列表 + */ +class CustomGameCollectionRefreshVerticalSlideViewHolder( + viewModel: CustomPageViewModel, + val binding: GameVerticalSlideItemCustomBinding, +) : + BaseCustomViewHolder(viewModel, binding.root) { + + private var spanCount = 3 + private var snapHelper: SpanCountPagerSnapHelper? = null + + private var _subjectEntity: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = null + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + GameSubjectCollectionEventHelper(viewModel) + } + + private val adapter by lazy { + CustomGameRefreshVerticalAdapter( + itemView.context, pageConfigure.transparentBackground, + childEventHelper + ) { + createTrackData(_item) + } + } + + override val gameChangedNotifier: IGameChangedNotifier + get() = adapter + + init { + binding.recyclerView.isNestedScrollingEnabled = false + binding.recyclerView.itemAnimator = null + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSubjectCollectionItem) { + + val isDarkModeChanged = DarkModeUtils.isDarkModeOn(binding.root.context) != darkMode + setSubjectCollectionTitle( + item, + binding.layoutTitle, + isDarkModeChanged, + childEventHelper, + true, + TITLE_TYPE_SMALL + ) + if (isDarkModeChanged) { + darkMode = DarkModeUtils.isDarkModeOn(binding.root.context) + } + + val subjectEntity = item.showSubject ?: return + if (subjectEntity.id == _subjectEntity?.id) { + adapter.notifyItemRangeChanged(0, adapter.itemCount) + return + } else { + _subjectEntity = subjectEntity + } + + val games = subjectEntity.games + spanCount = if (games.size < 3) games.size else 3 + + val listExposureEventList = arrayListOf() + val exposureEventList = arrayListOf() + val exposureClosure: (Int) -> Unit = { + + listExposureEventList.clear() + + val source = if (item.isSubjectCollection) { + listOf( + ExposureSource("专题合集", "${item.data.name}+${item.componentStyle}+${item.data.id}"), + ExposureSource( + "专题", + "${subjectEntity.title}+${subjectEntity.styleChinese}+${subjectEntity.id}" + ) + ) + } else { + listOf( + ExposureSource("游戏单合集", "${item.data.name}+${item.componentStyle}+${item.data.id}"), + ExposureSource("游戏单", "${subjectEntity.title}+${subjectEntity.id}") + ) + } + + val startOffset = it * spanCount + val endOffset = if (startOffset + spanCount >= games.size) { + games.size + } else { + startOffset + spanCount + } + for (i in startOffset until endOffset) { + val game = games.getOrNull(i) ?: break + game.isAdData = subjectEntity.adIconActive + val event = createExposureEvent( + game, + source, + pageConfigure.exposureSourceList, + i, + item.componentPosition + ) + + listExposureEventList.add(event) + } + exposureEventList.addAll(listExposureEventList) + } + item.exposureEventList = exposureEventList + + exposureClosure(0) + + + if (binding.recyclerView.adapter == null) { + binding.recyclerView.clearOnScrollListeners() + snapHelper = SpanCountPagerSnapHelper(spanCount, true) + binding.recyclerView.layoutManager = + GridLayoutManager(itemView.context, spanCount, RecyclerView.HORIZONTAL, false) + binding.recyclerView.adapter = adapter + snapHelper?.let { + it.attachToRecyclerView(binding.recyclerView) + binding.recyclerView.addOnScrollListener(OnPagerSnapScrollListener(it) { + exposureClosure(it) + }) + } + } + changeSpanCountIfNeeded() + + // 如果游戏全部被屏蔽,自动加载下一页 + if (games.isEmpty()) { + childEventHelper.onRotateRefresh() + } else { + if (item.isBackToStart) { + item.isBackToStart = false + binding.recyclerView.scrollToPosition(0) + } + adapter.setData(spanCount, data = item, subjectEntity) + } + } + } + + private fun changeSpanCountIfNeeded() { + binding.recyclerView.run { + if (spanCount != (layoutManager as GridLayoutManager).spanCount) { + snapHelper?.spanCount = spanCount + (layoutManager as GridLayoutManager).spanCount = spanCount + } + } + } +} diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGallerySlideViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGallerySlideViewHolder.kt new file mode 100644 index 0000000000..ddcea5ceb3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGallerySlideViewHolder.kt @@ -0,0 +1,144 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.content.Context +import android.view.ViewGroup +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.databinding.GameGallerySlideItemCustomBinding +import com.gh.gamecenter.databinding.ItemWithinGameGallerySlideCustomBinding +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSubjectItem +import com.lightgame.adapter.BaseRecyclerAdapter + +/** + * 横向滚动图集 + * 游戏专题-图集滚动-v2 + */ +class CustomGameGallerySlideViewHolder( + viewModel: CustomPageViewModel, + val binding: GameGallerySlideItemCustomBinding, + private val lifecycleOwner: LifecycleOwner, +) : BaseCustomViewHolder(viewModel, binding.root), DefaultLifecycleObserver { + + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + SubjectEventHelper(viewModel) + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSubjectItem) { + + item.exposureEventList.clear() + setSubjectTitle(binding.layoutTitle, item, childEventHelper) + val rvList = + arrayListOf(binding.firstRecyclerView, binding.secondRecyclerView, binding.thirdRecyclerView) + + for ((index, recyclerView) in rvList.withIndex()) { + if (recyclerView.adapter == null) { + recyclerView.layoutManager = + LinearLayoutManager(binding.root.context, RecyclerView.HORIZONTAL, false) + recyclerView.adapter = + GameGallerySlideAdapter( + binding.root.context, + index + ) { childPosition, game -> + runOnIoThread(true) { + game.sequence = childPosition + item.exposureEventList.add( + createExposureEvent( + game, + item.exposureSource, + pageConfigure.exposureSourceList, + childPosition, + item.componentPosition + ) + ) + } + } + recyclerView.isNestedScrollingEnabled = false + } + val gameList = item.data.data ?: emptyList() + (recyclerView.adapter as? GameGallerySlideAdapter)?.submitList(gameList) + } + binding.cardView.setCardBackgroundColor(R.color.text_FAFAFA.toColor(binding.root.context)) + } + + + } + + override fun onResume(owner: LifecycleOwner) { + super.onResume(owner) + binding.container.onResume(owner) + } + + override fun onPause(owner: LifecycleOwner) { + super.onPause(owner) + binding.container.onPause(owner) + } + + override fun onViewAttach(parent: RecyclerView?) { + super.onViewAttach(parent) + lifecycleOwner.lifecycle.addObserver(binding.container) + } + + override fun onViewDetach(parent: RecyclerView?) { + super.onViewDetach(parent) + lifecycleOwner.lifecycle.removeObserver(binding.container) + } + + inner class GameGallerySlideAdapter( + context: Context, + private val index: Int, + private val exposureInvoke: (Int, GameEntity) -> Unit + ) : BaseRecyclerAdapter(context) { + + private val dataList = arrayListOf() + fun submitList(data: List) { + dataList.clear() + dataList.addAll(data) + notifyDataSetChanged() + } + + override fun getItemCount() = Int.MAX_VALUE + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameGallerySlideItemViewHolder { + return GameGallerySlideItemViewHolder(parent.toBinding()) + } + + override fun onBindViewHolder(holder: GameGallerySlideItemViewHolder, position: Int) { + if (dataList.isEmpty()) return + + val dataPosition = position * 3 + index + val realPosition = dataPosition % dataList.size + val gameEntity = dataList[realPosition] + + exposureInvoke(realPosition, gameEntity) + holder.bindView(realPosition, gameEntity) + } + + inner class GameGallerySlideItemViewHolder(val binding: ItemWithinGameGallerySlideCustomBinding) : + BaseRecyclerViewHolder(binding.root) { + fun bindView(realPosition: Int, gameEntity: GameEntity) { + binding.iconIv.displayGameIcon(gameEntity) + binding.iconIv.setOnClickListener { + childEventHelper.navigateToGameDetailPage(realPosition, gameEntity) + } + } + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGalleryViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGalleryViewHolder.kt new file mode 100644 index 0000000000..a2401fc307 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameGalleryViewHolder.kt @@ -0,0 +1,105 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.content.Context +import android.view.View +import com.gh.gamecenter.R +import com.gh.gamecenter.common.utils.safelyGetInRelease +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.view.AsyncCell +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.databinding.GameGalleryItemCustomBinding +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.CustomPageItemChildEventHelper +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSubjectItem + +/** + * 游戏专题:静态图集 + */ +class CustomGameGalleryViewHolder( + viewModel: CustomPageViewModel, + cell: GameGalleryItemCell +) : BaseCustomViewHolder(viewModel, cell.rootView) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + SubjectEventHelper(viewModel) + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSubjectItem) { + val subjectEntity = item.data + item.exposureEventList.clear() + (itemView as? GameGalleryItemCell)?.let { + it.bindWhenInflated { + val binding = it.binding ?: return@bindWhenInflated + binding.titleTv.text = subjectEntity.name + binding.titleTv.setTextColor(R.color.text_primary.toColor(binding.root.context)) + + val gameIconList = arrayListOf( + binding.iconIv1, + binding.iconIv2, + binding.iconIv3, + binding.iconIv4, + binding.iconIv5, + binding.iconIv6, + binding.iconIv7, + binding.iconIv8 + ) + + for ((index, gameIcon) in gameIconList.withIndex()) { + val dataSize = subjectEntity.data?.size ?: 0 + if (index >= dataSize) { + gameIcon.visibility = View.GONE + } else { + val gameEntity = subjectEntity.data?.safelyGetInRelease(index) ?: continue + + gameEntity.subjectId = subjectEntity.id + + gameIcon.visibility = View.VISIBLE + gameIcon.rotation = 35F + gameIcon.displayGameIcon(gameEntity) + + gameIcon.setOnClickListener { + childEventHelper.navigateToGameDetailPage(index, gameEntity) + } + + runOnIoThread(true) { + item.exposureEventList.add( + createExposureEvent( + gameEntity, + item.exposureSource, + pageConfigure.exposureSourceList, + index, + item.componentPosition + ) + ) + } + } + } + + binding.cardView.setCardBackgroundColor(R.color.text_FAFAFA.toColor(binding.root.context)) + binding.titleTv.setTextColor(R.color.text_primary.toColor(binding.root.context)) + + binding.root.setOnClickListener { + childEventHelper.navigateSubjectPage() + } + } + } + } + } + + class GameGalleryItemCell(context: Context) : AsyncCell(context) { + var binding: GameGalleryItemCustomBinding? = null + + override val layoutId = R.layout.game_gallery_item_custom + override val delayFirstTimeBindView = false + override fun createDataBindingView(view: View): View? { + binding = GameGalleryItemCustomBinding.bind(view) + return view.rootView + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalItemViewHolder.kt new file mode 100644 index 0000000000..62c991469b --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalItemViewHolder.kt @@ -0,0 +1,107 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.view.View +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.databinding.GameHorizontalItemCustomBinding +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.custom.adapter.CustomViewExt + +class CustomGameHorizontalItemViewHolder(val binding: GameHorizontalItemCustomBinding) : + BaseRecyclerViewHolder(binding.root) { + + fun bindGameHorizontalItem( + gameEntity: GameEntity, + subjectEntity: SubjectEntity, + isShowFirstLine: Boolean, + isShowSecondLine: Boolean + ) { + val showStar = subjectEntity.typeStyle == "star" + && subjectEntity.data?.any { it.commentCount > 3 } ?: false + CustomViewExt.setGameRattingWithSubject(binding.gameRating, showStar, gameEntity) + + binding.firstRemark.text = gameEntity.assignRemark.firstLine + binding.firstRemark.setTextColor( + if (gameEntity.assignRemark.markedRed) Color.parseColor("#F10000") else R.color.text_primary.toColor( + binding.root.context + ) + ) + binding.secondRemark.text = gameEntity.assignRemark.secondLine + binding.remarkLine.setBackgroundColor(R.color.btn_gray_light.toColor(binding.root.context)) + setRemarkView(subjectEntity, gameEntity, isShowFirstLine, isShowSecondLine) + } + + /** + * 备注显示规则如下: + * 一个专题中,只要有一个或以上的备注信息存在则显示或者隐藏占位,没有则隐藏 + */ + private fun setRemarkView( + subjectEntity: SubjectEntity, + gameEntity: GameEntity, + isShowFirstLine: Boolean, + isShowSecondLine: Boolean + ) { + if (subjectEntity.typeStyle == "remark") { + if (isShowFirstLine) { + if (gameEntity.assignRemark.firstLine.isNotEmpty()) { + binding.firstRemark.visibility = View.VISIBLE + binding.lineContainer.visibility = View.VISIBLE + + binding.firstRemarkBadge.goneIf(!gameEntity.assignRemark.recommend) + + val startTime = gameEntity.assignRemark.startTime * 1000 + val endTime = gameEntity.assignRemark.endTime * 1000 + val currentTime = System.currentTimeMillis() + val isDarkModeOn = DarkModeUtils.isDarkModeOn(binding.root.context) + when { + startTime < currentTime && (endTime > currentTime || startTime > endTime) -> { + binding.remarkDot.setRoundedColorBackground(R.color.primary_theme, 5F) + } + + startTime == 0L && endTime > currentTime -> { + binding.remarkDot.setRoundedColorBackground(R.color.primary_theme, 5F) + } + + isDarkModeOn -> { + binding.remarkDot.background = GradientDrawable().apply { + cornerRadius = 5F.dip2px().toFloat() + setColor(Color.parseColor("#555555")) + } + } + + else -> { + binding.remarkDot.setRoundedColorBackground(R.color.text_instance, 5F) + } + } + } else { + binding.firstRemark.visibility = View.INVISIBLE + binding.firstRemarkBadge.visibility = View.INVISIBLE + binding.lineContainer.visibility = View.INVISIBLE + } + } else { + binding.firstRemark.visibility = View.GONE + binding.firstRemarkBadge.visibility = View.GONE + binding.lineContainer.visibility = View.GONE + } + + if (isShowSecondLine) { + if (gameEntity.assignRemark.secondLine.isNotEmpty()) { + binding.secondRemark.visibility = View.VISIBLE + } else { + binding.secondRemark.visibility = View.INVISIBLE + } + } else { + binding.secondRemark.visibility = View.GONE + } + } else { + binding.firstRemark.visibility = View.GONE + binding.firstRemarkBadge.visibility = View.GONE + binding.secondRemark.visibility = View.GONE + binding.lineContainer.visibility = View.GONE + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalSlideListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalSlideListViewHolder.kt new file mode 100644 index 0000000000..0cd7c16c0d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameHorizontalSlideListViewHolder.kt @@ -0,0 +1,148 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.view.View +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.R +import com.gh.gamecenter.common.view.FixLinearLayoutManager +import com.gh.gamecenter.core.utils.TimeUtils +import com.gh.gamecenter.databinding.RecyclerHorizontalSlideListCustomBinding +import com.gh.gamecenter.entity.SubjectEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.adapter.CustomGameHorizontalSlideAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSubjectItem + +/** + * 游戏专题-图标泳道 + * 游戏专题-横向时间轴 + * 游戏专题-横屏滑动-v2 + */ +class CustomGameHorizontalSlideListViewHolder( + viewModel: CustomPageViewModel, + val binding: RecyclerHorizontalSlideListCustomBinding +) : + BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + SubjectEventHelper(viewModel) + } + + private val adapter by lazy { + CustomGameHorizontalSlideAdapter(itemView.context, childEventHelper) { + createTrackData(_item) + } + } + + override val gameChangedNotifier: IGameChangedNotifier + get() = adapter + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSubjectItem) { + setSubjectTitle(binding.layoutTitle, item, childEventHelper) + val subjectEntity = item.data + if (binding.recyclerView.adapter == null) { + binding.recyclerView.layoutManager = + FixLinearLayoutManager(itemView.context, RecyclerView.HORIZONTAL, false) + (binding.recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false + binding.recyclerView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING) + binding.recyclerView.isNestedScrollingEnabled = false + binding.recyclerView.adapter = adapter + + + binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + item.scrolledOffset = recyclerView.computeHorizontalScrollOffset() + } + } + }) + + } + + if (item.scrolledOffset == 0 && subjectEntity.data?.firstOrNull()?.test?.testTime != null) { + scrollToSpecificItemForTheFirstTime(subjectEntity) + } else { + binding.recyclerView.scrollBy(item.scrolledOffset, 0) + } + + val exposureEventList = arrayListOf() + subjectEntity.data?.let { + it.forEachIndexed { index, game -> + game.isAdData = subjectEntity.adIconActive + game.sequence = index + val event = createExposureEvent( + game, + item.exposureSource, + pageConfigure.exposureSourceList, + index, + item.componentPosition + ) + exposureEventList.add(event) + } + item.exposureEventList = exposureEventList + } + adapter.setData(item, item.exposureEventList) + } + } + + private fun scrollToSpecificItemForTheFirstTime(subjectEntity: SubjectEntity) { + val position = findTheLatestTestGamePosition(subjectEntity) + binding.recyclerView.scrollToPosition(position) + } + + /** + * 获取游戏专题中 "今天/最靠近今天的一天(未来优先于过去)" 游戏的位置 + */ + private fun findTheLatestTestGamePosition(subjectEntity: SubjectEntity): Int { + // first 是日期偏差, second 是 position + var latestDayBeforeTodayPair = Pair(Int.MIN_VALUE, 0) + var latestDayAfterTodayPair = Pair(Int.MAX_VALUE, 0) + + for ((index, game) in subjectEntity.data!!.withIndex()) { + val testDayOffset = TimeUtils.getDaysOffset(game.test?.testTime ?: -999) + if (testDayOffset == 0) { + latestDayBeforeTodayPair = Pair(0, index) + latestDayAfterTodayPair = Pair(0, index) + break + } else if (testDayOffset < 0) { + if (latestDayBeforeTodayPair.first >= testDayOffset) { + latestDayBeforeTodayPair = Pair(testDayOffset, index) + } + } else if (testDayOffset > 0) { + if (testDayOffset <= latestDayAfterTodayPair.first) { + latestDayAfterTodayPair = Pair(testDayOffset, index) + } + } + } + + return if (latestDayAfterTodayPair.first != Int.MAX_VALUE) { + latestDayAfterTodayPair.second + } else { + latestDayBeforeTodayPair.second + } + } + + private fun getTopOfLineContainer(vg: ViewGroup): Int { + var height = 0 + for (i in 0 until vg.childCount) { + val v: View = vg.getChildAt(i) + if (v is ConstraintLayout && v.id == R.id.lineContainer) { + return v.top + } else if (v is ViewGroup) { + height = this.getTopOfLineContainer(v) + v.top + } + } + return height + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameItemViewHolder.kt new file mode 100644 index 0000000000..4f8e89f080 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameItemViewHolder.kt @@ -0,0 +1,286 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.text.TextUtils +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import com.alibaba.android.arouter.launcher.ARouter +import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.constant.RouteConsts +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.DrawableView +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.core.utils.StringUtils +import com.gh.gamecenter.databinding.GameItemCustomBinding +import com.gh.gamecenter.feature.R +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.entity.PluginLocation +import com.gh.gamecenter.feature.provider.IBindingAdaptersProvider +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.adapter.CustomViewExt +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem +import splitties.views.bottomPadding + +/** + * 长列表式 + * 专题游戏-竖屏-v2 + */ +class CustomGameItemViewHolder( + viewModel: CustomPageViewModel, + val binding: GameItemCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + override val onlyNotifyItemChanged: Boolean + get() = true + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + SubjectEventHelper(viewModel) + } + + fun bindView( + item: CustomPageItem, + adapter: RecyclerView.Adapter, + isShowPlatform: Boolean = false, + forceShowSubtitle: Boolean = false + ) { + super.bindView(item) + if (item is CustomSplitSubjectItem) { + fillExposureEventList(item) + + setSplitSubjectTitle(binding.layoutTitle, item, childEventHelper, TITLE_TYPE_SMALL) + + val subject = item.data + val entity = subject.data?.getOrNull(item.startChildPosition) ?: return + binding.run { + val paddingBottom = if (item.isLastLine) { + 8f.dip2px() + } else { + 0 + } + root.setPadding(root.paddingLeft, root.paddingTop, root.paddingRight, paddingBottom) + + + clContainer.background = R.drawable.reuse_listview_item_style.toDrawable(root.context) + selectIv.setImageDrawable(DrawableView.getCheckSelectorDrawable(root.context)) + gameKaifuType.setBackgroundColor(R.color.primary_theme.toColor(root.context)) + gameName.setTextColor(R.color.text_primary.toColor(root.context)) + gameDes.setTextColor(R.color.text_tertiary.toColor(root.context)) + recommendStarPref.setTextColor(R.color.text_tertiary.toColor(root.context)) + + gameIconView.displayGameIcon(entity) + val provider = ARouter.getInstance().build(RouteConsts.provider.bindingAdapters) + .navigation() as? IBindingAdaptersProvider + provider?.setGameName(gameName, entity, isShowPlatform) + provider?.setGameTags(labelList, entity) + + CustomViewExt.setGameRatting(gameRating, subject.briefStyle, entity) + CustomViewExt.setDescription(gameDes, subject.briefStyle, entity) + recommendStar.rating = entity.recommendStar.toFloat() + initCustomGameSubtitleAndAdLabel( + entity, + gameSubtitleTv, + gameNameContainer, + gameName, + entity.adIconActive, + adLabelTv, + forceShowSubtitle + ) + } + + entity.customPageTrackData = createTrackData(item) + DownloadItemUtils.setOnClickListener( + itemView.context, binding.downloadBtn, entity, bindingAdapterPosition, + adapter, + StringUtils.buildString( + "(游戏-专题:", + subject.name, + "-列表[", + (bindingAdapterPosition + 1).toString(), + "])" + ), + location = StringUtils.buildString("游戏-专题-", subject.name, ":", subject.name), + traceEvent = item.exposureEvent + ) { + childEventHelper.onDownloadButtonClick(item.startChildPosition, entity) + } + DownloadItemUtils.updateItem( + itemView.context, + entity, + CustomGameViewHolder(binding), + briefStyle = subject.briefStyle + ) + + itemView.setOnClickListener { + val entrance = if (entity.isPluggable) { + StringUtils.buildString("(游戏-专题:插件化-列表[", "${item.startChildPosition}", "])") + } else { + StringUtils.buildString( + "(游戏-专题:", + subject.name, + "-列表[", + "${item.startChildPosition}", + "])" + ) + } + childEventHelper.navigateToGameDetailPage(item.startChildPosition, entity) + } + + initServerType(binding.gameName, binding.gameKaifuType, entity) + } + } + + private fun fillExposureEventList(item: CustomSplitSubjectItem) { + val game = item.data.data?.getOrNull(item.startChildPosition) ?: return + runOnIoThread(true) { + item.exposureEvent = createExposureEvent( + game, + item.exposureSource, + pageConfigure.exposureSourceList, + item.startChildPosition, + item.componentPosition + ) + } + } + + companion object { + private const val ADVANCED_DOWNLOAD_WIDTH = 34F + private const val SUBTITLE_MIN_WIDTH = 24F + private const val SUBTITLE_MARGIN_START = 4F + private const val AD_LABEL_WITH_MARGIN_WIDTH = 25F + fun initServerType(gameNameTv: TextView, serverTypeTv: TextView, gameEntity: GameEntity) { + val serverLabel = gameEntity.serverLabel + when { + gameEntity.test != null -> { + serverTypeTv.visibility = View.GONE + serverTypeTv.text = "" + } + + serverLabel != null && !gameEntity.advanceDownload -> { + serverTypeTv.visibility = View.VISIBLE + serverTypeTv.text = serverLabel.value + serverTypeTv.setTextColor(Color.WHITE) + if (gameEntity.isUseDefaultServerStyle()) { + serverTypeTv.background = + R.drawable.server_label_default_bg.toDrawable(gameNameTv.context) + serverTypeTv.setTextColor(R.color.text_secondary.toColor(gameNameTv.context)) + } else { + serverTypeTv.background = DrawableView.getServerDrawable(serverLabel.color) + serverTypeTv.setTextColor(R.color.white.toColor(gameNameTv.context)) + } + } + + else -> serverTypeTv.visibility = View.GONE + } + + // 由于RecyclerView的复用机制 需要每次测量gameName的宽 + gameNameTv.requestLayout() + } + + @JvmStatic + fun initCustomGameSubtitleAndAdLabel( + entity: GameEntity, + gameSubtitleTv: TextView, + gameNameContainer: LinearLayout? = null, + gameNameTv: TextView? = null, + adIconActive: Boolean = false, + adLabelTv: TextView? = null, + forceShowSubtitle: Boolean = false, + modificationCallback: (() -> Unit)? = null + ) { + if (entity.id == gameSubtitleTv.getTag(R.string.tag_game_name_id)) return + gameSubtitleTv.setTag(R.string.tag_game_name_id, entity.id) + + var showSubtitle = false + var showAdvancedDownload = false + if ((entity.serverLabel == null && entity.subtitle.isNotEmpty() && !entity.advanceDownload) || (forceShowSubtitle && entity.subtitle.isNotEmpty())) { + showSubtitle = true + gameSubtitleTv.run { + visibility = View.VISIBLE + text = entity.subtitle + if (entity.subtitleStyle != null && !TextUtils.isEmpty(entity.subtitleStyle?.color)) { + setTextColor("#${entity.subtitleStyle?.color}".hexStringToIntColor()) + background = GradientDrawable().apply { + cornerRadius = 2F.dip2px().toFloat() + if (entity.subtitleStyle?.style == "border") { + setColor(Color.TRANSPARENT) + setStroke(0.5F.dip2px(), "#${entity.subtitleStyle?.background}".hexStringToIntColor()) + } else { + shape = GradientDrawable.RECTANGLE + setColor("#${entity.subtitleStyle?.background}".hexStringToIntColor()) + } + } + } + } + } + if (entity.advanceDownload) { + showAdvancedDownload = true + gameSubtitleTv.apply { + visibility = View.VISIBLE + text = "预下载" + setTextColor(R.color.text_secondary.toColor(context)) + background = R.drawable.bg_advance_download_game_subtitle.toDrawable(context) + } + } + if (showSubtitle || showAdvancedDownload) { + val minWidth = + if (showSubtitle) SUBTITLE_MIN_WIDTH.dip2px() else if (showAdvancedDownload) ADVANCED_DOWNLOAD_WIDTH.dip2px() else 0 + gameSubtitleTv.post { + if (adIconActive && gameNameContainer != null && gameNameTv != null && adLabelTv != null) { + val remainWidth = gameNameContainer.width - gameNameTv.width + if (remainWidth >= AD_LABEL_WITH_MARGIN_WIDTH.dip2px() + SUBTITLE_MARGIN_START.dip2px() + minWidth) { + val subtitleMaxWidth = + remainWidth - AD_LABEL_WITH_MARGIN_WIDTH.dip2px() - SUBTITLE_MARGIN_START.dip2px() + gameSubtitleTv.maxWidthExcludeZero(subtitleMaxWidth) + adLabelTv.visibility = View.VISIBLE + } else { + // 如果不够位置直接不展示广告标识 + adLabelTv.visibility = View.GONE + } + } + // 副标题至少展示2个字符,如果不够位置直接不展示副标题 + if (gameSubtitleTv.layout != null) { + val layoutText = gameSubtitleTv.layout.text.toString() + val shouldShow = + gameSubtitleTv.width >= minWidth || (!layoutText.contains("\uFEFF") && layoutText.length > 2) + gameSubtitleTv.isVisible = shouldShow + } + } + modificationCallback?.invoke() + } else { + gameSubtitleTv.visibility = View.GONE + adLabelTv?.post { + if (adIconActive && gameNameContainer != null && gameNameTv != null) { + val remainWidth = gameNameContainer.width - gameNameTv.width + if (remainWidth < AD_LABEL_WITH_MARGIN_WIDTH.dip2px()) { + gameNameTv.maxWidthExcludeZero(gameNameContainer.width - AD_LABEL_WITH_MARGIN_WIDTH.dip2px()) + } + adLabelTv.visibility = View.VISIBLE + } else { + adLabelTv.visibility = View.GONE + } + } + } + } + } + + class CustomGameViewHolder(binding: GameItemCustomBinding) : GameViewHolder(binding.root) { + init { + gameDownloadBtn = binding.downloadBtn + gameDes = binding.gameDes + recommendContainer = binding.recommendContainer + recommendIv = binding.recommendIv + recommendTv = binding.recommendTv + multiVersionDownloadTv = binding.multiVersionDownloadTv + gameDownloadTips = binding.downloadTipsLottie + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluggableViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluggableViewHolder.kt new file mode 100644 index 0000000000..4a407b90d5 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluggableViewHolder.kt @@ -0,0 +1,39 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.view.View +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.utils.toDrawable +import com.gh.gamecenter.common.view.DrawableView +import com.gh.gamecenter.databinding.GameItemCustomBinding +import com.gh.gamecenter.feature.entity.GameEntity + +class CustomGamePluggableViewHolder(val binding: GameItemCustomBinding) : BaseRecyclerViewHolder(binding.root) { + fun initServerType(gameEntity: GameEntity) { + val serverLabel = gameEntity.serverLabel + when { + gameEntity.test != null -> { + binding.gameKaifuType.visibility = View.GONE + binding.gameKaifuType.text = "" + } + serverLabel != null && !gameEntity.advanceDownload -> { + binding.gameKaifuType.visibility = View.VISIBLE + binding.gameKaifuType.text = serverLabel.value + if (gameEntity.isUseDefaultServerStyle()) { + binding.gameKaifuType.background = + R.drawable.server_label_default_bg.toDrawable(binding.root.context) + binding.gameKaifuType.setTextColor(R.color.text_secondary.toColor(binding.root.context)) + } else { + binding.gameKaifuType.background = + DrawableView.getServerDrawable(serverLabel.color) + binding.gameKaifuType.setTextColor(R.color.white.toColor(binding.root.context)) + } + } + else -> binding.gameKaifuType.visibility = View.GONE + } + + // 由于RecyclerView的复用机制 需要每次测量gameName的宽 + binding.gameName.requestLayout() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluginViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluginViewHolder.kt new file mode 100644 index 0000000000..e9923cd56e --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGamePluginViewHolder.kt @@ -0,0 +1,168 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.core.view.updateLayoutParams +import com.gh.common.util.HomePluggableHelper +import com.gh.gamecenter.R +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.databinding.GamePluginItemCustomBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.PluginDisplayStatus +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.adapter.CustomGamePluginAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomPluginItem +import java.util.* + +/** + * Created by khy on 15/08/17. + * 游戏插件模块 + */ +class CustomGamePluginViewHolder( + private val makeItemBackgroundTransparent: Boolean, + viewModel: CustomPageViewModel, + val binding: GamePluginItemCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + private val context: Context + get() = itemView.context + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + OtherItemEventHelper(viewModel) + } + + private var pluginDisplayStatus = PluginDisplayStatus.DEFAULT + + private var isManualChangedDisplayStatus = false + + override val onlyNotifyItemChanged: Boolean + get() = true + + private val adapter by lazy { + CustomGamePluginAdapter( + context, + viewModel, + childEventHelper, + object : CustomGamePluginAdapter.IUpdateDisplayType { + override fun update(status: PluginDisplayStatus) { + if (!isManualChangedDisplayStatus) { + pluginDisplayStatus = status + } + } + }) { + createTrackData(_item) + } + } + + init { + binding.recyclerView.itemAnimator = null + } + + fun bindView(item: CustomPageItem, notifyPluginCallback: () -> Unit) { + super.bindView(item) + if (item is CustomPluginItem) { + fillExposureEvent(item) + + val pluginList = item.data + if (pluginList.isEmpty()) { + binding.pluginHead.goneIf(true) + binding.pluginExtend.goneIf(true) + binding.pluginExtendContainer.goneIf(true) + binding.recyclerView.adapter = null + } else { + binding.pluginHead.goneIf(false) + binding.pluginExtend.goneIf(false) + binding.pluginExtendContainer.goneIf(false) + if (makeItemBackgroundTransparent) { + itemView.setBackgroundColor(R.color.transparent.toColor(context)) + } + + if (pluginDisplayStatus == PluginDisplayStatus.DEFAULT) { + pluginDisplayStatus = HomePluggableHelper.getPluginDisplayedStyle(pluginList) + } + + if (binding.recyclerView.adapter == null) { + binding.recyclerView.adapter = adapter + } + adapter.openList(pluginDisplayStatus) + adapter.setData(pluginList) + + if ((pluginDisplayStatus == PluginDisplayStatus.OPEN_AND_BUTTON || pluginDisplayStatus == PluginDisplayStatus.OPEN_TWO_AND_BUTTON)) { + binding.pluginExtendContainer.visibility = View.VISIBLE + binding.pluginHeadOpen.visibility = View.GONE + if (pluginDisplayStatus == PluginDisplayStatus.OPEN_TWO_AND_BUTTON) { + binding.pluginExtend.text = "展开" + binding.icPluginExtend.setImageResource(R.drawable.home_plugin_bottom_open) + binding.pluginExtend.setTextColor(R.color.primary_theme.toColor(context)) + } else { + binding.pluginExtend.text = "收起" + binding.icPluginExtend.setImageResource(R.drawable.home_plugin_bottom_close) + binding.pluginExtend.setTextColor(R.color.text_tertiary.toColor(context)) + } + } else { + binding.pluginExtendContainer.visibility = View.GONE + binding.pluginHeadOpen.visibility = View.VISIBLE + } + + if (pluginDisplayStatus == PluginDisplayStatus.CLOSE) { + binding.pluginHeadOpen.setImageResource(R.drawable.home_plugin_open) + binding.pluginHeadIcon.updateLayoutParams { + bottomMargin = 16F.dip2px() + } + } else { + binding.pluginHeadOpen.setImageResource(R.drawable.home_plugin_close) + binding.pluginHeadIcon.updateLayoutParams { + bottomMargin = 8F.dip2px() + } + } + + binding.pluginHeadTitle.text = + String.format(Locale.getDefault(), "你有%d个游戏可以升级插件版", pluginList.size) + val expandClick: (View) -> Unit = { + if (it.id != R.id.plugin_head || binding.pluginExtendContainer.visibility == View.GONE) { + if (pluginDisplayStatus == PluginDisplayStatus.CLOSE || pluginDisplayStatus == PluginDisplayStatus.OPEN_TWO_AND_BUTTON) { + if (pluginDisplayStatus == PluginDisplayStatus.CLOSE) pluginDisplayStatus = + PluginDisplayStatus.OPEN + if (pluginDisplayStatus == PluginDisplayStatus.OPEN_TWO_AND_BUTTON) pluginDisplayStatus = + PluginDisplayStatus.OPEN_AND_BUTTON + } else { + pluginDisplayStatus = PluginDisplayStatus.CLOSE + } + isManualChangedDisplayStatus = true + binding.recyclerView.recycledViewPool.clear() + notifyPluginCallback() + } + } + binding.pluginExtendContainer.setOnClickListener(View.OnClickListener(function = expandClick)) + binding.pluginHead.setOnClickListener(View.OnClickListener(function = expandClick)) + } + } + + } + + private fun fillExposureEvent(item: CustomPluginItem) { + val exposureList = arrayListOf() + runOnIoThread(true) { + item.data.forEachIndexed { index, game -> + exposureList.add( + createExposureEvent( + game, + listOf(ExposureSource("插件化", "")), + pageConfigure.exposureSourceList, + index, + item.componentPosition + ) + ) + } + } + item.exposureEventList = exposureList + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameVerticalSlideViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameVerticalSlideViewHolder.kt new file mode 100644 index 0000000000..c2b5954b42 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomGameVerticalSlideViewHolder.kt @@ -0,0 +1,112 @@ +package com.gh.gamecenter.home.custom.viewholder + +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.core.utils.MtaHelper +import com.gh.gamecenter.databinding.GameVerticalSlideItemCustomBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.game.vertical.OnPagerSnapScrollListener +import com.gh.gamecenter.game.vertical.SpanCountPagerSnapHelper +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.adapter.CustomGameVerticalAdapter +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSubjectItem + +/** + * 游戏专题-滑动列表 + */ +class CustomGameVerticalSlideViewHolder( + viewModel: CustomPageViewModel, + val binding: GameVerticalSlideItemCustomBinding, +) : + BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + SubjectEventHelper(viewModel) + } + + private val adapter by lazy { + CustomGameVerticalAdapter( + itemView.context, pageConfigure.transparentBackground, + { + createTrackData(_item) + }, + childEventHelper + ) + } + + override val gameChangedNotifier: IGameChangedNotifier? + get() = adapter + + init { + binding.recyclerView.isNestedScrollingEnabled = false + binding.recyclerView.itemAnimator = null + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSubjectItem) { + setSubjectTitle(binding.layoutTitle, item, childEventHelper, TITLE_TYPE_SMALL) + val subjectEntity = item.data + val games = subjectEntity.data ?: return + + binding.recyclerView.clearOnScrollListeners() + binding.recyclerView.onFlingListener = null + val spanCount = subjectEntity.list + + val snapHelper = SpanCountPagerSnapHelper(spanCount, true) + if (binding.recyclerView.layoutManager == null) { + val layoutManager = GridLayoutManager(itemView.context, spanCount, RecyclerView.HORIZONTAL, false) + binding.recyclerView.layoutManager = layoutManager + binding.recyclerView.adapter = adapter + + } else { + val gridLayoutManager = binding.recyclerView.layoutManager as? GridLayoutManager + if (gridLayoutManager?.spanCount != spanCount) { + gridLayoutManager?.spanCount = spanCount + } + } + adapter.setData(item.data) + snapHelper.attachToRecyclerView(binding.recyclerView) + + val listExposureEventList = arrayListOf() + val exposureEventList = arrayListOf() + item.exposureEventList = exposureEventList + val exposureClosure: (Int) -> Unit = { + runOnIoThread(true) { + listExposureEventList.clear() + + val startOffset = it * subjectEntity.list + val endOffset = if (startOffset + subjectEntity.list >= games.size) { + games.size + } else { + startOffset + subjectEntity.list + } + for (i in startOffset until endOffset) { + val game = games[i].apply { + isAdData = subjectEntity.adIconActive + } + val event = ExposureEvent.createEventWithSourceConcat( + game, + pageConfigure.exposureSourceList, + item.exposureSource + ) + game.exposureEvent = event + listExposureEventList.add(event) + } + exposureEventList.addAll(listExposureEventList) + } + } + + exposureClosure(0) + + binding.recyclerView.addOnScrollListener(OnPagerSnapScrollListener(snapHelper) { + MtaHelper.onEvent("游戏专题", "滑动", subjectEntity.name) + exposureClosure(it) + }) + } + } +} diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayItemViewHolder.kt new file mode 100644 index 0000000000..50582f73f9 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayItemViewHolder.kt @@ -0,0 +1,67 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.content.Context +import android.view.View +import com.gh.common.databind.BindingAdapters +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.AsyncCell +import com.gh.gamecenter.databinding.HomeAmwayItemBinding +import com.gh.gamecenter.databinding.HomeAmwayItemCustomBinding +import com.gh.gamecenter.entity.AmwayCommentEntity +import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity +import java.util.regex.Pattern + +class CustomHomeAmwayItemViewHolder(val binding: HomeAmwayItemCustomBinding) : BaseRecyclerViewHolder(binding.root) { + + fun bindAmway(amway: AmwayCommentEntity) { + val gameEntity = amway.game.toGameEntity() + + binding.gameIcon.displayGameIcon(gameEntity) + binding.gameName.text = amway.game.name + binding.amwayScoreNumber.text = amway.game.star.toString() + BindingAdapters.setGameTags(binding.gameTags, amway.game.tag, 2) + + ImageUtils.display(binding.userIcon, amway.comment.user.icon) + ImageUtils.display(binding.userBadge, amway.comment.user.auth?.icon) + binding.userName.text = amway.comment.user.name + binding.ratingStart.rating = amway.comment.star.toFloat() + + // 强制更改标签颜色 + val tags = amway.game.tag + if (!tags.isNullOrEmpty()) { + for (tag in tags) tag.color = "999999" + } + + val m = Pattern.compile(RatingEditActivity.LABEL_REGEX).matcher(amway.comment.content) + if (m.find()) { + val contents = TextHelper.getCommentLabelSpannableStringBuilder(amway.comment.content, R.color.text_theme) + binding.content.text = contents + } else { + binding.content.text = amway.comment.content + } + binding.run { + amwayBackground.background = R.drawable.home_amway_selector.toDrawable(root.context) + ratingBlock.background = R.drawable.home_amway_rating_selector.toDrawable(root.context) + all.background = R.drawable.home_amway_selector.toDrawable(root.context) + gameName.setTextColor(R.color.text_primary.toColor(root.context)) + content.setTextColor(R.color.text_primary.toColor(root.context)) + userName.setTextColor(R.color.text_secondary.toColor(root.context)) + ratingName.setTextColor(R.color.text_tertiary.toColor(root.context)) + all.setTextColor(R.color.text_primary.toColor(root.context)) + } + GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, binding.gameSubtitleTv) + } +} + +class HomeAmwayAsyncCell(context: Context) : AsyncCell(context) { + var binding: HomeAmwayItemBinding? = null + + override val layoutId = R.layout.home_amway_item + override fun createDataBindingView(view: View): View? { + binding = HomeAmwayItemBinding.bind(view) + return view.rootView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayListViewHolder.kt new file mode 100644 index 0000000000..fa2b583024 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeAmwayListViewHolder.kt @@ -0,0 +1,104 @@ +package com.gh.gamecenter.home.custom.viewholder + +import com.gh.common.util.NewLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.LeftPagerSnapHelper +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.databinding.HomeAmwayListCustomBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.adapter.CustomHomeAmwayAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper +import com.gh.gamecenter.home.custom.model.CustomAmwayItem +import com.gh.gamecenter.home.custom.model.CustomPageItem + +/** + * 安利墙 + */ +class CustomHomeAmwayListViewHolder( + viewModel: CustomPageViewModel, + val binding: HomeAmwayListCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + OtherItemEventHelper(viewModel) + } + + private val adapter by lazy { + CustomHomeAmwayAdapter( + itemView.context, + childEventHelper + ) { v, childPosition, amway -> + when (v.id) { + R.id.rating_block -> { + childEventHelper.navigateToRatingReplyPage(amway.game.id, amway.comment.id) + NewLogUtils.logHomeShareWallCardClick(amway.game.name ?: "", amway.game.id, "评论内容") + } + + R.id.user_icon_container -> { + childEventHelper.navigateUserHomePage(amway.comment.user.id ?: "") + NewLogUtils.logHomeShareWallCardClick(amway.game.name ?: "", amway.game.id, "用户信息") + } + + else -> { + childEventHelper.navigateToGameDetailPageById(amway.game.id, amway.game.name, childPosition) + NewLogUtils.logHomeShareWallCardClick(amway.game.name ?: "", amway.game.id, "游戏信息") + } + } + } + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomAmwayItem) { + + fillExposureEventList(item) + + setTitle() + + if (binding.recyclerView.adapter == null) { + binding.recyclerView.adapter = adapter + binding.recyclerView.isNestedScrollingEnabled = false + val snapHelper = LeftPagerSnapHelper(4F.dip2px()) + snapHelper.attachToRecyclerView(binding.recyclerView) + } + adapter.submitList(item.data) + + } + } + + private fun setTitle() { + setTitleStyle(binding.layoutTitle) + binding.layoutTitle.lavRefresh.goneIf(true) + binding.layoutTitle.gUser.goneIf(true) + binding.layoutTitle.tvTitle.text = "安利墙" + binding.layoutTitle.tvRight.text = "全部" + binding.layoutTitle.tvRight.setOnClickListener { + NewLogUtils.logHomeShareWallButtonClick("右上角") + childEventHelper.navigateToAmway() + + } + } + + private fun fillExposureEventList(item: CustomAmwayItem) { + val exposureList = arrayListOf() + runOnIoThread(true) { + item.data.forEachIndexed { index, amway -> + val event = createExposureEvent( + amway.game.toGameEntity(), + listOf(ExposureSource("安利墙", "")), + pageConfigure.exposureSourceList, + index, + item.componentPosition + ) + exposureList.add(event) + } + } + item.exposureEventList = exposureList + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeDiscoverCardViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeDiscoverCardViewHolder.kt new file mode 100644 index 0000000000..d1aae96057 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeDiscoverCardViewHolder.kt @@ -0,0 +1,194 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.content.Context +import android.view.Gravity +import android.widget.LinearLayout +import android.widget.TextView +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.constant.Config +import com.gh.common.util.NewFlatLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.databinding.HomeDiscoverCardItemCustomBinding +import com.gh.gamecenter.entity.DiscoveryGameCardLabel +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.game.vertical.OnPagerSnapScrollListener +import com.gh.gamecenter.game.vertical.SpanCountPagerSnapHelper +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.adapter.CustomDiscoverCardGameAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper +import com.gh.gamecenter.home.custom.model.CustomDiscoverCardItem +import com.gh.gamecenter.home.custom.model.CustomPageItem +import splitties.views.dsl.core.endMargin + +/** + * 光环推荐 + * 发现页 + */ +class CustomHomeDiscoverCardViewHolder( + viewModel: CustomPageViewModel, + val binding: HomeDiscoverCardItemCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + OtherItemEventHelper(viewModel) + } + + private val adapter by lazy { + CustomDiscoverCardGameAdapter( + itemView.context, + pageConfigure.entrance, + childEventHelper + ) { + createTrackData(_item) + } + } + + override val gameChangedNotifier: IGameChangedNotifier + get() = adapter + + init { + binding.recyclerView.isNestedScrollingEnabled = false + binding.recyclerView.itemAnimator = null + } + + fun bindView(item: CustomPageItem, position: Int) { + super.bindView(item) + if (item is CustomDiscoverCardItem) { + _item = item + + val spanCount = 3 + val snapHelper = SpanCountPagerSnapHelper(spanCount, false) + val entity = item.data ?: return + binding.run { + val context = root.context + labelHSV.background = R.drawable.background_shape_white_radius_6_bottom_only.toDrawable(context) + prefsIv.goneIf(!Config.getUserInterestedGame()) { + prefsIv.setOnClickListener { + context.ifLogin("${pageConfigure.entrance}-发现页卡片") { + childEventHelper.navigateToInterestedGamePage() + } + } + } + allTv.setOnClickListener { + childEventHelper.navigateToDiscoveryPage() + } + + if (recyclerView.adapter == null) { + recyclerView.layoutManager = GridLayoutManager(context, spanCount, RecyclerView.HORIZONTAL, false) + recyclerView.adapter = adapter + snapHelper.attachToRecyclerView(recyclerView) + } + adapter.submitList(entity.games) + + + val childCount = labelContainer.childCount + entity.label.forEachIndexed { index, label -> + if (index < childCount) { + val labelView = labelContainer.getChildAt(index) as TextView + updateLabelView( + context, + labelView, + label, + index == entity.label.size - 1, + pageConfigure.entrance, + "", + position + ) + } else { + val newLabelView = TextView(context) + updateLabelView( + context, + newLabelView, + label, + index == entity.label.size - 1, + pageConfigure.entrance, + "", + position + ) + labelContainer.addView(newLabelView) + } + } + } + + fillExposureEventList(0, item) + + binding.recyclerView.addOnScrollListener(OnPagerSnapScrollListener(snapHelper) { + fillExposureEventList(it, item) + }) + + } + } + + private fun updateLabelView( + context: Context, + textView: TextView, + label: DiscoveryGameCardLabel, + isLast: Boolean, + entrance: String, + blockName: String, + position: Int + ) { + textView.background = R.drawable.bg_shape_space_radius_4.toDrawable(context) + textView.setTextColor(R.color.text_secondary.toColor(context)) + + if (textView.text == label.title) return + + textView.run { + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + 36F.dip2px() + ).apply { endMargin = if (isLast) 14F.dip2px() else 8F.dip2px() } + setPadding(12F.dip2px(), 0, 12F.dip2px(), 0) + gravity = Gravity.CENTER + setDrawableEnd(R.drawable.ic_interest_arrow) + compoundDrawablePadding = 8F.dip2px() + textSize = 12F + text = label.title + label.text = label.linkText + setOnClickListener { + NewFlatLogUtils.logDiscoverPageRecommendedInterestCardClick( + position, + label.text ?: "", + label.type ?: "", + label.link ?: "", + entrance, + blockName + ) + childEventHelper.navigateToLinkPage(label) + } + } + } + + fun fillExposureEventList(position: Int, item: CustomDiscoverCardItem) { + val exposureEventList = arrayListOf() + val gameList = item.data?.games ?: return + runOnIoThread(true) { + val startOffset = position * item.spanCount + val endOffset = if (startOffset + item.spanCount >= gameList.size) { + gameList.size + } else { + startOffset + item.spanCount + } + for (i in startOffset until endOffset) { + val game = gameList.getOrNull(i) ?: return@runOnIoThread + val event = createExposureEvent( + game, + listOf(ExposureSource("发现", game.getTypeName())), + pageConfigure.exposureSourceList, + i, + item.componentPosition + ) + game.exposureEvent = event + exposureEventList.add(event) + } + } + item.exposureEventList = exposureEventList + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionCarouselViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionCarouselViewHolder.kt new file mode 100644 index 0000000000..c1f135c908 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionCarouselViewHolder.kt @@ -0,0 +1,105 @@ +package com.gh.gamecenter.home.custom.viewholder + +import androidx.lifecycle.LifecycleOwner +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnScrollListener +import com.gh.gamecenter.common.utils.DarkModeUtils +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.view.stacklayoutmanager.StackLayoutManager +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.databinding.HomeGameCollectionItemCustomBinding +import com.gh.gamecenter.home.custom.BannerInRecyclerController +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.adapter.CustomHomeGameCollectionCarouselAdapter +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.fillExposureInSubjectCollection +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem +import com.gh.gamecenter.home.gamecollection.carousel.GameCollectionStackAnimation +import com.gh.gamecenter.home.gamecollection.carousel.GameCollectionStackLayout + +/** + * 专题合集-轮播图 + * 游戏单合集-轮播图 + */ +class CustomHomeGameCollectionCarouselViewHolder( + viewModel: CustomPageViewModel, + licOwner: LifecycleOwner, + val binding: HomeGameCollectionItemCustomBinding +) : + BaseCustomViewHolder(viewModel, binding.root) { + + private val bannerController = BannerInRecyclerController { + if (adapter.itemCount > 0) { + binding.recyclerView.smoothScrollToPosition(layoutManager.getFirstVisibleItemPosition() + 1) + } + + } + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + GameSubjectCollectionEventHelper(viewModel) + } + + private val adapter by lazy { + CustomHomeGameCollectionCarouselAdapter( + itemView.context, + pageConfigure, + childEventHelper + ) + } + + private val layoutManager by lazy { + StackLayoutManager( + StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT, + 3, + GameCollectionStackAnimation::class.java, + GameCollectionStackLayout::class.java + ).apply { setPagerFlingVelocity(DisplayUtils.getScreenWidth() / 2) } + } + + init { + binding.recyclerView.isNestedScrollingEnabled = false + licOwner.lifecycle.addObserver(bannerController) + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSubjectCollectionItem) { + + fillExposureInSubjectCollection(item, pageConfigure.exposureSourceList) + + val isDarkModeChanged = DarkModeUtils.isDarkModeOn(binding.root.context) != darkMode + setSubjectCollectionTitle(item, binding.layoutTitle, isDarkModeChanged, childEventHelper) + if (isDarkModeChanged) { + darkMode = DarkModeUtils.isDarkModeOn(binding.root.context) + } + if (binding.recyclerView.adapter == null) { + binding.recyclerView.layoutManager = layoutManager + binding.recyclerView.adapter = adapter + layoutManager.setItemOffset(4F.dip2px()) + binding.recyclerView.addOnScrollListener(object : OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + bannerController.start() + } else { + bannerController.pause() + } + } + }) + + } + adapter.setData(item) + bannerController.start() + } + } + + override fun onViewAttach(parent: RecyclerView?) { + bannerController.onViewAttachedToWindow(parent) + } + + override fun onViewDetach(parent: RecyclerView?) { + bannerController.onViewDetachedFromWindow(parent) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionSlideViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionSlideViewHolder.kt new file mode 100644 index 0000000000..4b0c0f0e93 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameCollectionSlideViewHolder.kt @@ -0,0 +1,67 @@ +package com.gh.gamecenter.home.custom.viewholder + +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.common.utils.DarkModeUtils +import com.gh.gamecenter.common.view.FixLinearLayoutManager +import com.gh.gamecenter.databinding.HomeGameCollectionSlideItemCustomBinding +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.adapter.CustomHomeGameCollectionSlideAdapter +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.fillExposureInSubjectCollection +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem + +/** + * 专题合集:横排滑动大卡片/横排滑动小卡片 + * 游戏单合集:横排滑动大卡片/横排滑动小卡片 + */ +class CustomHomeGameCollectionSlideViewHolder( + private val isBigSlide: Boolean, + viewModel: CustomPageViewModel, + val binding: HomeGameCollectionSlideItemCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + GameSubjectCollectionEventHelper(viewModel) + } + + private val _adapter by lazy { + CustomHomeGameCollectionSlideAdapter( + binding.root.context, + isBigSlide, + pageConfigure.entrance, + childEventHelper + ) { + createTrackData(_item) + } + } + + + override val gameChangedNotifier: IGameChangedNotifier? + get() = if (isBigSlide) _adapter else null + + + override fun bindView(item: CustomPageItem) { + if (item is CustomSubjectCollectionItem) { + super.bindView(item) + + fillExposureInSubjectCollection(item, pageConfigure.exposureSourceList) + + val isDarkModeChanged = DarkModeUtils.isDarkModeOn(binding.root.context) != darkMode + setSubjectCollectionTitle(item, binding.layoutTitle, isDarkModeChanged, childEventHelper) + if (isDarkModeChanged) { + darkMode = DarkModeUtils.isDarkModeOn(binding.root.context) + } + + if (binding.recyclerView.adapter == null) { + binding.recyclerView.layoutManager = + FixLinearLayoutManager(itemView.context, RecyclerView.HORIZONTAL, false) + binding.recyclerView.adapter = _adapter + } + _adapter.setData(item) + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameItemViewHolder.kt new file mode 100644 index 0000000000..fd4d94e29d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameItemViewHolder.kt @@ -0,0 +1,222 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.graphics.drawable.ColorDrawable +import android.view.View +import android.view.ViewGroup.MarginLayoutParams +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.databind.BindingAdapters +import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.RandomUtils +import com.gh.gamecenter.databinding.HomeGameItemCustomBinding +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomGameItem +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder +import com.shuyu.gsyvideoplayer.video.base.GSYVideoView + +/** + * 展开大图 + * 游戏-首页大图/顶部视频-v2 + */ +class CustomHomeGameItemViewHolder( + viewModel: CustomPageViewModel, + val binding: HomeGameItemCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + SubjectEventHelper(viewModel) + } + + override val onlyNotifyItemChanged: Boolean + get() = true + + fun bindView( + item: CustomPageItem, + position: Int, + adapter: RecyclerView.Adapter + ) { + super.bindView(item) + if (item is CustomGameItem) { + + _item.exposureEventList.clear() + + val topMargin = if (item.isFirstLine) { + 0 + } else { + -16f.dip2px() + } + (itemView.layoutParams as? MarginLayoutParams)?.let { + it.topMargin = topMargin + } + + val isDarkMode = DarkModeUtils.isDarkModeOn(itemView.context) + if (isDarkMode) { + binding.vBackground.background = null + } else { + binding.vBackground.setBackgroundResource(R.drawable.bg_custom_game_item) + } + + binding.flCardContainer.background = + ContextCompat.getDrawable(itemView.context, R.drawable.bg_divider_game_expand_item_custom) + + val game = item.data + val topVideo = game.topVideo + val homeSetting = game.homeSetting + game.outerSequence = item.componentPosition + + val event = createExposureEvent( + item.data, + listOf( + ExposureSource( + "专题", + "${item.linkColumn?.name ?: ""}+${item.componentStyle}+${item.linkColumn?.id ?: ""}" + ) + ), + pageConfigure.exposureSourceList, + item.childPosition, + item.componentPosition + ) + item.exposureEventList.add(event) + item.exposureEvent = event + + bindGameInfo( + position, + game, + adapter, + position, + item.exposureEvent, + pageConfigure.entrance, + item.data.adIconActive + ) { + CustomPageTrackData( + viewModel.pageLocation, + item.componentName, + item.componentStyle, + item.linkColumn?.name ?: "", + item.linkColumn?.id ?: "" + ) + } + binding.gameBrief.text = game.recommendText + binding.gameImage.visibleIf(game.showImage) { + ImageUtils.display(binding.gameImage, game.homeSetting.image, false) + val hierarchy = binding.gameImage.hierarchy + try { + hierarchy.setPlaceholderImage(ColorDrawable(game.homeSetting.placeholderColor.hexStringToIntColor())) + } catch (ignore: Throwable) { + hierarchy.setPlaceholderImage(RandomUtils.getRandomPlaceholderColor()) + } + } + binding.autoVideoView.goneIf(game.showImage) { + if (!binding.autoVideoView.isInPlayingState) { + GSYVideoOptionBuilder() + .setIsTouchWiget(false) + .setUrl(topVideo?.url ?: "") + .setRotateViewAuto(false) + .setCacheWithPlay(true) + .setRotateWithSystem(false) + .setReleaseWhenLossAudio(true) + .setLooping(false) + .setShowFullAnimation(false) + .build(binding.autoVideoView) + binding.autoVideoView.updateThumb(topVideo?.poster ?: "") + binding.autoVideoView.setParamsData(game, homeSetting.placeholderColor, true) + binding.autoVideoView.detailBtn?.setOnClickListener { + itemView.performClick() + binding.autoVideoView.uploadVideoStreamingPlaying("点击遮罩", true) + } + binding.autoVideoView.setOnVideoClickListener { itemView.performClick() } + } + } + + binding.gameImage.setOnClickListener { + childEventHelper.navigateToGameDetailPage(item.childPosition, game) + } + itemView.setOnClickListener { + childEventHelper.navigateToGameDetailPage(item.childPosition, game) + + if (binding.autoVideoView.isInPlayingState) { + binding.autoVideoView.uploadVideoStreamingPlaying( + "游戏详情-播放点击", + binding.autoVideoView.detailBtn?.visibility == View.VISIBLE + ) + } else { + if (binding.autoVideoView.currentState == GSYVideoView.CURRENT_STATE_AUTO_COMPLETE) { + binding.autoVideoView.uploadVideoStreamingPlaying( + "游戏详情-完播点击", + binding.autoVideoView.detailBtn?.visibility == View.VISIBLE + ) + } + } + } + } + } + + private fun bindGameInfo( + childPosition: Int, + game: GameEntity, + adapter: RecyclerView.Adapter, + position: Int, + exposureEvent: ExposureEvent?, + entrance: String, + adIconActive: Boolean, + createTrackData: () -> CustomPageTrackData + ) { + binding.gameIcon.displayGameIcon(game) + binding.gameName.setTextColor(R.color.text_primary.toColor(binding.root.context)) + binding.gameBrief.setTextColor(R.color.text_primary.toColor(binding.root.context)) + binding.gameName.text = game.name + binding.gameRating.text = game.star.toString() + binding.gameRating2.text = game.star.toString() + binding.gameRating.setDrawableStart(R.drawable.home_game_rating, 12F.dip2px(), 12F.dip2px()) + BindingAdapters.setGameTags(binding.gameTags, game) + + GameItemViewHolder.initGameSubtitleAndAdLabel( + game, + binding.gameSubtitleTv, + binding.gameNameContainer, + binding.gameName, + adIconActive, + binding.adLabelTv + ) + + game.customPageTrackData = createTrackData() + + DownloadItemUtils.setOnClickListener( + binding.root.context, + binding.downloadBtn, + game, + position, + adapter, + entrance, + location = "", + traceEvent = exposureEvent + ) { + childEventHelper.onDownloadButtonClick(childPosition, game) + } + DownloadItemUtils.updateItem( + binding.root.context, + game, + GameViewHolder(binding.root).apply { + gameDownloadBtn = binding.downloadBtn + multiVersionDownloadTv = binding.multiVersionDownloadTv + gameDownloadTips = binding.downloadTipsLottie + }, + false + ) + binding.downloadBtn.putWidgetBusinessName("首页游戏大图样式") + binding.downloadBtn.goneIf(game.homeSetting.downloadBtnSwitch != "on") + binding.gameRating.goneIf(!(game.commentCount > 3 && game.star >= 7 && game.homeSetting.downloadBtnSwitch == "on")) + binding.gameRating2.goneIf(!(game.commentCount > 3 && game.star >= 7 && game.homeSetting.downloadBtnSwitch != "on")) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameTestV2RecommendViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameTestV2RecommendViewHolder.kt new file mode 100644 index 0000000000..890ccd43ef --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameTestV2RecommendViewHolder.kt @@ -0,0 +1,40 @@ +package com.gh.gamecenter.home.custom.viewholder + +import androidx.core.content.ContextCompat +import com.gh.common.util.DirectUtils +import com.gh.common.util.NewFlatLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.setDebouncedClickListener +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.databinding.ItemHomeGameTestV2RecommendLabelCustomBinding +import com.gh.gamecenter.entity.RecommendLabel +import com.gh.gamecenter.entity.SubjectRecommendEntity + +/** + * @author : liujiarui + * date : 2023/3/3 + * description : 新游开测 推荐标签 holder + */ +class CustomHomeGameTestV2RecommendViewHolder( + private val mBinding: ItemHomeGameTestV2RecommendLabelCustomBinding +) : BaseRecyclerViewHolder(mBinding.root) { + fun bind(data: RecommendLabel) { + val context = mBinding.tvLabel.context + mBinding.tvLabel.text = data.text + mBinding.tvLabel.setTextColor(R.color.text_secondary.toColor(context)) + mBinding.tvLabel.background = + ContextCompat.getDrawable( + context, + R.drawable.bg_game_test_v2_recommend_label + ) + mBinding.tvLabel.setDebouncedClickListener { + DirectUtils.directToLinkPage( + context, + data.link, + "新游开测", + "" + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameTestV2TimePointViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameTestV2TimePointViewHolder.kt new file mode 100644 index 0000000000..fdba427af0 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeGameTestV2TimePointViewHolder.kt @@ -0,0 +1,66 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.view.ViewGroup.MarginLayoutParams +import androidx.core.content.ContextCompat +import androidx.core.view.isInvisible +import androidx.core.view.updateLayoutParams +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.databinding.ItemHomeGameTestV2TimePointCustomBinding + +/** + * @author : liujiarui + * date : 2023/3/3 + * description : + */ +class CustomHomeGameTestV2TimePointViewHolder( + val binding: ItemHomeGameTestV2TimePointCustomBinding +) : BaseRecyclerViewHolder(binding.root) { + + fun bind(data: String, isStart: Boolean, isEnd: Boolean, isSelected: Boolean) { + binding.tvTimePoint.text = data + reset() + if (isStart) isStartNode() + if (isEnd) isEndNode() + if (isSelected) isSelected() + } + + private fun reset() { + val context = binding.root.context + binding.lineLeft.isInvisible = false + binding.lineLeft.setBackgroundColor(R.color.btn_gray_light.toColor(context)) + binding.lineRight.isInvisible = false + binding.lineRight.setBackgroundColor(R.color.btn_gray_light.toColor(context)) + binding.point.isEnabled = false + binding.point.background = + ContextCompat.getDrawable(context, R.drawable.ic_home_game_test_v2_time_point_dot) + binding.tvTimePoint.isChecked = false + binding.tvTimePoint.textSize = 12f + binding.tvTimePoint.paint.isFakeBoldText = false + val color = context.resources.getColorStateList(R.color.line_axis_text_selector) + binding.tvTimePoint.setTextColor(color) + binding.root.updateLayoutParams { + (this as? MarginLayoutParams)?.leftMargin = 0f.dip2px() + } + } + + private fun isSelected() { + binding.point.isEnabled = true + binding.tvTimePoint.isChecked = true + binding.tvTimePoint.textSize = 16f + binding.tvTimePoint.paint.isFakeBoldText = true + } + + private fun isStartNode() { + binding.lineLeft.isInvisible = true + binding.root.updateLayoutParams { + (this as? MarginLayoutParams)?.leftMargin = 16f.dip2px() + } + } + + private fun isEndNode() { + binding.lineRight.isInvisible = true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoItemViewHolder.kt new file mode 100644 index 0000000000..65bdca7696 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoItemViewHolder.kt @@ -0,0 +1,126 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.graphics.drawable.ColorDrawable +import android.view.View +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.recyclerview.widget.RecyclerView +import com.gh.common.databind.BindingAdapters +import com.gh.common.util.DownloadItemUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.RandomUtils +import com.gh.gamecenter.databinding.ItemHomeHorizontalSlideVideoCustomBinding +import com.gh.gamecenter.feature.entity.CustomPageTrackData +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder +import splitties.views.backgroundColor + +class CustomHomeHorizontalSlideVideoItemViewHolder( + private val eventHelper: SubjectEventHelper, + val binding: ItemHomeHorizontalSlideVideoCustomBinding +) : + BaseRecyclerViewHolder(binding.root) { + + fun bindAttachGame( + childPosition: Int, + gameEntity: GameEntity, + adapter: RecyclerView.Adapter, + entrance: String, + exposureEvent: ExposureEvent, + createTrackData: () -> CustomPageTrackData + ) { + binding.clRoot.backgroundColor = R.color.ui_surface.toColor(itemView.context) + bindGameInfo(childPosition, gameEntity, adapter, exposureEvent, entrance, createTrackData) + binding.gameImage.setTag(ImageUtils.TAG_TARGET_WIDTH, 280F.dip2px()) + binding.autoVideoView.thumbImage.setTag(ImageUtils.TAG_TARGET_WIDTH, 280F.dip2px()) + binding.gameImage.goneIf(!gameEntity.showImage) { + ImageUtils.display(binding.gameImage, gameEntity.homeSetting.image) + val hierarchy = binding.gameImage.hierarchy + try { + hierarchy.setPlaceholderImage(ColorDrawable(gameEntity.homeSetting.placeholderColor.hexStringToIntColor())) + } catch (ignore: Throwable) { + hierarchy.setPlaceholderImage(RandomUtils.getRandomPlaceholderColor()) + } + } + binding.autoVideoView.goneIf(gameEntity.showImage || gameEntity.topVideo == null) { + val videoUrl = gameEntity.topVideo?.url ?: "" + if (!binding.autoVideoView.isInPlayingState || binding.autoVideoView.getUrl() != videoUrl) { + GSYVideoOptionBuilder() + .setIsTouchWiget(false) + .setUrl(videoUrl) + .setRotateViewAuto(false) + .setCacheWithPlay(true) + .setRotateWithSystem(false) + .setReleaseWhenLossAudio(true) + .setLooping(false) + .setShowFullAnimation(false) + .build(binding.autoVideoView) + binding.autoVideoView.updateThumb(gameEntity.topVideo?.poster ?: "") + binding.autoVideoView.setParamsData(gameEntity, gameEntity.homeSetting.placeholderColor) + binding.autoVideoView.setOnVideoClickListener { itemView.performClick() } + } + } + + itemView.setOnClickListener { + eventHelper.navigateToGameDetailPage(childPosition, gameEntity) + } + } + + private fun bindGameInfo( + childPosition: Int, + gameEntity: GameEntity, + adapter: RecyclerView.Adapter, + exposureEvent: ExposureEvent?, + entrance: String, + createTrackData: () -> CustomPageTrackData + ) { + binding.gameIcon.displayGameIcon(gameEntity) + binding.gameName.text = gameEntity.name + binding.gameName.setTextColor(R.color.text_primary.toColor(binding.root.context)) + BindingAdapters.setGameTags(binding.gameTags, gameEntity) + GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, binding.gameSubtitleTv) + + gameEntity.customPageTrackData = createTrackData() + DownloadItemUtils.setOnClickListener( + binding.root.context, + binding.downloadBtn, + gameEntity, + childPosition, + adapter, + entrance, + location = "", + traceEvent = exposureEvent + ) { + eventHelper.onDownloadButtonClick(childPosition, gameEntity) + } + + DownloadItemUtils.updateItem( + binding.root.context, + gameEntity, + GameViewHolder(binding.root).apply { + gameDownloadBtn = binding.downloadBtn + multiVersionDownloadTv = binding.multiVersionDownloadTv + gameDownloadTips = binding.downloadTipsLottie + } + ) + + ConstraintSet().apply { + clone(binding.gameNameContainer.parent as ConstraintLayout) + clear(binding.gameNameContainer.id, ConstraintSet.END) + if (binding.downloadBtn.visibility == View.VISIBLE) { + connect(binding.gameNameContainer.id, ConstraintSet.END, binding.downloadBtn.id, ConstraintSet.START) + } else { + connect(binding.gameNameContainer.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) + } + setMargin(binding.gameNameContainer.id, ConstraintSet.END, 16F.dip2px()) + applyTo(binding.gameNameContainer.parent as ConstraintLayout) + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoListViewHolder.kt new file mode 100644 index 0000000000..e89a1cbd50 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeHorizontalSlideVideoListViewHolder.kt @@ -0,0 +1,101 @@ +package com.gh.gamecenter.home.custom.viewholder + +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.common.utils.LeftPagerSnapHelper +import com.gh.gamecenter.common.view.FixLinearLayoutManager +import com.gh.gamecenter.databinding.HomeHorizontalSlideVideoListCustomBinding +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.adapter.CustomHomeHorizontalSlideVideoAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSubjectItem +import com.gh.gamecenter.home.video.ScrollCalculatorHelper + +/** + * 游戏专题-视频横屏滑动 + * 游戏专题-视频横屏滑动-v2 + */ +class CustomHomeHorizontalSlideVideoListViewHolder( + viewModel: CustomPageViewModel, + private val scrollCalculatorHelper: ScrollCalculatorHelper, + val binding: HomeHorizontalSlideVideoListCustomBinding, +) : + BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + SubjectEventHelper(viewModel) + } + + private val adapter by lazy { + CustomHomeHorizontalSlideVideoAdapter( + itemView.context, + pageConfigure, + childEventHelper, + { + createTrackData(_item) + }) { game, childPosition -> + val item = _item as CustomSubjectItem + val event = createExposureEvent( + game, + item.exposureSource, + viewModel.pageConfigure.exposureSourceList, + childPosition, _item.componentPosition + ) + game.exposureEvent = event + _item.exposureEventList.add(event) + return@CustomHomeHorizontalSlideVideoAdapter event + } + } + + override val gameChangedNotifier: IGameChangedNotifier + get() = adapter + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSubjectItem) { + item.exposureEventList.clear() + setSubjectTitle(binding.layoutTitle, item, childEventHelper) + if (binding.recyclerView.adapter == null) { + binding.recyclerView.layoutManager = + FixLinearLayoutManager(itemView.context, RecyclerView.HORIZONTAL, false) + binding.recyclerView.adapter = adapter + val snapHelper = LeftPagerSnapHelper() + snapHelper.attachToRecyclerView(binding.recyclerView) + binding.recyclerView.itemAnimator = null + } + binding.recyclerView.clearOnScrollListeners() + binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + scrollCalculatorHelper.onScroll() + } + + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + scrollCalculatorHelper.onScrollStateChanged(newState) + } + }) + adapter.setNewData(item) + + fun isParentScrolling() = scrollCalculatorHelper.mListRv.scrollState != RecyclerView.SCROLL_STATE_IDLE + fun isSelfScrolling() = binding.recyclerView.scrollState != RecyclerView.SCROLL_STATE_IDLE + // 如果外部列表未发生滚动,但是又调用到了 bindView 方法,则说明是当前组件在刷新数据,需要重新检测是否需要播放视频 + if (!isParentScrolling()) { + // 暂停上一次视频播放 + scrollCalculatorHelper.forceStop() + // 在下一桢检测 视频播放 + itemView.postOnAnimation { + if (!isParentScrolling() && !isSelfScrolling()) { + scrollCalculatorHelper.onScrollStateChanged(RecyclerView.SCROLL_STATE_IDLE) + } + } + } + + + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeItemGameTestV2ViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeItemGameTestV2ViewHolder.kt new file mode 100644 index 0000000000..b11d29f20c --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeItemGameTestV2ViewHolder.kt @@ -0,0 +1,463 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.content.Context +import android.view.MotionEvent +import android.view.View +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import androidx.core.view.postDelayed +import androidx.lifecycle.LifecycleOwner +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.ethanhua.skeleton.Skeleton +import com.ethanhua.skeleton.ViewSkeletonScreen +import com.gh.common.util.DirectUtils +import com.gh.common.util.NewFlatLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.baselist.LoadStatus +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.divider.PaddingDecoration +import com.gh.gamecenter.databinding.ItemHomeGameTestV2CustomBinding +import com.gh.gamecenter.entity.HomeItemTestV2Entity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.game.vertical.SpanCountPagerSnapHelper +import com.gh.gamecenter.home.custom.CustomNewGameTestUseCase +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.adapter.CustomHomeGameTestV2GameListRvAdapter +import com.gh.gamecenter.home.custom.adapter.CustomHomeGameTestV2RecommendRvAdapter +import com.gh.gamecenter.home.custom.adapter.CustomHomeGameTestV2TimePointRvAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper +import com.gh.gamecenter.home.custom.model.CustomGameTestV2Item +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.test_v2.HomeGameTestV2Decoration +import com.gh.gamecenter.home.test_v2.HomeGameTestV2TimePointRvAdapter +import com.gh.gamecenter.servers.gametest2.GameServerTestV2Activity +import com.lightgame.utils.Utils +import java.text.SimpleDateFormat +import java.util.* +import kotlin.math.abs + +/** + * 新游开测 + */ +class CustomHomeItemGameTestV2ViewHolder( + viewModel: CustomPageViewModel, + private val lifecycleOwner: LifecycleOwner, + private val binding: ItemHomeGameTestV2CustomBinding, +) : BaseCustomViewHolder(viewModel, binding.root) { + + private val testUseCase: CustomNewGameTestUseCase + get() = viewModel.newGameTestUserCase + + private val context: Context + get() = itemView.context + + private var mSkeletonView: ViewSkeletonScreen? = null + + private var mLastParseIndex = -1 + + private var mLastX: Int = 0 + private var mLastY: Int = 0 + + private var mHomePageGameList: List? = null + + private var _position: Int = 0 + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + OtherItemEventHelper(viewModel) + } + + private val layoutManager by lazy { + GridLayoutManager(context, 3, GridLayoutManager.HORIZONTAL, false) + } + + private val adapter by lazy { + CustomHomeGameTestV2GameListRvAdapter(itemView.context, childEventHelper, { + createTrackData(_item) + }) { childPosition, game -> + val time = game.time?.time ?: 0L + val date = Date(time) + val sdf = SimpleDateFormat("MM.dd", Locale.CHINA) + val format = sdf.format(date) + var timeType = testUseCase.getTimePointLiveData().value ?: "recommend" + timeType = if (timeType == "recommend") { + "推荐" + } else { + "全部" + } + + val exposureEvent = createExposureEvent( + game, + listOf(ExposureSource("新游开测", "$timeType-$format")), + pageConfigure.exposureSourceList, + childPosition, + _item.componentPosition + + ) + _item.exposureEventList.add(exposureEvent) + return@CustomHomeGameTestV2GameListRvAdapter exposureEvent + } + } + + private val pointAdapter by lazy { + CustomHomeGameTestV2TimePointRvAdapter(itemView.context, testUseCase) + } + + private val recommendAdapter by lazy { + CustomHomeGameTestV2RecommendRvAdapter(itemView.context) + } + + override val gameChangedNotifier: IGameChangedNotifier + get() = adapter + + init { + testUseCase.getTimePointLiveData().observe(lifecycleOwner) { + val index = pointAdapter.dataList.indexOf(it) + if (index != -1) { + binding.timePointRv.smoothScrollToPosition(index) + pointAdapter.selectPoint(index, false) + } + } + + binding.labelRecommendRv.isNestedScrollingEnabled = false + binding.labelRecommendRv.addItemDecoration( + PaddingDecoration(leftSide = 16F.dip2px(), rightSide = 16F.dip2px(), rightSpace = 8F.dip2px()) + ) + } + + fun bindView( + item: CustomPageItem, + position: Int + ) { + super.bindView(item) + _position = position + + if (item is CustomGameTestV2Item) { + + item.exposureEventList.clear() + + val data = item.data + // 设置标题 + bindTitle(data) + //绑定时间轴数据 + bindTimePoint(data) + //绑定新游开测推荐 + bindRecommend(data) + //设置holder在父列表的位置 + testUseCase.setParentPosition(position) + //绑定游戏列表 + bindGameList(testUseCase, data) + } + + } + + private fun bindTitle(data: HomeItemTestV2Entity) { + with(binding.layoutTitle) { + lavRefresh.goneIf(true) + gUser.goneIf(true) + tvTitle.text = R.string.new_game_start_test.toResString() + val rightText = if (data.rightTop.text == ALL) { + RIGHT_TEXT_ALL + } else { + RIGHT_TEXT_MORE + } + tvRight.text = rightText + tvRight.setOnClickListener { + if (data.rightTop.text == ALL) { + NewFlatLogUtils.logGameTestV2MoreClick(rightText, "自定义页面") + //点击进入[新游开测]详情页面 + itemView.context.startActivity(GameServerTestV2Activity.getIntent(itemView.context, "新游开测")) + } else { + //点击跳转通用链接 + val link = data.rightTop.link + DirectUtils.directToLinkPage(itemView.context, link, "新游开测", "") + NewFlatLogUtils.logGameTestV2MoreClick( + rightText, + "自定义页面", + linkType = link.type ?: "", + linkId = link.link ?: "", + linkText = link.text ?: "" + ) + + } + } + } + } + + private fun bindGameList(viewModel: CustomNewGameTestUseCase, data: HomeItemTestV2Entity) { + + val isDarkModeOn = DarkModeUtils.isDarkModeOn(context) + if (isDarkModeOn) { + binding.container.background = null + } else { + binding.container.setBackgroundResource(R.drawable.bg_test_v2_card_shadow) + } + binding.containerLl.background = + ContextCompat.getDrawable(context, R.drawable.bg_item_game_test_v2) + + if (binding.recyclerView.adapter == null) { + binding.recyclerView.onFlingListener = null + binding.recyclerView.isNestedScrollingEnabled = false + binding.recyclerView.itemAnimator = null + binding.recyclerView.clearOnScrollListeners() + binding.recyclerView.addItemDecoration(HomeGameTestV2Decoration()) + val snapHelper = SpanCountPagerSnapHelper(3, false) + snapHelper.attachToRecyclerView(binding.recyclerView) + binding.recyclerView.layoutManager = layoutManager + binding.recyclerView.adapter = adapter + + //列表滑动处理 + binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + Utils.log("onScrollStateChanged: newState = $newState") + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + // 高速滑动时可能触发空指针闪退 + try { + recyclerView.handler.removeCallbacksAndMessages(null) + } catch (e: NullPointerException) { + e.printStackTrace() + } + recyclerView.postDelayed(300) { // 解决动画没结束时候条目和时间轴不对应问题 + onRecyclerViewChanged(true) + } + recyclerView.post { + recyclerView.suppressLayout(false) + } + } else if (newState == RecyclerView.SCROLL_STATE_SETTLING) { + recyclerView.suppressLayout(true) + } + } + }) + binding.recyclerView.addOnItemTouchListener(object : + RecyclerView.SimpleOnItemTouchListener() { + override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean { + val x = e.x.toInt() + val y = e.y.toInt() + + when (e.action) { + MotionEvent.ACTION_DOWN -> { + mLastX = e.x.toInt() + mLastY = e.y.toInt() + rv.parent.parent.requestDisallowInterceptTouchEvent(true) + } + + MotionEvent.ACTION_UP -> rv.parent.parent.requestDisallowInterceptTouchEvent( + false + ) + + MotionEvent.ACTION_MOVE -> { + val deltaX: Int = x - mLastX + val deltaY: Int = y - mLastY + val isHorizontalScroll = abs(deltaX) > abs(deltaY) + rv.parent.parent.requestDisallowInterceptTouchEvent(isHorizontalScroll) + } + } + return false + } + }) + } + + + viewModel.getDataListLiveData().observe(lifecycleOwner) { + adapter.submitList(it) + } + viewModel.getGameListScrollLiveData().observe(lifecycleOwner) { p -> + layoutManager.scrollToPositionWithOffset(p, 0) + //优化条目没有居中问题,因为上面没有触发snapHelper + binding.recyclerView.postDelayed(100) { + binding.recyclerView.jumpToPositionForSmoothScroller(p) + } + } + viewModel.getLoadStateLiveData().observe(lifecycleOwner) { + dispatchLoadState(it) + } + + setData(data) + } + + /** + * 设置初始数据 + */ + private fun setData(data: HomeItemTestV2Entity) { + //设置时间轴数据 + testUseCase.setTimePoint(data.startPoint) + //设置游戏列表占位数据 + testUseCase.setDataCount(data.dataCount) + //推荐列表显示今天的游戏列表后左侧剩余的游戏数量 + val recommendLeftSurplusNum = data.recommendLeftSurplusNum + //绑定游戏列表数据 + testUseCase.addGameList( + list = data.data, + timeType = data.timeType, + pageId = data.pageId, + action = CustomNewGameTestUseCase.Action.getActionByCommand(data.action), + firstIndex = recommendLeftSurplusNum, + isRefresh = true, + isSameTimeType = true + ) + //数据填充完毕后滑动到推荐列表指定位置 + if (recommendLeftSurplusNum > 0) { + binding.recyclerView.postDelayed(100) { + val gridLayoutManager = binding.recyclerView.layoutManager as? GridLayoutManager + gridLayoutManager?.scrollToPositionWithOffset(recommendLeftSurplusNum, 0) + binding.recyclerView.postDelayed(100) { + binding.recyclerView.jumpToPositionForSmoothScroller(recommendLeftSurplusNum) + } + } + } + //记录老的首页游戏列表数据 + val oldData = mutableListOf() + data.data.forEach { + it.games.forEach { game -> + oldData.add(game) + } + } + mHomePageGameList = oldData + } + + /** + * 列表滑动或者数据变更,分析第一个条目索引,决定是否加载数据 + */ + private fun onRecyclerViewChanged(isOnScroll: Boolean = true) { + val recyclerView = binding.recyclerView + val adapter = recyclerView.adapter as? CustomHomeGameTestV2GameListRvAdapter ?: return + val layoutManager = recyclerView.layoutManager as GridLayoutManager + val firstVisiblePosition = layoutManager.findFirstVisibleItemPosition() + Utils.log("onScrollStateChanged: firstVisiblePosition = $firstVisiblePosition") + if (firstVisiblePosition < 0) return + val firstVisibleItem = adapter.getItem(firstVisiblePosition) + //第一个完全可见的条目 + val firstCompletelyVisiblePosition = layoutManager.findFirstCompletelyVisibleItemPosition() + if (firstCompletelyVisiblePosition == -1) { + return + } + val firstCompletelyVisibleItem = + adapter.getItem(firstCompletelyVisiblePosition) + firstVisibleItem.let { + //通知时间轴变化 + val timeType = firstCompletelyVisibleItem.timeType + if (isOnScroll) { + testUseCase.onTimePointSelected(timeType) + } + val parsePosition = if (firstCompletelyVisiblePosition != -1) { + firstCompletelyVisiblePosition + } else { + firstVisiblePosition + } + //分析分页加载动作 + if (parsePosition != mLastParseIndex) { + testUseCase.parseActionWithPosition(parsePosition) + mLastParseIndex = parsePosition + Utils.log("onScrollStateChanged: parseActionWithPosition = $parsePosition") + } + //曝光条目 + adapter.exposureItem(parsePosition) + } + } + + private fun dispatchLoadState(it: LoadStatus?) { + when (it) { + LoadStatus.LIST_LOADING -> { + //mBinding.recyclerView.suppressLayout(true) + } + + LoadStatus.INIT_LOADING -> { + binding.recyclerView.isVisible = false + binding.reuseNoConnection.reuseNoConnection.isVisible = false + showSkeletonView(binding.recyclerView) + } + + LoadStatus.INIT_LOADED, + LoadStatus.LIST_OVER, + LoadStatus.LIST_LOADED -> { + binding.recyclerView.isVisible = true + binding.recyclerView.suppressLayout(false) + binding.reuseNoConnection.reuseNoConnection.isVisible = false + mSkeletonView?.hide() + //停顿一下再分析是否加载下一页 + binding.recyclerView.postDelayed(500) { + onRecyclerViewChanged(false) + } + } + + LoadStatus.LIST_FAILED, + LoadStatus.INIT_FAILED -> { + binding.recyclerView.isVisible = false + binding.recyclerView.suppressLayout(false) + binding.reuseNoConnection.reuseNoConnection.isVisible = true + binding.reuseNoConnection.reuseNetworkErrorDescTv.isVisible = false + binding.reuseNoConnection.connectionReloadTv.isVisible = false + mSkeletonView?.hide() + } + + else -> { + binding.recyclerView.suppressLayout(false) + if (binding.recyclerView.isVisible) return + binding.recyclerView.isVisible = true + binding.reuseNoConnection.reuseNoConnection.isVisible = false + mSkeletonView?.hide() + } + } + } + + private fun showSkeletonView(view: View) { + if (mSkeletonView != null) { + mSkeletonView?.hide() + mSkeletonView = null + } + mSkeletonView = Skeleton.bind(view) + .shimmer(true) + .angle(Constants.SHIMMER_ANGLE) + .color(R.color.ui_skeleton_highlight) + .duration(Constants.SHIMMER_DURATION) + .maskWidth(Constants.MASK_WIDTH) + .gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH) + .apply { + load(R.layout.item_game_test_v2_list_sheleton) + } + .show() + } + + /** + * 绑定时间轴数据 + */ + private fun bindTimePoint(data: HomeItemTestV2Entity) { + if (binding.timePointRv.adapter == null) { + binding.timePointRv.onFlingListener = null + binding.timePointRv.isNestedScrollingEnabled = false + binding.timePointRv.itemAnimator = null + binding.timePointRv.adapter = pointAdapter + } + val timePointList = data.startPoint.map { it.timeType } + pointAdapter.submitList(timePointList) + + } + + /** + * 绑定新游开测推荐标签数据 + */ + private fun bindRecommend(data: HomeItemTestV2Entity) { + val recommendList = data.recommendLabel + if (recommendList.isEmpty()) { + binding.labelRecommendRv.isVisible = false + return + } + binding.labelRecommendRv.isVisible = true + if (binding.labelRecommendRv.adapter == null) { + binding.labelRecommendRv.adapter = recommendAdapter + } + recommendAdapter.submitList(recommendList) + } + + companion object { + + private const val ALL = "all" + private const val RIGHT_TEXT_ALL = "全部" + private const val RIGHT_TEXT_MORE = "更多" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecentVGameViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecentVGameViewHolder.kt new file mode 100644 index 0000000000..88c9d0d703 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecentVGameViewHolder.kt @@ -0,0 +1,126 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnScrollListener +import com.gh.common.util.NewFlatLogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.visibleIf +import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.databinding.ItemHomeRecentVgameCustomBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.CustomPageTracker.Companion.LOCATION_CUSTOM_PAGE +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.adapter.CustomHomeRecentVGameAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.CustomPageItemChildEventHelper +import com.gh.gamecenter.home.custom.eventlistener.OtherItemEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomRecentGamesItem +import com.gh.vspace.VDownloadManagerActivity +import com.gh.vspace.VHelper + +/** + * 最近在玩 + */ +class CustomHomeRecentVGameViewHolder( + private val showVGameSquare: Boolean, + viewModel: CustomPageViewModel, + var binding: ItemHomeRecentVgameCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper: CustomPageItemChildEventHelper + get() = OtherItemEventHelper(viewModel) + + private val adapter by lazy(LazyThreadSafetyMode.NONE) { + CustomHomeRecentVGameAdapter(itemView.context) + } + + override val gameChangedNotifier: IGameChangedNotifier + get() = adapter + + init { + binding.recyclerView.itemAnimator = null + binding.recyclerView.addItemDecoration( + GridSpacingItemColorDecoration(binding.root.context, 4, 0, R.color.transparent) + ) + binding.recyclerView.addOnScrollListener(object : OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + val layoutManager = binding.recyclerView.layoutManager + if (layoutManager is LinearLayoutManager) { + val scrollToEnd = + layoutManager.findLastCompletelyVisibleItemPosition() == (binding.recyclerView.adapter!!.itemCount - 1) + binding.divider.visibleIf(scrollToEnd) + } + } + }) + + binding.moreTv.setOnClickListener { + binding.root.context.startActivity( + VDownloadManagerActivity.getIntent( + binding.root.context, + LOCATION_CUSTOM_PAGE, + pageLocation + ) + ) + } + + binding.vspaceIv.setOnClickListener { + NewFlatLogUtils.logHaloFunEvent("halo_fun_manage_square_entrance_click") + VHelper.startVSpaceSquare(it.context, ExposureEvent.createEvent( + null, + listOf( + ExposureSource("自定义页面", "${pageLocation.pageName}+${pageLocation.pageId}"), + ExposureSource("最近在玩") + ) + )) + } + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomRecentGamesItem) { + + fillExposureEventList(item) + + val entities = item.data + if (entities.isEmpty()) { + binding.clRoot.goneIf(true) + } else { + binding.clRoot.goneIf(false) + binding.divider.goneIf(!showVGameSquare) + binding.vspaceIv.goneIf(!showVGameSquare) + + if (binding.recyclerView.adapter == null) { + binding.recyclerView.adapter = adapter + binding.moreTv.visibility = View.VISIBLE + + } + adapter.submitList(entities) + } + } + } + + private fun fillExposureEventList(item: CustomRecentGamesItem) { + val exposureEventList = arrayListOf() + runOnIoThread(true) { + item.data.forEachIndexed { index, vGame -> + val event = createExposureEvent( + VHelper.toGameEntity(vGame.downloadEntity), + listOf(ExposureSource("最近在玩", "")), + pageConfigure.exposureSourceList, + index, + item.componentPosition + ) + exposureEventList.add(event) + } + } + item.exposureEventList = exposureEventList + } +} + diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecommendItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecommendItemViewHolder.kt new file mode 100644 index 0000000000..48f57d1831 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeRecommendItemViewHolder.kt @@ -0,0 +1,92 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.widget.GridView +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.databinding.ItemHomeRecommendListCustomBinding +import com.gh.gamecenter.entity.HomeRecommend +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.adapter.CustomHomeRecommendItemGridAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem +import com.gh.gamecenter.home.custom.model.CustomPageItem + +/** + * 通用内容合集-金刚区 + * 首页推荐入口 + */ + +class CustomHomeRecommendItemViewHolder( + viewModel: CustomPageViewModel, + val binding: ItemHomeRecommendListCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + CommonContentCollectionEventHelper(viewModel) + } + + private val adapter by lazy { + CustomHomeRecommendItemGridAdapter(childEventHelper) + } + + /** + * 数据处理 + * 当后台“日常管理(新)-首页推荐入口(新)/多渠道多版本-首页推荐入口”配置的可显示的内容≥10个时,前端需要支持展示两行首页推荐入口 + * 1, 如果 6≤推荐入口数量<10 ,前端均仅显示一行推荐入口的内容 + * 2, 如果 推荐入口数量<5 ,还是保留原有的规则平铺展示 + */ + private fun mapData(recommends: List): List { + _item.exposureEventList.clear() + val item = _item as CustomCommonContentCollectionItem + recommends.forEachIndexed { index, homeRecommend -> + val exposureEvent = createExposureEvent( + GameEntity().also { it.sequence = index }, + listOf( + ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"), + ExposureSource("金刚区", homeRecommend.name) + ), + pageConfigure.exposureSourceList, + index, + _item.componentPosition + ) + exposureEvent.payload.controlType = "推荐入口" + exposureEvent.payload.controlName = homeRecommend.name + exposureEvent.payload.controlLinkName = homeRecommend.linkText + exposureEvent.payload.controlLinkType = homeRecommend.linkType + homeRecommend.exposureEvent = exposureEvent + _item.exposureEventList.add(exposureEvent) + } + + val column = (recommends.size / MAX_SPAN_COUNT) // 显示几行 + val count = if (column == 0) { + recommends.count() + } else { + column * MAX_SPAN_COUNT + } + return recommends.take(minOf(10, count)) + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomCommonContentCollectionItem) { + val recommends = mapData(item.data.recommends) + val size = recommends.size + if (size == 0) return + if (binding.recommendGv.adapter == null) { + val spanCount = minOf(size, MAX_SPAN_COUNT) + binding.recommendGv.stretchMode = GridView.STRETCH_SPACING + binding.recommendGv.numColumns = spanCount + binding.recommendGv.columnWidth = 60F.dip2px() + } + binding.recommendGv.adapter = adapter + adapter.submitList(recommends, _item.exposureEventList) + } + } + + companion object { + //图标最多列数 + private const val MAX_SPAN_COUNT = 5 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListItemViewHolder.kt new file mode 100644 index 0000000000..88e9c579b6 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListItemViewHolder.kt @@ -0,0 +1,74 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.view.View +import com.facebook.imagepipeline.image.ImageInfo +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.core.utils.RandomUtils +import com.gh.gamecenter.databinding.HomeSlideListItemCustomBinding +import com.gh.gamecenter.entity.HomeSlide +import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.halo.assistant.HaloApp + +class CustomHomeSlideListItemViewHolder(val binding: HomeSlideListItemCustomBinding) : BaseRecyclerViewHolder(binding.root) { + + private val mImageWith = HaloApp.getInstance().application.resources.displayMetrics.widthPixels - 40F.dip2px() + + fun bindSlideListItem(homeSlide: HomeSlide) { + binding.playIv.goneIf(homeSlide.linkType != "video") + binding.title.goneIf(homeSlide.title.isEmpty()) + binding.title.text = homeSlide.title + binding.digest.goneIf(!(homeSlide.text.isNotEmpty() && homeSlide.linkGame == null)) + binding.digest.text = homeSlide.text + if (homeSlide.linkGame != null) { + binding.includeGame.root.visibility = View.VISIBLE + val linkGame = homeSlide.linkGame + binding.includeGame.run { + gameIcon.displayGameIcon(linkGame) + gameName.text = linkGame.name + gameRating.goneIf(!(linkGame.commentCount > 3 && linkGame.star >= 7)) + gameRating.text = linkGame.star.toString() + } + } else { + binding.includeGame.root.visibility = View.GONE + } + binding.includeGame.gameRating.setTextColor(R.color.white.toColor()) + binding.includeGame.gameName.setTextColor(R.color.white.toColor()) + + homeSlide.linkGame?.let { + GameItemViewHolder.initGameSubtitleAndAdLabel(it, binding.includeGame.gameSubtitleTv) + } + + binding.bottomGradient.visibility = View.GONE + ImageUtils.addLimitWidthAndLoad( + binding.slideBackground, + homeSlide.image, + mImageWith, + object : ImageUtils.OnImageloadListener { + override fun onLoadFinal(imageInfo: ImageInfo?) { + binding.bottomGradient.visibility = View.VISIBLE + } + }) + + val hierarchy = binding.slideBackground.hierarchy + try { + hierarchy.setPlaceholderImage(ColorDrawable(Color.parseColor(homeSlide.placeholderColor))) + } catch (ignore: Throwable) { + hierarchy.setPlaceholderImage(RandomUtils.getRandomPlaceholderColor()) + } + + val linkGame = homeSlide.linkGame ?: return + + // 强制更改标签颜色 + val homeTags = linkGame.tagStyle + if (!homeTags.isNullOrEmpty()) { + for (homeTag in homeTags) homeTag.color = "cccccc" + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListViewHolder.kt new file mode 100644 index 0000000000..be1c1824f4 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideListViewHolder.kt @@ -0,0 +1,303 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.graphics.drawable.GradientDrawable +import android.view.MotionEvent +import android.view.View +import androidx.core.graphics.ColorUtils +import androidx.lifecycle.LifecycleOwner +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.PagerSnapHelper +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.gh.gamecenter.R +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.DrawableView +import com.gh.gamecenter.common.view.ScrollEventListener +import com.gh.gamecenter.common.view.TouchSlopRecyclerView +import com.gh.gamecenter.databinding.HomeSlideListCustomBinding +import com.gh.gamecenter.home.custom.BannerInRecyclerController +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.OnCustomPageRefreshStateListener +import com.gh.gamecenter.home.custom.adapter.CustomHomeSlideListAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem +import com.gh.gamecenter.home.custom.model.CustomPageItem +import splitties.views.verticalPadding +import kotlin.math.abs + +/** + * 通用内容合集-轮播Banner + */ +class CustomHomeSlideListViewHolder ( + viewModel: CustomPageViewModel, + lifecycleOwner: LifecycleOwner, + val binding: HomeSlideListCustomBinding, +) : + BaseCustomViewHolder(viewModel, binding.root), OnCustomPageRefreshStateListener { + + private var mLastX: Int = 0 + private var mLastY: Int = 0 + + // 是否是首位 + private val isFirst: Boolean + get() = bindingAdapterPosition == 0 + + private var backgroundAlpha: Float = 1F + + private val layoutManager by lazy { + LinearLayoutManager(itemView.context, RecyclerView.HORIZONTAL, false) + } + + private val scrollEventListener by lazy { + ScrollEventListener(binding.recyclerView) + } + + private val snapHelper by lazy { + PagerSnapHelper() + } + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + CommonContentCollectionEventHelper(viewModel) + } + + private val adapter by lazy { + CustomHomeSlideListAdapter( + itemView.context, snapHelper, false, + childEventHelper + ) { childPosition, game -> + val item = _item as CustomCommonContentCollectionItem + createExposureEvent( + game, + listOf( + ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"), + ExposureSource("轮播图", "") + ), + pageConfigure.exposureSourceList, + childPosition, + _item.componentPosition + ) + } + } + + private val bannerController = BannerInRecyclerController { + adapter.scrollToNextPage() + } + + init { + binding.recyclerView.itemAnimator = null + lifecycleOwner.lifecycle.addObserver(bannerController) + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomCommonContentCollectionItem) { + + val slideList = item.data.slides.slide + + item.exposureEventList.clear() + + val paddingVertical = if (isFirst) { + 8f.dip2px() + } else { + 16f.dip2px() + } + binding.recyclerView.verticalPadding = paddingVertical + + if (binding.recyclerView.adapter == null) { + binding.recyclerView.layoutManager = layoutManager + binding.recyclerView.adapter = adapter + + snapHelper.attachToRecyclerView(binding.recyclerView) + binding.recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() { + override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean { + val x = e.x.toInt() + val y = e.y.toInt() + + when (e.action) { + MotionEvent.ACTION_DOWN -> { + mLastX = e.x.toInt() + mLastY = e.y.toInt() + rv.parent.parent.requestDisallowInterceptTouchEvent(true) + } + + MotionEvent.ACTION_UP -> rv.parent.parent.requestDisallowInterceptTouchEvent( + false + ) + + MotionEvent.ACTION_MOVE -> { + val deltaX: Int = x - mLastX + val deltaY: Int = y - mLastY + val isHorizontalScroll = abs(deltaX) > abs(deltaY) + rv.parent.parent.requestDisallowInterceptTouchEvent(isHorizontalScroll) + } + } + + val isStop = + e.action == MotionEvent.ACTION_DOWN || e.action == MotionEvent.ACTION_MOVE + if (isStop) bannerController.pause() else bannerController.start() + + val touchSlopRecyclerView = binding.recyclerView.parent.parent + if (touchSlopRecyclerView is TouchSlopRecyclerView) { + touchSlopRecyclerView.touchSlopEnabled = isStop + } else throwExceptionInDebug("TouchSlopRecyclerView not found") + return false + } + }) + + binding.recyclerView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> + requestTransform() + } + + binding.recyclerView.addOnScrollListener(scrollEventListener.apply { + setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + var lastStatePosition = -1 + var lastScrollState = RecyclerView.SCROLL_STATE_IDLE + + override fun onPageScrollStateChanged(state: Int) { + super.onPageScrollStateChanged(state) + + if (state == RecyclerView.SCROLL_STATE_IDLE) { + bannerController.start() + } else { + bannerController.pause() + } + + if (scrollState == RecyclerView.SCROLL_STATE_IDLE) { + val view = snapHelper.findSnapView(layoutManager) + var curPosition = 0 + if (view != null) { + curPosition = adapter.getDataPosition(layoutManager.getPosition(view)) + } + lastStatePosition = curPosition + lastScrollState = scrollState + item.currentPosition = curPosition + updateImmersiveColor( + slideList.safelyGetInRelease(curPosition)?.placeholderColor?.hexStringToIntColor() + ?: R.color.ui_surface.toColor(binding.root.context) + ) + } else if (scrollState == RecyclerView.SCROLL_STATE_DRAGGING) { + lastScrollState = scrollState + } + } + + override fun onPageSelected(position: Int) { + item.currentPosition = position + + } + + override fun onPageScrolled( + position: Int, + positionOffset: Float, + positionOffsetPixels: Int + ) { + item.currentPosition = position + val currentPosition = adapter.getDataPosition(position) + val nextPosition = adapter.getDataPosition(position + 1) + + val currentColor = + slideList.safelyGetInRelease(currentPosition)?.placeholderColor?.hexStringToIntColor() + ?: R.color.ui_surface.toColor(binding.root.context) + val nextColor = + slideList.safelyGetInRelease(nextPosition)?.placeholderColor?.hexStringToIntColor() + ?: currentColor + + val colorInBetween = + ColorUtils.blendARGB(currentColor, nextColor, positionOffset) + + transformPage(position, positionOffset) + updateImmersiveColor(colorInBetween) + } + }) + }) + } + + adapter.submitList(slideList) + val currentPosition = if (item.currentPosition == -1) { + adapter.getInitPosition() + } else { + item.currentPosition + } + item.currentPosition = currentPosition + binding.recyclerView.scrollToPosition(currentPosition) + + val dataPosition = adapter.getDataPosition(currentPosition) + updateImmersiveColor( + slideList.safelyGetInRelease(dataPosition)?.placeholderColor?.hexStringToIntColor() + ?: R.color.ui_surface.toColor(binding.root.context) + ) + bannerController.start() + } + } + + private fun requestTransform() { + val relativePosition: Double = scrollEventListener.relativeScrollPosition + val position = relativePosition.toInt() + val positionOffset = (relativePosition - position).toFloat() + transformPage(position, positionOffset) + } + + private fun transformPage(position: Int, positionOffset: Float) { + val transformOffset = -positionOffset + + for (i in 0 until layoutManager.childCount) { + val view: View = layoutManager.getChildAt(i) ?: return + val currPos: Int = layoutManager.getPosition(view) + val viewOffset = transformOffset + (currPos - position) + val scaleY = when { + viewOffset <= -1 -> { + 0.9f + } + + viewOffset < 0 -> { + (1 + viewOffset) * 0.1f + 0.9f + } + + viewOffset < 1 -> { + (1 - viewOffset) * 0.1f + 0.9f + } + + else -> { + 0.9f + } + } + view.scaleY = scaleY + } + } + + override fun onViewAttach(parent: RecyclerView?) { + bannerController.onViewAttachedToWindow(parent) + } + + override fun onViewDetach(parent: RecyclerView?) { + bannerController.onViewDetachedFromWindow(parent) + } + + private fun updateImmersiveColor(color: Int) { + if (pageConfigure.isInSearchToolbarTabWrapperPage) { + if (isFirst) { + viewModel.onChangeAppBarColor(color) + } + updateBackground(color) + } + } + + override fun onBackgroundAlphaChanged(alpha: Float) { + backgroundAlpha = alpha + binding.backgroundView.alpha = alpha + } + + private fun updateBackground(color: Int) { + if (isFirst) { + val gradientDrawable = DrawableView.getGradientDrawable( + color, + R.color.ui_surface.toColor(binding.root.context), GradientDrawable.Orientation.TOP_BOTTOM, 0f + ) + binding.backgroundView.background = gradientDrawable + } else { + binding.backgroundView.setBackgroundColor(R.color.ui_surface.toColor(itemView.context)) + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideWithCardsViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideWithCardsViewHolder.kt new file mode 100644 index 0000000000..492abd27c3 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSlideWithCardsViewHolder.kt @@ -0,0 +1,611 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.annotation.SuppressLint +import android.graphics.Typeface +import android.graphics.drawable.GradientDrawable +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup.MarginLayoutParams +import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.graphics.ColorUtils +import androidx.core.view.isVisible +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.PagerSnapHelper +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.gh.common.exposure.ExposureManager +import com.gh.gamecenter.R +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.json.json +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.DrawableView +import com.gh.gamecenter.common.view.FixLinearLayoutManager +import com.gh.gamecenter.common.view.ScrollEventListener +import com.gh.gamecenter.common.view.TouchSlopRecyclerView +import com.gh.gamecenter.core.utils.AlphaGradientProcess +import com.gh.gamecenter.core.utils.MtaHelper +import com.gh.gamecenter.databinding.HomeSlideCardItemCustomBinding +import com.gh.gamecenter.databinding.HomeSlideWithCardsCustomBinding +import com.gh.gamecenter.entity.HomeSlide +import com.gh.gamecenter.entity.HomeSubSlide +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.home.custom.BannerInRecyclerController +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.OnCustomPageRefreshStateListener +import com.gh.gamecenter.home.custom.adapter.CustomHomeSlideListAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomPageItem +import kotlin.math.abs + +/** + * 通用内容合集-轮播banner + * 带右侧2个卡片的轮播图 + */ +class CustomHomeSlideWithCardsViewHolder( + viewModel: CustomPageViewModel, + private val lifecycleOwner: LifecycleOwner, + val binding: HomeSlideWithCardsCustomBinding, +) : BaseCustomViewHolder(viewModel, binding.root), OnCustomPageRefreshStateListener { + + private var mLastX: Int = 0 + private var mLastY: Int = 0 + + private val hasCards = true + + private val isFirst: Boolean + get() = bindingAdapterPosition == 0 + + private var backgroundAlpha: Float = 1F + + private val layoutManager by lazy { + FixLinearLayoutManager(itemView.context, RecyclerView.HORIZONTAL, false) + } + + private val snapHelper by lazy { + PagerSnapHelper() + } + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + CommonContentCollectionEventHelper(viewModel) + } + + private val adapter by lazy { + CustomHomeSlideListAdapter( + itemView.context, snapHelper, hasCards, + childEventHelper + ) { childPosition, game -> + val item = _item as CustomCommonContentCollectionItem + createExposureEvent( + game, + listOf( + ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"), + ExposureSource("轮播图", "") + ), + pageConfigure.exposureSourceList, + childPosition, + _item.componentPosition + ) + } + } + + private val bannerController = BannerInRecyclerController { + adapter.scrollToNextPage() + } + + private var firstSubSlide: HomeSubSlide? = null + private var secondSubSlide: HomeSubSlide? = null + + init { + binding.recyclerView.itemAnimator = null + lifecycleOwner.lifecycle.addObserver(bannerController) + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomCommonContentCollectionItem) { + + val bannerTopMargin = if (isFirst) { + 8f.dip2px() + } else { + 16f.dip2px() + } + val layoutParams = binding.bannerCv.layoutParams as? MarginLayoutParams + layoutParams?.let { + it.topMargin = bannerTopMargin + it.bottomMargin = if (isFirst) 16f.dip2px() else 24f.dip2px() + binding.bannerCv.layoutParams = it + } + + val rootLayoutParams = itemView.layoutParams as? MarginLayoutParams + rootLayoutParams?.let { + it.bottomMargin = -8f.dip2px() + itemView.layoutParams = it + } + bindSlide(item, item.data.slides.slide) + bindCards(item, item.data.slides.subSlide) + } + } + + + private fun bindSlide( + item: CustomCommonContentCollectionItem, + slideList: List + ) { + + if (binding.recyclerView.adapter == null) { + binding.recyclerView.layoutManager = layoutManager + binding.recyclerView.adapter = adapter + + snapHelper.attachToRecyclerView(binding.recyclerView) + binding.recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() { + override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean { + val x = e.x.toInt() + val y = e.y.toInt() + + when (e.action) { + MotionEvent.ACTION_DOWN -> { + mLastX = e.x.toInt() + mLastY = e.y.toInt() + rv.parent.parent.requestDisallowInterceptTouchEvent(true) + } + + MotionEvent.ACTION_UP -> rv.parent.parent.requestDisallowInterceptTouchEvent( + false + ) + + MotionEvent.ACTION_MOVE -> { + val deltaX: Int = x - mLastX + val deltaY: Int = y - mLastY + val isHorizontalScroll = abs(deltaX) > abs(deltaY) + rv.parent.parent.requestDisallowInterceptTouchEvent(isHorizontalScroll) + } + } + + val isStop = + e.action == MotionEvent.ACTION_DOWN || e.action == MotionEvent.ACTION_MOVE + if (isStop) bannerController.pause() else bannerController.start() + + val touchSlopRecyclerView = binding.recyclerView.parent.parent.parent + if (touchSlopRecyclerView is TouchSlopRecyclerView) { + touchSlopRecyclerView.touchSlopEnabled = isStop + } else throwExceptionInDebug("TouchSlopRecyclerView not found") + return false + } + }) + binding.recyclerView.addOnScrollListener(ScrollEventListener(binding.recyclerView).apply { + setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + var lastStatePosition = -1 + var lastScrollState = RecyclerView.SCROLL_STATE_IDLE + + override fun onPageSelected(position: Int) { + item.currentPosition = position + } + + override fun onPageScrollStateChanged(state: Int) { + super.onPageScrollStateChanged(state) + + if (scrollState == RecyclerView.SCROLL_STATE_IDLE) { + val view = snapHelper.findSnapView(layoutManager) + var curPosition = 0 + if (view != null) { + curPosition = adapter.getDataPosition(layoutManager.getPosition(view)) + } + if (lastScrollState == RecyclerView.SCROLL_STATE_DRAGGING && curPosition != lastStatePosition) { +// SensorsBridge.trackEvent("BannerSlide", json { +// "position" to curPosition +// "location" to location +// }) + MtaHelper.onEvent( + "首页_新", + "轮播_滑动", + if (curPosition > lastStatePosition) "左滑" else "右滑" + ) + } + lastStatePosition = curPosition + lastScrollState = scrollState + item.currentPosition = curPosition + updateImmersiveColor( + slideList.safelyGetInRelease(curPosition)?.placeholderColor?.hexStringToIntColor() + ?: R.color.ui_surface.toColor(binding.root.context) + ) + } else if (scrollState == RecyclerView.SCROLL_STATE_DRAGGING) { + lastScrollState = scrollState + } + } + + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { + super.onPageScrolled(position, positionOffset, positionOffsetPixels) + item.currentPosition = position + binding.bannerIndicator.onPageScrolled( + adapter.getDataPosition(position), + positionOffset + ) + + val currentPosition = adapter.getDataPosition(position) + val nextPosition = adapter.getDataPosition(position + 1) + + val currentColor = + slideList.safelyGetInRelease(currentPosition)?.placeholderColor?.hexStringToIntColor() + ?: R.color.ui_surface.toColor(binding.root.context) + val nextColor = + slideList.safelyGetInRelease(nextPosition)?.placeholderColor?.hexStringToIntColor() + ?: currentColor + + val colorInBetween = + ColorUtils.blendARGB(currentColor, nextColor, positionOffset) + + updateImmersiveColor(colorInBetween) + } + }) + }) + + } + + adapter.submitList(slideList) + val currentPosition = if (item.currentPosition == -1) { + adapter.getInitPosition() + } else { + item.currentPosition + } + item.currentPosition = currentPosition + binding.recyclerView.scrollToPosition(currentPosition) + + binding.bannerIndicator.run { + pageSize = adapter.getActualSize() + notifyDataChanged() + } + + val dataPosition = adapter.getDataPosition(currentPosition) + updateImmersiveColor( + slideList.safelyGetInRelease(dataPosition)?.placeholderColor?.hexStringToIntColor() + ?: R.color.ui_surface.toColor(binding.root.context) + ) + + bannerController.start() + + } + + private fun bindCards(item: CustomCommonContentCollectionItem, slideList: List) { + val firstSubSlidePosition = (viewModel.refreshCount * 2) % slideList.size + val secondSubSlidePosition = (viewModel.refreshCount * 2 + 1) % slideList.size + slideList.getOrNull(firstSubSlidePosition)?.let { + firstSubSlide = it + it.sequence = firstSubSlidePosition + it.repeatCount = (viewModel.refreshCount * 2) / slideList.size + bindCard(item, binding.firstCv, it, pageConfigure.exposureSourceList, 0) + } + slideList.getOrNull(secondSubSlidePosition)?.let { + secondSubSlide = it + it.sequence = secondSubSlidePosition + it.repeatCount = (viewModel.refreshCount * 2 + 1) / slideList.size + bindCard(item, binding.secondCv, it, pageConfigure.exposureSourceList, 1) + } + } + + private fun getCardTypeChinese(type: String) = when (type) { + "column" -> "游戏专题" + "column_collection" -> "专题合集" + "column_test_v2" -> "新游开测" + "server" -> "开服表" + "game_explore" -> "发现页" + "game_list_detail" -> "游戏单" + "common_collection" -> "通用合集" + else -> "" + } + + @SuppressLint("SetTextI18n") + private fun bindCard( + item: CustomCommonContentCollectionItem, + homeSlideCardItemBinding: HomeSlideCardItemCustomBinding, + homeSubSlide: HomeSubSlide, + basicExposureSource: List, + position: Int + ) { + val cardCv = homeSlideCardItemBinding.root + val cardContainer = homeSlideCardItemBinding.cardContainer + val cardIv = homeSlideCardItemBinding.cardIv + val backgroundGroup = homeSlideCardItemBinding.backgroundGroup + val titleTv = homeSlideCardItemBinding.titleTv + val descTv = homeSlideCardItemBinding.descTv + val textContainer = homeSlideCardItemBinding.textContainer + val countTv = homeSlideCardItemBinding.countTv + val labelIv = homeSlideCardItemBinding.labelIv + val gameIconStackIv1 = homeSlideCardItemBinding.gameIconStackIv1 + val gameIconStackIv2 = homeSlideCardItemBinding.gameIconStackIv2 + val gameIconStackIv3 = homeSlideCardItemBinding.gameIconStackIv3 + val gameIconStackGroup = listOf(gameIconStackIv1, gameIconStackIv2, gameIconStackIv3) + val gameIconIv1 = homeSlideCardItemBinding.gameIconIv1 + val gameIconIv2 = homeSlideCardItemBinding.gameIconIv2 + val gameIconIv3 = homeSlideCardItemBinding.gameIconIv3 + val gameIconGroup = listOf(gameIconIv1, gameIconIv2, gameIconIv3) + + titleTv.text = homeSubSlide.title + titleTv.setTextColor(R.color.text_primary.toColor(titleTv.context)) + descTv.setTextColor(R.color.text_secondary.toColor(descTv.context)) + + val exposureEvent = ExposureEvent.createEventWithSourceConcat( + gameEntity = GameEntity().apply { + sequence = position + outerSequence = viewModel.refreshCount + }, + basicSource = basicExposureSource, + source = listOf( + ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"), + ExposureSource("右侧卡片", homeSubSlide.id) + ) + ) + + // 获取发现卡片游戏 + if (homeSubSlide.cardType == "game_explore" && homeSubSlide.cardData.games.isEmpty()) { + viewModel.slideDiscoveryCardGames.observe(lifecycleOwner, object : Observer> { + override fun onChanged(t: List?) { + val currentSubSlide = if (position == 0) firstSubSlide else secondSubSlide + if (t != null && homeSubSlide == currentSubSlide) { + bindCard(item, homeSlideCardItemBinding, homeSubSlide.apply { + cardData.games = t + }, basicExposureSource, position) + viewModel.slideDiscoveryCardGames.removeObserver(this) + } + } + }) + viewModel.loadSlideDiscoverCardGames(homeSubSlide) + return + } + + when (homeSubSlide.cardType) { + "column", + "column_test_v2", + "server", + "game_explore" -> { + titleTv.visibility = View.VISIBLE + descTv.visibility = View.VISIBLE + countTv.visibility = View.GONE + labelIv.visibility = View.GONE + backgroundGroup.visibility = View.GONE + gameIconStackGroup.forEach { it.isVisible = homeSubSlide.cardData.games.isNotEmpty() } + gameIconGroup.forEach { it.visibility = View.GONE } + + descTv.text = homeSubSlide.cardDesc + if (homeSubSlide.cardData.games.size == 1) { + gameIconStackIv2.visibility = View.GONE + gameIconStackIv1.visibility = View.GONE + } + if (homeSubSlide.cardData.games.size == 2) { + gameIconStackIv1.visibility = View.GONE + } + homeSubSlide.cardData.games.take(3).forEachIndexed { index, gameEntity -> + _item.exposureEventList.add( + getGameExposureEvent( + item.data, + homeSubSlide, + gameEntity, + position, + basicExposureSource + ) + ) + when (index) { + 0 -> gameIconStackIv3.displayGameIcon(gameEntity, true) + 1 -> gameIconStackIv2.displayGameIcon(gameEntity, true) + 2 -> gameIconStackIv1.displayGameIcon(gameEntity, true) + } + } + ConstraintSet().apply { + clone(cardContainer) + clear(textContainer.id, ConstraintSet.TOP) + clear(textContainer.id, ConstraintSet.BOTTOM) + connect(textContainer.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) + connect(textContainer.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM) + }.applyTo(cardContainer) + } + + "game_list_detail" -> { + titleTv.visibility = View.VISIBLE + descTv.visibility = View.GONE + backgroundGroup.visibility = View.GONE + gameIconStackGroup.forEach { it.visibility = View.GONE } + gameIconGroup.forEach { it.isVisible = homeSubSlide.cardData.games.isNotEmpty() } + + if (homeSubSlide.cardData.games.size == 1) { + gameIconIv2.visibility = View.GONE + gameIconIv3.visibility = View.GONE + } + if (homeSubSlide.cardData.games.size == 2) { + gameIconIv3.visibility = View.GONE + } + homeSubSlide.cardData.games.take(3).forEachIndexed { index, gameEntity -> + _item.exposureEventList.add( + getGameExposureEvent( + item.data, + homeSubSlide, + gameEntity, + position, + basicExposureSource + ) + ) + + when (index) { + 0 -> { + gameIconIv1.displayGameIcon(gameEntity, true) + gameIconIv1.setOnClickListener { + com.gh.common.util.NewFlatLogUtils.logRightSideCardClick( + homeSubSlide, + viewModel.refreshCount, + "游戏" + ) + childEventHelper.navigateToGameDetailPage(index, gameEntity, "右侧卡片") + } + } + + 1 -> { + gameIconIv2.displayGameIcon(gameEntity, true) + gameIconIv2.setOnClickListener { + com.gh.common.util.NewFlatLogUtils.logRightSideCardClick( + homeSubSlide, + viewModel.refreshCount, + "游戏" + ) + childEventHelper.navigateToGameDetailPage(index, gameEntity, "右侧卡片") + } + } + + 2 -> { + gameIconIv3.displayGameIcon(gameEntity, true) + gameIconIv3.setOnClickListener { + com.gh.common.util.NewFlatLogUtils.logRightSideCardClick( + homeSubSlide, + viewModel.refreshCount, + "游戏" + ) + childEventHelper.navigateToGameDetailPage(index, gameEntity, "右侧卡片") + } + } + } + } + countTv.isVisible = homeSubSlide.cardData.gameTotal.game > 3 + countTv.typeface = Typeface.createFromAsset(countTv.context.assets, Constants.DIN_FONT_PATH) + countTv.text = "+${homeSubSlide.cardData.gameTotal.game - 3}" + labelIv.isVisible = homeSubSlide.cardData.stamp.isNotEmpty() + labelIv.setImageDrawable( + when (homeSubSlide.cardData.stamp) { + "official" -> R.drawable.label_official.toDrawable(labelIv.context) + "special_choice" -> R.drawable.label_premium.toDrawable(labelIv.context) + else -> null + } + ) + ConstraintSet().apply { + clone(cardContainer) + clear(textContainer.id, ConstraintSet.TOP) + clear(textContainer.id, ConstraintSet.BOTTOM) + connect( + textContainer.id, + ConstraintSet.TOP, + ConstraintSet.PARENT_ID, + ConstraintSet.TOP, + 8F.dip2px() + ) + if (homeSubSlide.cardData.stamp.isEmpty()) { + clear(countTv.id, ConstraintSet.START) + connect(countTv.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END, 10F.dip2px()) + } else { + clear(countTv.id, ConstraintSet.END) + connect(countTv.id, ConstraintSet.START, gameIconIv3.id, ConstraintSet.END, 4F.dip2px()) + } + }.applyTo(cardContainer) + } + + "column_collection", "common_collection" -> { + titleTv.visibility = View.VISIBLE + descTv.visibility = View.VISIBLE + countTv.visibility = View.GONE + labelIv.visibility = View.GONE + + backgroundGroup.visibility = View.VISIBLE + val visibility = if (isFirst) View.VISIBLE else View.GONE + homeSlideCardItemBinding.cardMask.visibility = visibility + homeSlideCardItemBinding.cardGradientMask.visibility = visibility + homeSlideCardItemBinding.cardIv.visibility = View.VISIBLE + + gameIconStackGroup.forEach { it.visibility = View.GONE } + gameIconGroup.forEach { it.visibility = View.GONE } + descTv.text = homeSubSlide.cardDesc + // 防止重复加载图片导致闪烁 + if (homeSubSlide.image != cardIv.getTag(R.string.tag_img_url_id)) { + ImageUtils.display(cardIv, homeSubSlide.image, false, AlphaGradientProcess()) + cardIv.setTag(R.string.tag_img_url_id, homeSubSlide.image) + } + ConstraintSet().apply { + clone(cardContainer) + clear(textContainer.id, ConstraintSet.TOP) + clear(textContainer.id, ConstraintSet.BOTTOM) + connect(textContainer.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) + connect(textContainer.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM) + }.applyTo(cardContainer) + _item.exposureEventList.add(exposureEvent) + } + } + + cardCv.setOnClickListener { + val clickEvent = ExposureEvent.createEventWithSourceConcat( + gameEntity = GameEntity().apply { + sequence = position + outerSequence = viewModel.refreshCount + }, + basicSource = basicExposureSource, + source = listOf( + ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"), + ExposureSource("右侧卡片", homeSubSlide.id) + ), + event = ExposureType.CLICK + ) + if (homeSubSlide.toLinkEntity().type != "game") { + ExposureManager.log(clickEvent) + } + SensorsBridge.trackEvent("RightSideCardClick", json { + "position" to homeSubSlide.sequence + "title" to homeSubSlide.title + "card_id" to homeSubSlide.id + }) + com.gh.common.util.NewFlatLogUtils.logRightSideCardClick(homeSubSlide, viewModel.refreshCount, "卡片") + childEventHelper.navigateToLinkPage(homeSubSlide.toLinkEntity(), "右侧卡片", clickEvent) + } + } + + private fun getGameExposureEvent( + collectionData: CustomPageData.CommonContentCollection, + homeSubSlide: HomeSubSlide, + gameEntity: GameEntity, + position: Int, + basicExposureSource: List + ) = ExposureEvent.createEventWithSourceConcat( + gameEntity = gameEntity.apply { + sequence = position + outerSequence = viewModel.refreshCount + }, + basicSource = basicExposureSource, + source = listOf( + ExposureSource("通用内容合集", "${collectionData.name}+${collectionData.layoutChinese}+${collectionData.id}"), + ExposureSource("右侧卡片", homeSubSlide.id) + ) + ) + + override fun onViewAttach(parent: RecyclerView?) { + bannerController.onViewAttachedToWindow(parent) + } + + override fun onViewDetach(parent: RecyclerView?) { + bannerController.onViewDetachedFromWindow(parent) + } + + private fun updateImmersiveColor(color: Int) { + if (pageConfigure.isInSearchToolbarTabWrapperPage) { + if (isFirst) { + viewModel.onChangeAppBarColor(color) + } + updateBackground(color) + } + } + + override fun onBackgroundAlphaChanged(alpha: Float) { + backgroundAlpha = alpha + binding.backgroundView.alpha = alpha + } + + private fun updateBackground(color: Int) { + if (isFirst) { + val gradientDrawable = DrawableView.getGradientDrawable( + color, + R.color.ui_surface.toColor(binding.root.context), GradientDrawable.Orientation.TOP_BOTTOM, 0f + ) + binding.backgroundView.background = gradientDrawable + } else { + binding.backgroundView.setBackgroundColor(R.color.ui_surface.toColor(itemView.context)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSubSlideListItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSubSlideListItemViewHolder.kt new file mode 100644 index 0000000000..9963e1b0e7 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomHomeSubSlideListItemViewHolder.kt @@ -0,0 +1,82 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.GradientDrawable +import android.view.View +import androidx.core.graphics.ColorUtils +import androidx.core.view.isVisible +import com.facebook.imagepipeline.image.ImageInfo +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.RandomUtils +import com.gh.gamecenter.databinding.HomeSubSlideListItemBinding +import com.gh.gamecenter.databinding.HomeSubSlideListItemCustomBinding +import com.gh.gamecenter.entity.HomeSlide +import com.gh.gamecenter.feature.game.GameItemViewHolder + +class CustomHomeSubSlideListItemViewHolder(val binding: HomeSubSlideListItemCustomBinding) : + BaseRecyclerViewHolder(binding.root) { + + private val mImageWith = ((DisplayUtils.getScreenWidth() / 2F) - 20F.dip2px()).toInt() + + fun bindSubSlideListItem(homeSlide: HomeSlide) { + if (homeSlide.linkGame != null) { + binding.gameGroup.visibility = View.VISIBLE + binding.gameName.text = homeSlide.linkGame.name + binding.gameIconIv.displayGameIcon(homeSlide.linkGame) + GameItemViewHolder.initGameSubtitleAndAdLabel(homeSlide.linkGame, binding.gameSubtitleTv) + } else { + binding.gameGroup.visibility = View.GONE + } + binding.recommendTypeIv.isVisible = homeSlide.recommendType.isNotEmpty() + binding.recommendTypeIv.setImageDrawable( + when (homeSlide.recommendType) { + "activity" -> R.drawable.label_huodong.toDrawable(binding.recommendTypeIv.context) + "update" -> R.drawable.label_gengxin.toDrawable(binding.recommendTypeIv.context) + "new" -> R.drawable.label_shangxin.toDrawable(binding.recommendTypeIv.context) + "first_release" -> R.drawable.label_shoufa.toDrawable(binding.recommendTypeIv.context) + "hot" -> R.drawable.label_remen.toDrawable(binding.recommendTypeIv.context) + "libao" -> R.drawable.label_libao.toDrawable(binding.recommendTypeIv.context) + else -> null + } + ) + + if (homeSlide.placeholderColor.isNotEmpty()) { + val colorAlpha100 = ColorUtils.setAlphaComponent(homeSlide.placeholderColor.hexStringToIntColor(), 0xFF) + val colorHSV = colorAlpha100.colorToHSV().apply { + this[2] *= 0.6F + } + val startColor = Color.HSVToColor(colorHSV) + val endColor = ColorUtils.setAlphaComponent(startColor, 0) + + binding.slideMask.visibility = View.GONE + binding.slideMask.background = GradientDrawable().apply { + gradientType = GradientDrawable.LINEAR_GRADIENT + orientation = GradientDrawable.Orientation.BOTTOM_TOP + colors = intArrayOf(startColor, endColor) + } + } + + ImageUtils.addLimitWidthAndLoad( + binding.slideBackground, + homeSlide.image, + mImageWith, + object : ImageUtils.OnImageloadListener { + override fun onLoadFinal(imageInfo: ImageInfo?) { + if (homeSlide.placeholderColor.isNotEmpty() && homeSlide.linkGame != null) { + binding.slideMask.visibility = View.VISIBLE + } + } + }) + + val hierarchy = binding.slideBackground.hierarchy + try { + hierarchy.setPlaceholderImage(ColorDrawable(Color.parseColor(homeSlide.placeholderColor))) + } catch (ignore: Throwable) { + hierarchy.setPlaceholderImage(RandomUtils.getRandomPlaceholderColor()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomIconMatrixViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomIconMatrixViewHolder.kt new file mode 100644 index 0000000000..39862f7e2d --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomIconMatrixViewHolder.kt @@ -0,0 +1,78 @@ +package com.gh.gamecenter.home.custom.viewholder + +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.databinding.RecyclerIconMatrixCustomBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.adapter.CustomIconMatrixAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.SubjectEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSplitSubjectItem + +/** + * 游戏专题:图标矩阵 + */ +class CustomIconMatrixViewHolder( + viewModel: CustomPageViewModel, + val binding: RecyclerIconMatrixCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + SubjectEventHelper(viewModel) + } + + private val adapter by lazy { + CustomIconMatrixAdapter(itemView.context, childEventHelper) { + createTrackData(_item) + } + } + + override val gameChangedNotifier: IGameChangedNotifier + get() = adapter + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSplitSubjectItem) { + + setSplitSubjectTitle(binding.layoutTitle, item, childEventHelper) + + fillExposureEventList(item) + + if (binding.recyclerView.adapter == null) { + binding.recyclerView.layoutManager = + object : GridLayoutManager(itemView.context, 4, RecyclerView.VERTICAL, false) { + override fun canScrollVertically(): Boolean { + return false + } + } + binding.recyclerView.adapter = adapter + } + + adapter.setData(item, item.exposureEventList) + } + } + + private fun fillExposureEventList(item: CustomSplitSubjectItem) { + val eventList = arrayListOf() + runOnIoThread(true) { + (item.startChildPosition..item.endChildPosition).forEach { + val game = item.data.data?.getOrNull(it) ?: return@forEach + val event = createExposureEvent( + game, + item.exposureSource, + pageConfigure.exposureSourceList, + it, + item.componentPosition + ) + eventList.add(event) + } + } + item.exposureEventList = eventList + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomNavigationViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomNavigationViewHolder.kt new file mode 100644 index 0000000000..919b8fbee9 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomNavigationViewHolder.kt @@ -0,0 +1,80 @@ +package com.gh.gamecenter.home.custom.viewholder + +import androidx.recyclerview.widget.GridLayoutManager +import com.gh.gamecenter.common.entity.ExposureEntity +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.view.GridSpacingItemDecoration +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.databinding.RecyclerNavigationCustomBinding +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.adapter.CustomGameNavigationAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.CommonContentCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomCommonContentCollectionItem +import com.gh.gamecenter.home.custom.model.CustomPageItem + +/** + * 通用内容合集-导航栏 + */ +class CustomNavigationViewHolder( + viewModel: CustomPageViewModel, + val binding: RecyclerNavigationCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + private val layoutManager by lazy { + GridLayoutManager(itemView.context, 4) + } + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + CommonContentCollectionEventHelper(viewModel) + } + + private val adapter by lazy { + CustomGameNavigationAdapter(itemView.context, childEventHelper) + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomCommonContentCollectionItem) { + if (binding.rvNavigation.adapter == null) { + binding.rvNavigation.layoutManager = layoutManager + binding.rvNavigation.addItemDecoration(GridSpacingItemDecoration(4, 8f.dip2px(), false, 16f.dip2px())) + binding.rvNavigation.adapter = adapter + binding.rvNavigation.isNestedScrollingEnabled = false + } + + val navigationList = item.data.navigations + + if (!navigationList.isNullOrEmpty()) { + val exposureEventList = arrayListOf() + runOnIoThread(true) { + navigationList.forEachIndexed { index, entity -> + val event = createExposureEvent( + GameEntity( + containerId = entity.id, + containerType = ExposureEntity.NAVIGATION_ID + ).also { + it.sequence = index + it.outerSequence = item.position + }, + listOf( + ExposureSource("通用内容合集", "${item.data.name}+${item.data.layoutChinese}+${item.data.id}"), + ExposureSource("导航栏", entity.entryName) + ), + pageConfigure.exposureSourceList, + index, + item.componentPosition + ) + exposureEventList.add(event) + } + + } + item.exposureEventList = exposureEventList + adapter.setData(navigationList, exposureEventList) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRankCollectionViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRankCollectionViewHolder.kt new file mode 100644 index 0000000000..6198dda7af --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRankCollectionViewHolder.kt @@ -0,0 +1,77 @@ +package com.gh.gamecenter.home.custom.viewholder + +import androidx.recyclerview.widget.LinearLayoutManager +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.core.runOnIoThread +import com.gh.gamecenter.databinding.RankCollectionListCustomBinding +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.IGameChangedNotifier +import com.gh.gamecenter.home.custom.adapter.CustomRankCollectionAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem + +/** + * 专题合集-排行榜 + */ +class CustomRankCollectionViewHolder( + viewModel: CustomPageViewModel, + val binding: RankCollectionListCustomBinding +) : BaseCustomViewHolder(viewModel, binding.root) { + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + GameSubjectCollectionEventHelper(viewModel) + } + + private val _adapter by lazy { + CustomRankCollectionAdapter(itemView.context, childEventHelper) { + createTrackData(_item) + } + } + + override val gameChangedNotifier: IGameChangedNotifier + get() = _adapter + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSubjectCollectionItem) { + if (binding.recyclerView.adapter == null) { + binding.recyclerView.isNestedScrollingEnabled = false + binding.recyclerView.layoutManager = + LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false) + binding.recyclerView.adapter = _adapter + } + + _adapter.setData(item) + + val collection = item.data + runOnIoThread(true) { + val exposureEventList = arrayListOf() + collection.data.forEach { customSubject -> + customSubject.games.forEachIndexed { index, gameEntity -> + val source = listOf( + ExposureSource("专题合集", "${item.data.name}+${item.componentStyle}+${item.data.id}"), + ExposureSource( + "专题", + "${customSubject.title}+${customSubject.styleChinese}+${customSubject.id}" + ) + ) + exposureEventList.add( + createExposureEvent( + gameEntity, + source, + pageConfigure.exposureSourceList, + index, + item.componentPosition + ) + ) + } + } + _item.exposureEventList = exposureEventList + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRefreshIconViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRefreshIconViewHolder.kt new file mode 100644 index 0000000000..223365eb35 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRefreshIconViewHolder.kt @@ -0,0 +1,153 @@ +package com.gh.gamecenter.home.custom.viewholder + +import android.content.Context +import android.graphics.Rect +import android.view.View +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.DarkModeUtils +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.databinding.HomeGameCollectionRefreshItemCustomBinding +import com.gh.gamecenter.home.custom.CustomPageViewModel +import com.gh.gamecenter.home.custom.adapter.CustomRefreshIconAdapter +import com.gh.gamecenter.home.custom.createExposureEvent +import com.gh.gamecenter.home.custom.eventlistener.GameSubjectCollectionEventHelper +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMPONENTS_COLLECTION_STYLE_REFRESH_ICONS_4_2 +import com.gh.gamecenter.home.custom.model.CustomSubjectCollectionItem + +/** + * 专题合集:轮换刷新-图标泳道 + * 专题合集:轮换刷新-图标矩阵 + * 游戏单合集-轮换刷新-横排列表 + */ +class CustomRefreshIconViewHolder( + private val context: Context, + viewModel: CustomPageViewModel, + private val isMatrix: Boolean, + val binding: HomeGameCollectionRefreshItemCustomBinding +) : + BaseCustomViewHolder(viewModel, binding.root) { + + private val showSubject: CustomPageData.LinkColumnCollection.CustomSubjectEntity? + get() = (_item as? CustomSubjectCollectionItem)?.showSubject + + override val childEventHelper by lazy(LazyThreadSafetyMode.NONE) { + GameSubjectCollectionEventHelper(viewModel) + } + + private + val adapter by lazy { + CustomRefreshIconAdapter(context, childEventHelper) { childPosition, game -> + val subject = showSubject ?: return@CustomRefreshIconAdapter + (_item as? CustomSubjectCollectionItem)?.let { + val source = if (it.isSubjectCollection) { + listOf( + ExposureSource("专题合集", "${it.data.name}+${it.componentStyle}+${it.data.id}"), + ExposureSource("专题", "${subject.title}+${subject.styleChinese}+${subject.id}") + ) + } else { + listOf( + ExposureSource("游戏单合集", "${it.data.name}+${it.componentStyle}+${it.data.id}"), + ExposureSource("游戏单", "${subject.title}+${subject.id}") + ) + } + game.isAdData = subject.adIconActive + val event = createExposureEvent( + game, + source, + pageConfigure.exposureSourceList, + childPosition, + _item.componentPosition + ) + _item.exposureEventList.add(event) + } + } + } + + private var _subjectEntity: CustomPageData.LinkColumnCollection.CustomSubjectEntity? = null + + init { + binding.recyclerView.itemAnimator = null + binding.recyclerView.isNestedScrollingEnabled = false + } + + override fun bindView(item: CustomPageItem) { + super.bindView(item) + if (item is CustomSubjectCollectionItem) { + + _item.exposureEventList.clear() + + val collection = item.data + + val isDarkModeChanged = DarkModeUtils.isDarkModeOn(binding.root.context) != darkMode + setSubjectCollectionTitle( + item, + binding.layoutTitle, + isDarkModeChanged, + childEventHelper, + true + ) + if (isDarkModeChanged) { + darkMode = DarkModeUtils.isDarkModeOn(binding.root.context) + } + + if (binding.recyclerView.adapter == null) { + val layoutManager = if (isMatrix) { + binding.recyclerView.isNestedScrollingEnabled = false + GridLayoutManager(context, 4, RecyclerView.VERTICAL, false) + } else { + LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) + } + binding.recyclerView.layoutManager = layoutManager + if (isMatrix) { + binding.recyclerView.addItemDecoration(MyItemDecoration()) + } + + binding.recyclerView.adapter = adapter + } + + val subjectEntity = item.showSubject ?: return + if (subjectEntity.id == _subjectEntity?.id) { + adapter.notifyItemRangeChanged(0, adapter.itemCount) + return + } else { + _subjectEntity = subjectEntity + } + + // 如果游戏全部被屏蔽,自动加载下一页 + if (subjectEntity.games.isEmpty()) { + childEventHelper.onRotateRefresh() + } else { + if (item.isBackToStart) { + item.isBackToStart = false + binding.recyclerView.scrollToPosition(0) + } + // 如果是 专题合集-图标矩阵,则需要读取后台返回的rows_num字段 + val games = if (item.data.style == COMPONENTS_COLLECTION_STYLE_REFRESH_ICONS_4_2) { + val needGameCount = item.data.styleSetting.rowsNum * 4 + subjectEntity.games.take(needGameCount) + } else { + subjectEntity.games + } + adapter.setData(collection.name, collection.id, subjectEntity, games) + } + } + } + + class MyItemDecoration : RecyclerView.ItemDecoration() { + + override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { + val position = parent.getChildAdapterPosition(view) + val isFirstLine = position <= 3 + outRect.top = if (isFirstLine) { + 0 + } else { + 16f.dip2px() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/home/gamecollection/refresh/HomeGameCollectionRefreshAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/gamecollection/refresh/HomeGameCollectionRefreshAdapter.kt index 2244e7b82e..08d7acb80a 100644 --- a/app/src/main/java/com/gh/gamecenter/home/gamecollection/refresh/HomeGameCollectionRefreshAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/gamecollection/refresh/HomeGameCollectionRefreshAdapter.kt @@ -10,6 +10,7 @@ import com.gh.gamecenter.common.json.json import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.game.horizontal.GameHorizontalSimpleItemViewHolder +import com.gh.gamecenter.game.vertical.GameItemUi import com.gh.gamecenter.game.vertical.GameVerticalAdapter import com.lightgame.adapter.BaseRecyclerAdapter import com.lightgame.download.DownloadEntity @@ -42,8 +43,7 @@ class HomeGameCollectionRefreshAdapter( if (mIsHorizontal) { GameHorizontalSimpleItemViewHolder(parent.toBinding()) } else { - GameVerticalAdapter.SimpleGameItemViewHolder( - GameVerticalAdapter.AsyncGameItemUi(parent.context).apply { inflate() }) + GameVerticalAdapter.SimpleGameItemViewHolder(GameItemUi(parent.context)) } override fun getItemCount(): Int = mGameList.size diff --git a/app/src/main/java/com/gh/gamecenter/home/skip/PackageSkipAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/skip/PackageSkipAdapter.kt index f1b2a56bcb..986f47de96 100644 --- a/app/src/main/java/com/gh/gamecenter/home/skip/PackageSkipAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/home/skip/PackageSkipAdapter.kt @@ -48,7 +48,7 @@ class PackageSkipAdapter( if (holder is GameItemViewHolder) { val gameEntity = games[position] val subjectData = gameEntity.subjectData - holder.bindGameItem(gameEntity, subjectData?.isShowSuffix ?: true, subjectData?.briefStyle ?: "") + holder.bindGameItem(gameEntity, subjectData?.briefStyle ?: "") holder.initServerType(gameEntity) if (games.size == 1) { diff --git a/app/src/main/java/com/gh/gamecenter/home/slide/HomeSlideListItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/slide/HomeSlideListItemViewHolder.kt index a06a7925f0..8caf670dae 100644 --- a/app/src/main/java/com/gh/gamecenter/home/slide/HomeSlideListItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/slide/HomeSlideListItemViewHolder.kt @@ -26,8 +26,6 @@ class HomeSlideListItemViewHolder(val binding: HomeSlideListItemBinding) : BaseR if (homeSlide.linkGame != null) { binding.includeGame.root.visibility = View.VISIBLE val linkGame = homeSlide.linkGame - // 轮播图的标题不需要游戏后缀 - linkGame.nameSuffix = "" binding.includeGame.run { gameIcon.displayGameIcon(linkGame) gameName.text = linkGame.name diff --git a/app/src/main/java/com/gh/gamecenter/home/slide/HomeSlideWithCardsViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/slide/HomeSlideWithCardsViewHolder.kt index f77dbe65c8..ec4276262a 100644 --- a/app/src/main/java/com/gh/gamecenter/home/slide/HomeSlideWithCardsViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/slide/HomeSlideWithCardsViewHolder.kt @@ -17,6 +17,7 @@ import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.gamecenter.common.base.activity.BaseActivity +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.json.json import com.gh.gamecenter.common.utils.* @@ -398,7 +399,7 @@ class HomeSlideWithCardsViewHolder( } } countTv.isVisible = homeSubSlide.cardData.gameTotal.game > 3 - countTv.typeface = Typeface.createFromAsset(countTv.context.assets, "fonts/d_din_bold_only_number.ttf") + countTv.typeface = Typeface.createFromAsset(countTv.context.assets, Constants.DIN_FONT_PATH) countTv.text = "+${homeSubSlide.cardData.gameTotal.game - 3}" labelIv.isVisible = homeSubSlide.cardData.stamp.isNotEmpty() labelIv.setImageDrawable( diff --git a/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2GameListViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2GameListViewHolder.kt index 19df7626ee..8cb1575192 100644 --- a/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2GameListViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2GameListViewHolder.kt @@ -43,7 +43,7 @@ class HomeGameTestV2GameListViewHolder( fun bindGameItem(gameEntity: GameEntity) { mBinding.run { gameIconView.displayGameIcon(gameEntity) - BindingAdapters.setGameName(gameName, gameEntity, false, null) + BindingAdapters.setGameName(gameName, gameEntity, false) BindingAdapters.setGameTags(labelList, gameEntity) GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitleTv) diff --git a/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2ViewModel.kt b/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2ViewModel.kt index 886d6628ee..489c58dae0 100644 --- a/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2ViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeGameTestV2ViewModel.kt @@ -10,10 +10,7 @@ import com.gh.gamecenter.common.baselist.LoadStatus import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.common.utils.observableToMain import com.gh.gamecenter.core.utils.UrlFilterUtils -import com.gh.gamecenter.entity.GameData -import com.gh.gamecenter.entity.GameDataWrapper -import com.gh.gamecenter.entity.HomeItemTestV2Entity -import com.gh.gamecenter.entity.SubjectRecommendEntity +import com.gh.gamecenter.entity.* import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.retrofit.RetrofitManager import com.halo.assistant.HaloApp @@ -75,12 +72,12 @@ class HomeGameTestV2ViewModel(application: Application) : AndroidViewModel(appli /** * 时间轴数据 */ - private var mTimePoint: Map? = null + private var mTimePoint: List? = null /** * 时间轴节点对应游戏总数 */ - private var mDataCount: Map? = null + private var mDataCount: List? = null /** * 记录过滤掉的游戏 @@ -90,16 +87,16 @@ class HomeGameTestV2ViewModel(application: Application) : AndroidViewModel(appli /** * 设置时间轴数据 */ - fun setTimePoint(timePoint: Map) { + fun setTimePoint(timePoint: List) { this.mTimePoint = timePoint - mTimePointLiveData.value = timePoint.keys.first() + mTimePointLiveData.value = timePoint.firstOrNull()?.timeType ?: "" } /** * 设置时间轴节点游戏总数 * 用占位和空条目填充数据列表 */ - fun setDataCount(dataCount: Map) { + fun setDataCount(dataCount: List) { this.mDataCount = dataCount if (dataCount.isEmpty()) return val rawCount = COLUMN_COUNT @@ -331,7 +328,7 @@ class HomeGameTestV2ViewModel(application: Application) : AndroidViewModel(appli } mDataLiveData.value = currentData.toMutableList() //如果推荐定位不是第一个,向前加载一页 - if (firstIndex > 0){ + if (firstIndex > 0) { parseActionWithPosition(firstIndex) } } @@ -470,10 +467,10 @@ class HomeGameTestV2ViewModel(application: Application) : AndroidViewModel(appli mGameListScrollLiveData.postValue(index) mLoadStateLiveData.postValue(LoadStatus.INIT_LOADED) } - val startId = mTimePoint?.get(timeType) ?: "" + val startId = mTimePoint?.find { it.timeType == timeType }?.id ?: "" if (firstItemWithTimeType.isPlaceHolder) { //加载当前页数据 , 不足limit 则用下一页补足 - val timeTypeList = mTimePoint?.keys?.toList() + val timeTypeList = mTimePoint?.map { it.timeType } val currentTimeTypeIndex = timeTypeList?.indexOf(timeType) var loadTimeType = "" if (currentTimeTypeIndex != null && currentTimeTypeIndex != -1) { diff --git a/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeItemGameTestV2ViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeItemGameTestV2ViewHolder.kt index 172b07f201..1ffc3dad93 100644 --- a/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeItemGameTestV2ViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/home/test_v2/HomeItemGameTestV2ViewHolder.kt @@ -341,7 +341,7 @@ class HomeItemGameTestV2ViewHolder( private fun bindTimePoint(data: HomeItemTestV2Entity) { val context = mBinding.timePointRv.context var adapter = mBinding.timePointRv.adapter - val timePointList = data.startPoint.map { it.key } + val timePointList = data.startPoint.map { it.timeType } if (adapter is HomeGameTestV2TimePointRvAdapter) { adapter.data = timePointList adapter.notifyItemRangeChanged(0, timePointList.count()) diff --git a/app/src/main/java/com/gh/gamecenter/home/video/AutomaticVideoView.kt b/app/src/main/java/com/gh/gamecenter/home/video/AutomaticVideoView.kt index d0a6965615..b39bc59239 100644 --- a/app/src/main/java/com/gh/gamecenter/home/video/AutomaticVideoView.kt +++ b/app/src/main/java/com/gh/gamecenter/home/video/AutomaticVideoView.kt @@ -12,6 +12,7 @@ import android.view.ViewGroup import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat +import com.facebook.drawee.generic.RoundingParams import com.facebook.drawee.view.SimpleDraweeView import com.gh.common.util.LogUtils import com.gh.download.cache.ExoCacheManager @@ -52,11 +53,19 @@ class AutomaticVideoView @JvmOverloads constructor(context: Context, attrs: Attr private var mVideoViewRadius = 6F private var mIsBottomRightAngle = true private var mLastGetContentLengthTime = 0L + private var mIsSurfaceSizeChanged = false init { val array = context.obtainStyledAttributes(attrs, R.styleable.AutomaticVideoView) mVideoViewRadius = array.getFloat(R.styleable.AutomaticVideoView_avRadius, 6F) mIsBottomRightAngle = array.getBoolean(R.styleable.AutomaticVideoView_avBottomRightAngle, true) + + val bottomAngle = if (mIsBottomRightAngle) 0F else mVideoViewRadius + val roundingParams = + RoundingParams().setRoundAsCircle(false) + thumbImage.hierarchy.roundingParams = roundingParams + + containerView.setRadius(mVideoViewRadius, mVideoViewRadius, bottomAngle, bottomAngle) array.recycle() } @@ -64,7 +73,7 @@ class AutomaticVideoView @JvmOverloads constructor(context: Context, attrs: Attr return R.layout.empty_control_video } - fun setParamsData(entity: GameEntity?, color: String?) { + fun setParamsData(entity: GameEntity?, color: String?, isOnlyTop: Boolean = false) { mGameEntity = entity mPlaceholderColor = color setPlaceholderColor(mThumbImageViewLayout, false) @@ -210,7 +219,12 @@ class AutomaticVideoView @JvmOverloads constructor(context: Context, attrs: Attr fun resetDetailMask() { detailBtn?.visibility = View.GONE - containerView.setRadius(mVideoViewRadius, mVideoViewRadius, mVideoViewRadius, mVideoViewRadius) + containerView.setRadius( + mVideoViewRadius, + mVideoViewRadius, + if (mIsBottomRightAngle) 0F else mVideoViewRadius, + if (mIsBottomRightAngle) 0F else mVideoViewRadius + ) if (mMaskDisposable != null && !mMaskDisposable!!.isDisposed) { mMaskDisposable?.dispose() mMaskDisposable = null @@ -275,13 +289,49 @@ class AutomaticVideoView @JvmOverloads constructor(context: Context, attrs: Attr forceChange: Boolean ) { if (mGSYVideoProgressListener != null && mCurrentState == CURRENT_STATE_PLAYING) { - mGSYVideoProgressListener.onProgress(progress, secProgress, currentTime, totalTime); + mGSYVideoProgressListener.onProgress(progress, secProgress, currentTime, totalTime) } + + // https://jira.shanqu.cc/browse/GHZS-4986 + // 参考 https://blog.csdn.net/qq_22706515/article/details/135296398 + // 处理部分设备下拉推送自动弹出时有声音无画面,视频画面没有正确渲染(onSurfaceSizeChanged没有调用) + // 需要循环向上调用 requestLayout 重新创建 surface + if (currentState == CURRENT_STATE_PLAYING || currentState == CURRENT_STATE_PLAYING_BUFFERING_START) { + if (!mIsSurfaceSizeChanged) { + try { + requestLayoutLoop(this) + } catch (e: Exception) { + onCompletion() + } catch (o: OutOfMemoryError) { + onCompletion() + } + return + } + } + } + + // 一直循环向上调用requestLayout + private fun requestLayoutLoop(obj: Any?) { + if (obj is View) { + obj.requestLayout() + requestLayoutLoop(obj.parent) + } + } + + override fun onSurfaceSizeChanged(surface: Surface?, width: Int, height: Int) { + super.onSurfaceSizeChanged(surface, width, height) + mIsSurfaceSizeChanged = true + } + + override fun addTextureView() { + super.addTextureView() + mIsSurfaceSizeChanged = false } override fun resolveUIState(state: Int) { if ((state == CURRENT_STATE_NORMAL || state == CURRENT_STATE_AUTO_COMPLETE || state == CURRENT_STATE_ERROR) - && thumbImageViewLayout.visibility != View.VISIBLE) { + && thumbImageViewLayout.visibility != View.VISIBLE + ) { setViewShowState(thumbImageViewLayout, VISIBLE) } } @@ -319,7 +369,7 @@ class AutomaticVideoView @JvmOverloads constructor(context: Context, attrs: Attr } fun updateThumb(url: String) { - ImageUtils.display(thumbImage, url) + ImageUtils.display(thumbImage, url, false) } /********************************各类UI的状态显示*********************************************/ diff --git a/app/src/main/java/com/gh/gamecenter/home/video/ScrollCalculatorHelper.kt b/app/src/main/java/com/gh/gamecenter/home/video/ScrollCalculatorHelper.kt index c0ab8a6d23..1433a12c13 100644 --- a/app/src/main/java/com/gh/gamecenter/home/video/ScrollCalculatorHelper.kt +++ b/app/src/main/java/com/gh/gamecenter/home/video/ScrollCalculatorHelper.kt @@ -13,7 +13,7 @@ import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.video.detail.CustomManager import com.halo.assistant.HaloApp -class ScrollCalculatorHelper(private val mListRv: RecyclerView, private val mPlayId: Int, private val mRangeTop: Int) { +class ScrollCalculatorHelper(val mListRv: RecyclerView, private val mPlayId: Int, private val mRangeTop: Int) { private var mFirstVisible = -1 private var mLastVisible = 0 private var mRunnable: PlayRunnable? = null @@ -55,6 +55,17 @@ class ScrollCalculatorHelper(private val mListRv: RecyclerView, private val mPla } } + fun forceStop() { + //保存进度 + currentPlayer ?: return + val currentScheduler = currentPlayer?.currentPositionWhenPlaying?.toLong() ?: 0L + savePlaySchedule(MD5Utils.getContentMD5(currentPlayer?.getUrl()), currentScheduler) + currentPlayer?.initUIState() + currentPlayer?.resetDetailMask() + currentPlayer = null + mRunnable?.let { mPlayHandler.removeCallbacks(it) } + } + fun release() { if (currentPlayer != null) { val currentScheduler = currentPlayer?.currentPositionWhenPlaying?.toLong() ?: 0L @@ -93,6 +104,7 @@ class ScrollCalculatorHelper(private val mListRv: RecyclerView, private val mPla mRunnable = null } if (currentPlayer == gsyBaseVideoPlayer) return + val screenPosition = IntArray(2) gsyBaseVideoPlayer.getLocationInWindow(screenPosition) val rangePosition = screenPosition[1] diff --git a/app/src/main/java/com/gh/gamecenter/info/InfoFragment.java b/app/src/main/java/com/gh/gamecenter/info/InfoFragment.java index a8451244aa..309371614d 100644 --- a/app/src/main/java/com/gh/gamecenter/info/InfoFragment.java +++ b/app/src/main/java/com/gh/gamecenter/info/InfoFragment.java @@ -2,27 +2,26 @@ package com.gh.gamecenter.info; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.view.View; +import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; -import android.view.View; -import android.widget.FrameLayout; - import com.ethanhua.skeleton.Skeleton; import com.ethanhua.skeleton.ViewSkeletonScreen; import com.gh.common.util.DataCollectionUtils; -import com.gh.gamecenter.core.utils.StringUtils; -import com.gh.gamecenter.common.view.CustomDividerItemDecoration; -import com.gh.gamecenter.NewsDetailActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.common.baselist.ListAdapter; import com.gh.gamecenter.common.baselist.ListFragment; import com.gh.gamecenter.common.baselist.LoadType; +import com.gh.gamecenter.common.view.CustomDividerItemDecoration; +import com.gh.gamecenter.core.utils.StringUtils; import com.gh.gamecenter.databinding.FragmentListBaseSkeletonBinding; import com.gh.gamecenter.feature.entity.NewsEntity; +import com.gh.gamecenter.newsdetail.NewsDetailActivity; /** * Created by LGT on 2016/6/29. diff --git a/app/src/main/java/com/gh/gamecenter/info/InfoViewModel.java b/app/src/main/java/com/gh/gamecenter/info/InfoViewModel.java index 4ae26abae7..4aac64d866 100644 --- a/app/src/main/java/com/gh/gamecenter/info/InfoViewModel.java +++ b/app/src/main/java/com/gh/gamecenter/info/InfoViewModel.java @@ -74,7 +74,6 @@ public class InfoViewModel extends ListViewModel { @Override protected List filterData(@NonNull List list) { - Config.filterPluginArticle(list); return list; } } diff --git a/app/src/main/java/com/gh/gamecenter/info/OriginalFragment.java b/app/src/main/java/com/gh/gamecenter/info/OriginalFragment.java index 9b40b16f76..99feaa76e5 100644 --- a/app/src/main/java/com/gh/gamecenter/info/OriginalFragment.java +++ b/app/src/main/java/com/gh/gamecenter/info/OriginalFragment.java @@ -10,15 +10,15 @@ import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; import com.gh.common.util.DataCollectionUtils; -import com.gh.gamecenter.core.utils.StringUtils; -import com.gh.gamecenter.common.view.CustomDividerItemDecoration; -import com.gh.gamecenter.NewsDetailActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.common.baselist.ListAdapter; import com.gh.gamecenter.common.baselist.ListFragment; import com.gh.gamecenter.common.baselist.LoadType; import com.gh.gamecenter.common.databinding.FragmentListBaseBinding; +import com.gh.gamecenter.common.view.CustomDividerItemDecoration; +import com.gh.gamecenter.core.utils.StringUtils; import com.gh.gamecenter.feature.entity.NewsEntity; +import com.gh.gamecenter.newsdetail.NewsDetailActivity; /** * Created by khy on 2016/6/29. diff --git a/app/src/main/java/com/gh/gamecenter/info/OriginalViewModel.java b/app/src/main/java/com/gh/gamecenter/info/OriginalViewModel.java index 1a46a8f3df..1a89c82d60 100644 --- a/app/src/main/java/com/gh/gamecenter/info/OriginalViewModel.java +++ b/app/src/main/java/com/gh/gamecenter/info/OriginalViewModel.java @@ -75,7 +75,6 @@ public class OriginalViewModel extends ListViewModel { @Override protected List filterData(@NonNull List list) { - Config.filterPluginArticle(list); return list; } } diff --git a/app/src/main/java/com/gh/gamecenter/info/StrategyAdapter.java b/app/src/main/java/com/gh/gamecenter/info/StrategyAdapter.java index 97ceb4f25d..01632d5051 100644 --- a/app/src/main/java/com/gh/gamecenter/info/StrategyAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/info/StrategyAdapter.java @@ -130,7 +130,6 @@ class StrategyAdapter extends BaseRecyclerAdapter { .getApi() .getArticleList(UrlFilterUtils.getFilterQuery("type_group", "攻略"), mPage) .map(newsEntities -> { - Config.filterPluginArticle(newsEntities); return NewsUtils.removeDuplicateData(mNewsList, newsEntities); }) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/gh/gamecenter/info/StrategyFragment.java b/app/src/main/java/com/gh/gamecenter/info/StrategyFragment.java index 7562116d52..669ae19ead 100644 --- a/app/src/main/java/com/gh/gamecenter/info/StrategyFragment.java +++ b/app/src/main/java/com/gh/gamecenter/info/StrategyFragment.java @@ -14,28 +14,28 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import com.gh.gamecenter.common.base.fragment.BaseFragment; import com.gh.common.filter.RegionSettingHelper; -import com.gh.gamecenter.feature.utils.ApkActiveUtils; import com.gh.common.util.CheckLoginUtils; -import com.gh.gamecenter.common.constant.Constants; -import com.gh.gamecenter.core.utils.DisplayUtils; import com.gh.common.util.NewsUtils; -import com.gh.gamecenter.core.utils.StringUtils; import com.gh.gamecenter.ConcernActivity; import com.gh.gamecenter.GameNewsActivity; -import com.gh.gamecenter.NewsDetailActivity; import com.gh.gamecenter.R; +import com.gh.gamecenter.common.base.fragment.BaseFragment; +import com.gh.gamecenter.common.constant.Constants; +import com.gh.gamecenter.common.eventbus.EBReuse; +import com.gh.gamecenter.common.retrofit.Response; +import com.gh.gamecenter.core.utils.DisplayUtils; +import com.gh.gamecenter.core.utils.StringUtils; import com.gh.gamecenter.databinding.DialogStrategySelectGameBinding; import com.gh.gamecenter.databinding.FragmentInfoStrategyBinding; +import com.gh.gamecenter.eventbus.EBUISwitch; import com.gh.gamecenter.feature.entity.GameEntity; import com.gh.gamecenter.feature.entity.NewsEntity; import com.gh.gamecenter.feature.eventbus.EBConcernChanged; -import com.gh.gamecenter.common.eventbus.EBReuse; -import com.gh.gamecenter.eventbus.EBUISwitch; -import com.gh.gamecenter.manager.DataCollectionManager; +import com.gh.gamecenter.feature.utils.ApkActiveUtils; import com.gh.gamecenter.login.user.UserManager; -import com.gh.gamecenter.common.retrofit.Response; +import com.gh.gamecenter.manager.DataCollectionManager; +import com.gh.gamecenter.newsdetail.NewsDetailActivity; import com.gh.gamecenter.retrofit.RetrofitManager; import com.jakewharton.rxbinding2.view.RxView; diff --git a/app/src/main/java/com/gh/gamecenter/libao/Libao2Fragment.java b/app/src/main/java/com/gh/gamecenter/libao/Libao2Fragment.java index 6750611645..a5f5bd0806 100644 --- a/app/src/main/java/com/gh/gamecenter/libao/Libao2Fragment.java +++ b/app/src/main/java/com/gh/gamecenter/libao/Libao2Fragment.java @@ -15,16 +15,15 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.gh.common.util.CheckLoginUtils; import com.gh.gamecenter.ConcernActivity; -import com.gh.gamecenter.LibaoDetailActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.common.base.fragment.BaseFragment; +import com.gh.gamecenter.common.eventbus.EBReuse; import com.gh.gamecenter.common.utils.ExtensionsKt; import com.gh.gamecenter.common.view.CustomDividerItemDecoration; import com.gh.gamecenter.databinding.FragmentLibao2Binding; +import com.gh.gamecenter.eventbus.EBUISwitch; import com.gh.gamecenter.feature.entity.LibaoEntity; import com.gh.gamecenter.feature.eventbus.EBConcernChanged; -import com.gh.gamecenter.common.eventbus.EBReuse; -import com.gh.gamecenter.eventbus.EBUISwitch; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; diff --git a/app/src/main/java/com/gh/gamecenter/libao/Libao3FragmentAdapter.java b/app/src/main/java/com/gh/gamecenter/libao/Libao3FragmentAdapter.java index 685a4f4882..15a9af96fe 100644 --- a/app/src/main/java/com/gh/gamecenter/libao/Libao3FragmentAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/libao/Libao3FragmentAdapter.java @@ -13,18 +13,19 @@ import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; +import com.gh.common.util.LibaoUtils; +import com.gh.gamecenter.R; +import com.gh.gamecenter.adapter.viewholder.LibaoNormalViewHolder; import com.gh.gamecenter.common.callback.OnRequestCallBackListener; +import com.gh.gamecenter.common.retrofit.Response; import com.gh.gamecenter.common.utils.DialogHelper; import com.gh.common.util.LibaoUtils; import com.gh.gamecenter.common.utils.SensorsBridge; import com.gh.gamecenter.feature.utils.PlatformUtils; import com.gh.gamecenter.common.utils.ExtensionsKt; -import com.gh.gamecenter.core.utils.TimeUtils; import com.gh.gamecenter.common.view.BugFixedPopupWindow; -import com.gh.gamecenter.LibaoDetailActivity; -import com.gh.gamecenter.R; import com.gh.gamecenter.common.viewholder.FooterViewHolder; -import com.gh.gamecenter.adapter.viewholder.LibaoNormalViewHolder; +import com.gh.gamecenter.core.utils.TimeUtils; import com.gh.gamecenter.databinding.LibaoItemBinding; import com.gh.gamecenter.feature.entity.GameEntity; import com.gh.gamecenter.feature.entity.LibaoEntity; @@ -32,8 +33,8 @@ import com.gh.gamecenter.feature.entity.LibaoStatusEntity; import com.gh.gamecenter.feature.entity.MeEntity; import com.gh.gamecenter.feature.entity.UserDataLibaoEntity; import com.gh.gamecenter.feature.game.GameItemViewHolder; +import com.gh.gamecenter.feature.utils.PlatformUtils; import com.gh.gamecenter.login.user.UserManager; -import com.gh.gamecenter.common.retrofit.Response; import com.gh.gamecenter.retrofit.RetrofitManager; import com.lightgame.adapter.BaseRecyclerAdapter; import com.lightgame.utils.Utils; diff --git a/app/src/main/java/com/gh/gamecenter/libao/LibaoDetailActivity.java b/app/src/main/java/com/gh/gamecenter/libao/LibaoDetailActivity.java new file mode 100644 index 0000000000..98870ec687 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/libao/LibaoDetailActivity.java @@ -0,0 +1,77 @@ +package com.gh.gamecenter.libao; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.NonNull; + +import com.alibaba.android.arouter.facade.annotation.Route; +import com.gh.gamecenter.R; +import com.gh.gamecenter.common.base.activity.ToolBarActivity; +import com.gh.gamecenter.common.constant.EntranceConsts; +import com.gh.gamecenter.common.constant.RouteConsts; +import com.gh.gamecenter.common.utils.ExtensionsKt; +import com.gh.gamecenter.feature.entity.LibaoEntity; +import com.halo.assistant.HaloApp; + +import kotlin.Pair; + +/** + * Created by khy on 2016/12/13. + */ +@Route(path = RouteConsts.activity.libaoDetailActivity) +public class LibaoDetailActivity extends ToolBarActivity { + @Override + protected Intent provideNormalIntent() { + return getTargetIntent(this, LibaoDetailActivity.class, LibaoDetailFragment.class, getIntent().getExtras()); + } + + public static Intent getIntent(Context context, LibaoEntity libaoEntity, boolean isClickReceiveBtnIn, String entrance) { + Bundle bundle = new Bundle(); + HaloApp.put(LibaoEntity.TAG, libaoEntity); + bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance); + bundle.putBoolean(EntranceConsts.KEY_IS_CLICK_RECEIVE_BTN, isClickReceiveBtnIn); + return getTargetIntent(context, LibaoDetailActivity.class, LibaoDetailFragment.class, bundle); + } + + @NonNull + public static Intent getIntent(Context context, LibaoEntity libaoEntity, String entrance) { + return getIntent(context, libaoEntity, false, entrance); + } + + @NonNull + public static Intent getIntentById(Context context, String id, String entrance) { + Bundle bundle = new Bundle(); + bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance); + bundle.putString(EntranceConsts.KEY_ID, id); + return getTargetIntent(context, LibaoDetailActivity.class, LibaoDetailFragment.class, bundle); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ExtensionsKt.updateStatusBarColor(this, R.color.ui_surface, R.color.ui_surface); + } + + @Override + protected void onDarkModeChanged() { + super.onDarkModeChanged(); + ExtensionsKt.updateStatusBarColor(this, R.color.ui_surface, R.color.ui_surface); + } + + @Override + public Pair getBusinessId() { + LibaoEntity libaoEntity = null; + if (getTargetFragment() != null) { + libaoEntity = ((LibaoDetailFragment) getTargetFragment()).getLibaoEntity(); + } + if (libaoEntity != null) { + return new Pair(libaoEntity.getId(), ""); + } else if (getIntent().getStringExtra(EntranceConsts.KEY_ID) != null) { + return new Pair(getIntent().getStringExtra(EntranceConsts.KEY_ID), ""); + } else { + return super.getBusinessId(); + } + } +} diff --git a/app/src/main/java/com/gh/gamecenter/LibaoDetailActivity.java b/app/src/main/java/com/gh/gamecenter/libao/LibaoDetailFragment.java similarity index 82% rename from app/src/main/java/com/gh/gamecenter/LibaoDetailActivity.java rename to app/src/main/java/com/gh/gamecenter/libao/LibaoDetailFragment.java index 65b2038871..43e6cb6451 100644 --- a/app/src/main/java/com/gh/gamecenter/LibaoDetailActivity.java +++ b/app/src/main/java/com/gh/gamecenter/libao/LibaoDetailFragment.java @@ -1,9 +1,8 @@ -package com.gh.gamecenter; +package com.gh.gamecenter.libao; import static com.gh.gamecenter.common.constant.Constants.LOGIN_TAG; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Message; @@ -14,40 +13,40 @@ import android.widget.RelativeLayout; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.alibaba.android.arouter.facade.annotation.Route; import com.ethanhua.skeleton.Skeleton; import com.ethanhua.skeleton.ViewSkeletonScreen; import com.gh.common.filter.RegionSettingHelper; -import com.gh.gamecenter.common.constant.RouteConsts; -import com.gh.gamecenter.feature.utils.ApkActiveUtils; import com.gh.common.util.CheckLoginUtils; import com.gh.common.util.DetailDownloadUtils; import com.gh.common.util.DeviceTokenUtils; import com.gh.common.util.LibaoUtils; -import com.gh.gamecenter.feature.view.DownloadButton; import com.gh.download.DownloadManager; +import com.gh.gamecenter.BuildConfig; +import com.gh.gamecenter.R; import com.gh.gamecenter.adapter.LibaoDetailAdapter; import com.gh.gamecenter.adapter.viewholder.DetailViewHolder; -import com.gh.gamecenter.common.base.activity.ToolBarActivity; -import com.gh.gamecenter.common.callback.OnRequestCallBackListener; +import com.gh.gamecenter.common.base.fragment.ToolbarFragment; import com.gh.gamecenter.common.constant.EntranceConsts; +import com.gh.gamecenter.common.eventbus.EBReuse; import com.gh.gamecenter.common.retrofit.Response; import com.gh.gamecenter.common.utils.ExtensionsKt; import com.gh.gamecenter.common.view.VerticalItemDecoration; +import com.gh.gamecenter.eventbus.EBDownloadStatus; +import com.gh.gamecenter.eventbus.EBPackage; +import com.gh.gamecenter.eventbus.EBUISwitch; import com.gh.gamecenter.feature.entity.ApkEntity; import com.gh.gamecenter.feature.entity.GameEntity; import com.gh.gamecenter.feature.entity.LibaoEntity; import com.gh.gamecenter.feature.entity.LibaoStatusEntity; import com.gh.gamecenter.feature.entity.MeEntity; import com.gh.gamecenter.feature.entity.UserDataLibaoEntity; -import com.gh.gamecenter.eventbus.EBDownloadStatus; -import com.gh.gamecenter.eventbus.EBPackage; -import com.gh.gamecenter.common.eventbus.EBReuse; -import com.gh.gamecenter.eventbus.EBUISwitch; +import com.gh.gamecenter.feature.utils.ApkActiveUtils; +import com.gh.gamecenter.feature.view.DownloadButton; import com.gh.gamecenter.retrofit.RetrofitManager; import com.halo.assistant.HaloApp; import com.lightgame.download.DataWatcher; @@ -56,7 +55,6 @@ import com.lightgame.utils.Utils; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import org.jetbrains.annotations.NotNull; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -66,16 +64,9 @@ import java.util.Locale; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; -import kotlin.Pair; import retrofit2.HttpException; -/** - * Created by khy on 2016/12/13. - */ -@Route(path = RouteConsts.activity.libaoDetailActivity) -public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailAdapter.OnCodeScrollListener, - OnRequestCallBackListener { - +public class LibaoDetailFragment extends ToolbarFragment implements LibaoDetailAdapter.OnCodeScrollListener { RecyclerView mLibaoDetailRv; LinearLayout mLlLoading; LinearLayout mNoConnection; @@ -103,7 +94,7 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA protected void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 0) { - long time = Utils.getTime(LibaoDetailActivity.this) - 5; // -5 防止获取服务器时间延迟发生的计算错误() + long time = Utils.getTime(requireContext()) - 5; // -5 防止获取服务器时间延迟发生的计算错误() Calendar calendar = Calendar.getInstance(); Date date = new Date(time * 1000); calendar.setTime(date); @@ -151,66 +142,52 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA } }; - private DetailViewHolder getDetailViewHolder() { // 每次获取需要重新创建, 防止数据刷新 - return new DetailViewHolder(mContentView, mGameEntity, false, mEntrance, mName, mTitle, null, false); // 下载按钮ViewHolder + return new DetailViewHolder(mCachedView, mGameEntity, false, mEntrance, mName, mTitle, null, false); // 下载按钮ViewHolder } - public static Intent getIntent(Context context, LibaoEntity libaoEntity, boolean isClickReceiveBtnIn, String entrance) { - Intent intent = new Intent(context, LibaoDetailActivity.class); - HaloApp.put(LibaoEntity.TAG, libaoEntity); - intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance); - intent.putExtra(EntranceConsts.KEY_IS_CLICK_RECEIVE_BTN, isClickReceiveBtnIn); - return intent; - } - - @NonNull - public static Intent getIntent(Context context, LibaoEntity libaoEntity, String entrance) { - return getIntent(context, libaoEntity, false, entrance); - } - - @NonNull - public static Intent getIntentById(Context context, String id, String entrance) { - Intent intent = new Intent(context, LibaoDetailActivity.class); - intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance); - intent.putExtra(EntranceConsts.KEY_ID, id); - return intent; + public LibaoEntity getLibaoEntity() { + return mLibaoEntity; } @Override - protected void onSaveInstanceState(@NotNull Bundle outState) { + protected int getLayoutId() { + return R.layout.fragment_libao_detail; + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); HaloApp.put(LibaoEntity.TAG, mAdapter.getLibaoEntity()); } @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); - mLibaoDetailRv = findViewById(R.id.libaodetail_rv_show); - mLlLoading = findViewById(R.id.reuse_ll_loading); - mNoConnection = findViewById(R.id.reuse_no_connection); - mNoneDataTv = findViewById(R.id.reuseNoneDataTv); - mNoneData = findViewById(R.id.reuse_none_data); - mDataExceptionView = findViewById(R.id.reuse_data_exception); - mDownloadPb = findViewById(R.id.detail_progressbar); - mListSkeleton = findViewById(R.id.list_skeleton); - mDetailBottom = findViewById(R.id.detail_ll_bottom); + mLibaoDetailRv = view.findViewById(R.id.libaodetail_rv_show); + mLlLoading = view.findViewById(R.id.reuse_ll_loading); + mNoConnection = view.findViewById(R.id.reuse_no_connection); + mNoneDataTv = view.findViewById(R.id.reuseNoneDataTv); + mNoneData = view.findViewById(R.id.reuse_none_data); + mDataExceptionView = view.findViewById(R.id.reuse_data_exception); + mDownloadPb = view.findViewById(R.id.detail_progressbar); + mListSkeleton = view.findViewById(R.id.list_skeleton); + mDetailBottom = view.findViewById(R.id.detail_ll_bottom); mName = getString(R.string.libao_detail); setNavigationTitle(mName); mLibaoEntity = (LibaoEntity) HaloApp.get(LibaoEntity.TAG, false); - isClickReceiveBtnIn = getIntent().getBooleanExtra(EntranceConsts.KEY_IS_CLICK_RECEIVE_BTN, false); + isClickReceiveBtnIn = requireArguments().getBoolean(EntranceConsts.KEY_IS_CLICK_RECEIVE_BTN, false); if (mLibaoEntity != null) { mLibaoEntity.setClickReceiveBtnIn(isClickReceiveBtnIn); } - ExtensionsKt.updateStatusBarColor(this, R.color.ui_surface, R.color.ui_surface); mIsScroll = true; mSkeleton = Skeleton.bind(mListSkeleton).shimmer(false).load(R.layout.activity_libaodetail_skeleton).show(); - mAdapter = new LibaoDetailAdapter(this, this, this, mLibaoEntity, mDownloadPb, mEntrance); - mLibaoDetailRv.setLayoutManager(new LinearLayoutManager(this) { + mAdapter = new LibaoDetailAdapter(requireContext(), this, this, mLibaoEntity, mDownloadPb, mEntrance); + mLibaoDetailRv.setLayoutManager(new LinearLayoutManager(requireContext()) { @Override public boolean canScrollVertically() { return mIsScroll; @@ -227,7 +204,7 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA }); if (mLibaoEntity == null) { - String id = getIntent().getStringExtra(EntranceConsts.KEY_ID); + String id = requireArguments().getString(EntranceConsts.KEY_ID); if (!TextUtils.isEmpty(id)) { getLibaoDigest(id); } @@ -241,21 +218,20 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mDetailBottom.getLayoutParams(); params.topMargin = 0; mDetailBottom.setLayoutParams(params); - } private RecyclerView.ItemDecoration getItemDecoration() { - return new VerticalItemDecoration(this, 8, false); + return new VerticalItemDecoration(requireContext(), 8, false); } @Override - protected void onPause() { + public void onPause() { super.onPause(); DownloadManager.getInstance().removeObserver(dataWatcher); } @Override - protected void onResume() { + public void onResume() { super.onResume(); if (mGameEntity != null && (mGameEntity.getApk().size() == 1 || mGameEntity.isReservable())) { @@ -264,11 +240,6 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA DownloadManager.getInstance().addObserver(dataWatcher); } - @Override - protected int getLayoutId() { - return R.layout.activity_libaodetail; - } - // 检查礼包是否可以重复领取 private void checkLibaoStatus() { if (mLibaoEntity != null && mLibaoEntity.getRepeat() > 0 && mLibaoEntity.getMe() != null @@ -316,7 +287,7 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA mAdapter.notifyItemChanged(0); performClickReceiveBtn(); - DeviceTokenUtils.syncServerTime(LibaoDetailActivity.this); + DeviceTokenUtils.syncServerTime(requireContext()); mBaseHandler.sendEmptyMessageDelayed(0, 5000); } } @@ -327,7 +298,7 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA private void performClickReceiveBtn() { if (isClickReceiveBtnIn) { - CheckLoginUtils.checkLogin(this, mEntrance, () -> + CheckLoginUtils.checkLogin(requireContext(), mEntrance, () -> mLibaoDetailRv.postDelayed(() -> { if (mAdapter.libaoDetailTopViewHolder != null) { try { @@ -350,7 +321,7 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA UserDataLibaoEntity userDataLibaoEntity = userDataLibaoList.get(userDataLibaoList.size() - 1); SimpleDateFormat formatDay = new SimpleDateFormat("dd", Locale.CHINA); long lingTime = userDataLibaoEntity.getTime() * 1000; - long curTime = Utils.getTime(this) * 1000; + long curTime = Utils.getTime(requireContext()) * 1000; int lingDay = Integer.parseInt(formatDay.format(lingTime)); int curDay = Integer.parseInt(formatDay.format(curTime)); if (curDay != lingDay || curTime - lingTime > 24 * 60 * 60 * 1000) { @@ -405,6 +376,7 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA if (mLibaoEntity.getGame() == null) return; String gameId = mLibaoEntity.getGame().getId(); if (RegionSettingHelper.shouldThisGameBeFiltered(gameId)) { + loadNotFound(); return; } RetrofitManager.getInstance().getApi().getGameNewsDigest(gameId) @@ -426,13 +398,13 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA DetailDownloadUtils.updateViewHolder(getDetailViewHolder()); - mAdapter.addLibaoDetail(LibaoDetailActivity.this, true); + mAdapter.addLibaoDetail(requireContext(), true); } @Override public void onFailure(HttpException e) { if (e != null && e.code() == 404) { - mAdapter.addLibaoDetail(LibaoDetailActivity.this, false); + mAdapter.addLibaoDetail(requireContext(), false); } else { loadError(); } @@ -476,7 +448,7 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA if (obj != null) { Intent data = new Intent(); data.putExtra(UserDataLibaoEntity.TAG, (UserDataLibaoEntity) obj); - setResult(Activity.RESULT_OK, data); + requireActivity().setResult(Activity.RESULT_OK, data); } } @@ -559,11 +531,12 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA } } + @Override protected void onDarkModeChanged() { super.onDarkModeChanged(); - ExtensionsKt.setRootBackgroundColor(mContentView, R.color.ui_background); - mDetailBottom.setBackgroundColor(ContextCompat.getColor(this, R.color.ui_background)); + ExtensionsKt.setRootBackgroundColor(mCachedView, R.color.ui_background); + mDetailBottom.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.ui_background)); mLibaoDetailRv.getRecycledViewPool().clear(); mAdapter.notifyItemRangeChanged(0, mAdapter.getItemCount()); if (mGameEntity != null) { @@ -573,17 +546,5 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA mLibaoDetailRv.removeItemDecorationAt(0); mLibaoDetailRv.addItemDecoration(getItemDecoration()); } - ExtensionsKt.updateStatusBarColor(this, R.color.ui_surface, R.color.ui_surface); - } - - @Override - public Pair getBusinessId() { - if (mLibaoEntity != null) { - return new Pair(mLibaoEntity.getId(), ""); - } else if (getIntent().getStringExtra(EntranceConsts.KEY_ID) != null) { - return new Pair(getIntent().getStringExtra(EntranceConsts.KEY_ID), ""); - } else { - return super.getBusinessId(); - } } } diff --git a/app/src/main/java/com/gh/gamecenter/libao/LibaoFragment.java b/app/src/main/java/com/gh/gamecenter/libao/LibaoFragment.java index f3ced4660b..58a0ea9aaa 100644 --- a/app/src/main/java/com/gh/gamecenter/libao/LibaoFragment.java +++ b/app/src/main/java/com/gh/gamecenter/libao/LibaoFragment.java @@ -19,11 +19,11 @@ import androidx.fragment.app.Fragment; import com.gh.gamecenter.ConcernActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.common.base.fragment.BaseFragment_TabLayout; +import com.gh.gamecenter.common.eventbus.EBReuse; import com.gh.gamecenter.common.utils.ExtensionsKt; import com.gh.gamecenter.common.utils.SensorsBridge; import com.gh.gamecenter.core.utils.MtaHelper; import com.gh.gamecenter.databinding.FragmentLibaoWrapperBinding; -import com.gh.gamecenter.common.eventbus.EBReuse; import com.gh.gamecenter.eventbus.EBUISwitch; import com.google.android.material.tabs.TabLayout; @@ -87,11 +87,13 @@ public class LibaoFragment extends BaseFragment_TabLayout { @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - setNavigationTitle(R.string.title_libao); - initMenu(R.menu.menu_manage); - mManageMenu = getItemMenu(R.id.layout_menu_manage); - ((TextView) mManageMenu.getActionView().findViewById(R.id.manageTv)).setText("管理"); - mManageMenu.getActionView().findViewById(R.id.manageTv).setVisibility(View.GONE); + if (!mIsFromTabWrapper) { + setNavigationTitle(R.string.title_libao); + initMenu(R.menu.menu_manage); + mManageMenu = getItemMenu(R.id.layout_menu_manage); + ((TextView) mManageMenu.getActionView().findViewById(R.id.manageTv)).setText("管理"); + mManageMenu.getActionView().findViewById(R.id.manageTv).setVisibility(View.GONE); + } } @Override @@ -140,7 +142,9 @@ public class LibaoFragment extends BaseFragment_TabLayout { MtaHelper.onEvent("我的光环_新", "礼包中心", mTabTitleList.get(position) + "Tab"); SensorsBridge.trackEvent("GameGiftCenterTabSelected", "tab_name", mTabTitleList.get(position)); EventBus.getDefault().post(new EBUISwitch(EB_LIBAOACTIVITY_TAG, position)); - mManageMenu.getActionView().findViewById(R.id.manageTv).setVisibility(position == 1 ? View.VISIBLE : View.GONE); + if (!mIsFromTabWrapper) { + mManageMenu.getActionView().findViewById(R.id.manageTv).setVisibility(position == 1 ? View.VISIBLE : View.GONE); + } } @Override diff --git a/app/src/main/java/com/gh/gamecenter/libao/LibaoNewFragment.java b/app/src/main/java/com/gh/gamecenter/libao/LibaoNewFragment.java index 50ba0219a7..d30f5e37d9 100644 --- a/app/src/main/java/com/gh/gamecenter/libao/LibaoNewFragment.java +++ b/app/src/main/java/com/gh/gamecenter/libao/LibaoNewFragment.java @@ -16,14 +16,13 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.ethanhua.skeleton.Skeleton; import com.ethanhua.skeleton.ViewSkeletonScreen; +import com.gh.gamecenter.R; import com.gh.gamecenter.common.base.fragment.BaseFragment; +import com.gh.gamecenter.common.eventbus.EBReuse; import com.gh.gamecenter.common.view.CustomDividerItemDecoration; import com.gh.gamecenter.common.view.SwipeLayout; -import com.gh.gamecenter.LibaoDetailActivity; -import com.gh.gamecenter.R; import com.gh.gamecenter.databinding.FragmentLibaoNewBinding; import com.gh.gamecenter.feature.entity.LibaoEntity; -import com.gh.gamecenter.common.eventbus.EBReuse; import org.greenrobot.eventbus.EventBus; diff --git a/app/src/main/java/com/gh/gamecenter/libao/LibaoSearchFragment.java b/app/src/main/java/com/gh/gamecenter/libao/LibaoSearchFragment.java index d9febfae51..206da24891 100644 --- a/app/src/main/java/com/gh/gamecenter/libao/LibaoSearchFragment.java +++ b/app/src/main/java/com/gh/gamecenter/libao/LibaoSearchFragment.java @@ -12,14 +12,13 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import com.gh.gamecenter.LibaoDetailActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.common.base.fragment.BaseFragment; +import com.gh.gamecenter.common.eventbus.EBReuse; import com.gh.gamecenter.common.utils.ExtensionsKt; import com.gh.gamecenter.common.view.CustomDividerItemDecoration; import com.gh.gamecenter.databinding.FragmentLibaoSerachBinding; import com.gh.gamecenter.feature.entity.LibaoEntity; -import com.gh.gamecenter.common.eventbus.EBReuse; import org.greenrobot.eventbus.EventBus; diff --git a/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java b/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java deleted file mode 100644 index adfc5db9df..0000000000 --- a/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java +++ /dev/null @@ -1,637 +0,0 @@ -package com.gh.gamecenter.manager; - -import android.app.Activity; -import android.app.Dialog; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Build; -import android.os.Handler; -import android.os.Message; -import android.preference.PreferenceManager; -import android.text.Html; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.widget.ProgressBar; -import android.widget.TextView; - -import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.core.app.NotificationManagerCompat; - -import com.gh.common.exposure.ExposureUtils; -import com.gh.common.util.DialogUtils; -import com.gh.common.util.DirectUtils; -import com.gh.common.util.PackageInstaller; -import com.gh.common.util.PackageUtils; -import com.gh.download.DownloadManager; -import com.gh.gamecenter.MainActivity; -import com.gh.gamecenter.R; -import com.gh.gamecenter.common.constant.Constants; -import com.gh.gamecenter.common.retrofit.Response; -import com.gh.gamecenter.common.utils.DataLogUtils; -import com.gh.gamecenter.common.utils.ExtensionsKt; -import com.gh.gamecenter.common.utils.NetworkUtils; -import com.gh.gamecenter.common.utils.SensorsBridge; -import com.gh.gamecenter.common.view.CustomLinkMovementMethod; -import com.gh.gamecenter.core.AppExecutor; -import com.gh.gamecenter.core.utils.CurrentActivityHolder; -import com.gh.gamecenter.core.utils.DisplayUtils; -import com.gh.gamecenter.core.utils.EmptyCallback; -import com.gh.gamecenter.core.utils.GsonUtils; -import com.gh.gamecenter.core.utils.MD5Utils; -import com.gh.gamecenter.core.utils.MtaHelper; -import com.gh.gamecenter.core.utils.SPUtils; -import com.gh.gamecenter.core.utils.SpanBuilder; -import com.gh.gamecenter.core.utils.SpeedUtils; -import com.gh.gamecenter.entity.AppEntity; -import com.gh.gamecenter.feature.entity.GameEntity; -import com.gh.gamecenter.feature.exposure.ExposureEvent; -import com.gh.gamecenter.retrofit.RetrofitManager; -import com.gh.ndownload.NDataChanger; -import com.halo.assistant.HaloApp; -import com.lightgame.download.DataWatcher; -import com.lightgame.download.DownloadEntity; -import com.lightgame.download.DownloadStatus; -import com.lightgame.download.FileUtils; -import com.lightgame.utils.AppManager; -import com.lightgame.utils.Utils; - -import java.io.File; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.concurrent.atomic.AtomicBoolean; - -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import retrofit2.HttpException; - -// 吐了,这个祖传的类,代码写得不是一般的糟糕,还不敢随便改 : ( - -/** - * Created by LGT on 2016/12/8. - * 助手更新 工具类 - */ -public class UpdateManager { - - // 用于控制第二次打开更新弹窗 - private final static String ONCE_ONLY_SECOND_DEFAULT = "ONCE_ONLY_SECOND_DEFAULT"; - private final static String ONCE_ONLY_SECOND_CLOSE = "ONCE_ONLY_SECOND_CLOSE"; - private final static String ONCE_ONLY_SECOND_OPEN = "ONCE_ONLY_SECOND_OPEN"; - - private Context mContext; - private Context mApplicationContext; - - private SharedPreferences mSp; - - private AppEntity appEntity; - - private EmptyCallback mOnDismissCallback; - - private Dialog downloadDialog; - private Dialog loadingDialog; - private Dialog updateDialog; - - private ProgressBar app_pb_progress; - private TextView appProgressSize; - private TextView appProgressRemain; - private TextView appProgressPercent; - private View appProgressFilling; - private View appProgressAnchor; - - private TextView cancelUpdateTextView; - private TextView downloadedHintView; - private TextView confirmTextView; - - private boolean isShowDownload; - private boolean isChecking; - private long lastUpdateFileSize; - - private String currentUpdateMd5; - - private DataWatcher dataWatcher = new DataWatcher() { - @Override - public void onDataChanged(DownloadEntity downloadEntity) { - if (downloadEntity.getName().contains("光环助手")) { - if (downloadDialog != null && downloadDialog.isShowing() && isShowDownload) { - - // 被取消任务的状态不用来更新页面 - if (!DownloadStatus.cancel.equals(downloadEntity.getStatus())) { - float size = (((float) downloadEntity.getProgress() / 1024) / 1024); - DecimalFormat df = new DecimalFormat("0.00"); - appProgressSize.setText((df.format(size) + "MB")); - appProgressRemain.setText(String.format("剩余%s", SpeedUtils.getRemainSecondTime(downloadEntity.getSize(), - downloadEntity.getProgress(), downloadEntity.getSpeed() * 1024))); - app_pb_progress.setProgress((int) (downloadEntity.getPercent() * 10)); - - int width = app_pb_progress.getWidth(); - int marLeft = (int) (downloadEntity.getPercent() / 100 * width); - ViewGroup.LayoutParams anchorLp = appProgressAnchor.getLayoutParams(); - if (anchorLp instanceof ConstraintLayout.LayoutParams) { - ((ConstraintLayout.LayoutParams) anchorLp).leftMargin = marLeft; - appProgressAnchor.setLayoutParams(anchorLp); - } - - if (downloadEntity.getSize() != lastUpdateFileSize) { - lastUpdateFileSize = downloadEntity.getSize(); - SPUtils.setLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, lastUpdateFileSize); - } - - ViewGroup.LayoutParams fillingLp = appProgressFilling.getLayoutParams(); - fillingLp.width = marLeft + DisplayUtils.dip2px(5); - appProgressFilling.setLayoutParams(fillingLp); - - appProgressPercent.setText(((int) downloadEntity.getPercent() + "%")); - } - - if (DownloadStatus.done.equals(downloadEntity.getStatus())) { - DownloadManager.getInstance().cancel(downloadEntity.getUrl(), false, true, false); - if (downloadDialog != null) { - try { - downloadDialog.dismiss(); - } catch (IllegalArgumentException ignored) { - // do nothing - } - } - if (appEntity != null && appEntity.isForce()) { - AppExecutor.getUiExecutor().executeWithDelay(() -> exitApp(), 1000); - } - } else if (DownloadStatus.neterror.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "网络错误,请稍后重试"); - } else if (DownloadStatus.diskisfull.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "磁盘已满,请清理后重试"); - } else if (DownloadStatus.diskioerror.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "磁盘 IO 异常,请稍后重试"); - } else if (DownloadStatus.timeout.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "请求超时,请稍后重试"); - } else if (DownloadStatus.notfound.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "下载链接异常,请稍后重试"); - } else if (DownloadStatus.hijack.equals(downloadEntity.getStatus())) { - Utils.toast(mApplicationContext, "网络劫持,请稍后重试"); - } - } else { - if (downloadEntity.getSize() != lastUpdateFileSize) { - lastUpdateFileSize = downloadEntity.getSize(); - SPUtils.setLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, lastUpdateFileSize); - } - - if (updateDialog != null && updateDialog.isShowing()) { - // 强制更新的时候完成下载不用更新页面 - if (DownloadStatus.done.equals(downloadEntity.getStatus()) && !appEntity.isForce()) { - updateUpdateDialogView(true); - } - } else { - if (mContext instanceof MainActivity - && AppManager.getInstance().getRecentActiveActivity() == mContext) { - if (DownloadStatus.done.equals(downloadEntity.getStatus())) { - showUpdateDialog(currentUpdateMd5); - } - } - } - } - } - } - }; - - private UpdateManager(Context context) { - mContext = context; - mApplicationContext = context.getApplicationContext(); - mSp = PreferenceManager.getDefaultSharedPreferences(mApplicationContext); - - this.isShowDownload = false; - this.isChecking = false; - this.loadingDialog = null; - - lastUpdateFileSize = SPUtils.getLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, 0L); - } - - public static UpdateManager getInstance(Context context) { - return new UpdateManager(context); - } - - // 检查更新 - public void checkUpdate(final boolean isAutoCheck, final Handler handler) { - if (isChecking) { - return; - } - isChecking = true; - if (!isAutoCheck) { - loadingDialog = DialogUtils.showWaitDialog(mContext, "检查更新中..."); - } - String channel = HaloApp.getInstance().getChannel(); - RetrofitManager.getInstance().getApi().getUpdate( - PackageUtils.getGhVersionName(), - PackageUtils.getGhVersionCode(), - channel, - Build.VERSION.SDK_INT) - .map(appEntity -> { - boolean isShowUpdateDialog = false; - - if (appEntity.getVersionCode() > PackageUtils.getGhVersionCode()) { - // 助手有更新 - UpdateManager.this.appEntity = appEntity; - - // 手动更新 强制更新 每次都提示 - if (!isAutoCheck || "EVERY_TIME_OPEN".equals(appEntity.getAlert())) { - isShowUpdateDialog = true; - } else if ("ONCE_ONLY".equals(appEntity.getAlert())) { - if (mSp.getBoolean(getUpdateOnceOnlySpKey(), true)) { - isShowUpdateDialog = true; - mSp.edit().putBoolean(getUpdateOnceOnlySpKey(), false).apply(); - } - } else if ("ONCE_ONLY_SECOND".equals(appEntity.getAlert())) { - String onceOnlySecond = mSp.getString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_DEFAULT); - if (ONCE_ONLY_SECOND_OPEN.equals(onceOnlySecond)) { - isShowUpdateDialog = true; - mSp.edit().putString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_CLOSE).apply(); - } - - if (ONCE_ONLY_SECOND_DEFAULT.equals(onceOnlySecond)) { - mSp.edit().putString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_OPEN).apply(); - } - } else if (!"NEVER".equals(appEntity.getAlert())) { // 一天提示一次 - String showUpdateTime = mSp.getString("show_update_time", null); - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); - String today = format.format(new Date()); - if (!today.equals(showUpdateTime)) { - isShowUpdateDialog = true; - mSp.edit().putString("show_update_time", today).apply(); - } - } - } - return isShowUpdateDialog ? MD5Utils.getUpdateMD5(appEntity.getUrl(), appEntity.getContent()) : null; - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Response() { - @Override - public void onResponse(String response) { - isChecking = false; - currentUpdateMd5 = response; - if (loadingDialog != null) { - loadingDialog.dismiss(); - } - if (response != null) { - if (!appEntity.isForce() - && isAutoCheck - && !isUpdateFileDownloaded(response) - && NetworkUtils.isWifiConnected(mApplicationContext)) { - createUpdate(response, true); - } else { - showUpdateDialog(response); - } - if (handler != null) { - Message message = new Message(); - message.what = 0; - message.obj = appEntity.getVersion(); - handler.sendMessage(message); - } - } else if (!isAutoCheck) { - Utils.toast(mApplicationContext, "已是最新版本"); - if (handler != null) { - handler.sendEmptyMessage(1); - } - - invokeDismissCallback(); - } - } - - @Override - public void onFailure(HttpException e) { - isChecking = false; - if (loadingDialog != null) { - loadingDialog.dismiss(); - } - if (!isAutoCheck) { - if (handler != null) { - handler.sendEmptyMessage(1); - } - if (e != null && (e.code() == 304 || e.code() == 404)) { - Utils.toast(mApplicationContext, "您的光环助手已是最新版本"); - return; - } - - Utils.toast(mApplicationContext, "检查更新失败"); - } - - invokeDismissCallback(); - } - }); - } - - // 显示助手有更新提示框 - private void showUpdateDialog(final String md5) { - Context context = getValidContext(); - - AtomicBoolean dismissByTouchInside = new AtomicBoolean(false); - - updateDialog = new Dialog(context); - updateDialog.setOnDismissListener(dialog -> { - invokeDismissCallback(); - if (!isShowDownload) { - DownloadManager.getInstance().removeObserver(dataWatcher); - } - if (!dismissByTouchInside.get() && appEntity != null) { - SensorsBridge.trackVersionUpdateDialogClick( - "关闭弹窗", - appEntity.getAlert(), - appEntity.isForce() ? "关闭且强退" : "仅关闭" - ); - } - }); - Window window = updateDialog.getWindow(); - if (window != null) { - window.setBackgroundDrawableResource(android.R.color.transparent); - } - - View view = View.inflate(context, R.layout.app_update_hint_dialog, null); - - cancelUpdateTextView = view.findViewById(R.id.cancel); - downloadedHintView = view.findViewById(R.id.downloadedHint); - confirmTextView = view.findViewById(R.id.confirm); - - if (!TextUtils.isEmpty(appEntity.getSpareLink())) { - String defaultText = "部分设备若无法更新安装,请前往官网进行下载安装:"; - TextView externalTextTv = view.findViewById(R.id.externalTextTv); - SpannableStringBuilder builder = - new SpanBuilder(defaultText + appEntity.getSpareLink()) - .click(mContext, - defaultText.length(), - defaultText.length() + appEntity.getSpareLink().length(), - R.color.text_theme, - false, - () -> { - DirectUtils.directToExternalBrowser(context, appEntity.getSpareLink()); - return null; - }) - .build(); - externalTextTv.setText(builder); - externalTextTv.setMovementMethod(CustomLinkMovementMethod.getInstance()); - externalTextTv.setVisibility(View.VISIBLE); - } - - if (NetworkUtils.isWifiConnected(context)) { - if (!isUpdateFileDownloaded(md5)) { - updateUpdateDialogView(false); - createUpdate(md5, true); - } else { - updateUpdateDialogView(true); - } - } else { - updateUpdateDialogView(isUpdateFileDownloaded(md5)); - } - - TextView content = view.findViewById(R.id.desc); - content.setText(Html.fromHtml(appEntity.getContent())); - - TextView version = view.findViewById(R.id.version); - version.setText(String.format("版本%s更新日志:", appEntity.getVersion())); - - TextView size = view.findViewById(R.id.size); - size.setText(String.format("大小 %s", appEntity.getSize())); - - view.setOnClickListener(v -> { - if (appEntity.isForce()) { - exitApp(); - } else { - updateDialog.dismiss(); - } - }); - - cancelUpdateTextView.setOnClickListener(v -> { - dismissByTouchInside.set(true); - if (appEntity.isForce()) { - String cancelText = cancelUpdateTextView.getText().toString(); - SensorsBridge.trackVersionUpdateDialogClick( - cancelText, - appEntity.getAlert(), - appEntity.isForce() ? "关闭且强退" : "仅关闭"); - AppExecutor.getUiExecutor().executeWithDelay(this::exitApp, 500L); - } else { - updateDialog.dismiss(); - } - }); - - confirmTextView.setOnClickListener(v -> { - if (!isUpdateFileDownloaded(md5)) { - dismissByTouchInside.set(true); - updateDialog.dismiss(); - } else if (isUpdateFileDownloaded(md5) && !appEntity.isForce()) { - dismissByTouchInside.set(true); - updateDialog.dismiss(); - } - String path = FileUtils.getDownloadPath(context, "光环助手V" + appEntity.getVersion() + "_" + md5 + ".apk"); - // 产品不接受显示下载完成文案从立即更新变为立即安装,所以文案为立即更新时一律不执行安装 - if (isUpdateFileDownloaded(md5) && confirmTextView.getText() != "立即更新") { - DataLogUtils.uploadUpgradeLog(context, "install"); //上传更新安装数据 - PackageInstaller.install(context, false, path, null); - } else { - MtaHelper.onEvent("软件更新", "下载开始"); - - showDownloadDialog(md5); - } - String buttonName = confirmTextView.getText().toString(); - SensorsBridge.trackVersionUpdateDialogClick( - buttonName, - appEntity.getAlert(), - appEntity.isForce() ? "关闭且强退" : "仅关闭"); - }); - - if (appEntity.isForce()) { - updateDialog.setCanceledOnTouchOutside(false); - updateDialog.setCancelable(false); - } - updateDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - updateDialog.setContentView(view); - updateDialog.show(); - - SensorsBridge.trackVersionUpdateDialogShow(appEntity.getAlert(), appEntity.isForce() ? "关闭且强退" : "仅关闭"); - - DataLogUtils.uploadUpgradeLog(context, "notice"); //上传更新通知弹窗数据 - } - - private void exitApp() { - NotificationManagerCompat.from(mApplicationContext).cancelAll(); - AppManager.getInstance().finishAllActivity(); - } - - private void showDownloadDialog(String md5) { - Context context = getValidContext(); - - if (NetworkUtils.isMobileConnected(context)) { - Utils.toast(context, "当前使用移动数据进行下载"); - } - - downloadDialog = new Dialog(context); - downloadDialog.setOnDismissListener(dialog -> { - invokeDismissCallback(); - }); - Window window = downloadDialog.getWindow(); - if (window != null) { - window.setBackgroundDrawableResource(android.R.color.transparent); - } - - View view = View.inflate(context, R.layout.app_updating_dialog, null); - - app_pb_progress = view.findViewById(R.id.progress); - appProgressSize = view.findViewById(R.id.size); - appProgressRemain = view.findViewById(R.id.remain); - appProgressAnchor = view.findViewById(R.id.progress_anchor); - appProgressPercent = view.findViewById(R.id.percent); - appProgressFilling = view.findViewById(R.id.progress_filling); - - view.findViewById(R.id.app_tv_cancel).setOnClickListener(v -> { - DownloadManager.getInstance().cancel(appEntity.getUrl()); - if (appEntity.isForce()) { - exitApp(); - } else { - downloadDialog.dismiss(); - } - }); - - downloadDialog.setOnDismissListener(dialog -> { - DownloadManager.getInstance().removeObserver(dataWatcher); - isShowDownload = false; - }); - - int dialogWidth = context.getResources().getDisplayMetrics().widthPixels - DisplayUtils.dip2px(60); - downloadDialog.setCanceledOnTouchOutside(false); - downloadDialog.setCancelable(false); - downloadDialog.closeOptionsMenu(); - downloadDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - downloadDialog.setContentView(view, new ViewGroup.LayoutParams(dialogWidth, ViewGroup.LayoutParams.WRAP_CONTENT)); - - isShowDownload = true; - - createUpdate(md5, false); - - downloadDialog.show(); - } - - private void createUpdate(String md5, boolean isSilentUpdate) { - DownloadManager.getInstance().addObserver(dataWatcher); - boolean shouldCancelPreviousDownload = true; // 是否应该取消旧更新任务 - - // 在部分设备上取消正在进行中的旧下载任务再创建新下载任务有机率造成新下载任务依然带有 "静默更新" 标签导致无法唤起安装 - // 所以这里对当前静默更新任务正在进行中的时候就不用删旧任务,直接改 meta 标签 - if (!isSilentUpdate - && DownloadManager.getInstance().isTaskDownloading(appEntity.getUrl())) { - try { - DownloadEntity entity = NDataChanger.INSTANCE.getDownloadEntries().get(appEntity.getUrl()); - - if (entity != null) { - ExtensionsKt.addMetaExtra(entity, Constants.EXTRA_DOWNLOAD_TYPE, "不再是静默更新"); - DownloadManager.getInstance().updateDownloadEntity(entity); - DownloadManager.getInstance().resume(entity, false); - - shouldCancelPreviousDownload = false; - } - } catch (Exception e) { - // 出现异常走删旧下载任务重下流程 - shouldCancelPreviousDownload = true; - e.printStackTrace(); - } - } - - // 预下载完成或者还没进行预下载的都进这里,先删掉旧的下载文件再进行下载 - if (shouldCancelPreviousDownload) { - String path = FileUtils.getDownloadPath(mApplicationContext, "光环助手V" + appEntity.getVersion() + "_" + md5 + ".apk"); - File file = new File(path); - if (file.exists() && file.delete()) { - Utils.log(file.getName() + " file delete success."); - } - - ExposureEvent event = ExposureUtils.logADownloadExposureEvent( - new GameEntity(Constants.GHZS_GAME_ID, "光环助手V" + appEntity.getVersion()), - null, - null, - ExposureUtils.DownloadType.DOWNLOAD); - - DownloadEntity downloadEntity = new DownloadEntity(); - downloadEntity.setUrl(appEntity.getUrl()); - downloadEntity.setName("光环助手V" + appEntity.getVersion()); - downloadEntity.setPath(path); - downloadEntity.setPlatform("官方版"); - downloadEntity.setGameId(Constants.GHZS_GAME_ID); - downloadEntity.setFormat("apk"); - downloadEntity.setExposureTrace(GsonUtils.toJson(event)); - - if (isSilentUpdate) { - ExtensionsKt.addMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE, Constants.SILENT_UPDATE); - } - - downloadEntity.setPackageName(mApplicationContext.getPackageName()); - DownloadManager.getInstance().cancel(appEntity.getUrl(), true, true, false); - DownloadManager.getInstance().pauseAll(); - - AppExecutor.getUiExecutor().executeWithDelay(() -> { - DownloadManager.getInstance().add(downloadEntity); - }, 200); - } - } - - /** - * 尽量获取有效的 activity context - */ - private Context getValidContext() { - Context context = mContext; - - Activity currentActivity = CurrentActivityHolder.getCurrentActivity(); - if (currentActivity != null - && mContext != currentActivity - && !currentActivity.isFinishing()) { - context = currentActivity; - } - - return context; - } - - private boolean isUpdateFileDownloaded(String md5) { - String path = FileUtils.getDownloadPath(mApplicationContext, "光环助手V" + appEntity.getVersion() + "_" + md5 + ".apk"); - File file = new File(path); - - return file.exists() && file.length() == SPUtils.getLong(Constants.LAST_GHZS_UPDATE_FILE_SIZE, 0); - } - - private void updateUpdateDialogView(boolean isUpdateDownloaded) { - if (isUpdateDownloaded) { - confirmTextView.setText("立即安装"); - downloadedHintView.setVisibility(View.VISIBLE); - if (appEntity.isForce()) { - cancelUpdateTextView.setText("暂不安装,退出光环"); - } else { - cancelUpdateTextView.setText("暂不安装"); - } - } else { - confirmTextView.setText("立即更新"); - downloadedHintView.setVisibility(View.GONE); - if (appEntity.isForce()) { - cancelUpdateTextView.setText("暂不更新,退出光环"); - } else { - cancelUpdateTextView.setText("暂不更新"); - } - } - } - - public void setDismissCallback(EmptyCallback callback) { - mOnDismissCallback = callback; - } - - private void invokeDismissCallback() { - if (mOnDismissCallback != null && (appEntity == null || !appEntity.isForce())) { - mOnDismissCallback.onCallback(); - } - } - - private String getUpdateOnceOnlySpKey() { - return "UPDATE_ONCE_ONLY_KEY" + PackageUtils.getGhVersionCode(); - } - - private String getUpdateOnceOnlySecondSpKey() { - return "UPDATE_ONCE_ONLY_SECOND_KEY" + PackageUtils.getGhVersionCode(); - } - -} diff --git a/app/src/main/java/com/gh/gamecenter/mygame/MyFollowedGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/mygame/MyFollowedGameAdapter.kt index 44175df4b2..299b9d1671 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/MyFollowedGameAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/MyFollowedGameAdapter.kt @@ -80,7 +80,7 @@ class MyFollowedGameAdapter(context: Context, var mViewModel: MyFollowedGameView gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F - BindingAdapters.setGameName(gameName, gameEntity, false, null) + BindingAdapters.setGameName(gameName, gameEntity, false) BindingAdapters.setGameTags(labelList, gameEntity) gameRating.setDrawableStart(if (gameEntity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable() else null) gameRating.setPadding(0, 0, if (gameEntity.commentCount > 3) 8F.dip2px() else 0, 0) diff --git a/app/src/main/java/com/gh/gamecenter/mygame/MyReservationAdapter.kt b/app/src/main/java/com/gh/gamecenter/mygame/MyReservationAdapter.kt index fb1c34fedb..77c1d0a6e0 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/MyReservationAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/MyReservationAdapter.kt @@ -86,7 +86,7 @@ class MyReservationAdapter(context: Context, var mViewModel: MyReservationViewMo gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F - BindingAdapters.setGameName(gameName, gameEntity, false, null) + BindingAdapters.setGameName(gameName, gameEntity, false) BindingAdapters.setGameTags(labelList, gameEntity) gameRating.setDrawableStart( if (gameEntity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable(mContext) else null diff --git a/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameAdapter.kt b/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameAdapter.kt index c39a29991d..1af565f1f1 100644 --- a/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/mygame/PlayedGameAdapter.kt @@ -118,7 +118,7 @@ open class PlayedGameAdapter( } gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F - BindingAdapters.setGameName(gameName, gameEntity, false, null) + BindingAdapters.setGameName(gameName, gameEntity, false) BindingAdapters.setGameTags(labelList, gameEntity) gameRating.setDrawableStart(if (gameEntity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable() else null) gameRating.setPadding(0, 0, if (gameEntity.commentCount > 3) 8F.dip2px() else 0, 0) diff --git a/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailActivity.java b/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailActivity.java new file mode 100644 index 0000000000..a3c9c57b25 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailActivity.java @@ -0,0 +1,122 @@ +package com.gh.gamecenter.newsdetail; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import com.alibaba.android.arouter.facade.annotation.Route; +import com.gh.base.DownloadToolbarActivity; +import com.gh.gamecenter.R; +import com.gh.gamecenter.WebActivity; +import com.gh.gamecenter.common.constant.EntranceConsts; +import com.gh.gamecenter.common.constant.RouteConsts; +import com.gh.gamecenter.common.utils.ExtensionsKt; +import com.gh.gamecenter.core.utils.MtaHelper; +import com.gh.gamecenter.feature.entity.NewsEntity; + +import kotlin.Pair; + + +/** + * 文章详情页面 要启动该页面 需要传入一下参数 放入 EssayEntity中传过来 文章 id 文章标题 title 文章发表时间 time + * + * @author 黄壮华 + */ +@Route(path = RouteConsts.activity.newsDetailActivity) +public class NewsDetailActivity extends DownloadToolbarActivity { + + /** + * 启动新闻详情页面 + */ + public static void startNewsDetailActivity(Context context, NewsEntity newsEntity, String entrance) { + MtaHelper.onEvent("详情页面", "文章详情", newsEntity != null ? newsEntity.getTitle() : ""); + if (newsEntity != null && !TextUtils.isEmpty(newsEntity.getLink())) { + context.startActivity(WebActivity.getIntentByNews(context, newsEntity, entrance)); + } else { + Bundle bundle = new Bundle(); + bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance); + bundle.putParcelable(NewsEntity.TAG, newsEntity); + Intent intent = getTargetIntent(context, NewsDetailActivity.class, NewsDetailFragment.class, bundle); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + } + + /** + * 在收藏启动新闻详情页面 + */ + public static void startNewsDetailByCollection(Context context, NewsEntity newsEntity, String entrance) { + MtaHelper.onEvent("详情页面", "文章详情", newsEntity != null ? newsEntity.getTitle() : ""); + if (newsEntity != null && !TextUtils.isEmpty(newsEntity.getLink())) { + Intent intent = WebActivity.getIntentByNews(context, newsEntity, entrance); + context.startActivity(intent); + } else { + Bundle bundle = new Bundle(); + bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance); + bundle.putParcelable(NewsEntity.TAG, newsEntity); + bundle.putBoolean(NewsDetailFragment.KEY_COLLECTIONNEWS, true); + Intent intent = getTargetIntent(context, NewsDetailActivity.class, NewsDetailFragment.class, bundle); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + + } + + @NonNull + public static Intent getIntentById(Context context, String newsId, boolean isCollectionNews, String entrance) { + Bundle bundle = new Bundle(); + bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance); + bundle.putBoolean(NewsDetailFragment.KEY_COLLECTIONNEWS, isCollectionNews); + bundle.putString(EntranceConsts.KEY_NEWSID, newsId); + return getTargetIntent(context, NewsDetailActivity.class, NewsDetailFragment.class, bundle); + } + + @NonNull + public static Intent getIntentById(Context context, String newsId, String entrance) { + Bundle bundle = new Bundle(); + bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance); + bundle.putString(EntranceConsts.KEY_NEWSID, newsId); + return getTargetIntent(context, NewsDetailActivity.class, NewsDetailFragment.class, bundle); + } + + @Override + protected Intent provideNormalIntent() { + return getTargetIntent(this, NewsDetailActivity.class, NewsDetailFragment.class, getIntent().getExtras()); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ExtensionsKt.updateStatusBarColor(this, R.color.ui_surface, R.color.ui_surface); + } + + @Override + protected boolean showDownloadMenu() { + return true; + } + + @Override + protected void onDarkModeChanged() { + super.onDarkModeChanged(); + ExtensionsKt.updateStatusBarColor(this, R.color.ui_surface, R.color.ui_surface); + } + + @Override + public Pair getBusinessId() { + NewsEntity newsEntity = null; + Bundle bundle = getIntent().getBundleExtra(NORMAL_FRAGMENT_BUNDLE); + if (bundle != null) { + newsEntity = bundle.getParcelable(NewsEntity.TAG); + } + if (getIntent().getStringExtra(EntranceConsts.KEY_NEWSID) != null) { + return new Pair(getIntent().getStringExtra(EntranceConsts.KEY_NEWSID), ""); + } else if (newsEntity != null) { + return new Pair(newsEntity.getId(), ""); + } else { + return super.getBusinessId(); + } + } +} diff --git a/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailAdapter.java b/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailAdapter.java index befbb414b2..51c0a0b58b 100644 --- a/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailAdapter.java @@ -22,17 +22,10 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.ViewHolder; import com.gh.common.DefaultUrlHandler; -import com.gh.common.constant.Config; import com.gh.common.databind.BindingAdapters; import com.gh.common.history.HistoryHelper; import com.gh.common.util.CheckLoginUtils; import com.gh.common.util.CommentUtils; -import com.gh.gamecenter.databinding.FragmentWebWarningBinding; -import com.gh.gamecenter.databinding.GamedetailItemNewsBinding; -import com.gh.gamecenter.databinding.NewsDetailCommentBinding; -import com.gh.gamecenter.databinding.NewsdetailItemContentBinding; -import com.gh.gamecenter.databinding.NewsdetailItemGameBinding; -import com.gh.gamecenter.feature.utils.ConcernUtils; import com.gh.common.util.DataCollectionUtils; import com.gh.common.util.DialogUtils; import com.gh.common.util.DirectUtils; @@ -42,7 +35,6 @@ import com.gh.common.view.RichEditor; import com.gh.gamecenter.GameDetailActivity; import com.gh.gamecenter.ImageViewerActivity; import com.gh.gamecenter.MessageDetailActivity; -import com.gh.gamecenter.NewsDetailActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.adapter.viewholder.CommentViewHolder; import com.gh.gamecenter.adapter.viewholder.GameDetailNewsViewHolder; @@ -52,10 +44,10 @@ import com.gh.gamecenter.adapter.viewholder.UnAvaliableWebviewViewHolder; import com.gh.gamecenter.common.callback.OnRequestCallBackListener; import com.gh.gamecenter.common.constant.Constants; import com.gh.gamecenter.common.retrofit.Response; +import com.gh.gamecenter.common.utils.DarkModeUtils; import com.gh.gamecenter.common.utils.DialogHelper; import com.gh.gamecenter.common.utils.ExtensionsKt; import com.gh.gamecenter.common.utils.ImageUtils; -import com.gh.gamecenter.common.utils.DarkModeUtils; import com.gh.gamecenter.common.utils.TextHelper; import com.gh.gamecenter.common.view.DrawableView; import com.gh.gamecenter.core.utils.DisplayUtils; @@ -63,15 +55,21 @@ import com.gh.gamecenter.core.utils.MtaHelper; import com.gh.gamecenter.core.utils.NumberUtils; import com.gh.gamecenter.core.utils.RandomUtils; import com.gh.gamecenter.core.utils.StringUtils; -import com.gh.gamecenter.feature.eventbus.EBDeleteComment; +import com.gh.gamecenter.databinding.FragmentWebWarningBinding; +import com.gh.gamecenter.databinding.GamedetailItemNewsBinding; +import com.gh.gamecenter.databinding.NewsDetailCommentBinding; +import com.gh.gamecenter.databinding.NewsdetailItemContentBinding; +import com.gh.gamecenter.databinding.NewsdetailItemGameBinding; +import com.gh.gamecenter.entity.NewsDetailEntity; import com.gh.gamecenter.feature.entity.ApkEntity; import com.gh.gamecenter.feature.entity.ArticleCommentParent; import com.gh.gamecenter.feature.entity.ColorEntity; import com.gh.gamecenter.feature.entity.CommentEntity; import com.gh.gamecenter.feature.entity.CommentnumEntity; import com.gh.gamecenter.feature.entity.GameEntity; -import com.gh.gamecenter.entity.NewsDetailEntity; import com.gh.gamecenter.feature.entity.NewsEntity; +import com.gh.gamecenter.feature.eventbus.EBDeleteComment; +import com.gh.gamecenter.feature.utils.ConcernUtils; import com.gh.gamecenter.qa.editor.OnLinkClickListener; import com.gh.gamecenter.retrofit.RetrofitManager; import com.lightgame.adapter.BaseRecyclerAdapter; @@ -651,7 +649,6 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter { .subscribe(new Response>() { @Override public void onResponse(List response) { - Config.filterPluginArticle(response); // 去除与当前文章重复的文章 for (int i = 0, size = response.size(); i < size; i++) { if (mNewsDetailEntity.getId().equals(response.get(i).getId())) { diff --git a/app/src/main/java/com/gh/gamecenter/NewsDetailActivity.java b/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailFragment.java similarity index 75% rename from app/src/main/java/com/gh/gamecenter/NewsDetailActivity.java rename to app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailFragment.java index c7a663fc11..6deac012b5 100644 --- a/app/src/main/java/com/gh/gamecenter/NewsDetailActivity.java +++ b/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailFragment.java @@ -1,9 +1,8 @@ -package com.gh.gamecenter; +package com.gh.gamecenter.newsdetail; import static com.gh.gamecenter.common.constant.Constants.LOGIN_TAG; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; @@ -14,21 +13,19 @@ import android.text.TextUtils; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.View.OnClickListener; import android.view.Window; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.core.view.MotionEventCompat; import androidx.recyclerview.widget.RecyclerView; -import com.alibaba.android.arouter.facade.annotation.Route; import com.ethanhua.skeleton.Skeleton; import com.ethanhua.skeleton.ViewSkeletonScreen; -import com.gh.base.DownloadToolbarActivity; import com.gh.common.exposure.ExposureManager; import com.gh.common.filter.RegionSettingHelper; import com.gh.common.history.HistoryHelper; @@ -38,13 +35,17 @@ import com.gh.common.util.DataCollectionUtils; import com.gh.common.util.DetailDownloadUtils; import com.gh.common.util.NewLogUtils; import com.gh.download.DownloadManager; +import com.gh.gamecenter.ImageViewerActivity; +import com.gh.gamecenter.MessageDetailActivity; +import com.gh.gamecenter.R; import com.gh.gamecenter.adapter.viewholder.DetailViewHolder; -import com.gh.gamecenter.common.callback.OnRequestCallBackListener; +import com.gh.gamecenter.common.base.activity.BaseActivity; +import com.gh.gamecenter.common.base.fragment.ToolbarFragment; import com.gh.gamecenter.common.constant.Constants; import com.gh.gamecenter.common.constant.EntranceConsts; -import com.gh.gamecenter.common.constant.RouteConsts; import com.gh.gamecenter.common.eventbus.EBNetworkState; import com.gh.gamecenter.common.eventbus.EBReuse; +import com.gh.gamecenter.common.exposure.ExposureSource; import com.gh.gamecenter.common.retrofit.Response; import com.gh.gamecenter.common.utils.ExtensionsKt; import com.gh.gamecenter.common.utils.ShareUtils; @@ -54,24 +55,21 @@ import com.gh.gamecenter.core.utils.ClickUtils; import com.gh.gamecenter.core.utils.DisplayUtils; import com.gh.gamecenter.core.utils.MtaHelper; import com.gh.gamecenter.entity.NewsDetailEntity; -import com.gh.gamecenter.feature.eventbus.EBConcernChanged; -import com.gh.gamecenter.feature.eventbus.EBAddComment; -import com.gh.gamecenter.feature.eventbus.EBDeleteComment; import com.gh.gamecenter.eventbus.EBDownloadStatus; import com.gh.gamecenter.eventbus.EBPackage; import com.gh.gamecenter.feature.entity.GameEntity; import com.gh.gamecenter.feature.entity.MeEntity; import com.gh.gamecenter.feature.entity.NewsEntity; +import com.gh.gamecenter.feature.eventbus.EBAddComment; +import com.gh.gamecenter.feature.eventbus.EBConcernChanged; +import com.gh.gamecenter.feature.eventbus.EBDeleteComment; import com.gh.gamecenter.feature.exposure.ExposureEvent; -import com.gh.gamecenter.common.exposure.ExposureSource; import com.gh.gamecenter.feature.exposure.ExposureType; import com.gh.gamecenter.feature.utils.ApkActiveUtils; -import com.gh.gamecenter.newsdetail.NewsDetailAdapter; import com.gh.gamecenter.retrofit.RetrofitManager; import com.lightgame.download.DataWatcher; import com.lightgame.download.DownloadEntity; import com.lightgame.download.DownloadStatus; -import com.lightgame.utils.Utils; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -81,18 +79,9 @@ import java.util.HashSet; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; -import kotlin.Pair; import retrofit2.HttpException; - -/** - * 文章详情页面 要启动该页面 需要传入一下参数 放入 EssayEntity中传过来 文章 id 文章标题 title 文章发表时间 time - * - * @author 黄壮华 - */ -@Route(path = RouteConsts.activity.newsDetailActivity) -public class NewsDetailActivity extends DownloadToolbarActivity implements OnClickListener, OnRequestCallBackListener { - +public class NewsDetailFragment extends ToolbarFragment { RecyclerView mDetailRv; LinearLayout mDetailBottomLl; LinearLayout mReuseLoading; @@ -111,7 +100,7 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli private NewsDetailAdapter adapter; - private static final String KEY_COLLECTIONNEWS = "isCollectionNews"; + public static final String KEY_COLLECTIONNEWS = "isCollectionNews"; private boolean isSentReport = false; private boolean isSecondDown = false; @@ -152,7 +141,7 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli DetailDownloadUtils.updateViewHolder(getDetailViewHolder()); } } - } else if (gameEntity != null && gameEntity.getApk().size() > 1) { + } else if (gameEntity != null && gameEntity.getApk().size() > 1 && gameEntity.getId().equals(downloadEntity.getGameId())) { if (DownloadStatus.downloading.equals(downloadEntity.getStatus())) { if (getDetailViewHolder().getDownloadTips().getVisibility() == View.GONE || !getDetailViewHolder().getDownloadTips().isAnimating()) { getDetailViewHolder().getDownloadTips().setVisibility(View.VISIBLE); @@ -197,7 +186,7 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli private DetailViewHolder getDetailViewHolder() { // 每次获取需要重新创建, 防止数据刷新 return new DetailViewHolder( - mContentView, + mCachedView, gameEntity, true, mEntrance, @@ -207,61 +196,13 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli false); // 下载按钮ViewHolder } - /** - * 启动新闻详情页面 - */ - public static void startNewsDetailActivity(Context context, NewsEntity newsEntity, String entrance) { - MtaHelper.onEvent("详情页面", "文章详情", newsEntity != null ? newsEntity.getTitle() : ""); - if (newsEntity != null && !TextUtils.isEmpty(newsEntity.getLink())) { - context.startActivity(WebActivity.getIntentByNews(context, newsEntity, entrance)); - } else { - Intent intent = new Intent(context, NewsDetailActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance); - intent.putExtra(NewsEntity.TAG, newsEntity); - context.startActivity(intent); - } - } - - - /** - * 在收藏启动新闻详情页面 - */ - public static void startNewsDetailByCollection(Context context, NewsEntity newsEntity, String entrance) { - MtaHelper.onEvent("详情页面", "文章详情", newsEntity != null ? newsEntity.getTitle() : ""); - if (newsEntity != null && !TextUtils.isEmpty(newsEntity.getLink())) { - Intent intent = WebActivity.getIntentByNews(context, newsEntity, entrance); - context.startActivity(intent); - } else { - Intent intent = new Intent(context, NewsDetailActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance); - intent.putExtra(NewsEntity.TAG, newsEntity); - intent.putExtra(KEY_COLLECTIONNEWS, true); - context.startActivity(intent); - } - - } - - @NonNull - public static Intent getIntentById(Context context, String newsId, boolean isCollectionNews, String entrance) { - Intent intent = new Intent(context, NewsDetailActivity.class); - intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance); - intent.putExtra(KEY_COLLECTIONNEWS, isCollectionNews); - intent.putExtra(EntranceConsts.KEY_NEWSID, newsId); - return intent; - } - - @NonNull - public static Intent getIntentById(Context context, String newsId, String entrance) { - Intent intent = new Intent(context, NewsDetailActivity.class); - intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance); - intent.putExtra(EntranceConsts.KEY_NEWSID, newsId); - return intent; + @Override + protected int getLayoutId() { + return R.layout.fragment_news_detail; } @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { + public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == ImageViewerActivity.REQUEST_FOR_VIEWED_IMAGE && resultCode == Activity.RESULT_OK) { HashSet imageSet = (HashSet) data.getExtras().get(ImageViewerActivity.VIEWED_IMAGE); @@ -269,29 +210,22 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli } } - @Override - protected int getLayoutId() { - return R.layout.activity_news_detail; - } + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - ExtensionsKt.updateStatusBarColor(this, R.color.ui_surface, R.color.ui_surface); - - mDetailRv = findViewById(R.id.news_detail_rv_show); - mDetailBottomLl = findViewById(R.id.news_detail_ll_bottom); - mReuseLoading = findViewById(R.id.reuse_ll_loading); - mNoConn = findViewById(R.id.reuse_no_connection); - mNoneData = findViewById(R.id.reuse_none_data); - mExceptionDataView = findViewById(R.id.reuse_data_exception); - mNoneDataTv = findViewById(R.id.reuseNoneDataTv); - mDetailCommentLl = findViewById(R.id.news_detail_ll_coment); - mSkeletonView = findViewById(R.id.news_skeleton); - mCommentContainer = findViewById(R.id.commentContainer); - mDownloadContainer = findViewById(R.id.detail_ll_bottom); - mBottomContainer = findViewById(R.id.bottomContainer); + mDetailRv = view.findViewById(R.id.news_detail_rv_show); + mDetailBottomLl = view.findViewById(R.id.news_detail_ll_bottom); + mReuseLoading = view.findViewById(R.id.reuse_ll_loading); + mNoConn = view.findViewById(R.id.reuse_no_connection); + mNoneData = view.findViewById(R.id.reuse_none_data); + mExceptionDataView = view.findViewById(R.id.reuse_data_exception); + mNoneDataTv = view.findViewById(R.id.reuseNoneDataTv); + mDetailCommentLl = view.findViewById(R.id.news_detail_ll_coment); + mSkeletonView = view.findViewById(R.id.news_skeleton); + mCommentContainer = view.findViewById(R.id.commentContainer); + mDownloadContainer = view.findViewById(R.id.detail_ll_bottom); + mBottomContainer = view.findViewById(R.id.bottomContainer); mViewSkeletonScreen = Skeleton.bind(mSkeletonView).shimmer(false).load(R.layout.news_detail_skeleton).show(); // init toolbar @@ -300,31 +234,37 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli // init RecyclerView mDetailRv.setHasFixedSize(true); - mDetailRv.setLayoutManager(new FixLinearLayoutManager(this)); - mItemDecoration = new VerticalItemDecoration(this, 8, false); + mDetailRv.setLayoutManager(new FixLinearLayoutManager(requireContext())); + mItemDecoration = new VerticalItemDecoration(requireContext(), 8, false); mDetailRv.addItemDecoration(mItemDecoration); - mHideUselessInfo = getIntent().getBooleanExtra(EntranceConsts.KEY_HIDE_USELESS_INFO, false); + mHideUselessInfo = requireArguments().getBoolean(EntranceConsts.KEY_HIDE_USELESS_INFO, false); - setToolbarMenu(R.menu.menu_news_detail); - mNewsShare = getMenuItem(R.id.menu_share); - mNewsCollection = getMenuItem(R.id.menu_collect); - mNewsShare.setVisible(false); - mNewsCollection.setVisible(false); + initMenu(R.menu.menu_news_detail); + mNewsShare = getItemMenu(R.id.menu_share); + mNewsCollection = getItemMenu(R.id.menu_collect); + if (mNewsShare != null) { + mNewsShare.setVisible(false); + } + if (mNewsCollection != null) { + mNewsCollection.setVisible(false); + } if (mHideUselessInfo) { - getMenuItem(R.id.menu_download).setVisible(false); + if (getItemMenu(R.id.menu_download) != null) { + getItemMenu(R.id.menu_download).setVisible(false); + } mCommentContainer.setVisibility(View.GONE); } - adapter = new NewsDetailAdapter(this, this, mHideUselessInfo, mEntrance); + adapter = new NewsDetailAdapter(requireContext(), this, mHideUselessInfo, mEntrance); mDetailRv.setAdapter(adapter); - newsId = getIntent().getStringExtra(EntranceConsts.KEY_NEWSID); - mIsCollectionNews = getIntent().getBooleanExtra(KEY_COLLECTIONNEWS, false); + newsId = requireArguments().getString(EntranceConsts.KEY_NEWSID); + mIsCollectionNews = requireArguments().getBoolean(KEY_COLLECTIONNEWS, false); if (newsId == null) { - mNewsEntity = getIntent().getParcelableExtra(NewsEntity.TAG); + mNewsEntity = requireArguments().getParcelable(NewsEntity.TAG); if (mNewsEntity != null) { if (mNewsEntity.getType() != null) { setNavigationTitle(mNewsEntity.getType()); @@ -336,7 +276,9 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli adapter.setTitle(mNewsEntity.getTitle()); adapter.getNewsDetail(); if (!mHideUselessInfo) { - mNewsShare.setVisible(true); + if (mNewsShare != null) { + mNewsShare.setVisible(true); + } setTitleCenter(); } } @@ -346,9 +288,9 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli start = System.currentTimeMillis(); - View view = getWindow().findViewById(Window.ID_ANDROID_CONTENT); - if (view != null) { - view.setOnClickListener(v -> { + View contentView = requireActivity().getWindow().findViewById(Window.ID_ANDROID_CONTENT); + if (contentView != null) { + contentView.setOnClickListener(v -> { System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1); mHits[mHits.length - 1] = SystemClock.uptimeMillis(); if (mHits[0] >= (SystemClock.uptimeMillis() - 1000)) { @@ -357,7 +299,7 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli }); } - sp = PreferenceManager.getDefaultSharedPreferences(this); + sp = PreferenceManager.getDefaultSharedPreferences(requireContext()); fontsize = sp.getInt(Constants.SP_FONT_SIZE, 1); if (fontsize == 0) { fontsize = 1; @@ -368,11 +310,11 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli } @Override - public boolean onMenuItemClick(MenuItem item) { + public void onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.menu_share: if (adapter != null && adapter.getNewsDetailEntity() != null && !ClickUtils.isFastDoubleClick(R.id.menu_question_post)) { - DataCollectionUtils.uploadClick(NewsDetailActivity.this, "分享", "新闻详情" + DataCollectionUtils.uploadClick(requireContext(), "分享", "新闻详情" , adapter.getNewsDetailEntity().getTitle()); String url = getString(R.string.share_news_article_url, adapter.getNewsDetailEntity().getShortId()); @@ -383,14 +325,16 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli } else { shareIcon = gameEntity.getIcon(); } - showShare(url, shareIcon, adapter.getNewsDetailEntity().getTitle(), + ((BaseActivity) requireActivity()).showShare(url, shareIcon, adapter.getNewsDetailEntity().getTitle(), "来自光环助手(最强卡牌神器)", ShareUtils.ShareEntrance.news, adapter.getNewsDetailEntity().getId()); } break; case R.id.menu_collect: - CheckLoginUtils.checkLogin(this, "资讯文章详情-收藏", () -> { + CheckLoginUtils.checkLogin(requireContext(), "资讯文章详情-收藏", () -> { final NewsDetailEntity newsDetailEntity = adapter.getNewsDetailEntity(); - mNewsCollection.setEnabled(false); + if (mNewsCollection != null) { + mNewsCollection.setEnabled(false); + } if (newsDetailEntity.getMe() != null && newsDetailEntity.getMe().isArticleFavorite()) { CollectionUtils.INSTANCE.deleteCollection(mNewsEntity.getId(), CollectionUtils.CollectionType.ARTICLE, @@ -398,14 +342,18 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli @Override public void onSuccess() { newsDetailEntity.getMe().setArticleFavorite(false); - mNewsCollection.setEnabled(true); - mNewsCollection.setIcon(R.drawable.community_content_detail_collect_unselect); + if (mNewsCollection != null) { + mNewsCollection.setEnabled(true); + mNewsCollection.setIcon(R.drawable.community_content_detail_collect_unselect); + } toast(R.string.collection_cancel); } @Override public void onError() { - mNewsCollection.setEnabled(true); + if (mNewsCollection != null) { + mNewsCollection.setEnabled(true); + } toast(R.string.collection_cancel_failure); } }); @@ -422,14 +370,18 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli newsDetailEntity.setMe(userData); } userData.setArticleFavorite(true); - mNewsCollection.setEnabled(true); - mNewsCollection.setIcon(R.drawable.community_content_detail_collect_select); + if (mNewsCollection != null) { + mNewsCollection.setEnabled(true); + mNewsCollection.setIcon(R.drawable.community_content_detail_collect_select); + } toast(R.string.collection_success); } @Override public void onError() { - mNewsCollection.setEnabled(true); + if (mNewsCollection != null) { + mNewsCollection.setEnabled(true); + } toast(R.string.collection_failure); } }); @@ -437,18 +389,17 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli }); break; } - return super.onMenuItemClick(item); } @Override - protected void onDestroy() { + public void onDestroy() { super.onDestroy(); handler.removeCallbacksAndMessages(null); DownloadManager.getInstance().removeObserver(dataWatcher); } @Override - protected void onPause() { + public void onPause() { super.onPause(); if (!isSentReport) { long end = System.currentTimeMillis(); @@ -456,7 +407,7 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli int seconds = (int) ((end - start) / 1000); if (adapter.getNewsDetailEntity() != null && seconds > 0) { - DataCollectionUtils.uploadNews(this, adapter.getNewsDetailEntity(), gameEntity, seconds, mEntrance); + DataCollectionUtils.uploadNews(requireContext(), adapter.getNewsDetailEntity(), gameEntity, seconds, mEntrance); } isSentReport = true; @@ -464,7 +415,7 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli } @Override - protected void onResume() { + public void onResume() { super.onResume(); if (gameEntity != null && (gameEntity.getApk().size() == 1 || gameEntity.isReservable())) { @@ -476,13 +427,15 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli @Override public void loadDone() { // 通知更新收藏按钮 if (!mHideUselessInfo) { - mNewsCollection.setVisible(true); setTitleCenter(); NewsDetailEntity newsDetailEntity = adapter.getNewsDetailEntity(); - if (newsDetailEntity.getMe() != null && newsDetailEntity.getMe().isArticleFavorite()) { - mNewsCollection.setIcon(R.drawable.community_content_detail_collect_select); - } else { - mNewsCollection.setIcon(R.drawable.community_content_detail_collect_unselect); + if (mNewsCollection != null) { + mNewsCollection.setVisible(true); + if (newsDetailEntity.getMe() != null && newsDetailEntity.getMe().isArticleFavorite()) { + mNewsCollection.setIcon(R.drawable.community_content_detail_collect_select); + } else { + mNewsCollection.setIcon(R.drawable.community_content_detail_collect_unselect); + } } } } @@ -553,7 +506,9 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli adapter.getNewsDetail(); if (!mHideUselessInfo) { - mNewsShare.setVisible(true); + if (mNewsShare != null) { + mNewsShare.setVisible(true); + } setTitleCenter(); } @@ -583,9 +538,8 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli adapter.getNewsCommentNum(); } - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { + public void onTouchEvent(MotionEvent ev) { switch (ev.getAction() & MotionEventCompat.ACTION_MASK) { case MotionEvent.ACTION_DOWN: isSecondDown = false; @@ -608,20 +562,20 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli adapter.setFontSize(fontsize); sp.edit().putInt(Constants.SP_FONT_SIZE, fontsize).apply(); String fontSizeText = getFontSize(fontsize); - Utils.toast(this, fontSizeText); + toast(fontSizeText); } if ((R11 - R12) < -scrollSize && fontsize < 4) { fontsize++; adapter.setFontSize(fontsize); sp.edit().putInt(Constants.SP_FONT_SIZE, fontsize).apply(); String fontSizeText = getFontSize(fontsize); - Utils.toast(this, fontSizeText); + toast(fontSizeText); } } break; case MotionEvent.ACTION_MOVE: if (isSecondDown) { - return true; + return; } break; case MotionEventCompat.ACTION_POINTER_DOWN: @@ -642,9 +596,9 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli X22 = MotionEventCompat.getX(ev, pointerIndex21); break; } - return super.dispatchTouchEvent(ev); } + @Override public void onClick(final View v) { if (v == mNoConn) { @@ -653,8 +607,8 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli mNoConn.setVisibility(View.GONE); handler.postDelayed(runnable, 1000); } else if (v == mDetailCommentLl) { - CheckLoginUtils.checkLogin(this, "资讯文章详情-写评论", () -> { - Intent intent = MessageDetailActivity.getIntentById(NewsDetailActivity.this, adapter.getId(), null, true + CheckLoginUtils.checkLogin(requireContext(), "资讯文章详情-写评论", () -> { + Intent intent = MessageDetailActivity.getIntentById(requireContext(), adapter.getId(), null, true , mEntrance + "(新闻详情[" + adapter.getTitle() + "])"); startActivity(intent); }); @@ -692,6 +646,7 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli .subscribe(new Response() { @Override public void onResponse(GameEntity response) { + if (!isAdded()) return; ApkActiveUtils.filterHideApk(response); gameEntity = response; @@ -725,6 +680,12 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli private void logExposure() { String sourceValue = mNewsEntity.getTitle() + "+" + mNewsEntity.getId(); ArrayList sourceList = new ArrayList<>(); + if (getArguments() != null) { + ArrayList basicSourceList = getArguments().getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST); + if (basicSourceList != null) { + sourceList.addAll(basicSourceList); + } + } sourceList.add(new ExposureSource("资讯文章", sourceValue)); mExposureEvent = ExposureEvent.createEvent( gameEntity, @@ -736,7 +697,7 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli } @Override - protected void onStop() { + public void onStop() { super.onStop(); if (mEntrance.contains("板块成员") || mEntrance.contains("论坛详情")) { long stayTime = (System.currentTimeMillis() - startPageTime) / 1000; @@ -763,7 +724,7 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli if (mNoConn.getVisibility() == View.VISIBLE) { mDetailRv.setVisibility(View.VISIBLE); mReuseLoading.setVisibility(View.VISIBLE); - mDetailRv.setPadding(0, 0, 0, DisplayUtils.dip2px(getApplicationContext(), 60)); + mDetailRv.setPadding(0, 0, 0, DisplayUtils.dip2px(requireContext(), 60)); mNoConn.setVisibility(View.GONE); handler.postDelayed(runnable, 1000); } @@ -799,38 +760,26 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli } } - @Override - protected boolean showDownloadMenu() { - return true; - } - @Override protected void onDarkModeChanged() { super.onDarkModeChanged(); - ExtensionsKt.updateStatusBarColor(this, R.color.ui_surface, R.color.ui_surface); - mNewsShare.setIcon(R.drawable.icon_share_black); - mNewsCollection.setIcon(R.drawable.community_content_detail_collect_unselect); + if (mNewsShare != null) { + mNewsShare.setIcon(R.drawable.icon_share_black); + } + if (mNewsCollection != null) { + mNewsCollection.setIcon(R.drawable.community_content_detail_collect_unselect); + } mDetailRv.removeItemDecoration(mItemDecoration); - mItemDecoration = new VerticalItemDecoration(this, 8, false); + mItemDecoration = new VerticalItemDecoration(requireContext(), 8, false); mDetailRv.addItemDecoration(mItemDecoration); - if (mContentView != null) { - mContentView.setBackgroundColor(ContextCompat.getColor(this, R.color.ui_background)); - findViewById(R.id.bottomContainer).setBackgroundColor(ContextCompat.getColor(this, R.color.ui_surface)); - ((TextView) findViewById(R.id.commentTv)).setTextColor(ContextCompat.getColor(this, R.color.text_secondary)); + if (mCachedView != null) { + mCachedView.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.ui_background)); + mCachedView.findViewById(R.id.bottomContainer).setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.ui_surface)); + ((TextView) mCachedView.findViewById(R.id.commentTv)).setTextColor(ContextCompat.getColor(requireContext(), R.color.text_secondary)); } if (adapter != null) { adapter.notifyItemRangeChanged(0, adapter.getItemCount()); } } - @Override - public Pair getBusinessId() { - if (getIntent().getStringExtra(EntranceConsts.KEY_NEWSID) != null) { - return new Pair(getIntent().getStringExtra(EntranceConsts.KEY_NEWSID), ""); - } else if (mNewsEntity != null) { - return new Pair(mNewsEntity.getId(), ""); - } else { - return super.getBusinessId(); - } - } } diff --git a/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt b/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt index 39993bb367..9844178cf2 100644 --- a/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt +++ b/app/src/main/java/com/gh/gamecenter/packagehelper/PackageRepository.kt @@ -3,6 +3,7 @@ package com.gh.gamecenter.packagehelper import android.annotation.SuppressLint import android.os.Build import android.text.TextUtils +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.gh.common.filter.RegionSettingHelper import com.gh.gamecenter.feature.utils.ApkActiveUtils @@ -21,6 +22,7 @@ import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.entity.* import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.GameInstall +import com.gh.gamecenter.livedata.Event import com.gh.gamecenter.manager.PackagesManager import com.gh.gamecenter.login.user.UserManager import com.gh.gamecenter.retrofit.RetrofitManager @@ -66,6 +68,12 @@ object PackageRepository { val gameUpdate = ArrayList() val currentVersionList = ArrayList() // 当前版本游戏的列表 + private val _recentVaPlayedChanged = MutableLiveData() + val recentVaPlayedChanged: LiveData = _recentVaPlayedChanged + fun changeRecentVaPlayed() { + _recentVaPlayedChanged.postValue(Unit) + } + /** * 预留方法,如果想手动初始化可以调用 */ @@ -331,8 +339,9 @@ object PackageRepository { && !TextUtils.isEmpty(apk.ghVersion) && !PackageUtils.isSignedByGh(HaloApp.getInstance().application, apk.packageName) ) { - // 使用了镜像的游戏无需插件化 - if (game.shouldUseMirrorInfo()) { + // 使用了镜像的游戏;插件化关闭的游戏;无需插件化 + if (game.shouldUseMirrorInfo() + || apk.plugin == "close") { return false } @@ -368,7 +377,8 @@ object PackageRepository { for (entity in gameUpdate) { if (entity.packageName == data.packageName && entity.id == data.id - && entity.isVGameUpdate == data.isVGameUpdate) { + && entity.isVGameUpdate == data.isVGameUpdate + ) { isExist = true } } @@ -399,6 +409,7 @@ object PackageRepository { updateFilterPackage(list) { loadInstalledGameDigestAndNotifyData(list, true) } + changeRecentVaPlayed() } /** @@ -415,6 +426,7 @@ object PackageRepository { updateFilterPackage(pkgNameList) { loadInstalledGameDigestAndNotifyData(pkgNameList, true, isVGame) } + changeRecentVaPlayed() } /** @@ -444,7 +456,8 @@ object PackageRepository { val game = gameUpdate[j] // 仅类型一致时移除更新实体 if (game.packageName == pkgName - && game.isVGameUpdate == isVGame) { + && game.isVGameUpdate == isVGame + ) { gameUpdate.remove(game) notifyGameUpdateData() } else { @@ -466,6 +479,7 @@ object PackageRepository { } } notifyGameInstallData() + changeRecentVaPlayed() } private fun notifyGameInstallData() { diff --git a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalBannerAdapter.kt b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalBannerAdapter.kt index 7c72d55dda..1345097447 100644 --- a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalBannerAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalBannerAdapter.kt @@ -19,7 +19,7 @@ import com.gh.gamecenter.login.user.UserManager import com.gh.gamecenter.setting.SettingBridge import com.gh.gamecenter.simulatorgame.SimulatorGameActivity import com.gh.gamecenter.teenagermode.TeenagerModeActivity -import com.gh.gamecenter.toolbox.ToolBoxBlockActivity +import com.gh.gamecenter.toolbox.ToolBoxActivity import com.gh.gamecenter.video.videomanager.VideoManagerActivity import com.lightgame.adapter.BaseRecyclerAdapter @@ -167,7 +167,7 @@ class HaloPersonalBannerAdapter(context: Context) : BaseRecyclerAdapter { DataCollectionUtils.uploadClick(mContext, "工具箱", "发现") - mContext.startActivity(ToolBoxBlockActivity.getIntent(mContext, "(发现:工具箱)")) + mContext.startActivity(ToolBoxActivity.getIntent(mContext, "(发现:工具箱)")) } "安装包清理" -> { diff --git a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFragment.kt b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFragment.kt index 0360aaa64b..95a105818f 100644 --- a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFragment.kt @@ -20,13 +20,19 @@ import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 import com.gh.common.constant.Config import com.gh.common.databind.BindingAdapters +import com.gh.common.iinterface.ISuperiorChain import com.gh.common.prioritychain.AccelerateNotificationHandler +import com.gh.common.prioritychain.PriorityChain import com.gh.common.util.* import com.gh.common.util.LogUtils import com.gh.common.util.NewFlatLogUtils import com.gh.common.util.NewLogUtils -import com.gh.gamecenter.* +import com.gh.gamecenter.CollectionActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.ShareGhActivity +import com.gh.gamecenter.WebActivity import com.gh.gamecenter.common.base.fragment.BaseLazyFragment +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.eventbus.EBNetworkState import com.gh.gamecenter.common.eventbus.EBReuse import com.gh.gamecenter.common.retrofit.ApiResponse @@ -39,8 +45,6 @@ import com.gh.gamecenter.databinding.FragmentPersonalStubBinding import com.gh.gamecenter.entity.BadgeEntity import com.gh.gamecenter.feature.entity.Badge import com.gh.gamecenter.feature.eventbus.EBConcernChanged -import com.gh.gamecenter.fragment.MainWrapperFragment -import com.gh.gamecenter.fragment.MainWrapperViewModel import com.gh.gamecenter.gamecollection.mine.MyGameCollectionActivity import com.gh.gamecenter.history.HistoryActivity import com.gh.gamecenter.login.entity.UserInfoEntity @@ -54,18 +58,17 @@ import com.gh.gamecenter.mypost.MyPostActivity import com.gh.gamecenter.personalhome.UserHomeViewModel import com.gh.gamecenter.room.AppDatabase import com.gh.gamecenter.setting.SettingBridge +import com.gh.gamecenter.wrapper.MainWrapperFragment +import com.gh.gamecenter.wrapper.MainWrapperViewModel import com.gh.vspace.IExternalGamesUsage import com.google.android.material.appbar.AppBarLayout import com.halo.assistant.HaloApp import com.jakewharton.rxbinding2.view.RxView -import com.lightgame.utils.Utils -import com.tencent.connect.common.Constants import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import splitties.views.dsl.core.endMargin import java.lang.ref.WeakReference -import java.util.* import java.util.concurrent.TimeUnit import kotlin.math.abs @@ -94,6 +97,9 @@ class HaloPersonalFragment : BaseLazyFragment() { private var mIsUpdate = false private var mIsLogging = false + private val mPriorityChain by lazy { PriorityChain() } + private var mSuperiorChain: ISuperiorChain? = null + override fun getInflatedLayout(): View { mBinding = FragmentPersonalStubBinding.inflate(layoutInflater, null, false) mBinding.stub.setOnInflateListener { _, inflateId -> @@ -107,7 +113,7 @@ class HaloPersonalFragment : BaseLazyFragment() { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { - Constants.REQUEST_LOGIN -> { + com.tencent.connect.common.Constants.REQUEST_LOGIN -> { LoginHelper.onQQLoginCallback(requestCode, resultCode, data) } 32973 -> { @@ -286,6 +292,7 @@ class HaloPersonalFragment : BaseLazyFragment() { super.onFragmentFirstVisible() inflateRealView() observeUIRelatedChanges() + buildPriorityChain() } override fun onFragmentResume() { @@ -297,10 +304,19 @@ class HaloPersonalFragment : BaseLazyFragment() { mUserHomeViewModel.availableBadges() mMainWrapperViewModel.requestAccelerateNotificationPopup() } - val currentItem = (parentFragment as MainWrapperFragment).currentItem - if (currentItem == MainWrapperFragment.INDEX_PERSONAL) { + val currentTab = (parentFragment as? MainWrapperFragment)?.currentTab + if (currentTab?.link?.type == ViewPagerFragmentHelper.TYPE_MY_HALO) { + DisplayUtils.transparentStatusBar(requireActivity()) DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn) } + + mSuperiorChain?.registerInferiorChain(mPriorityChain) + } + + override fun onFragmentPause() { + super.onFragmentPause() + + mSuperiorChain?.unregisterInferiorChain(mPriorityChain) } @SuppressLint("CheckResult", "SetTextI18n") @@ -455,28 +471,6 @@ class HaloPersonalFragment : BaseLazyFragment() { } } - mMainWrapperViewModel.accelerateNotificationPopup.observeNonNull(viewLifecycleOwner) { - if (parentFragment is MainWrapperFragment && isSupportVisible) { - val isHandlerQueueEmpty = (parentFragment as MainWrapperFragment).isPriorityChainHandlerQueueEmpty() - val accelerateSet = - HashSet(SPUtils.getStringSet(com.gh.gamecenter.common.constant.Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET)) - if (isHandlerQueueEmpty && it.isNotEmpty() && !accelerateSet.contains(it[0].messageId)) { - AccelerateNotificationHandler.showAccelerateNotificationPopupWindow( - requireActivity(), - mMainWrapperViewModel, - mBaseHandler as BaseHandler, - it[0], - null - ) - accelerateSet.add(it[0].messageId) - SPUtils.setStringSet( - com.gh.gamecenter.common.constant.Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET, - accelerateSet - ) - } - } - } - // 微信/签到 RxView.clicks(mStubBinding.personalSign) .throttleFirst(1, TimeUnit.SECONDS) @@ -522,6 +516,39 @@ class HaloPersonalFragment : BaseLazyFragment() { } } + private fun buildPriorityChain() { + val accelerateNotificationHandler = AccelerateNotificationHandler(4) + + mMainWrapperViewModel.accelerateNotificationPopup.observe(this) { + if (mPriorityChain.isHandlerQueueEmpty()) { + if (it == null) return@observe + + val accelerateSet = + HashSet(SPUtils.getStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET)) + if (it.isNotEmpty() && !accelerateSet.contains(it[0].messageId)) { + AccelerateNotificationHandler.showAccelerateNotificationPopupWindow( + requireActivity(), + mMainWrapperViewModel, + mBaseHandler as BaseHandler, + it[0], + null + ) + accelerateSet.add(it[0].messageId) + SPUtils.setStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET, accelerateSet) + } + } else { + accelerateNotificationHandler.doPreProcess( + requireActivity(), + mBaseHandler!! as BaseHandler, + it, + mMainWrapperViewModel + ) + } + } + + mPriorityChain.addHandler(accelerateNotificationHandler) + } + private fun getSignSkipText(text: String): String = if (TextUtils.isEmpty(text)) getString(R.string.go_to_home) else text @@ -609,7 +636,7 @@ class HaloPersonalFragment : BaseLazyFragment() { mStubBinding.darkModeIv.setImageResource(if (mIsDarkModeOn) R.drawable.ic_personal_light_mode else R.drawable.ic_personal_dark_mode) mStubBinding.ivArrow.enlargeTouchArea() mStubBinding.loginMessageHint.typeface = - Typeface.createFromAsset(requireContext().assets, "fonts/d_din_bold_only_number.ttf") + Typeface.createFromAsset(requireContext().assets, Constants.DIN_FONT_PATH) mStubBinding.motionLayout.setOnClickListener(this) mStubBinding.toolbarContainer.setOnClickListener(this) @@ -721,8 +748,8 @@ class HaloPersonalFragment : BaseLazyFragment() { root.setOnClickListener { NewFlatLogUtils.logHaloSelfClick("其他功能", getString(R.string.setting_privacy_policy)) SPUtils.setString( - com.gh.gamecenter.common.constant.Constants.SP_PRIVACY_MD5, - SPUtils.getString(com.gh.gamecenter.common.constant.Constants.SP_PRIVACY_CURRENT_MD5) + Constants.SP_PRIVACY_MD5, + SPUtils.getString(Constants.SP_PRIVACY_CURRENT_MD5) ) redDot.visibility = if (checkPrivacyIsSame()) View.GONE else View.VISIBLE startActivity( @@ -874,8 +901,8 @@ class HaloPersonalFragment : BaseLazyFragment() { } private fun checkPrivacyIsSame(): Boolean { - val currentMd5 = SPUtils.getString(com.gh.gamecenter.common.constant.Constants.SP_PRIVACY_CURRENT_MD5) - val privacyMd5 = SPUtils.getString(com.gh.gamecenter.common.constant.Constants.SP_PRIVACY_MD5) + val currentMd5 = SPUtils.getString(Constants.SP_PRIVACY_CURRENT_MD5) + val privacyMd5 = SPUtils.getString(Constants.SP_PRIVACY_MD5) return currentMd5 == privacyMd5 } @@ -936,6 +963,11 @@ class HaloPersonalFragment : BaseLazyFragment() { } } + fun setSuperiorChain(superiorChain: ISuperiorChain?): HaloPersonalFragment { + this.mSuperiorChain = superiorChain + return this + } + override fun onDarkModeChanged() { super.onDarkModeChanged() if (isSupportVisible) DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn) diff --git a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFunctionAdapter.kt b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFunctionAdapter.kt index 8cd587086b..0ad9ba3ce7 100644 --- a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFunctionAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFunctionAdapter.kt @@ -22,7 +22,7 @@ import com.gh.gamecenter.savegame.GameArchiveListActivity import com.gh.gamecenter.setting.SettingBridge import com.gh.gamecenter.simulatorgame.SimulatorGameActivity import com.gh.gamecenter.teenagermode.TeenagerModeActivity -import com.gh.gamecenter.toolbox.ToolBoxBlockActivity +import com.gh.gamecenter.toolbox.ToolBoxActivity import com.gh.gamecenter.video.videomanager.VideoManagerActivity import com.lightgame.adapter.BaseRecyclerAdapter @@ -155,7 +155,7 @@ class HaloPersonalFunctionAdapter(context: Context) : BaseRecyclerAdapter { DataCollectionUtils.uploadClick(mContext, "工具箱", "发现") - mContext.startActivity(ToolBoxBlockActivity.getIntent(mContext, "(发现:工具箱)")) + mContext.startActivity(ToolBoxActivity.getIntent(mContext, "(发现:工具箱)")) } "安装包清理" -> { diff --git a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalRecommendAdapter.kt b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalRecommendAdapter.kt index 0dd67ce242..7f28d13079 100644 --- a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalRecommendAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalRecommendAdapter.kt @@ -23,7 +23,7 @@ import com.gh.gamecenter.savegame.GameArchiveListActivity import com.gh.gamecenter.setting.SettingBridge import com.gh.gamecenter.simulatorgame.SimulatorGameActivity import com.gh.gamecenter.teenagermode.TeenagerModeActivity -import com.gh.gamecenter.toolbox.ToolBoxBlockActivity +import com.gh.gamecenter.toolbox.ToolBoxActivity import com.gh.gamecenter.video.videomanager.VideoManagerActivity import com.lightgame.adapter.BaseRecyclerAdapter @@ -189,7 +189,7 @@ class HaloPersonalRecommendAdapter(val context: Context) : BaseRecyclerAdapter { DataCollectionUtils.uploadClick(mContext, "工具箱", "发现") - mContext.startActivity(ToolBoxBlockActivity.getIntent(mContext, "(发现:工具箱)")) + mContext.startActivity(ToolBoxActivity.getIntent(mContext, "(发现:工具箱)")) } "安装包清理" -> { diff --git a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalViewModel.kt b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalViewModel.kt index b297a3ca30..4478065f2d 100644 --- a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalViewModel.kt @@ -11,7 +11,6 @@ import com.gh.common.util.DirectUtils import com.gh.common.util.PackageUtils import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.MainActivity -import com.gh.gamecenter.NewsDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.common.retrofit.BiResponse @@ -22,10 +21,11 @@ import com.gh.gamecenter.entity.AppEntity import com.gh.gamecenter.entity.HaloAddonEntity import com.gh.gamecenter.entity.SignEntity import com.gh.gamecenter.eventbus.EBSkip -import com.gh.gamecenter.fragment.MainWrapperFragment import com.gh.gamecenter.login.user.UserManager +import com.gh.gamecenter.newsdetail.NewsDetailActivity import com.gh.gamecenter.retrofit.RetrofitManager import com.gh.gamecenter.subject.SubjectActivity +import com.gh.gamecenter.wrapper.MainWrapperFragment import com.halo.assistant.HaloApp import com.lightgame.utils.Utils import io.reactivex.android.schedulers.AndroidSchedulers @@ -154,7 +154,7 @@ class HaloPersonalViewModel(application: Application) : AndroidViewModel(applica val data = signEntity.data val entrance = "(我的光环)+(签到)" if (data == null || TextUtils.isEmpty(data.type)) { - EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_HOME)) + DirectUtils.directToHomeDefaultTab(context) return } when (data.type) { diff --git a/app/src/main/java/com/gh/gamecenter/personalhome/border/AvatarBorderFragment.kt b/app/src/main/java/com/gh/gamecenter/personalhome/border/AvatarBorderFragment.kt index 5a734032d1..fca64958f5 100644 --- a/app/src/main/java/com/gh/gamecenter/personalhome/border/AvatarBorderFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/personalhome/border/AvatarBorderFragment.kt @@ -12,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.os.bundleOf import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentStatePagerAdapter import androidx.lifecycle.Observer @@ -105,8 +106,8 @@ class AvatarBorderFragment : ToolbarFragment() { super.onViewCreated(view, savedInstanceState) mBinding?.run { ViewCompat.setOnApplyWindowInsetsListener(appbar) { _, insets -> - (toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop - insets.consumeSystemWindowInsets() + (toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top + WindowInsetsCompat.CONSUMED } toolbar.setNavigationOnClickListener { requireActivity().finish() } diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailActivity.kt index 04adff47b5..4926a4e165 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailActivity.kt @@ -2,13 +2,11 @@ package com.gh.gamecenter.qa.article.detail import android.content.Context import android.content.Intent -import android.os.Bundle import com.alibaba.android.arouter.facade.annotation.Route -import com.gh.gamecenter.common.base.activity.ToolBarActivity import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.activity.ToolBarActivity import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.constant.RouteConsts -import com.gh.gamecenter.common.utils.updateStatusBarColor import com.gh.gamecenter.common.entity.CommunityEntity import com.gh.gamecenter.entity.SpecialColumn @@ -19,14 +17,6 @@ class ArticleDetailActivity : ToolBarActivity() { override fun getFragmentPlaceholderId() = R.id.placeholder - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - -// TODO 让状态栏透明 -// DisplayUtils.transparentStatusBar(this) - updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) - } - override fun provideNormalIntent(): Intent { return getTargetIntent(this, ArticleDetailActivity::class.java, ArticleDetailFragment::class.java) } @@ -40,12 +30,6 @@ class ArticleDetailActivity : ToolBarActivity() { override fun isAutoResetViewBackgroundEnabled(): Boolean = true - override fun onDarkModeChanged() { - super.onDarkModeChanged() - updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) - } - - companion object { const val ARTICLE_PATCH_REQUEST = 123 diff --git a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt index 216439d6c2..158f9d651b 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/article/detail/ArticleDetailFragment.kt @@ -7,11 +7,10 @@ import android.graphics.Color import android.os.Bundle import android.view.MenuItem import android.view.View -import android.view.ViewGroup import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat -import androidx.core.view.ViewCompat +import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.RecyclerView import com.ethanhua.skeleton.Skeleton @@ -36,9 +35,14 @@ import com.gh.gamecenter.common.syncpage.SyncDataEntity import com.gh.gamecenter.common.syncpage.SyncFieldConstants import com.gh.gamecenter.common.syncpage.SyncPageRepository import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.HtmlUtils +import com.gh.gamecenter.core.utils.TimeElapsedHelper +import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.databinding.FragmentArticleDetailBinding -import com.gh.gamecenter.entity.* +import com.gh.gamecenter.entity.ActivityLabelEntity +import com.gh.gamecenter.entity.MenuItemEntity +import com.gh.gamecenter.entity.SpecialColumn import com.gh.gamecenter.eventbus.EBDeleteCommentDetail import com.gh.gamecenter.eventbus.EBDeleteDetail import com.gh.gamecenter.eventbus.EBTopCommunityChanged @@ -59,7 +63,6 @@ import com.halo.assistant.HaloApp import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode -import java.util.* class ArticleDetailFragment : BaseCommentFragment() { private var mElapsedHelper: TimeElapsedHelper? = null @@ -132,6 +135,14 @@ class ArticleDetailFragment : BaseCommentFragment - (mBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = - insets.systemWindowInsetTop - insets.consumeSystemWindowInsets() - } mBinding.root.setBackgroundColor(Color.WHITE) mSkeletonScreen = Skeleton.bind(skeletonView) .shimmer(true) @@ -558,6 +563,7 @@ class ArticleDetailFragment : BaseCommentFragment? = null ) : Parcelable { companion object { @@ -50,7 +49,6 @@ class EditorInsertEntity( entity.id = game.id entity.type = "game" entity.title = game.name - entity.tags = game.getTag() entity.icon = game.icon return entity } diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailActivity.kt index e1e8b5d3c4..00f7836adf 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailActivity.kt @@ -2,13 +2,11 @@ package com.gh.gamecenter.qa.questions.newdetail import android.content.Context import android.content.Intent -import android.os.Bundle import com.alibaba.android.arouter.facade.annotation.Route -import com.gh.gamecenter.common.base.activity.ToolBarActivity import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.activity.ToolBarActivity import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.constant.RouteConsts -import com.gh.gamecenter.common.utils.updateStatusBarColor import com.gh.gamecenter.common.entity.CommunityEntity @Route(path = RouteConsts.activity.questionDetailActivity) @@ -18,14 +16,6 @@ class NewQuestionDetailActivity : ToolBarActivity() { override fun getFragmentPlaceholderId() = R.id.placeholder - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - -// TODO 让状态栏透明 -// DisplayUtils.transparentStatusBar(this) - updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) - } - override fun provideNormalIntent(): Intent { return getTargetIntent(this, NewQuestionDetailActivity::class.java, NewQuestionDetailFragment::class.java) } @@ -40,11 +30,6 @@ class NewQuestionDetailActivity : ToolBarActivity() { override fun isAutoResetViewBackgroundEnabled(): Boolean = true - override fun onDarkModeChanged() { - super.onDarkModeChanged() - updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) - } - companion object { @JvmStatic fun getIntent( diff --git a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt index ea11af2d9d..d5ba71ec5b 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/questions/newdetail/NewQuestionDetailFragment.kt @@ -8,10 +8,9 @@ import android.text.TextUtils import android.view.LayoutInflater import android.view.MenuItem import android.view.View -import android.view.ViewGroup import android.widget.TextView import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat +import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.RecyclerView import com.ethanhua.skeleton.Skeleton @@ -32,6 +31,7 @@ import com.gh.gamecenter.common.entity.AdditionalParamsEntity import com.gh.gamecenter.common.entity.CommunityEntity import com.gh.gamecenter.common.entity.NormalShareEntity import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.databinding.FragmentArticleDetailBinding import com.gh.gamecenter.entity.MenuItemEntity import com.gh.gamecenter.eventbus.EBDeleteDetail @@ -128,6 +128,14 @@ class NewQuestionDetailFragment : mViewModel.getQuestionDetail() } + override fun onResume() { + super.onResume() + if (!mIsFromTabWrapper) { + DisplayUtils.transparentStatusBar(requireActivity()) + DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn) + } + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode != Activity.RESULT_OK || data == null) return @@ -192,6 +200,7 @@ class NewQuestionDetailFragment : } private fun initToolbar() { + mBinding.statusBar.isVisible = !mIsFromTabWrapper mBinding.toolbar.inflateMenu(R.menu.menu_article_detail) mBinding.toolbar.setNavigationOnClickListener { onBackPressed() @@ -203,17 +212,17 @@ class NewQuestionDetailFragment : NewLogUtils.logShareEnter("提问详情页") } } + if (mIsFromMainWrapper || mIsFromTabWrapper) { + mBinding.toolbar.navigationIcon = null + mBinding.userContainer.translationX = 16F.dip2px().toFloat() + mBinding.forumContainer.translationX = 16F.dip2px().toFloat() + } mAttentionMenu = mBinding.toolbar.menu.findItem(R.id.menu_follow) mAttentionMenu?.isVisible = false } private fun initView() { mBinding.reuseNoneData.reuseNoneDataTv.text = getString(R.string.content_delete_hint) - ViewCompat.setOnApplyWindowInsetsListener(mBinding.toolbar) { _, insets -> - (mBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = - insets.systemWindowInsetTop - insets.consumeSystemWindowInsets() - } mBinding.root.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) mSkeletonScreen = Skeleton.bind(skeletonView) .shimmer(true) @@ -811,6 +820,9 @@ class NewQuestionDetailFragment : override fun onDarkModeChanged() { super.onDarkModeChanged() + if (!mIsFromTabWrapper) { + DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn) + } mAdapter?.let { it.notifyItemRangeChanged(0, it.itemCount) } mBinding.fixedTopFilterView.orderSfv.run { setContainerBackground(R.drawable.button_round_f5f5f5.toDrawable(requireContext())) diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailActivity.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailActivity.kt index 66fbb9f14a..cebcee1848 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailActivity.kt @@ -6,12 +6,11 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import com.alibaba.android.arouter.facade.annotation.Route +import com.gh.gamecenter.R import com.gh.gamecenter.common.base.activity.BaseActivity import com.gh.gamecenter.common.base.fragment.BaseFragment_TabLayout.PAGE_INDEX -import com.gh.gamecenter.core.utils.DisplayUtils -import com.gh.gamecenter.common.constant.EntranceConsts -import com.gh.gamecenter.R import com.gh.gamecenter.common.base.fragment.ToolbarFragment +import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.constant.RouteConsts @Route(path = RouteConsts.activity.forumVideoDetailActivity) @@ -24,7 +23,6 @@ class ForumVideoDetailActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - DisplayUtils.transparentStatusBar(this) containerFragment = supportFragmentManager.findFragmentByTag(ForumVideoDetailFragment::class.java.name) ?: ForumVideoDetailFragment().with(intent.extras) diff --git a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt index 2bbcdf9a8b..ec63a32a11 100644 --- a/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/qa/video/detail/ForumVideoDetailFragment.kt @@ -17,6 +17,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.core.os.bundleOf import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle import com.ethanhua.skeleton.Skeleton @@ -25,23 +26,30 @@ import com.gh.common.browse.ValueBrowseTimer import com.gh.common.browse.asObserver import com.gh.common.browse.withLifecycle import com.gh.common.databind.BindingAdapters -import com.gh.common.util.* +import com.gh.common.exposure.ExposureManager +import com.gh.common.util.BbsReportHelper +import com.gh.common.util.DownloadItemUtils import com.gh.common.util.NewLogUtils +import com.gh.common.util.SyncDataBetweenPageHelper import com.gh.download.DownloadManager import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.GameViewHolder -import com.gh.gamecenter.common.base.fragment.BaseFragment_TabLayout +import com.gh.gamecenter.common.base.fragment.BaseLazyTabFragment import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.entity.AdditionalParamsEntity import com.gh.gamecenter.common.entity.NormalShareEntity +import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.mvvm.Resource import com.gh.gamecenter.common.mvvm.Status import com.gh.gamecenter.common.utils.* -import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.MD5Utils +import com.gh.gamecenter.core.utils.ToastUtils import com.gh.gamecenter.databinding.FragmentForumVideoDetailBinding -import com.gh.gamecenter.entity.* +import com.gh.gamecenter.entity.ActivityLabelEntity +import com.gh.gamecenter.entity.MenuItemEntity import com.gh.gamecenter.eventbus.EBDeleteDetail import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage @@ -49,6 +57,7 @@ import com.gh.gamecenter.eventbus.EBTopCommunityChanged import com.gh.gamecenter.feature.entity.ForumVideoEntity import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.Permissions +import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper import com.gh.gamecenter.gamedetail.GameDetailFragment import com.gh.gamecenter.login.user.UserManager @@ -70,7 +79,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import kotlin.math.abs -class ForumVideoDetailFragment : BaseFragment_TabLayout() { +class ForumVideoDetailFragment : BaseLazyTabFragment() { private lateinit var mBinding: FragmentForumVideoDetailBinding private lateinit var mViewModel: ForumVideoDetailViewModel private var mOrientationUtils: OrientationUtils? = null @@ -87,6 +96,7 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { private var mVideoHeight = 0 private var mLastOffset = 0 private var sourceEntrance: String = "" + private lateinit var mBasicExposureSourceList: ArrayList private val browserTimer = ValueBrowseTimer>() .withLifecycle(this) @@ -161,20 +171,25 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { mVideoId = arguments?.getString(EntranceConsts.KEY_VIDEO_ID) ?: "" mBbsId = arguments?.getString(EntranceConsts.KEY_BBS_ID) ?: "" mTopCommentId = arguments?.getString(EntranceConsts.KEY_TOP_COMMENT_ID) ?: "" + mBasicExposureSourceList = arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) ?: arrayListOf() sourceEntrance = arguments?.getString(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: "" super.onCreate(savedInstanceState) NewLogUtils.logVideoDetailClick("view_video_detail") } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - mBinding.topVideoView.parentTag = tag ?: "" + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() mBinding.toolbar.inflateMenu(R.menu.menu_forum_video_detail) mMoreMenuItem = mBinding.toolbar.menu.findItem(R.id.menu_more) - ViewCompat.setOnApplyWindowInsetsListener(mBinding.appbar) { _, insets -> - (mBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = - insets.systemWindowInsetTop - insets.consumeSystemWindowInsets() + if (!mIsFromTabWrapper) { + ViewCompat.setOnApplyWindowInsetsListener(mBinding.appbar) { _, insets -> + (mBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = + insets.getInsets(WindowInsetsCompat.Type.systemBars()).top + WindowInsetsCompat.CONSUMED + } + } + if (mIsFromMainWrapper) { + (mBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = DisplayUtils.getStatusBarHeight(requireContext().resources) } mViewModel = viewModelProviderFromParent( ForumVideoDetailViewModel.Factory( @@ -196,7 +211,7 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { mOrientationUtils = OrientationUtils(requireActivity(), mBinding.topVideoView) mOrientationUtils?.isEnable = false - mBinding.appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset -> + mBinding.appbar.addOnOffsetChangedListener { appBarLayout, verticalOffset -> val absVerticalOffset = abs(verticalOffset) val total = appBarLayout.totalScrollRange if (absVerticalOffset > total / 2) { @@ -205,7 +220,7 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { updateToolbarStyle(isToolbarWhite = false) } updateVideoViewParams(absVerticalOffset) - }) + } mViewPager.doOnPageSelected { if (it == 0) { NewLogUtils.logVideoDetailClick("click_detail_tab") @@ -373,6 +388,15 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { mBinding.gameNameTv.text = gameEntity.name mBinding.gameScoreTv.text = gameEntity.star.toString() BindingAdapters.setVideoDetailGameTags(mBinding.gameTagsContainer, gameEntity) + + val exposureEvent = ExposureEvent.createEventWithSourceConcat( + gameEntity, + mBasicExposureSourceList, + listOf(ExposureSource("其他"), ExposureSource("视频详情")) + ) + gameEntity.exposureEvent = exposureEvent + ExposureManager.log(exposureEvent) + mBinding.gameInfoContainer.setOnClickListener { NewLogUtils.logVideoDetailGameClick( "click_game", @@ -382,7 +406,12 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { mForumVideoEntity?.id ?: "", "" ) - GameDetailActivity.startGameDetailActivity(requireContext(), gameEntity, "视频详情") + GameDetailActivity.startGameDetailActivity( + requireContext(), + gameEntity, + "视频详情", + traceEvent = exposureEvent + ) } setDownloadButton(gameEntity) } @@ -399,7 +428,11 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { if (isToolbarWhite == mViewModel.currentToolbarStatus) return mViewModel.currentToolbarStatus = isToolbarWhite - mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) + if (!mIsFromMainWrapper && !mIsFromTabWrapper) { + mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) + } else { + mBinding.toolbar.navigationIcon = null + } mMoreMenuItem?.setIcon(R.drawable.ic_menu_more_light) mBinding.toolbar.setBackgroundColor( ContextCompat.getColor( @@ -508,7 +541,7 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { gameEntity, 0, null, mEntrance, location = "视频详情", - traceEvent = null, + traceEvent = gameEntity.exposureEvent, clickCallback = { NewLogUtils.logVideoDetailGameClick( "click_game_status", @@ -877,8 +910,11 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { setDownloadButton(mForumVideoEntity?.game) } - override fun onResume() { - super.onResume() + override fun onFragmentResume() { + super.onFragmentResume() + if (!mIsFromTabWrapper && isSupportVisible) { + DisplayUtils.transparentStatusBar(requireActivity()) + } if (!mBinding.topVideoView.isInPlayingState) { mBinding.topVideoView.startPlayLogic(isAutoPlay = true) } else { @@ -887,8 +923,8 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { DownloadManager.getInstance().addObserver(dataWatcher) } - override fun onPause() { - super.onPause() + override fun onFragmentPause() { + super.onFragmentPause() mBinding.topVideoView.onVideoPause() val currentPosition = mBinding.topVideoView.getCurrentPosition() mForumVideoEntity?.let { @@ -926,22 +962,5 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() { override fun onDarkModeChanged() { super.onDarkModeChanged() setDownloadButton(mForumVideoEntity?.game) - if (mTabLayout != null) { - for (i in 0 until mTabLayout.tabCount) { - val tab = mTabLayout.getTabAt(i) - if (tab != null) { - tab.customView?.findViewById(R.id.tab_title)?.setTextColor( - if (tab.isSelected) R.color.text_theme.toColor(requireContext()) else R.color.text_secondary.toColor( - requireContext() - ) - ) - tab.customView?.findViewById(R.id.tab_count)?.setTextColor( - if (tab.isSelected) R.color.text_theme.toColor(requireContext()) else R.color.text_secondary.toColor( - requireContext() - ) - ) - } - } - } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/qgame/QGameSearchActivity.kt b/app/src/main/java/com/gh/gamecenter/qgame/QGameSearchActivity.kt index 782c0c0242..4cdc16943d 100644 --- a/app/src/main/java/com/gh/gamecenter/qgame/QGameSearchActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/qgame/QGameSearchActivity.kt @@ -2,16 +2,10 @@ package com.gh.gamecenter.qgame import android.content.Context import android.content.Intent -import android.os.Bundle import com.gh.gamecenter.DisplayType import com.gh.gamecenter.SearchActivity -import com.gh.gamecenter.SearchType -import com.gh.gamecenter.common.base.activity.ToolBarActivity import com.gh.gamecenter.common.constant.EntranceConsts -import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.core.AppExecutor -import com.gh.gamecenter.core.utils.DisplayUtils -import com.gh.gamecenter.login.R import com.gh.gamecenter.search.SearchGameIndexFragment import com.lightgame.utils.Util_System_Keyboard @@ -52,11 +46,23 @@ class QGameSearchActivity : SearchActivity() { fun getIntent( context: Context, hint: String, - sourceEntrance: String + sourceEntrance: String, + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + customPageId: String = "", + customPageName: String = "", + searchBoxPattern: String = "" ): Intent { val intent = Intent(context, QGameSearchActivity::class.java) intent.putExtra(EntranceConsts.KEY_HINT, hint) intent.putExtra(EntranceConsts.KEY_SOURCE_ENTRANCE, sourceEntrance) + intent.putExtra(EntranceConsts.KEY_BOTTOM_TAB_NAME, bottomTab) + intent.putExtra(EntranceConsts.KEY_MULTI_TAB_NAV_ID, multiTabId) + intent.putExtra(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, multiTabName) + intent.putExtra(EntranceConsts.KEY_CUSTOM_PAGE_ID, customPageId) + intent.putExtra(EntranceConsts.KEY_CUSTOM_PAGE_NAME, customPageName) + intent.putExtra(EntranceConsts.KEY_SEARCH_BOX_PATTERN, searchBoxPattern) return intent } } diff --git a/app/src/main/java/com/gh/gamecenter/qgame/QGameViewModel.kt b/app/src/main/java/com/gh/gamecenter/qgame/QGameViewModel.kt index 55bd9bb81e..526bf288cd 100644 --- a/app/src/main/java/com/gh/gamecenter/qgame/QGameViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/qgame/QGameViewModel.kt @@ -186,8 +186,6 @@ class QGameViewModel(application: Application, blockData: SubjectRecommendEntity if (!data.isNullOrEmpty()) { for (game in data) { mSubjectGameIdList.add(game.id) - // 应用专题是否显示游戏名后缀的配置 - game.shouldShowNameSuffix = subjectEntity.showSuffix } } @@ -310,7 +308,6 @@ class QGameViewModel(application: Application, blockData: SubjectRecommendEntity position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, isOrder = subjectEntity.isOrder, briefStyle = subjectEntity.briefStyle, - isShowSuffix = subjectEntity.showSuffix ) addGamePositionAndPackage(game) @@ -486,7 +483,6 @@ class QGameViewModel(application: Application, blockData: SubjectRecommendEntity position = i + if (data[0].image.isNullOrEmpty()) 1 else 0, isOrder = subjectEntity.isOrder, briefStyle = subjectEntity.briefStyle, - isShowSuffix = subjectEntity.showSuffix ) val itemDataGame = GameItemData() itemDataGame.game = game diff --git a/app/src/main/java/com/gh/gamecenter/receiver/DownloadReceiver.java b/app/src/main/java/com/gh/gamecenter/receiver/DownloadReceiver.java index 633cc0304b..f93d4fe959 100644 --- a/app/src/main/java/com/gh/gamecenter/receiver/DownloadReceiver.java +++ b/app/src/main/java/com/gh/gamecenter/receiver/DownloadReceiver.java @@ -22,7 +22,7 @@ public class DownloadReceiver extends BroadcastReceiver { ExtensionsKt.doOnMainProcessOnly(context, () -> { try { if (DownloadNotificationHelper.ACTION_VDOWNLOAD.equals(intent.getAction())) { - DirectUtils.directToVGameDownload(context, true); + DirectUtils.directToVGameDownload(context, "其他", true); } else { DirectUtils.directToDownloadManager(context, ENTRANCE_DOWNLOAD); } 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 c8cd9ed0a8..ad74789f88 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 @@ -1,6 +1,8 @@ package com.gh.gamecenter.retrofit.service; import com.gh.common.filter.RegionSetting; +import com.gh.gamecenter.common.entity.LaunchRedirect; +import com.gh.gamecenter.common.entity.LaunchRedirectWrapper; import com.gh.gamecenter.common.entity.LinkEntity; import com.gh.gamecenter.common.entity.OssEntity; import com.gh.gamecenter.common.entity.SimpleGameEntity; @@ -16,14 +18,13 @@ import com.gh.gamecenter.entity.ArchiveEntity; import com.gh.gamecenter.entity.AvatarBorderCategoryEntity; import com.gh.gamecenter.entity.BadgeEntity; import com.gh.gamecenter.entity.BlockEntity; +import com.gh.gamecenter.entity.BottomTab; import com.gh.gamecenter.entity.CarouselEntity; import com.gh.gamecenter.entity.CatalogEntity; import com.gh.gamecenter.entity.CategoryEntity; -import com.gh.gamecenter.entity.ServerSubscriptionEntity; -import com.gh.gamecenter.feature.entity.CommentEntity; -import com.gh.gamecenter.feature.entity.CommentnumEntity; import com.gh.gamecenter.entity.CommonCollectionContentEntity; import com.gh.gamecenter.entity.CommonCollectionEntity; +import com.gh.gamecenter.entity.DataUnionEntity; import com.gh.gamecenter.entity.DefaultAvatar; import com.gh.gamecenter.entity.DeviceDialogEntity; import com.gh.gamecenter.entity.DialogEntity; @@ -55,11 +56,7 @@ import com.gh.gamecenter.entity.HomeItemTestV2Entity; import com.gh.gamecenter.entity.ImageInfoEntity; import com.gh.gamecenter.entity.InterestedGameEntity; import com.gh.gamecenter.entity.LibaoDetailEntity; -import com.gh.gamecenter.feature.entity.LibaoEntity; -import com.gh.gamecenter.feature.entity.LibaoStatusEntity; -import com.gh.gamecenter.feature.entity.MessageDigestEntity; -import com.gh.gamecenter.feature.entity.MessageUnreadCount; -import com.gh.gamecenter.feature.entity.MessageUnreadEntity; +import com.gh.gamecenter.entity.MultiTabNav; import com.gh.gamecenter.entity.MyVideoEntity; import com.gh.gamecenter.entity.NewApiSettingsEntity; import com.gh.gamecenter.entity.NewSettingsEntity; @@ -74,6 +71,7 @@ import com.gh.gamecenter.entity.RatingReplyEntity; import com.gh.gamecenter.entity.RecommendPopupEntity; import com.gh.gamecenter.entity.SearchSubjectEntity; import com.gh.gamecenter.entity.ServerPublishEntity; +import com.gh.gamecenter.entity.ServerSubscriptionEntity; import com.gh.gamecenter.entity.ServerTestEntity; import com.gh.gamecenter.entity.ServersGameCategory; import com.gh.gamecenter.entity.SidebarsEntity; @@ -94,17 +92,11 @@ import com.gh.gamecenter.entity.VideoDataOverViewEntity; import com.gh.gamecenter.entity.VideoDraftEntity; import com.gh.gamecenter.entity.VideoEntity; import com.gh.gamecenter.entity.VideoTagEntity; -import com.gh.gamecenter.feature.entity.ServerCalendarFormEntity; -import com.gh.gamecenter.feature.entity.ServerCalendarGame; -import com.gh.gamecenter.feature.entity.ServerCalendarNotifySetting; -import com.gh.gamecenter.feature.entity.WXSubscribeMsgConfig; -import com.gh.gamecenter.feature.entity.ViewsEntity; import com.gh.gamecenter.entity.VoteEntity; import com.gh.gamecenter.feature.entity.AnswerEntity; import com.gh.gamecenter.feature.entity.ApkEntity; import com.gh.gamecenter.feature.entity.ArticleDraftEntity; import com.gh.gamecenter.feature.entity.ArticleEntity; -import com.gh.gamecenter.feature.entity.AuthDialogEntity; import com.gh.gamecenter.feature.entity.AvatarBorderEntity; import com.gh.gamecenter.feature.entity.BackgroundImageEntity; import com.gh.gamecenter.feature.entity.CommentEntity; @@ -114,6 +106,7 @@ import com.gh.gamecenter.feature.entity.ForumVideoEntity; import com.gh.gamecenter.feature.entity.GameEntity; import com.gh.gamecenter.feature.entity.LibaoEntity; import com.gh.gamecenter.feature.entity.LibaoStatusEntity; +import com.gh.gamecenter.feature.entity.MessageDigestEntity; import com.gh.gamecenter.feature.entity.MessageUnreadCount; import com.gh.gamecenter.feature.entity.MessageUnreadEntity; import com.gh.gamecenter.feature.entity.NewsEntity; @@ -121,14 +114,19 @@ import com.gh.gamecenter.feature.entity.PersonalEntity; import com.gh.gamecenter.feature.entity.QuestionDraftEntity; import com.gh.gamecenter.feature.entity.Questions; import com.gh.gamecenter.feature.entity.ServerCalendarEntity; +import com.gh.gamecenter.feature.entity.ServerCalendarFormEntity; +import com.gh.gamecenter.feature.entity.ServerCalendarGame; +import com.gh.gamecenter.feature.entity.ServerCalendarNotifySetting; import com.gh.gamecenter.feature.entity.SettingsEntity; import com.gh.gamecenter.feature.entity.SimulatorEntity; import com.gh.gamecenter.feature.entity.ViewsEntity; +import com.gh.gamecenter.feature.entity.WXSubscribeMsgConfig; +import com.gh.gamecenter.floatingwindow.FloatingWindowEntity; import com.gh.gamecenter.gamedetail.entity.BigEvent; import com.gh.gamecenter.gamedetail.entity.NewGameDetailEntity; +import com.gh.gamecenter.home.custom.model.CustomPageData; import com.gh.gamecenter.login.entity.UserInfoEntity; import com.gh.gamecenter.personalhome.rating.MyRating; -import com.gh.gamecenter.qa.entity.TopCommunityCategory; import com.gh.gamecenter.qa.entity.AnswerDetailEntity; import com.gh.gamecenter.qa.entity.AnswerDraftEntity; import com.gh.gamecenter.qa.entity.ArticleDetailEntity; @@ -1138,6 +1136,7 @@ public interface ApiService { /** * 内容详情-相关内容列表 + * * @param contentId 内容ID */ @GET("bbses/contents/{content_id}/related_contents") @@ -1313,7 +1312,7 @@ public interface ApiService { * 获取首页游戏补充库 */ @GET("reserve_columns") - Observable> getReserveColumns(); + Observable getReserveColumns(); /** * 设置回答详情的的评论区域是否开启 @@ -1765,18 +1764,6 @@ public interface ApiService { @GET("users/{user_id}/played_games") Single> getPlayedGames(@Path("user_id") String userId, @Query("page") int page, @Query("timestamp") long timestamp, @QueryMap Map params); - /** - * 获取用户玩过的游戏列表(仅游戏ID) - */ - @GET("users/{user_id}/played_game_ids") - Single> getIdListOfPlayedGames(@Path("user_id") String userId, @Query("timestamp") long timestamp); - - /** - * 获取用户设备下载过的游戏列表(仅游戏ID) - */ - @GET("devices/{device_id}/downloaded_game_ids") - Single> getIdListOfDownloadedGames(@Path("device_id") String deviceId, @Query("timestamp") long timestamp); - /** * 获取开服过滤标签 */ @@ -1972,11 +1959,6 @@ public interface ApiService { @POST("articles/{article_id}:stat_visit") Observable postArticleVisit(@Path("article_id") String articleId); - /** - * 获取游戏实名认证弹窗 - */ - @GET("games/auth_dialogs") - Observable> authDialog(); /** * 获取游戏大事件 @@ -2611,6 +2593,12 @@ public interface ApiService { @POST("questions/{question_id}/answers/{answer_id}:accept") Observable acceptQuestionComment(@Path("question_id") String questionId, @Path("answer_id") String answerId, @Body RequestBody body); + /** + * 首次启动跳转 + */ + @GET("app/first_jump") + Single getLaunchRedirect(@Query("version") String version, @Query("channel") String channel); + /** * 获取引导浮窗 */ @@ -3311,11 +3299,57 @@ public interface ApiService { */ @POST("messages/wechat/one_time/call_back") Single postWxSubscribeMsgCallback(@Query("filter") String filter, - @Body RequestBody body); + @Body RequestBody body); + /** * 未读消息-埋点数据提供 */ @GET("messages/unread/event") Observable> getMessageUnreadDigestList(@Query("page") int page, @Query("page_size") int pageSize); + + /** + * 自定义页面列表数据 + */ + @GET("app/custom_page/{page_id}") + Single getCustomPageData( + @Path("page_id") String pageId, + @Query("page") int page); + + @GET("app/custom_page/{page_id}/suspended_window") + Single> loadSuspendedWindow( + @Path("page_id") String pageId, + @Query("version") String version, + @Query("channel") String channel); + + /** + * 页面数据聚合[底部tab+多tab导航页+默认数据] + */ + @GET("app/data_union") + Single getDataUnion(); + + /** + * 底部tab + */ + @GET("app/bottom_tab") + Single> getHomeBottomTab(); + + /** + * 多tab导航页-详情 + */ + @GET("app/multi_tab_nav/{nav_id}") + Single getTabNav(@Path("nav_id") String id); + + /** + * 专题合集-内容列表 (适用于刷新轮换) + */ + @GET("games/columns/collections/{collection_id}/contents") + Single> getColumnsCollectionContents(@Path("collection_id") String collectionId, @Query("view") String view, @Query("page") int page, @Query("page_size") int pageSize); + + /** + * 游戏单合集-内容列表 (适用于刷新轮换) + */ + @GET("game_lists/collections/{collection_id}/contents") + Single> getGameCollectionContents(@Path("collection_id") String collectionId, @Query("view") String view, @Query("page") int page, @Query("page_size") int pageSize); + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchDefaultRankAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/SearchDefaultRankAdapter.kt index 312899149f..2cd8ff66d5 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchDefaultRankAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchDefaultRankAdapter.kt @@ -7,6 +7,7 @@ import com.gh.common.util.DirectUtils import com.gh.common.util.NewFlatLogUtils import com.gh.gamecenter.R import com.gh.gamecenter.common.base.BaseRecyclerViewHolder +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.databinding.SearchDefaultRankItemBinding import com.gh.gamecenter.feature.entity.SettingsEntity @@ -35,7 +36,7 @@ class SearchDefaultRankAdapter( } name.text = if (rank.link.type == "game") gameEntity?.name else rank.name.ifBlank { rank.link.text } index.run { - typeface = Typeface.createFromAsset(mContext.assets, "fonts/d_din_bold_only_number.ttf") + typeface = Typeface.createFromAsset(mContext.assets, Constants.DIN_FONT_PATH) text = (position + 1).toString() setTextColor( when (position + 1) { 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 0a0ac89f74..50a7135f34 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt @@ -127,7 +127,7 @@ class SearchGameFirstItemViewHolder( binding.gameItemIncluded.run { gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F - BindingAdapters.setGameName(gameName, gameEntity, false, null) + BindingAdapters.setGameName(gameName, gameEntity, false) BindingAdapters.setGameTags(labelList, gameEntity) gameRating.setDrawableStart(if (gameEntity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable() else null) gameRating.setPadding(0, 0, if (gameEntity.commentCount > 3) 8F.dip2px() else 0, 0) 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 a4a5dadda8..121e09c9cd 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt @@ -131,7 +131,7 @@ class SearchGameIndexAdapter( binding.gameItemIncluded.run { gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F - BindingAdapters.setGameName(gameName, gameEntity, false, null) + BindingAdapters.setGameName(gameName, gameEntity, false) BindingAdapters.setGameTags(labelList, gameEntity) gameRating.setDrawableStart(if (gameEntity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable() else null) gameRating.setPadding(0, 0, if (gameEntity.commentCount > 3) 8F.dip2px() else 0, 0) 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 e08b56b7fc..e17c96ad65 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexItemViewHolder.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexItemViewHolder.kt @@ -109,6 +109,17 @@ class SearchSubjectItemViewHolder(var binding: SearchSubjectItemBinding) : Recyc it.name ?: "" ) } + + if (!it.isQQMiniGame()) { + SensorsBridge.trackColumnClick( + location = "游戏搜索", + gameColumnId = entity.columnId, + gameColumnName = entity.name, + gameId = it.id, + gameName = it.name ?: "", + text = "游戏" + ) + } } headContainer.adLabelTv.goneIf(!entity.adIconActive) headContainer.headTitle.text = entity.name @@ -130,6 +141,13 @@ class SearchSubjectItemViewHolder(var binding: SearchSubjectItemBinding) : Recyc "($type-专题)" ) } + SensorsBridge.trackColumnClick( + location = "游戏搜索", + gameColumnId = entity.columnId, + gameColumnName = entity.name, + text = "右上角", + buttonType = "全部" + ) } } } 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 654069f33b..0d9a2b28ad 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt @@ -32,7 +32,6 @@ import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.viewholder.FooterViewHolder import com.gh.gamecenter.core.utils.DisplayUtils -import com.gh.gamecenter.core.utils.EmptyCallback import com.gh.gamecenter.core.utils.SPUtils import com.gh.gamecenter.core.utils.StringUtils import com.gh.gamecenter.databinding.* @@ -284,7 +283,7 @@ class SearchGameResultAdapter( binding.gameItemIncluded.run { gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F - BindingAdapters.setGameName(gameName, gameEntity, false, null) + BindingAdapters.setGameName(gameName, gameEntity, false) BindingAdapters.setGameTags(labelList, gameEntity) gameRating.setDrawableStart(if (gameEntity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable() else null) gameRating.setPadding(0, 0, if (gameEntity.commentCount > 3) 8F.dip2px() else 0, 0) @@ -649,6 +648,12 @@ class SearchGameResultAdapter( GlobalActivityManager.currentActivity?.let { DirectUtils.directToQGameById(it, gameEntity.qqMiniGameAppId) } + SensorsBridge.trackQQGameClick( + location = "QQ小游戏搜索结果列表", + searchContent = key, + gameId = gameEntity.id, + gameName = gameEntity.name ?: "" + ) } else { GameDetailActivity.startGameDetailActivity( context, gameEntity, diff --git a/app/src/main/java/com/gh/gamecenter/servers/GameServersPublishFragment.kt b/app/src/main/java/com/gh/gamecenter/servers/GameServersPublishFragment.kt index c022735c7b..1f15137288 100644 --- a/app/src/main/java/com/gh/gamecenter/servers/GameServersPublishFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/servers/GameServersPublishFragment.kt @@ -13,13 +13,13 @@ import android.widget.TextView import com.gh.gamecenter.R import com.gh.gamecenter.common.base.fragment.LazyFragment import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.eventbus.EBNetworkState import com.gh.gamecenter.common.retrofit.BiResponse import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.view.DrawableView import com.gh.gamecenter.databinding.FragmentGameServersPublishBinding import com.gh.gamecenter.entity.ServersGameCategory -import com.gh.gamecenter.common.eventbus.EBNetworkState import com.gh.gamecenter.retrofit.RetrofitManager import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -48,7 +48,10 @@ class GameServersPublishFragment : LazyFragment() { override fun initRealView() { super.initRealView() - + if (mIsFromMainWrapper && !mIsFromTabWrapper) { + setNavigationTitle("开服表") + initMenu(R.menu.menu_download) + } mBinding?.run { serverStatus.text = "游戏分类" serverTime.text = "开服时间" diff --git a/app/src/main/java/com/gh/gamecenter/servers/gametest2/GameServerTestV2Activity.kt b/app/src/main/java/com/gh/gamecenter/servers/gametest2/GameServerTestV2Activity.kt index 65efdd7a0f..bb9f89ba95 100644 --- a/app/src/main/java/com/gh/gamecenter/servers/gametest2/GameServerTestV2Activity.kt +++ b/app/src/main/java/com/gh/gamecenter/servers/gametest2/GameServerTestV2Activity.kt @@ -8,14 +8,12 @@ import com.gh.gamecenter.common.base.activity.ToolBarActivity import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.updateStatusBarColor import com.gh.gamecenter.feature.exposure.ExposureEvent -import java.util.ArrayList class GameServerTestV2Activity : ToolBarActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) - setNavigationTitle("新游开测") } override fun provideNormalIntent(): Intent { 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 06e95c82ad..72308d7b41 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 @@ -46,6 +46,7 @@ class GameServerTestV2Fragment : LazyFragment() { override fun initRealView() { super.initRealView() + setNavigationTitle("新游开测") mBinding?.filterView?.setupFilter(mViewModel?.timeFilterList!!, mViewModel?.selectedTimeFilterLiveData?.value) { mViewModel?.setSelectedTimeFilter(it) mCurrentFragment?.changeTimeFilter() diff --git a/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListAdapter.kt b/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListAdapter.kt index c6ad035bdb..5ed3537d5c 100644 --- a/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/simulatorgame/SimulatorGameListAdapter.kt @@ -184,7 +184,7 @@ class SimulatorGameListAdapter( gameItemIncluded.run { gameIconView.displayGameIcon(gameEntity) gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F - BindingAdapters.setGameName(gameName, gameEntity, false, null) + BindingAdapters.setGameName(gameName, gameEntity, false) BindingAdapters.setGameTags(labelList, gameEntity) gameRating.setDrawableStart(if (gameEntity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable() else null) gameRating.setPadding(0, 0, if (gameEntity.commentCount > 3) 8F.dip2px() else 0, 0) diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectActivity.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectActivity.kt index a048b96327..b1c9ae7353 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectActivity.kt @@ -3,7 +3,9 @@ package com.gh.gamecenter.subject import android.content.Context import android.content.Intent import android.os.Bundle +import android.view.MenuItem import com.gh.base.DownloadToolbarActivity +import com.gh.common.util.DirectUtils import com.gh.gamecenter.R import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.exposure.ExposureSource @@ -12,10 +14,13 @@ import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.entity.SubjectData open class SubjectActivity : DownloadToolbarActivity() { + private var subjectData: SubjectData? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) + subjectData = intent.getBundleExtra(NORMAL_FRAGMENT_BUNDLE)?.getParcelable(EntranceConsts.KEY_SUBJECT_DATA) + ?: SubjectData("", "", false) } override fun showDownloadMenu() = true @@ -23,10 +28,7 @@ open class SubjectActivity : DownloadToolbarActivity() { override fun getActivityNameInChinese() = "专题详情" override fun getBusinessId(): Pair { - val subjectData: SubjectData = - intent.getBundleExtra(NORMAL_FRAGMENT_BUNDLE)?.getParcelable(EntranceConsts.KEY_SUBJECT_DATA) - ?: SubjectData("", "", false) - return Pair(subjectData.subjectId ?: "", "") + return Pair(subjectData?.subjectId ?: "", "") } override fun provideNormalIntent(): Intent { @@ -35,6 +37,19 @@ open class SubjectActivity : DownloadToolbarActivity() { override fun isAutoResetViewBackgroundEnabled() = true + override fun onMenuItemClick(item: MenuItem?): Boolean { + if (item!!.itemId == R.id.menu_download) { + DirectUtils.directToDownloadManager( + this, + "游戏专题", + gameColumnId = subjectData?.subjectId ?: "", + gameColumnName = subjectData?.subjectName ?: "", + ) + return true + } + return super.onMenuItemClick(item) + } + override fun onDarkModeChanged() { super.onDarkModeChanged() diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt index 34fb827c2e..0397a7e48e 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectAdapter.kt @@ -33,6 +33,7 @@ import com.gh.gamecenter.feature.databinding.GameItemBinding import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.gh.gamecenter.home.custom.model.CustomPageItem import com.lightgame.download.DownloadEntity class SubjectAdapter( @@ -40,7 +41,10 @@ class SubjectAdapter( private val mViewModel: SubjectListViewModel, private val mSubjectViewModel: SubjectViewModel?, private val mEntrance: String?, - private val mIsColumnCollection: Boolean + private val mIsColumnCollection: Boolean, + private val mCollectionId: String, + private val mCollectionName: String, + private val mCollectionStyle: String ) : ListAdapter(context), IExposable { private val mExposureEventSparseArray: SparseArray = SparseArray() @@ -114,7 +118,7 @@ class SubjectAdapter( val gameEntity = mEntityList[position] // 专题详情均显示游戏后缀 https://git.ghzs.com/pm/halo-app-issues/-/issues/1098 (3) - holder.bindGameItem(gameEntity, true, subjectData.briefStyle) + holder.bindGameItem(gameEntity, subjectData.briefStyle) holder.binding.downloadBtn.putWidgetBusinessName("专题列表") holder.initServerType(gameEntity) @@ -218,6 +222,15 @@ class SubjectAdapter( GlobalActivityManager.currentActivity?.let { DirectUtils.directToQGameById(it, gameEntity.qqMiniGameAppId) } + if (!mIsColumnCollection) { + SensorsBridge.trackQQGameClick( + location = "专题详情", + gameColumnId = subjectData.subjectId ?: "", + gameColumnName = subjectData.subjectName ?: "", + gameId = gameEntity.id, + gameName = gameEntity.name ?: "" + ) + } } else { GameDetailActivity.startGameDetailActivity( mContext, @@ -234,6 +247,31 @@ class SubjectAdapter( ), traceEvent = exposureEvent ) + if (!mIsColumnCollection) { + SensorsBridge.trackColumnClick( + location = "游戏专题详情", + gameColumnId = subjectData.subjectId ?: "", + gameColumnName = subjectData.subjectName ?: "", + gameId = gameEntity.id, + gameName = gameEntity.name ?: "", + text = "游戏" + ) + } + } + + if (mIsColumnCollection) { + SensorsBridge.trackColumnCollectionClick( + location = "合集详情", + columnCollectionName = mCollectionName, + columnCollectionId = mCollectionId, + gameColumnName = subjectData.subjectName ?: "", + gameColumnId = subjectData.subjectId ?: "", + text = "游戏", + gameId = gameEntity.id, + gameName = gameEntity.name ?: "", + columnCollectionPattern = CustomPageItem.collectionTypeToComponentName[mCollectionStyle] + ?: mCollectionStyle + ) } } @@ -254,7 +292,32 @@ class SubjectAdapter( "=", (humanReadablePosition).toString(), "])" ), location = StringUtils.buildString(subjectData.subjectName, ":", gameEntity.name), - traceEvent = exposureEvent + traceEvent = exposureEvent, + clickCallback = { + if (mIsColumnCollection) { + SensorsBridge.trackColumnCollectionClick( + location = "合集详情", + columnCollectionName = mCollectionName, + columnCollectionId = mCollectionId, + gameColumnName = subjectData.subjectName ?: "", + gameColumnId = subjectData.subjectId ?: "", + gameId = gameEntity.id, + gameName = gameEntity.name ?: "", + text = "按钮", + columnCollectionPattern = CustomPageItem.collectionTypeToComponentName[mCollectionStyle] + ?: mCollectionStyle + ) + } else { + SensorsBridge.trackColumnClick( + location = "游戏专题详情", + gameColumnId = subjectData.subjectId ?: "", + gameColumnName = subjectData.subjectName ?: "", + gameId = gameEntity.id, + gameName = gameEntity.name ?: "", + text = "按钮" + ) + } + } ) DownloadItemUtils.updateItem( @@ -328,6 +391,31 @@ class SubjectAdapter( "头图", subjectData.subjectName ?: "", subjectData.subjectId ?: "", linkEntity.link ?: "", linkEntity.type ?: "", linkEntity.text ?: "", "专题详情", "" ) + if (mIsColumnCollection) { + SensorsBridge.trackColumnCollectionClick( + location = "合集详情", + columnCollectionName = mCollectionName, + columnCollectionId = mCollectionId, + gameColumnName = subjectData.subjectName ?: "", + gameColumnId = subjectData.subjectId ?: "", + gameId = gameEntity.id, + gameName = gameEntity.name ?: "", + text = "头图", + columnCollectionPattern = CustomPageItem.collectionTypeToComponentName[mCollectionStyle] ?: mCollectionStyle + ) + } else { + SensorsBridge.trackColumnClick( + location = "游戏专题详情", + gameColumnId = subjectData.subjectId ?: "", + gameColumnName = subjectData.subjectName ?: "", + gameId = gameEntity.id, + gameName = gameEntity.name ?: "", + linkId = linkEntity.link ?: "", + linkText = linkEntity.text ?: "", + linkType = linkEntity.type ?: "", + text = "头图" + ) + } } } else if (holder is FooterViewHolder) { holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver) @@ -344,11 +432,12 @@ class SubjectAdapter( exposureSourceList.addAll(mViewModel.exposureSourceList!!) } if (mIsColumnCollection) { - exposureSourceList.add(ExposureSource("排行榜")) - exposureSourceList.add(ExposureSource("专题", subjectData.subjectName.toString())) + exposureSourceList.add(ExposureSource("专题合集", "$mCollectionName+${CustomPageItem.collectionTypeToComponentName[mCollectionStyle] ?: mCollectionStyle}+$mCollectionId")) + exposureSourceList.add(ExposureSource("合集详情")) + exposureSourceList.add(ExposureSource("专题", "${subjectData.subjectName}+${subjectData.subjectStyleChinese}+${subjectData.subjectId}")) } else { var sourceString = "" - exposureSourceList.add(ExposureSource("专题", subjectData.subjectName.toString())) + exposureSourceList.add(ExposureSource("专题", "${subjectData.subjectName}+${subjectData.subjectStyleChinese}+${subjectData.subjectId}")) when (subjectStyle) { "tile" -> { @@ -401,11 +490,11 @@ class SubjectAdapter( val filterSize = mViewModel.selectedFilterSize.text - sourceString = - if (sourceString.isEmpty()) "$sort+$filterSize" else "$sourceString+$sort+$filterSize" - if (sourceString.isNotEmpty()) { - exposureSourceList.add(ExposureSource("专题详情", sourceString)) + if (isFilterOn) { + sourceString = + if (sourceString.isEmpty()) "$sort+$filterSize" else "$sourceString+$sort+$filterSize" } + exposureSourceList.add(ExposureSource("专题详情", sourceString)) } } } diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt index 9b227218e7..bdaa631ea8 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt @@ -38,6 +38,9 @@ open class SubjectListFragment : LazyListFragment() @@ -73,7 +76,10 @@ open class SubjectListFragment : LazyListFragment diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt index 22dbc4a939..582c3ddbaa 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectListViewModel.kt @@ -87,7 +87,6 @@ open class SubjectListViewModel( subjectData.tagType = response?.tag subjectData.isOrder = response?.order subjectData.briefStyle = response?.briefStyle ?: "" - subjectData.showSuffix = response?.showSuffix ?: true loadData() } diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectViewModel.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectViewModel.kt index 8c529f9bad..7e0c61f65d 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectViewModel.kt @@ -1,12 +1,10 @@ package com.gh.gamecenter.subject import android.app.Application -import android.text.TextUtils import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider -import com.gh.common.constant.Config import com.gh.gamecenter.common.retrofit.JSONObjectResponse import com.gh.gamecenter.common.retrofit.Response import com.gh.gamecenter.entity.SubjectData @@ -51,10 +49,7 @@ class SubjectViewModel( .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : JSONObjectResponse() { override fun onResponse(response: JSONObject) { - var name = response.getString("name") - if (!TextUtils.isEmpty(name) && !Config.isShowPlugin()) { - name = name.replace("插件", "游戏") - } + val name = response.getString("name") subjectData?.subjectName = name subjectNameLD.postValue(name) loadSubjectType() @@ -81,9 +76,9 @@ class SubjectViewModel( content.add(0, "全部") subjectData?.tagType = response.tag subjectData?.briefStyle = response.briefStyle - subjectData?.showSuffix = response.showSuffix subjectData?.isOrder = response.order subjectData?.isAdData = response.adIconActive + subjectData?.subjectStyle = response.columnStyle subjectSettingLD.postValue(response) } diff --git a/app/src/main/java/com/gh/gamecenter/subject/tab/SubjectTabFragment.kt b/app/src/main/java/com/gh/gamecenter/subject/tab/SubjectTabFragment.kt index 9891895d4b..94877e5395 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/tab/SubjectTabFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/tab/SubjectTabFragment.kt @@ -67,7 +67,7 @@ class SubjectTabFragment : BaseFragment() { fragments.add(element) } } else { - // 排行榜 + // 专题合集详情 mBinding.subjectTabContainer.visibility = View.VISIBLE subjectList.filterIndexed { index, subject -> @@ -93,8 +93,8 @@ class SubjectTabFragment : BaseFragment() { mBinding.subjectViewpager.setCurrentItem(position, false) } } - val categoryId = requireArguments().getString(EntranceConsts.KEY_GAME_COLLECTION_ID, "") - val categoryName = requireArguments().getString(EntranceConsts.KEY_GAME_COLLECTION_TITLE, "") + val categoryId = requireArguments().getString(EntranceConsts.KEY_COLUMN_COLLECTION_ID, "") + val categoryName = requireArguments().getString(EntranceConsts.KEY_COLUMN_COLLECTION_NAME, "") mBinding.subjectViewpager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { diff --git a/app/src/main/java/com/gh/gamecenter/tag/TagsActivity.kt b/app/src/main/java/com/gh/gamecenter/tag/TagsActivity.kt index 082f253433..e3440f9925 100644 --- a/app/src/main/java/com/gh/gamecenter/tag/TagsActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/tag/TagsActivity.kt @@ -51,7 +51,6 @@ class TagsActivity : DownloadToolbarActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setToolbarMenu(R.menu.menu_download) updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) } diff --git a/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt b/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt index c8dcf0db3d..9858dd29a0 100644 --- a/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt @@ -11,21 +11,21 @@ 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.common.constant.Constants -import com.gh.gamecenter.common.constant.EntranceConsts -import com.gh.gamecenter.common.utils.observeNonNull -import com.gh.gamecenter.common.utils.toColor -import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.common.baselist.ListAdapter import com.gh.gamecenter.common.baselist.ListFragment import com.gh.gamecenter.common.baselist.LoadType +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.observeNonNull +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.databinding.FragmentTagsBinding -import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.entity.SubjectSettingEntity import com.gh.gamecenter.entity.TagEntity import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.entity.GameEntity import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity import org.greenrobot.eventbus.Subscribe @@ -62,7 +62,7 @@ class TagsListFragment : ListFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - + initMenu(R.menu.menu_download) setNavigationTitle("标签详情") mCachedView?.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) diff --git a/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxActivity.kt b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxActivity.kt new file mode 100644 index 0000000000..0f83a65459 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxActivity.kt @@ -0,0 +1,31 @@ +package com.gh.gamecenter.toolbox + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.activity.ToolBarActivity +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.utils.updateStatusBarColor + + +class ToolBoxActivity : ToolBarActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) + } + + override fun onDarkModeChanged() { + super.onDarkModeChanged() + updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) + } + + companion object { + @JvmStatic + fun getIntent(context: Context, entrance: String?): Intent { + val bundle = Bundle() + bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance) + return getTargetIntent(context, ToolBoxActivity::class.java, ToolboxFragment::class.java, bundle) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxItemAdapter.kt b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxItemAdapter.kt index 655042f64c..0f5dd048c1 100644 --- a/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxItemAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxItemAdapter.kt @@ -5,7 +5,6 @@ import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.gh.common.constant.Config -import com.gh.gamecenter.NewsDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity.Companion.getWebByCollectionTools import com.gh.gamecenter.common.base.BaseRecyclerViewHolder @@ -17,6 +16,7 @@ import com.gh.gamecenter.common.utils.goneIf import com.gh.gamecenter.common.utils.safelyGetInRelease import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.databinding.ItemToolboxBinding +import com.gh.gamecenter.newsdetail.NewsDetailActivity import com.lightgame.adapter.BaseRecyclerAdapter class ToolBoxItemAdapter( diff --git a/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxBlockActivity.kt b/app/src/main/java/com/gh/gamecenter/toolbox/ToolboxFragment.kt similarity index 69% rename from app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxBlockActivity.kt rename to app/src/main/java/com/gh/gamecenter/toolbox/ToolboxFragment.kt index db65fa30f0..feb406fdf5 100644 --- a/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxBlockActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/toolbox/ToolboxFragment.kt @@ -1,65 +1,57 @@ package com.gh.gamecenter.toolbox -import android.content.Context -import android.content.Intent import android.os.Bundle import android.view.KeyEvent import android.view.MotionEvent import android.view.View -import android.view.View.OnFocusChangeListener import android.view.inputmethod.EditorInfo import android.widget.EditText import android.widget.TextView -import androidx.activity.viewModels +import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.SimpleItemAnimator import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity -import com.gh.gamecenter.common.base.activity.ToolBarActivity +import com.gh.gamecenter.common.base.fragment.ToolbarFragment import com.gh.gamecenter.common.baselist.LoadStatus import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.entity.SuggestType -import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.utils.TextHelper +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.observeNonNull +import com.gh.gamecenter.common.utils.toColor import com.gh.gamecenter.common.view.CustomLinkMovementMethod import com.gh.gamecenter.core.utils.SpanBuilder -import com.gh.gamecenter.databinding.ActivityToolboxBlockBinding +import com.gh.gamecenter.databinding.FragmentToolboxBinding import com.gh.gamecenter.help.HelpAndFeedbackBridge -import com.google.android.material.appbar.AppBarLayout import com.lightgame.utils.Util_System_Keyboard -import com.lightgame.utils.Utils - - -class ToolBoxBlockActivity : ToolBarActivity() { +class ToolboxFragment : ToolbarFragment() { private var mIsEverPause = false + private val mBinding by lazy { FragmentToolboxBinding.inflate(layoutInflater) } private val mViewModel: ToolBoxViewModel by viewModels() - private lateinit var mBinding: ActivityToolboxBlockBinding - private lateinit var mAdapter: ToolBoxBlockAdapter - private lateinit var mSearchAdapter: ToolBoxItemAdapter - private lateinit var mLayoutManager: LinearLayoutManager + private val mAdapter: ToolBoxBlockAdapter by lazy { ToolBoxBlockAdapter(requireContext(), mViewModel) } + private val mSearchAdapter: ToolBoxItemAdapter by lazy { ToolBoxItemAdapter(requireContext(), false, mViewModel) } + private val mLayoutManager: LinearLayoutManager by lazy { LinearLayoutManager(requireContext()) } private var mIsSearch = false // 记录页面状态 搜索页面/普通页面 - override fun getLayoutId() = R.layout.activity_toolbox_block + override fun getLayoutId(): Int = 0 + override fun getInflatedLayout(): View = mBinding.root override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - mBinding = ActivityToolboxBlockBinding.bind(mContentView) - mAdapter = ToolBoxBlockAdapter(this, mViewModel) - mSearchAdapter = ToolBoxItemAdapter(this, false, mViewModel) - mLayoutManager = LinearLayoutManager(this@ToolBoxBlockActivity) setNavigationTitle("光环工具箱") - updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) // 跳转到工具箱 https://gitlab.ghzs.com/pm/halo-app-issues/issues/636 - val gameId = intent.getStringExtra(EntranceConsts.KEY_GAMEID) - val targetUrl = intent.getStringExtra(EntranceConsts.KEY_URL) + val gameId = arguments?.getString(EntranceConsts.KEY_GAMEID) + val targetUrl = arguments?.getString(EntranceConsts.KEY_URL) if (!gameId.isNullOrEmpty() && !targetUrl.isNullOrEmpty()) { mViewModel.getGameToolBoxData(gameId) { for (toolbox in it) { if (targetUrl == toolbox.url) { mViewModel.addToHistoryList(toolbox) - startActivity(WebActivity.getWebByCollectionTools(this, toolbox, false)) + startActivity(WebActivity.getWebByCollectionTools(requireContext(), toolbox, false)) } } } @@ -70,22 +62,23 @@ class ToolBoxBlockActivity : ToolBarActivity() { toolboxRefresh.setOnRefreshListener { refresh() } - feedbackTv.text = SpanBuilder("需要其他工具,点击反馈").click(this@ToolBoxBlockActivity, 7, 11, R.color.primary_theme, false) { - HelpAndFeedbackBridge.startSuggestionActivity( - this@ToolBoxBlockActivity, - SuggestType.UPDATE, - null, - null - ) - }.build() + feedbackTv.text = + SpanBuilder("需要其他工具,点击反馈").click(requireContext(), 7, 11, R.color.primary_theme, false) { + HelpAndFeedbackBridge.startSuggestionActivity( + requireContext(), + SuggestType.UPDATE, + null, + null + ) + }.build() feedbackTv.movementMethod = CustomLinkMovementMethod.getInstance() closeIv.setOnClickListener { mBinding.feedbackCv.visibility = View.GONE } toolboxRv.adapter = mAdapter toolboxRv.layoutManager = mLayoutManager (toolboxRv.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false - toolboxAppbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset -> + toolboxAppbar.addOnOffsetChangedListener { _, verticalOffset -> toolboxRefresh.isEnabled = verticalOffset == 0 - }) + } } mViewModel.toolBoxBlockList.observeNonNull(this) { list -> mAdapter.setDataList(list) @@ -128,7 +121,7 @@ class ToolBoxBlockActivity : ToolBarActivity() { if (!mIsSearch && mIsEverPause) mAdapter.setHistoryList(mViewModel.getHistoryList()) } - private fun loadDone() { + override fun loadDone() { mBinding.toolboxRv.visibility = View.VISIBLE mBinding.toolboxRefresh.isRefreshing = false mBinding.reuseNoneData.root.visibility = View.GONE @@ -136,7 +129,7 @@ class ToolBoxBlockActivity : ToolBarActivity() { mBinding.reuseLoading.root.visibility = View.GONE } - private fun loadError() { + override fun loadError() { mBinding.toolboxRv.visibility = View.GONE mBinding.toolboxRefresh.isRefreshing = false mBinding.reuseNoneData.root.visibility = View.GONE @@ -149,7 +142,7 @@ class ToolBoxBlockActivity : ToolBarActivity() { } } - private fun loadEmpty() { + override fun loadEmpty() { mBinding.toolboxRv.visibility = View.GONE mBinding.toolboxRefresh.isRefreshing = false mBinding.reuseNoneData.root.visibility = View.VISIBLE @@ -159,7 +152,7 @@ class ToolBoxBlockActivity : ToolBarActivity() { if (mIsSearch) "这儿还没有内容噢~" else resources.getString(R.string.game_empty) mBinding.reuseNoneData.root.setOnClickListener { if (mIsSearch) HelpAndFeedbackBridge.startSuggestionActivity( - this, + requireContext(), SuggestType.UPDATE, null, null @@ -195,23 +188,18 @@ class ToolBoxBlockActivity : ToolBarActivity() { val searchEt = mBinding.reuseSearchBar.etSearch mBinding.reuseSearchBar.root.setPadding(16F.dip2px(), 8F.dip2px(), 16F.dip2px(), 8F.dip2px()) backTv.setOnClickListener { backToBlock() } - TextHelper.limitTheLengthOfEditText(searchEt, 20, - object : TextHelper.ExceedTextLengthLimitCallback { - override fun onExceed() { - toast("最多输入20字") - } - }) + TextHelper.limitTheLengthOfEditText(searchEt, 20) { toast("最多输入20字") } searchTv.setOnClickListener { if (searchEt.text.toString().isEmpty()) { - Utils.toast(this, R.string.search_hint) + toast(R.string.search_hint) return@setOnClickListener } - Util_System_Keyboard.hideSoftKeyboard(this, searchEt) + Util_System_Keyboard.hideSoftKeyboard(requireContext(), searchEt) search(true, searchEt.text.toString()) } - searchEt.onFocusChangeListener = OnFocusChangeListener { _: View?, hasFocus: Boolean -> + searchEt.onFocusChangeListener = View.OnFocusChangeListener { _: View?, hasFocus: Boolean -> if (!hasFocus) { - Util_System_Keyboard.hideSoftKeyboard(this, searchEt) + Util_System_Keyboard.hideSoftKeyboard(requireContext(), searchEt) } } searchEt.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> @@ -227,14 +215,13 @@ class ToolBoxBlockActivity : ToolBarActivity() { mBinding.reuseSearchBar.tvBack.visibility = if (mIsSearch) View.VISIBLE else View.GONE } - override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { + override fun onTouchEvent(ev: MotionEvent?) { if (ev?.action == MotionEvent.ACTION_DOWN) { - val v = currentFocus + val v = requireActivity().currentFocus if (isShouldHideKeyboard(v, ev)) { - Util_System_Keyboard.hideSoftKeyboard(this) + Util_System_Keyboard.hideSoftKeyboard(requireActivity()) } } - return super.dispatchTouchEvent(ev) } private fun isShouldHideKeyboard(v: View?, event: MotionEvent): Boolean { @@ -258,33 +245,20 @@ class ToolBoxBlockActivity : ToolBarActivity() { loadDone() } - override fun onBackPressed() { - if (mIsSearch) { + override fun onBackPressed(): Boolean { + return if (mIsSearch) { backToBlock() + true } else { super.onBackPressed() } } - override fun isAutoResetViewBackgroundEnabled(): Boolean = true - override fun onDarkModeChanged() { super.onDarkModeChanged() - updateStatusBarColor(R.color.ui_surface, R.color.ui_surface) - if (::mBinding.isInitialized) { - mBinding.run { - toolboxRv.adapter?.run { notifyItemRangeChanged(0, itemCount) } - feedbackCv.setCardBackgroundColor(R.color.ui_surface.toColor(this@ToolBoxBlockActivity)) - } - } - } - - companion object { - @JvmStatic - fun getIntent(context: Context, entrance: String?): Intent { - val intent = Intent(context, ToolBoxBlockActivity::class.java) - intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance) - return intent + mBinding.run { + toolboxRv.adapter?.run { notifyItemRangeChanged(0, itemCount) } + feedbackCv.setCardBackgroundColor(R.color.ui_surface.toColor(requireContext())) } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/update/UpdateHelper.kt b/app/src/main/java/com/gh/gamecenter/update/UpdateHelper.kt new file mode 100644 index 0000000000..bd35e1a68a --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/update/UpdateHelper.kt @@ -0,0 +1,265 @@ +package com.gh.gamecenter.update + +import android.content.Context +import android.os.Build +import androidx.core.app.NotificationManagerCompat +import androidx.fragment.app.FragmentActivity +import com.gh.common.exposure.ExposureUtils +import com.gh.common.exposure.ExposureUtils.logADownloadExposureEvent +import com.gh.common.util.PackageUtils +import com.gh.download.DownloadManager +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.common.utils.addMetaExtra +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.utils.* +import com.gh.gamecenter.core.utils.GsonUtils.toJson +import com.gh.gamecenter.entity.AppEntity +import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.fragment.UpdateDialogFragment +import com.gh.gamecenter.retrofit.RetrofitManager +import com.gh.ndownload.NDataChanger +import com.halo.assistant.HaloApp +import com.lightgame.download.DownloadEntity +import com.lightgame.download.FileUtils +import com.lightgame.utils.AppManager +import com.lightgame.utils.Utils +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import retrofit2.HttpException +import java.io.File +import java.text.SimpleDateFormat +import java.util.* + +/** + * 应用更新的辅助类 + */ +object UpdateHelper { + + private const val ONCE_ONLY_SECOND_DEFAULT = "ONCE_ONLY_SECOND_DEFAULT" + private const val ONCE_ONLY_SECOND_CLOSE = "ONCE_ONLY_SECOND_CLOSE" + private const val ONCE_ONLY_SECOND_OPEN = "ONCE_ONLY_SECOND_OPEN" + + private const val ALERT_EVERY_TIME_OPEN = "EVERY_TIME_OPEN" // 每次启动都提示 + private const val ALERT_ONCE_ONLY = "ONCE_ONLY" // 仅一次 + private const val ALERT_ONCE_ONLY_SECOND = "ONCE_ONLY_SECOND" // 仅一次(第二次打开) + private const val ALERT_NEVER = "NEVER" // 从不 + + private const val SP_KEY_SHOW_UPDATE_TIME = "show_update_time" // 上次显示更新弹窗的时间 + + // 缓存的更新实体 + private var mCachedUpdateEntity: AppEntity? = null + private val mApi by lazy { RetrofitManager.getInstance().api } + private val mSp by lazy { HaloApp.getInstance().getSharedPreferences("update", Context.MODE_PRIVATE) } + + private val mVersionName by lazy { +// "5.12.5-debug" + PackageUtils.getGhVersionName() + } + + private val mVersionCode by lazy { +// 410 + PackageUtils.getGhVersionCode() + } + + /** + * 检查更新 + * @param dumpCache 是否清除更新的实体缓存 (重新检查更新) + * @param resultCallback 结果回调,无论成功或失败都会回调,可在此 callback 检查是否有更新 + */ + fun getUpdate(dumpCache: Boolean, resultCallback: EmptyCallback) { + if (dumpCache) { + mCachedUpdateEntity = null + } else if (mCachedUpdateEntity != null) { + // 若缓存的更新实体不为空,则直接使用缓存的更新实体 + resultCallback.onCallback() + return + } + + val channel: String = HaloApp.getInstance().channel + mApi.getUpdate(mVersionName, mVersionCode, channel, Build.VERSION.SDK_INT) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + override fun onFailure(e: HttpException?) { + super.onFailure(e) + resultCallback.onCallback() + } + + override fun onResponse(response: AppEntity?) { + super.onResponse(response) + +// response?.isForce = true +// response?.alert = ALERT_EVERY_TIME_OPEN + + mCachedUpdateEntity = response + + resultCallback.onCallback() + } + }) + } + + /** + * 更新是否有效 + * @param ignoreSuppressOption 是否忽略提示 + */ + fun isUpdateValid(ignoreSuppressOption: Boolean): Boolean { + val updateEntity = mCachedUpdateEntity ?: return false + + if (updateEntity.versionCode >= mVersionCode) { + if (ignoreSuppressOption || ALERT_EVERY_TIME_OPEN == updateEntity.alert) { + // 手动检查更新或者后台配置每次启动都提示 + return true + } else if (ALERT_ONCE_ONLY == updateEntity.alert) { + // 仅显示一次更新 + if (mSp.getBoolean(getUpdateOnceOnlySpKey(), true)) { + return true + } + } else if (ALERT_ONCE_ONLY_SECOND == updateEntity.alert) { + // 仅显示一次更新(第二次打开) + val onceOnlySecond: String? = mSp.getString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_DEFAULT) + if (ONCE_ONLY_SECOND_OPEN == onceOnlySecond) { + return true + } + } else if (ALERT_NEVER != updateEntity.alert) { // 一天提示一次 + val showUpdateTime: String? = mSp.getString(SP_KEY_SHOW_UPDATE_TIME, null) + val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) + val today: String = format.format(Date()) + if (today != showUpdateTime) { + return true + } + } + } + return false + } + + /** + * 标记更新的使用情况 + */ + private fun markUpdate(updateEntity: AppEntity) { + if (ALERT_ONCE_ONLY == updateEntity.alert) { + // 仅显示一次更新 + mSp.edit().putBoolean(getUpdateOnceOnlySpKey(), false).apply() + } else if (ALERT_ONCE_ONLY_SECOND == updateEntity.alert) { + // 仅显示一次更新(第二次打开) + val onceOnlySecond: String? = mSp.getString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_DEFAULT) + if (ONCE_ONLY_SECOND_OPEN == onceOnlySecond) { + mSp.edit().putString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_CLOSE).apply() + } + if (ONCE_ONLY_SECOND_DEFAULT == onceOnlySecond) { + mSp.edit().putString(getUpdateOnceOnlySecondSpKey(), ONCE_ONLY_SECOND_OPEN).apply() + } + } else if (ALERT_NEVER != updateEntity.alert) { // 一天提示一次 + val showUpdateTime: String? = mSp.getString(SP_KEY_SHOW_UPDATE_TIME, null) + val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) + val today: String = format.format(Date()) + if (today != showUpdateTime) { + mSp.edit().putString(SP_KEY_SHOW_UPDATE_TIME, today).apply() + } + } + } + + /** + * 显示更新弹窗 + */ + fun showUpdateDialog(activity: FragmentActivity, callback: EmptyCallback) { + if (mCachedUpdateEntity == null) { + callback.onCallback() + return + } + + UpdateDialogFragment + .newInstance(mCachedUpdateEntity!!, callback) + .show(activity.supportFragmentManager, "update") + + markUpdate(mCachedUpdateEntity!!) + } + + /** + * 创建下载任务 + */ + fun createUpdate(updateEntity: AppEntity, isSilentUpdate: Boolean) { + var shouldCancelPreviousDownload = true // 是否应该取消旧更新任务 + + // 在部分设备上取消正在进行中的旧下载任务再创建新下载任务有机率造成新下载任务依然带有 "静默更新" 标签导致无法唤起安装 + // 所以这里对当前静默更新任务正在进行中的时候就不用删旧任务,直接改 meta 标签 + if (!isSilentUpdate + && DownloadManager.getInstance().isTaskDownloading(updateEntity.url) + ) { + try { + val entity = NDataChanger.downloadEntries[updateEntity.url] + if (entity != null) { + entity.addMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE, "不再是静默更新") + DownloadManager.getInstance().updateDownloadEntity(entity) + DownloadManager.getInstance().resume(entity, false) + shouldCancelPreviousDownload = false + } + } catch (e: Exception) { + // 出现异常走删旧下载任务重下流程 + shouldCancelPreviousDownload = true + e.printStackTrace() + } + } + + // 预下载完成或者还没进行预下载的都进这里,先删掉旧的下载文件再进行下载 + if (shouldCancelPreviousDownload) { + val path = getUpdateDownloadPath(updateEntity) + val file = File(path) + if (file.exists() && file.delete()) { + Utils.log(file.name + " file delete success.") + } + val event = logADownloadExposureEvent( + GameEntity(Constants.GHZS_GAME_ID, "光环助手V" + updateEntity.version), + null, + null, + ExposureUtils.DownloadType.DOWNLOAD + ) + val downloadEntity = DownloadEntity() + downloadEntity.url = updateEntity.url + downloadEntity.name = "光环助手V" + updateEntity.version + downloadEntity.path = path + downloadEntity.platform = "官方版" + downloadEntity.gameId = Constants.GHZS_GAME_ID + downloadEntity.exposureTrace = toJson(event) + downloadEntity.format = "apk" + if (isSilentUpdate) { + downloadEntity.addMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE, Constants.SILENT_UPDATE) + } + downloadEntity.packageName = HaloApp.getInstance().packageName + DownloadManager.getInstance().cancel(downloadEntity.url, true, true, false) + DownloadManager.getInstance().pauseAll() + AppExecutor.uiExecutor.executeWithDelay({ + DownloadManager.getInstance().add(downloadEntity) + }, 200) + } + } + + /** + * 更新文件是否已下载 + */ + fun isUpdateFileDownloaded(updateEntity: AppEntity): Boolean { + val file = File(getUpdateDownloadPath(updateEntity)) + + return file.exists() && PackageUtils.getPackageNameByPath(HaloApp.getInstance(), file.path) != null + } + + private fun getUpdateOnceOnlySpKey(): String { + return "UPDATE_ONCE_ONLY_KEY" + PackageUtils.getGhVersionCode() + } + + private fun getUpdateOnceOnlySecondSpKey(): String { + return "UPDATE_ONCE_ONLY_SECOND_KEY" + PackageUtils.getGhVersionCode() + } + + fun exitApp() { + NotificationManagerCompat.from(HaloApp.getInstance()).cancelAll() + AppManager.getInstance().finishAllActivity() + } + + fun getUpdateDownloadPath(updateEntity: AppEntity): String { + val md5 = MD5Utils.getUpdateMD5(updateEntity.url, updateEntity.content) + + return FileUtils.getDownloadPath(HaloApp.getInstance(), "光环助手V" + updateEntity.version + "_" + md5 + ".apk") + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/video/detail/HomeVideoFragment.kt b/app/src/main/java/com/gh/gamecenter/video/detail/HomeVideoFragment.kt index c353654562..0eaa80b1d0 100644 --- a/app/src/main/java/com/gh/gamecenter/video/detail/HomeVideoFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/video/detail/HomeVideoFragment.kt @@ -17,6 +17,7 @@ import com.gh.gamecenter.common.base.adapter.FragmentAdapter import com.gh.gamecenter.common.base.fragment.BaseLazyFragment import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.constant.EntranceConsts.IS_DETAIL_PAGE import com.gh.gamecenter.common.entity.LinkEntity import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.HorizontalScrollableView @@ -183,11 +184,22 @@ class HomeVideoFragment : BaseLazyFragment() { mNewHint?.visibleIf(!homeNewVideoTips) } setAdvertisement(0) + + val isDetailPage = arguments?.getBoolean(IS_DETAIL_PAGE) ?: false + if (isDetailPage) { + mBinding.ivBack.visibility = View.VISIBLE + mBinding.ivBack.setOnClickListener { + activity?.finish() + } + } } override fun onFragmentResume() { super.onFragmentResume() - DisplayUtils.setLightStatusBar(requireActivity(), false) + if (!mIsFromTabWrapper) { + DisplayUtils.transparentStatusBar(requireActivity()) + DisplayUtils.setLightStatusBar(requireActivity(), false) + } } fun getCurrentFragment(): VideoDetailContainerFragment? { diff --git a/app/src/main/java/com/gh/gamecenter/video/detail/VideoAdapter.kt b/app/src/main/java/com/gh/gamecenter/video/detail/VideoAdapter.kt index 384723fcf7..cc2ed6ca41 100644 --- a/app/src/main/java/com/gh/gamecenter/video/detail/VideoAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/video/detail/VideoAdapter.kt @@ -23,7 +23,6 @@ import com.gh.gamecenter.common.utils.ImageUtils import com.gh.gamecenter.common.utils.goneIf import com.gh.gamecenter.common.utils.putWidgetBusinessName import com.gh.gamecenter.common.utils.safelyGetInRelease -import com.gh.gamecenter.core.utils.EmptyCallback import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.entity.VideoEntity import com.gh.gamecenter.eventbus.EBDownloadStatus @@ -42,7 +41,8 @@ class VideoAdapter( private val mFragment: Fragment, private val mRecyclerView: RecyclerView, private val mRefreshLayout: SwipeRefreshLayout, - private val mViewModel: VideoDetailContainerViewModel + private val mViewModel: VideoDetailContainerViewModel, + private val mBasicExposureSourceList: ArrayList ) : RecyclerView.Adapter(), IExposable { var videoList = arrayListOf() @@ -223,7 +223,10 @@ class VideoAdapter( val exposureSourceList = arrayListOf() when { - mViewModel.isHomeVideo -> exposureSourceList.add(ExposureSource("视频流")) + mViewModel.isHomeVideo -> { + exposureSourceList.addAll(mBasicExposureSourceList) + exposureSourceList.add(ExposureSource("视频流")) + } mViewModel.entrance.contains("首页-游戏") -> { exposureSourceList.add(ExposureSource("新首页")) exposureSourceList.add(ExposureSource("游戏")) diff --git a/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailActivity.kt b/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailActivity.kt index 4604d458d2..2c3a67d64d 100644 --- a/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import com.gh.gamecenter.common.base.activity.BaseActivity import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.R +import com.gh.gamecenter.common.constant.EntranceConsts.IS_DETAIL_PAGE import java.util.* // 由于兼容性问题,本游戏详情页不应该被低于 Android 4.4 的设备打开 @@ -22,6 +23,7 @@ class VideoDetailActivity : BaseActivity() { val extras = intent.extras extras?.putString("uuid", uuid.toString()) + extras?.putBoolean(IS_DETAIL_PAGE, true) mContainerFragment = supportFragmentManager.findFragmentByTag(HomeVideoFragment::class.java.name) as? HomeVideoFragment ?: HomeVideoFragment().with(extras) as HomeVideoFragment diff --git a/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt b/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt index 497cdfb62c..7dd8e6104d 100644 --- a/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/video/detail/VideoDetailContainerFragment.kt @@ -3,10 +3,8 @@ package com.gh.gamecenter.video.detail import android.app.Activity import android.content.Intent import android.os.Bundle -import android.view.Gravity import android.view.LayoutInflater import android.view.View -import android.widget.RelativeLayout import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.recyclerview.widget.OrientationHelper @@ -24,6 +22,7 @@ import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.entity.NormalShareEntity import com.gh.gamecenter.common.eventbus.EBReuse import com.gh.gamecenter.common.eventbus.EBShare +import com.gh.gamecenter.common.exposure.ExposureSource import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.vertical_recycler.OnPagerListener import com.gh.gamecenter.common.view.vertical_recycler.PagerLayoutManager @@ -60,6 +59,7 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener { private lateinit var mLocation: String private lateinit var mUserViewModel: UserViewModel private lateinit var mBinding: FragmentVideoDetailContainerBinding + private lateinit var mBasicExposureSourceList: ArrayList private var mIsFirstIn = true private val mOldList = ArrayList() @@ -94,7 +94,7 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - + mBasicExposureSourceList = arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) ?: arrayListOf() mInitialVideoId = arguments?.getString(EntranceConsts.KEY_ID) ?: "" mLocation = arguments?.getString(EntranceConsts.KEY_LOCATION) ?: "" mReferer = arguments?.getString(EntranceConsts.KEY_REFERER) ?: "" @@ -154,7 +154,7 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener { mBinding.recyclerview.visibility = View.VISIBLE mBinding.attentionNoDataContainer.visibility = View.GONE if (!::mAdapter.isInitialized) { - mAdapter = VideoAdapter(this, mBinding.recyclerview, mBinding.refresh, mViewModel) + mAdapter = VideoAdapter(this, mBinding.recyclerview, mBinding.refresh, mViewModel, mBasicExposureSourceList) mExposureListener = ExposureListener(this, mAdapter) mBinding.recyclerview.addOnScrollListener(mExposureListener!!) mBinding.recyclerview.adapter = mAdapter @@ -446,7 +446,7 @@ class VideoDetailContainerFragment : BaseLazyFragment(), OnBackPressedListener { mViewModel.isPauseVideo = true } - if (mIsHomeVideo) { + if (mIsHomeVideo && !mIsFromTabWrapper) { postDelayedRunnable({ //这里设置不兼容低版本MIUI,否则可能导致TextureView被销毁,出现有声音无画面的情况 DisplayUtils.setLightStatusBar(requireActivity(), false, false) diff --git a/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt b/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt index df7227eb09..59206799b2 100644 --- a/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/video/upload/view/UploadVideoActivity.kt @@ -25,29 +25,28 @@ import android.widget.TextView import androidx.core.content.ContextCompat import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders -import com.gh.gamecenter.common.base.activity.ToolBarActivity -import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment -import com.gh.gamecenter.core.AppExecutor import com.gh.common.util.* -import com.gh.common.util.DialogUtils import com.gh.common.util.LogUtils -import com.gh.gamecenter.common.view.DrawableView import com.gh.gamecenter.CropImageActivity -import com.gh.gamecenter.NewsDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.WebActivity import com.gh.gamecenter.common.* +import com.gh.gamecenter.common.base.activity.ToolBarActivity +import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment import com.gh.gamecenter.common.callback.CancelListener import com.gh.gamecenter.common.callback.ConfirmListener import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.entity.NotificationUgc import com.gh.gamecenter.common.entity.SimpleGameEntity +import com.gh.gamecenter.common.mvvm.Status import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.DrawableView +import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.core.utils.* import com.gh.gamecenter.databinding.ActivityVideoUplaodBinding import com.gh.gamecenter.entity.* -import com.gh.gamecenter.common.mvvm.Status import com.gh.gamecenter.feature.entity.GameEntity +import com.gh.gamecenter.newsdetail.NewsDetailActivity import com.gh.gamecenter.qa.editor.GameActivity import com.gh.gamecenter.video.label.VideoLabelActivity import com.gh.gamecenter.video.poster.PosterEditActivity diff --git a/app/src/main/java/com/gh/gamecenter/vote/VoteAdapter.kt b/app/src/main/java/com/gh/gamecenter/vote/VoteAdapter.kt index c5e7a7ab88..83b5b222c3 100644 --- a/app/src/main/java/com/gh/gamecenter/vote/VoteAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/vote/VoteAdapter.kt @@ -15,7 +15,6 @@ import com.gh.common.util.DirectUtils.directToLinkPage import com.gh.common.util.NewFlatLogUtils import com.gh.gamecenter.CleanApkActivity import com.gh.gamecenter.GameDetailActivity.Companion.startGameDetailActivity -import com.gh.gamecenter.NewsDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.VoteViewHolder import com.gh.gamecenter.common.baselist.ListAdapter @@ -35,6 +34,7 @@ import com.gh.gamecenter.feature.entity.MeEntity import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.feature.game.SelectGameAdapter import com.gh.gamecenter.game.upload.GameUploadFragment +import com.gh.gamecenter.newsdetail.NewsDetailActivity import com.lightgame.utils.Utils class VoteAdapter( diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/BaseBottomTabFragment.kt b/app/src/main/java/com/gh/gamecenter/wrapper/BaseBottomTabFragment.kt new file mode 100644 index 0000000000..abae374c27 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/BaseBottomTabFragment.kt @@ -0,0 +1,127 @@ +package com.gh.gamecenter.wrapper + +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.widget.Checkable +import android.widget.LinearLayout +import androidx.viewbinding.ViewBinding +import androidx.viewpager2.adapter.FragmentStateAdapter +import androidx.viewpager2.widget.ViewPager2 +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.ToolbarFragment +import com.gh.gamecenter.common.utils.ImageUtils +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.core.utils.GsonUtils +import com.gh.gamecenter.databinding.PieceBottomTabBinding +import com.gh.gamecenter.entity.BottomTab + +/** + * 底部Tab 基类 + */ +abstract class BaseBottomTabFragment: ToolbarFragment() { + + protected var mViewPager: ViewPager2? = null + protected var mBottomTabContainer: LinearLayout? = null + protected val mBottomTabBindingList = ArrayList() + + protected var mCheckedIndex = 0 + + protected open fun provideBottomTabContainer(): LinearLayout? = mCachedView.findViewById(R.id.bottomTabContainer) + protected open fun provideViewPager(): ViewPager2? = mCachedView.findViewById(R.id.viewPager) + protected abstract fun provideAdapter(): FragmentStateAdapter + protected open fun isSmoothScroll() = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mViewPager = provideViewPager() + mBottomTabContainer = provideBottomTabContainer() + + initViewPager() + } + + protected fun initBottomTab(bottomTabList: List) { + mBottomTabBindingList.clear() + mBottomTabContainer?.run { + removeAllViews() + bottomTabList.forEachIndexed { index, bottomTab -> + addView( + provideBottomTabView(index, bottomTab), + LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT).apply { + weight = 1F + setMargins(0, 2F.dip2px(), 0, 0) + }) + } + } + } + + protected open fun provideBottomTabView(position: Int, bottomTab: BottomTab): View = + PieceBottomTabBinding.bind(LayoutInflater.from(requireContext()).inflate(R.layout.piece_bottom_tab, null)) + .apply { + tabTv.text = bottomTab.name + if (GsonUtils.isJSONValid(bottomTab.jsCode)) { + tabLottie.setAnimationFromJson(bottomTab.jsCode, bottomTab.id + bottomTab.name) + } + if (bottomTab.iconSelector != 0) { + tabIv.setImageResource(bottomTab.iconSelector) + } else { + ImageUtils.picasso + .load(Uri.parse(bottomTab.iconUnselect)) + .placeholder(R.drawable.occupy) + .into(tabIv) + } + mBottomTabBindingList.add(this as T) + root.setOnClickListener(this@BaseBottomTabFragment) + }.root + + protected fun initViewPager() { + mViewPager?.run { + isUserInputEnabled = false + adapter = provideAdapter() + registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { + super.onPageScrolled(position, positionOffset, positionOffsetPixels) + this@BaseBottomTabFragment.onPageScrolled(position, positionOffset, positionOffsetPixels) + } + + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + this@BaseBottomTabFragment.onPageSelected(position) + } + + override fun onPageScrollStateChanged(state: Int) { + super.onPageScrollStateChanged(state) + this@BaseBottomTabFragment.onPageScrollStateChanged(state) + } + }) + } + } + + override fun handleOnClick(view: View?): Boolean { + val toCheck = mBottomTabContainer?.indexOfChild(view) ?: -1 + if (toCheck != -1) { + mViewPager?.setCurrentItem(toCheck, isSmoothScroll()) + return true + } + return super.handleOnClick(view) + } + + protected open fun checkIndex(index: Int) { + if (index < mBottomTabBindingList.size && mCheckedIndex < mBottomTabBindingList.size) { + val binding = mBottomTabBindingList[index] + (binding.root as? Checkable)?.isChecked = true + if (index != mCheckedIndex) { + val checkedBinding = mBottomTabBindingList[mCheckedIndex] + (checkedBinding.root as? Checkable)?.isChecked = false + } + mCheckedIndex = index + } + } + + protected open fun onPageSelected(position: Int) { + checkIndex(position) + } + protected open fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} + protected open fun onPageScrollStateChanged(state: Int) {} +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/BaseTabWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/wrapper/BaseTabWrapperFragment.kt new file mode 100644 index 0000000000..a93a1f6b45 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/BaseTabWrapperFragment.kt @@ -0,0 +1,608 @@ +package com.gh.gamecenter.wrapper + +import android.graphics.Typeface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.core.graphics.ColorUtils +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import androidx.fragment.app.Fragment +import androidx.viewpager.widget.ViewPager +import com.gh.common.iinterface.IMultiTab +import com.gh.common.iinterface.ISmartRefreshContent +import com.gh.common.iinterface.ISuperiorChain +import com.gh.common.util.ViewPagerFragmentHelper +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.adapter.FragmentAdapter +import com.gh.gamecenter.common.base.fragment.BaseLazyFragment +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.json.json +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.TabIndicatorView +import com.gh.gamecenter.common.view.WrapContentDraweeView +import com.gh.gamecenter.databinding.TabItemMainBinding +import com.gh.gamecenter.entity.MultiTabNav +import com.gh.gamecenter.fragment.ReloadFragment +import com.gh.gamecenter.home.custom.CustomPageFragment +import com.google.android.material.tabs.TabLayout +import com.halo.assistant.fragment.WebFragment +import kotlin.math.abs + +/** + * 多Tab导航页 基类 + */ +abstract class BaseTabWrapperFragment : BaseLazyFragment(), IMultiTab { + protected var mViewPager: ViewPager? = null + protected var mTabLayout: TabLayout? = null + protected var mIndicatorView: TabIndicatorView? = null + protected var mNoneDataContainer: ViewGroup? = null + protected var mNoConnectionContainer: ViewGroup? = null + protected var mLoadingContainer: ViewGroup? = null + + protected var mTabBindingList = arrayListOf() + protected var mTabImageStyleList = arrayListOf() + + protected var mFragmentList = ArrayList() + + protected var mMultiTabNavId = "" + protected var mMultiTabNavName = "" + protected var mBottomTabName = "" + protected var mDefaultSelectedTab = -1 + protected var mLastSelectedPosition = 0 + + protected var mBackgroundColor: Int = 0 + protected var mBackgroundWhiteColor: Int = 0 + protected var mAmwayPrimaryColor: Int = 0 + protected var mTabDefaultColor: Int = 0 + protected var mTabSelectedColor: Int = 0 + protected var mTabDefaultLightColor: Int = 0 + + abstract fun provideViewModel(): TabWrapperViewModel + protected open fun provideTabLayout(): TabLayout? = mCachedView.findViewById(R.id.tabLayout) + protected open fun provideViewPager(): ViewPager? = mCachedView.findViewById(R.id.viewPager) + protected open fun provideIndicatorView(): TabIndicatorView? = mCachedView.findViewById(R.id.indicatorView) + protected open fun provideIndicatorWidth(): Int = DEFAULT_INDICATOR_WIDTH + protected open fun provideTabTextSize(): Float = DEFAULT_TAB_TEXT_SIZE + protected open fun provideTabDefaultColor(): Int = TAB_DEFAULT_COLOR + protected open fun provideTabSelectedColor(): Int = TAB_SELECTED_COLOR + + override fun provideMultiTabId(): String = mMultiTabNavId + override fun provideMultiTabName(): String = mMultiTabNavName + override fun provideCurrentTabEntity(): MultiTabNav.LinkMultiTabNav? = getCurrentTabEntity() + override fun provideLastSelectedPosition(): Int = mLastSelectedPosition + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + savedInstanceState?.let { mDefaultSelectedTab = it.getInt(LAST_SELECTED_POSITION) } + mMultiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: "" + mMultiTabNavName = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, "") ?: "" + mBottomTabName = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_NAME, "") ?: "" + + mNoneDataContainer = mCachedView.findViewById(R.id.reuse_none_data) + mNoConnectionContainer = mCachedView.findViewById(R.id.reuse_no_connection) + mLoadingContainer = mCachedView.findViewById(R.id.reuse_ll_loading) + } + + override fun onSaveInstanceState(outState: Bundle) { + mViewPager?.let { outState.putInt(LAST_SELECTED_POSITION, it.currentItem) } + super.onSaveInstanceState(outState) + } + + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() + mBackgroundColor = R.color.ui_background.toColor(requireContext()) + mBackgroundWhiteColor = R.color.ui_surface.toColor(requireContext()) + mAmwayPrimaryColor = R.color.amway_primary_color.toColor(requireContext()) + mTabDefaultColor = provideTabDefaultColor().toColor(requireContext()) + mTabSelectedColor = provideTabSelectedColor().toColor(requireContext()) + mTabDefaultLightColor = TAB_DEFAULT_COLOR_LIGHT.toColor(requireContext()) + + if (mMultiTabNavId.isNotEmpty()) { + provideViewModel().tabListLiveData.observe(viewLifecycleOwner) { + if (mDefaultSelectedTab == -1) { + mDefaultSelectedTab = provideViewModel().defaultTabPosition + } + + initViewPager(ArrayList(it)) + + // 当 tab 少于或等于一个的时候隐藏顶部 tab 栏,停用 nestedScroll + if (it.size <= 1) { + hideTab() + } + + mNoneDataContainer?.isVisible = it.isEmpty() + mNoConnectionContainer?.visibility = View.GONE + mLoadingContainer?.visibility = View.GONE + } + + provideViewModel().errorLiveData.observe(viewLifecycleOwner) { + mNoConnectionContainer?.visibility = View.VISIBLE + mLoadingContainer?.visibility = View.GONE + mNoConnectionContainer?.setOnClickListener { + provideViewModel().getMultiTabNav() + mNoConnectionContainer?.visibility = View.GONE + mLoadingContainer?.visibility = View.VISIBLE + } + } + + SensorsBridge.trackEvent( + "ViewSeveralTabPage", + "bottom_tab", + mBottomTabName, + "several_tab_page_name", + mMultiTabNavName, + "several_tab_page_id", + mMultiTabNavId + ) + } + } + + abstract fun hideTab() + + override fun onBackPressed(): Boolean { + mViewPager?.run { + val currentFragment = mFragmentList.safelyGetInRelease(currentItem) + if (currentFragment is WebFragment) { + return currentFragment.onBackPressed() + } + } + return super.onBackPressed() + } + + protected open fun initViewPager(tabEntityList: ArrayList) { + mViewPager = provideViewPager() + mTabLayout = provideTabLayout() + mIndicatorView = provideIndicatorView() + + val fragmentList = generateFragments(tabEntityList).apply { mFragmentList = this } + val tabTitleList = arrayListOf() + + tabEntityList.forEach { tabTitleList.add(it.name) } + + mViewPager?.run { + offscreenPageLimit = fragmentList.size + doOnScroll( + onPageSelected = { position -> + onPageSelected(position) + tabEntityList.safelyGetInRelease(position)?.let { logTabSelected(it, position) } + }, + onPageScrolled = { position, positionOffset, _ -> + onPageScrolled(position, positionOffset, tabEntityList) + } + ) + setRestoredCurItem(this) + adapter = FragmentAdapter(childFragmentManager, fragmentList, tabTitleList) + } + + mTabLayout?.setupWithViewPager(mViewPager) + mIndicatorView?.setupWithTabLayout(mTabLayout) + mIndicatorView?.setupWithViewPager(mViewPager) + mIndicatorView?.setIndicatorWidth(provideIndicatorWidth()) + + initTabView(tabEntityList) + } + + protected open fun generateFragments(tabEntityList: ArrayList): ArrayList { + val fragmentList = arrayListOf() + for ((index, tabEntity) in tabEntityList.withIndex()) { + val bundle = Bundle() + bundle.putAll(arguments) + bundle.putBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, false) + bundle.putBoolean(EntranceConsts.KEY_IS_FROM_TAB_WRAPPER, true) + bundle.putInt(EntranceConsts.KEY_TAB_COUNT, tabEntityList.size) + bundle.putInt(EntranceConsts.KEY_TAB_INDEX, index) + bundle.putInt(EntranceConsts.KEY_POSITION, index) + + val bottomTabExposureSource = arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST) + val multiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: "" + val multiTabNavName = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, "") ?: "" + val multiTabNavExposureSource = ExposureSource("多tab导航页", "${multiTabNavName}+${multiTabNavId}") + val tabExposureSource = ExposureSource("顶部tab", tabEntity.name) + val exposureSourceList = if (bottomTabExposureSource != null) { + bottomTabExposureSource + multiTabNavExposureSource + tabExposureSource + } else { + arrayListOf(multiTabNavExposureSource, tabExposureSource) + } + bundle.putParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST, exposureSourceList.toArrayList()) + + val fragment = when (tabEntity.link?.type) { + ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE -> { + val superiorChain = if (this is ISuperiorChain) this else null + bundle.putString(EntranceConsts.KEY_CUSTOM_PAGE_ID, tabEntity.link.link) + bundle.putString(EntranceConsts.KEY_CUSTOM_PAGE_NAME, tabEntity.link.text) + bundle.putString(EntranceConsts.KEY_TAB_NAME, tabEntity.name) + CustomPageFragment().setSuperiorChain(superiorChain).with(bundle) + } + + else -> tabEntity.link?.let { ViewPagerFragmentHelper.createFragment(this, bundle, it, true) } + ?: ReloadFragment() + } + fragmentList.add(fragment) + } + return fragmentList + } + + /** + * 设置 ViewPager 默认的选中项 + */ + protected open fun setRestoredCurItem(viewPager: ViewPager) { + // 设置默认 position 避免 position 为 0 的 fragment 被先加载显示再跳转至具体页面 + tryCatchInRelease { + val field = ViewPager::class.java.getDeclaredField("mRestoredCurItem") + field.isAccessible = true + field.set(viewPager, mDefaultSelectedTab) + mLastSelectedPosition = mDefaultSelectedTab + } + } + + /** + * 初始化每个需要显示的 TabView + */ + protected open fun initTabView(tabEntityList: ArrayList) { + for (i in 0 until tabEntityList.size) { + val tabView = mTabLayout?.getTabAt(i) ?: continue + val tabEntity = tabEntityList[i] + val tabTitle = if (tabView.text != null) tabView.text.toString() else "" + val tabViewBinding = generateTabView(tabTitle) + val tabImageStyle = if (tabEntity.img.isNullOrEmpty()) { + TAB_IMAGE_EMPTY + } else if (tabEntity.showImgOnSelected == true) { + TAB_IMAGE_ALWAYS_ON + } else { + TAB_IMAGE_HIDE_ON_SELECTED + } + + mTabBindingList.add(tabViewBinding) + mTabImageStyleList.add(tabImageStyle) + + if (!tabEntity.img.isNullOrEmpty()) { + tabViewBinding.titleTv.visibility = View.GONE + tabViewBinding.invisibleTitleTv.visibility = View.GONE + tabViewBinding.titleIv.setFixedHeight(16) + // 部分设备加载图片时会获取到错误的最小宽度,这里为它做个 64DP 的保底尺寸 + tabViewBinding.titleIv.setTag(ImageUtils.TAG_TARGET_WIDTH, 64F.dip2px()) + tabViewBinding.titleIv.display(tabEntity.img) + tabViewBinding.titleIv.registerLoadingCallback(object : WrapContentDraweeView.LoadingCallback { + override fun loaded() { + postRunnable { + mViewPager?.run { + // 图片 layout 完成后,更新 tabBar 和 indicatorView 的位置,避免偏移 + mTabLayout?.setScrollPosition(currentItem, 0F, false) + mIndicatorView?.generatePath(currentItem, 0F) + } + } + } + }) + } else { + tabViewBinding.titleTv.visibility = View.VISIBLE + tabViewBinding.invisibleTitleTv.visibility = View.INVISIBLE + } + tabView.customView = tabViewBinding.root + tabView.view.setPadding(0, 0, 0, 0) + tabView.view.setOnClickListener { + onTabViewClick() + } + + // 第一个和最后一个分别设置额外的外边距 + if (i == 0) { + tabView.view.updateLayoutParams { + this as LinearLayout.LayoutParams + setMargins(getFirstTabLeftMargin(), 0, 0, 0) + } + } else if (i == tabEntityList.size - 1) { + tabView.view.updateLayoutParams { + this as LinearLayout.LayoutParams + setMargins(0, 0, getLastTabRightMargin(), 0) + } + } + } + } + + protected open fun getFirstTabLeftMargin(): Int = 10F.dip2px() + + protected open fun getLastTabRightMargin(): Int = 10F.dip2px() + + protected open fun onTabViewClick() {} + + /** + * 生成 TabView + */ + protected open fun generateTabView(title: String): TabItemMainBinding { + val binding = TabItemMainBinding.inflate(LayoutInflater.from(requireContext())) + binding.titleTv.run { + text = title + textSize = provideTabTextSize() + setTextColor(mTabDefaultColor) + } + binding.invisibleTitleTv.run { + text = title + textSize = provideTabTextSize() + } + return binding + } + + protected open fun onPageSelected(position: Int) { + notifyChildFragmentLifecycle(position) + } + + abstract fun onPageScrolled(position: Int, positionOffset: Float, tabEntityList: ArrayList) + + protected open fun logTabSelected(tabEntity: MultiTabNav.LinkMultiTabNav, position: Int) { + SensorsBridge.trackEvent("SeveralTabPageTabSelect", json { + "position" to position + "tab_content" to tabEntity.name + "link_type" to tabEntity.link?.type + "link_id" to tabEntity.link?.link + "link_text" to tabEntity.link?.text + "bottom_tab" to mBottomTabName + "several_tab_page_name" to mMultiTabNavName + "several_tab_page_id" to mMultiTabNavId + }) + } + + /** + * 安全地从根据 position 获取 fragment + */ + protected fun getFragment(position: Int): Fragment? { + return if (position < mFragmentList.size) { + mFragmentList.safelyGetInRelease(position) + } else { + null + } + } + + protected fun getValidSmartRefreshContent(position: Int? = mViewPager?.currentItem): ISmartRefreshContent? { + if (position == null) return null + val fragment = getFragment(position) + return if (fragment?.isAdded == true) fragment as? ISmartRefreshContent else null + } + + // 滑动切换Tab是否使用缩放效果 + protected open fun isTabScaleEnabled(): Boolean = true + + /** + * 更新 tab 的样式 + * @param selectedPosition 选中的 tab + * @param positionOffset 与选中 tab 的位置偏移,为负时在选中的 tab 左边,为正时在选中 tab 的右边 + * @param currentTabDefaultColor tab默认颜色 + * @param currentTabSelectedColor tab选中颜色 + */ + protected open fun updateTabStyle(selectedPosition: Int, positionOffset: Float, currentTabDefaultColor: Int, currentTabSelectedColor: Int) { + if (mTabBindingList.isEmpty()) return + + val prePosition = selectedPosition - 1 + val nextPosition = selectedPosition + 1 + + // positionOffset 小于零,表示 indicator 当前位置处于选中的 tab 的左边 + val indicatorOnLeft = positionOffset < 0F + + val selectedTabBinding = mTabBindingList.safelyGetInRelease(selectedPosition) ?: return + val selectedTabImageStyle = mTabImageStyleList.safelyGetInRelease(selectedPosition) ?: return + + // 前一个 tab、当前选中的 tab、后一个 tab 的显示比例 + val preScaleRatio = 1 + abs(positionOffset) / 4 + val selectedScaleRatio = 1 + (1 - abs(positionOffset)) / 4 + val nextScaleRatio = 1 + positionOffset / 4 + + // 处理前一个 tab + if (prePosition != -1) { + mTabBindingList[prePosition].run { + if (indicatorOnLeft) { + when (mTabImageStyleList[prePosition]) { + TAB_IMAGE_ALWAYS_ON -> { + if (isTabScaleEnabled()) { + titleIv.scaleX = preScaleRatio + titleIv.scaleY = preScaleRatio + } + } + + TAB_IMAGE_HIDE_ON_SELECTED -> { + if (isTabScaleEnabled()) { + titleIv.scaleX = preScaleRatio + titleIv.scaleY = preScaleRatio + } + titleIv.visibility = View.VISIBLE + titleTv.visibility = View.GONE + invisibleTitleTv.visibility = View.GONE + } + + else -> { + titleIv.visibility = View.GONE + titleTv.visibility = View.VISIBLE + invisibleTitleTv.visibility = View.INVISIBLE + } + } + + if (isTabScaleEnabled()) { + titleTv.scaleX = preScaleRatio + titleTv.scaleY = preScaleRatio + } + titleTv.setTextColor( + ColorUtils.blendARGB( + currentTabDefaultColor, + currentTabSelectedColor, + abs(positionOffset) + ) + ) + } else { + titleTv.setTextColor(currentTabDefaultColor) + } + } + } + + // 更新当前选中的 tab + selectedTabBinding.run { + // 更新当前选中的 tab 的图片显示状态 + when (selectedTabImageStyle) { + TAB_IMAGE_ALWAYS_ON -> { + if (isTabScaleEnabled()) { + val scaleRatio = (1 + (1 - abs(positionOffset)) * 0.25).toFloat() + titleIv.scaleX = scaleRatio + titleIv.scaleY = scaleRatio + } + } + + else -> { + titleIv.visibility = View.GONE + titleTv.visibility = View.VISIBLE + invisibleTitleTv.visibility = View.INVISIBLE + + mIndicatorView?.visibility = View.VISIBLE + } + } + + // 选中常驻型的图片时隐藏 indicatorView + mIndicatorView?.goneIf(selectedTabImageStyle == TAB_IMAGE_ALWAYS_ON) + + if (isTabScaleEnabled()) { + titleTv.scaleX = selectedScaleRatio + titleTv.scaleY = selectedScaleRatio + } + titleTv.setTextColor( + ColorUtils.blendARGB( + currentTabDefaultColor, + currentTabSelectedColor, + 1 - abs(positionOffset) + ) + ) + } + + // 处理后一个 tab + if (nextPosition < mTabBindingList.size) { + mTabBindingList[nextPosition].run { + if (!indicatorOnLeft) { + when (mTabImageStyleList[nextPosition]) { + TAB_IMAGE_ALWAYS_ON -> { + if (isTabScaleEnabled()) { + titleIv.scaleX = nextScaleRatio + titleIv.scaleY = nextScaleRatio + } + } + + TAB_IMAGE_HIDE_ON_SELECTED -> { + if (isTabScaleEnabled()) { + titleIv.scaleX = nextScaleRatio + titleIv.scaleY = nextScaleRatio + } + titleIv.visibility = View.VISIBLE + titleTv.visibility = View.GONE + invisibleTitleTv.visibility = View.GONE + } + + else -> { + titleIv.visibility = View.GONE + titleTv.visibility = View.VISIBLE + invisibleTitleTv.visibility = View.INVISIBLE + } + } + if (isTabScaleEnabled()) { + titleTv.scaleX = nextScaleRatio + titleTv.scaleY = nextScaleRatio + } + titleTv.setTextColor( + ColorUtils.blendARGB( + currentTabDefaultColor, + currentTabSelectedColor, + positionOffset + ) + ) + } else { + titleTv.setTextColor(currentTabDefaultColor) + } + } + } + + // 多 tab 切换的时候可能会出现某些 tab 的文字没有回归到原始大小的问题的问题 (positionOffset 不保证连续) + for ((index, binding) in mTabBindingList.withIndex()) { + if (index != prePosition && index != selectedPosition && index != nextPosition) { + if (binding.titleTv.scaleX != 1F) { + binding.titleTv.scaleX = 1F + binding.titleTv.scaleY = 1F + } + binding.titleTv.setTextColor(currentTabDefaultColor) + if (binding.titleIv.scaleX != 1F) { + binding.titleIv.scaleX = 1F + binding.titleIv.scaleY = 1F + } + } + + if (index == selectedPosition) { + if (positionOffset == 0F) { + binding.titleTv.setTextColor(currentTabSelectedColor) + } + + binding.titleTv.setTypeface(binding.titleTv.typeface, Typeface.BOLD) + } else { + if (positionOffset == 0F) { + binding.titleTv.setTextColor(currentTabDefaultColor) + } + + binding.titleTv.setTypeface(null, Typeface.NORMAL) + } + } + } + + protected open fun notifyChildFragmentLifecycle(currentSelectedPosition: Int) { + tryWithDefaultCatch { + // 补充 Viewpager Fragment 的生命周期, 先调用旧选中 fragment 的 onPause 再当前的 onResume + // 避免部分被内嵌的 Fragment 不能正常运作 + if (mFragmentList.size > mLastSelectedPosition) { + val fragment: Fragment = mFragmentList[mLastSelectedPosition] + if (!fragment.isAdded) return + + fragment.onPause() + val childFragmentManager = fragment.childFragmentManager + val fragments = childFragmentManager.fragments + for (childFragment in fragments) { + childFragment.onPause() + } + } + if (mFragmentList.size > currentSelectedPosition) { + val fragment: Fragment = mFragmentList[currentSelectedPosition] + if (!fragment.isAdded) return + + fragment.onResume() + val childFragmentManager = fragment.childFragmentManager + val fragments = childFragmentManager.fragments + for (childFragment in fragments) { + childFragment.onResume() + } + } + mLastSelectedPosition = currentSelectedPosition + } + } + + protected fun getCurrentTabEntity(): MultiTabNav.LinkMultiTabNav? = + provideViewPager()?.currentItem?.let { + provideViewModel().tabListLiveData.value?.safelyGetInRelease(it) + } + + override fun onDarkModeChanged() { + super.onDarkModeChanged() + mBackgroundColor = R.color.ui_background.toColor(requireContext()) + mBackgroundWhiteColor = R.color.ui_surface.toColor(requireContext()) + mAmwayPrimaryColor = R.color.amway_primary_color.toColor(requireContext()) + mTabDefaultColor = provideTabDefaultColor().toColor(requireContext()) + mTabSelectedColor = provideTabSelectedColor().toColor(requireContext()) + mTabDefaultLightColor = TAB_DEFAULT_COLOR_LIGHT.toColor(requireContext()) + } + + companion object { + const val LAST_SELECTED_POSITION = "last_selected_position" + + var TAB_SELECTED_COLOR: Int = R.color.text_primary + var TAB_DEFAULT_COLOR: Int = R.color.text_secondary + + var TAB_DEFAULT_COLOR_LIGHT: Int = R.color.search_text_color_light + var DEFAULT_TAB_TEXT_SIZE = 16F + var DEFAULT_INDICATOR_WIDTH = 18 + + const val TAB_IMAGE_EMPTY = 0 + const val TAB_IMAGE_HIDE_ON_SELECTED = 1 + const val TAB_IMAGE_ALWAYS_ON = 2 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/MainFragmentStateAdapter.kt b/app/src/main/java/com/gh/gamecenter/wrapper/MainFragmentStateAdapter.kt new file mode 100644 index 0000000000..a702715c60 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/MainFragmentStateAdapter.kt @@ -0,0 +1,51 @@ +package com.gh.gamecenter.wrapper + + +import android.os.Bundle +import androidx.fragment.app.Fragment +import com.gh.common.util.ViewPagerFragmentHelper +import com.gh.gamecenter.common.base.adapter.BaseDiffFragmentStateAdapter +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.entity.BottomTab +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() + 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) { + ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE -> { + bundle.putParcelable(LinkEntity::class.java.simpleName, data.link) + SearchToolbarTabWrapperFragment().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) + SearchToolbarTabWrapperFragment().apply { arguments = bundle } + } + else -> ViewPagerFragmentHelper.createFragment(mFragment, bundle, data.link, false) + } + } else { + return ReloadFragment() + } + } + + override fun areItemsTheSame(oldItem: BottomTab?, newItem: BottomTab?): Boolean { + return oldItem?.link?.type == newItem?.link?.type + } + + override fun areContentsTheSame(oldItem: BottomTab?, newItem: BottomTab?): Boolean { + return oldItem?.link?.link == newItem?.link?.link + } +} diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperFragment.kt new file mode 100644 index 0000000000..128c11a633 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperFragment.kt @@ -0,0 +1,262 @@ +package com.gh.gamecenter.wrapper + +import android.graphics.PorterDuff +import android.net.Uri +import android.os.Bundle +import android.text.TextUtils +import android.view.View +import androidx.annotation.ColorRes +import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.view.doOnNextLayout +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.gh.gamecenter.R +import com.gh.gamecenter.ShellActivity +import com.gh.gamecenter.common.base.fragment.ToolbarFragment +import com.gh.gamecenter.common.callback.OnDoubleTapListener +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.eventbus.EBReuse +import com.gh.gamecenter.common.json.json +import com.gh.gamecenter.common.syncpage.SyncPageRepository +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.databinding.FragmentMainBinding +import com.gh.gamecenter.databinding.PieceBottomTabBinding +import com.gh.gamecenter.entity.BottomTab +import com.gh.gamecenter.login.entity.UserInfoEntity +import com.halo.assistant.HaloApp +import com.lightgame.listeners.OnBackPressedListener +import org.greenrobot.eventbus.EventBus + +class MainWrapperFragment : BaseBottomTabFragment(), OnBackPressedListener { + private val mBinding by lazy { FragmentMainBinding.inflate(layoutInflater) } + private var mViewModel: MainWrapperViewModel? = null + private val mBottomTabList = ArrayList() + private val mAdapter by lazy { MainFragmentStateAdapter(this) } + private var mTransparentStyle = false + + override fun getLayoutId(): Int = 0 + override fun getInflatedLayout(): View = mBinding.root + override fun provideAdapter(): FragmentStateAdapter = mAdapter + + val currentItem: Int + get() = mViewPager?.currentItem ?: 0 + + val currentTab: BottomTab? + get() = mBottomTabList.safelyGetInRelease(currentItem) + + override fun onCreate(savedInstanceState: Bundle?) { + mViewModel = viewModelProviderFromParent(MainWrapperViewModel.Factory(HaloApp.getInstance())) + super.onCreate(savedInstanceState) + DisplayUtils.transparentStatusBar(requireActivity()) + + mBinding.viewShadow.visibility = if (mIsDarkModeOn) View.GONE else View.VISIBLE + + mViewModel?.bottomTabListLiveData?.observe(this) { + mBottomTabList.clear() + mBottomTabList.addAll(it) + mViewPager?.offscreenPageLimit = it.size + initBottomTab(mBottomTabList) + mAdapter.submitList(mBottomTabList) + mViewPager?.doOnNextLayout { + setCurrentItem(mViewModel!!.defaultBottomTabIndex) + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + mViewModel?.tabSelectedLiveData?.observe(viewLifecycleOwner) { + val selectedTab = it.getContentWithHandled() + if (selectedTab is MainSelectedEvent.SelectedTab && selectedTab.bottomTabIndex != -1) { + setCurrentItem(selectedTab.bottomTabIndex) + } + } + + mViewModel?.realNameInfoUpdateLiveData?.observe(viewLifecycleOwner) { + updateRealNameErrorContainer() + } + } + + fun setCurrentItem(page: Int) { + if (page < mBottomTabList.size) { + mViewPager?.setCurrentItem(page, false) + onPageSelected(page) + changeBottomTabStyle(page) + trackBottomTabEvent(page) + } + } + + fun setCurrentItem(type: String) { + val toCheck = mBottomTabList.indexOfFirst { type == it.link?.type } + if (toCheck != -1) { + setCurrentItem(toCheck) + } + } + + override fun provideBottomTabView(position: Int, bottomTab: BottomTab): View { + return super.provideBottomTabView(position, bottomTab).apply { + setOnTouchListener(object : OnDoubleTapListener(requireContext()) { + override fun onDoubleTap() { + mViewModel?.bottomDoubleTabAction?.postValue(bottomTab) + } + }) + } + } + + override fun onPause() { + super.onPause() + SyncPageRepository.clearSyncData() + } + + override fun handleOnClick(view: View?): Boolean { + val toCheck = mBottomTabContainer?.indexOfChild(view) ?: return true + playTabAnimation(toCheck) + changeBottomTabStyle(toCheck) + EventBus.getDefault().post(EBReuse(Constants.FINISH_PULL_DOWN_PUSH)) + trackBottomTabEvent(toCheck) + return super.handleOnClick(view) + } + + private fun trackBottomTabEvent(position: Int) { + SensorsBridge.trackEvent("HomeBottomTabSelect", json { + "tab_content" to mBottomTabList.safelyGetInRelease(position)?.name + "link_type" to mBottomTabList.safelyGetInRelease(position)?.link?.type + "link_id" to mBottomTabList.safelyGetInRelease(position)?.link?.link + "link_text" to mBottomTabList.safelyGetInRelease(position)?.link?.text + "position" to position + }) + } + + override fun onHandleBackPressed(): Boolean { + val currentFragment = childFragmentManager.findFragmentByTag(mAdapter.getFragmentTag(mBinding.viewPager.currentItem)) + if (currentFragment != null && currentFragment is ToolbarFragment) { + return currentFragment.onBackPressed() + } + return false + } + + private fun playTabAnimation(toCheck: Int) { + mBottomTabBindingList.forEachIndexed { index, binding -> + if (index == toCheck) { + playAnimation(binding) + } else { + stopAnimation(binding) + } + } + } + + private fun playAnimation(binding: PieceBottomTabBinding) { + binding.tabIv.visibility = View.INVISIBLE + binding.tabLottie.run { + visibility = View.VISIBLE + playAnimation() + doOnAnimationEnd { + visibility = View.GONE + binding.tabIv.visibility = View.VISIBLE + } + } + } + + private fun stopAnimation(binding: PieceBottomTabBinding) { + binding.tabIv.visibility = View.VISIBLE + binding.tabLottie.visibility = View.GONE + } + + private fun changeBottomTabStyle(toCheck: Int) { + if (mBottomTabList.safelyGetInRelease(toCheck)?.isTransparentStyle == true) { + if (!mTransparentStyle) { + ConstraintSet().apply { + clone(mBinding.root) + clear(mBinding.viewPager.id, ConstraintSet.BOTTOM) + connect(mBinding.viewPager.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM) + }.applyTo(mBinding.root) + } + + mTransparentStyle = true + mBinding.viewShadow.visibility = View.GONE + mBottomTabContainer?.setBackgroundColor(R.color.transparent.toColor(requireContext())) + changeTabImageColor(R.color.text_A1A5B7, PorterDuff.Mode.SRC_ATOP, toCheck) + } else { + if (mTransparentStyle) { + ConstraintSet().apply { + clone(mBinding.root) + clear(mBinding.viewPager.id, ConstraintSet.BOTTOM) + connect(mBinding.viewPager.id, ConstraintSet.BOTTOM, mBinding.bottomTabContainer.id, ConstraintSet.TOP) + }.applyTo(mBinding.root) + } + + mTransparentStyle = false + mBinding.viewShadow.visibility = if (mIsDarkModeOn) View.GONE else View.VISIBLE + mBottomTabContainer?.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) + changeTabImageColor( + if (mIsDarkModeOn) R.color.text_A1A5B7 else R.color.text_50556B, + if (mIsDarkModeOn) PorterDuff.Mode.SRC_ATOP else PorterDuff.Mode.DST, + toCheck + ) + } + changeTabTextColor(toCheck) + } + + private fun changeTabImageColor(@ColorRes colorRes: Int, mode: PorterDuff.Mode, toCheck: Int) { + mBottomTabBindingList.forEachIndexed { index, binding -> + if (index != toCheck) { + binding.tabIv.setColorFilter(colorRes.toColor(requireContext()), mode) + } else { + binding.tabIv.colorFilter = null + } + } + } + + private fun changeTabTextColor(toCheck: Int) { + mBottomTabBindingList.forEachIndexed { index, binding -> + val isChecked = index == toCheck + val unselectColor = + if (mBottomTabList.safelyGetInRelease(toCheck)?.isTransparentStyle == true) R.color.text_A1A5B7 else R.color.tab_text_unselect + binding.tabTv.setTextColor((if (isChecked) R.color.text_theme else unselectColor).toColor(requireContext())) + } + } + + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + mBottomTabList.forEachIndexed { index, bottomTab -> + if (index < mBottomTabBindingList.size && bottomTab.iconSelector == 0) { + ImageUtils.picasso + .load(Uri.parse(if (position == index) bottomTab.iconSelect else bottomTab.iconUnselect)) + .placeholder(R.drawable.occupy) + .into(mBottomTabBindingList[index].tabIv) + } + } + } + + private fun updateRealNameErrorContainer() { + val deviceCertificationInfoString = + SPUtils.getString(Constants.SP_DEVICE_CERTIFICATION_PREFIX + HaloApp.getInstance().gid) + + // 未点过关闭按钮并且处于实名认证失败状态的,显示提示 view + if (!SPUtils.getBoolean(Constants.SP_REALNAME_ERROR_HINT_IGNORED) + && !TextUtils.isEmpty(deviceCertificationInfoString) + ) { + val entity: UserInfoEntity? = deviceCertificationInfoString.toObject() + if (entity?.idCard != null && entity.idCard!!.status == 2) { + mBinding.realNameErrorContainer.visibility = View.VISIBLE + mBinding.realNameErrorCloseIv.enlargeTouchArea(100) + mBinding.realNameErrorCloseIv.setOnClickListener { + SPUtils.setBoolean(Constants.SP_REALNAME_ERROR_HINT_IGNORED, true) + mBinding.realNameErrorContainer.visibility = View.GONE + } + mBinding.realNameErrorContainer.setOnClickListener { + startActivity(ShellActivity.getIntent(requireActivity(), ShellActivity.Type.REAL_NAME_INFO, null)) + } + } else { + mBinding.realNameErrorContainer.visibility = View.GONE + } + } + } + + override fun onDarkModeChanged() { + super.onDarkModeChanged() + changeBottomTabStyle(mBinding.viewPager.currentItem) + } +} diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperRepository.kt b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperRepository.kt new file mode 100644 index 0000000000..55734f8764 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperRepository.kt @@ -0,0 +1,250 @@ +package com.gh.gamecenter.wrapper + +import android.annotation.SuppressLint +import androidx.lifecycle.MutableLiveData +import com.gh.common.util.HomeBottomBarHelper +import com.gh.common.util.ViewPagerFragmentHelper +import com.gh.gamecenter.common.entity.LaunchRedirect +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.utils.singleToMain +import com.gh.gamecenter.core.utils.SingletonHolder +import com.gh.gamecenter.entity.BottomTab +import com.gh.gamecenter.entity.DataUnionEntity +import com.gh.gamecenter.entity.MultiTabNav +import com.gh.gamecenter.home.custom.model.CustomPageData +import com.gh.gamecenter.home.custom.model.CustomPageItem +import com.gh.gamecenter.retrofit.RetrofitManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.launch + +class MainWrapperRepository { + private val mNewApi = RetrofitManager.getInstance().newApi + private var mMainDataIsLoaded = false // 首页数据是否已加载 + + private var mHighPrioritySelectedBottomType = "" // 首页底部 tab 选中的类型 (优先级最高) + + private var mHighPrioritySelectedBottomTabId = "" // 首页底部 tab 选中的id (优先级比 default 高) + private var mHighPrioritySelectedTopTabId = "" // 首页顶部 tab 选中的id (优先级比 default 高) + private var mHighPrioritySelectedNavId = "" // 首页顶部 tab 所属的 navId + + val highPrioritySelectedNavId + get() = mHighPrioritySelectedNavId + val highPrioritySelectedTopTabId + get() = mHighPrioritySelectedTopTabId + + var defaultNavId = "" + var defaultCustomPageId = "" + var defaultBottomTabIndex = 0 + + val bottomTabListLiveData = MutableLiveData>() + val multiTabNavLiveData = MutableLiveData() + val customPageLiveData = MutableLiveData() + val errorLiveData = MutableLiveData() + + private val mTabSelectEventFlow = MutableSharedFlow() + val tabSelectEventFlow = mTabSelectEventFlow as SharedFlow + + /** + * 发送首次启动跳转事件的选中事件 + */ + fun sendSelectTabEvent(launchRedirect: LaunchRedirect) { + if (!mMainDataIsLoaded) { + mHighPrioritySelectedBottomTabId = launchRedirect.link ?: "" + mHighPrioritySelectedTopTabId = launchRedirect.topTabLink?.link ?: "" + mHighPrioritySelectedNavId = launchRedirect.navTabLink?.link ?: "" + } else { + CoroutineScope(SupervisorJob()).launch { + val bottomTabIndex = bottomTabListLiveData.value?.indexOfFirst { + it.id == launchRedirect.link + } ?: -1 + val topTabIndex = multiTabNavLiveData.value?.linkMultiTabNav?.indexOfFirst { + it.id == launchRedirect.topTabLink?.link + } ?: -1 + mTabSelectEventFlow.emit( + MainSelectedEvent.SelectedTab( + bottomTabIndex = bottomTabIndex, + navTabId = launchRedirect.navTabLink?.link ?: "", + topTabIndex = topTabIndex, + topTabId = launchRedirect.topTabLink?.link ?: "" + ) + ) + } + } + } + + /** + * 发送首页底部 tab 选中事件,选中找到的第一个 type (优先级最高) + * @param bottomTabType 底部 tab 选中的类型 + */ + fun sendSelectTabEvent(bottomTabType: String) { + if (!mMainDataIsLoaded) { + mHighPrioritySelectedBottomType = bottomTabType + } else { + CoroutineScope(SupervisorJob()).launch { + val bottomTabIndex = bottomTabListLiveData.value?.indexOfFirst { + it.link?.type == bottomTabType + } ?: -1 + mTabSelectEventFlow.emit( + MainSelectedEvent.SelectedTab( + bottomTabIndex = bottomTabIndex, + navTabId = defaultNavId, + topTabIndex = -1, + topTabId = "" + ) + ) + } + } + } + + /** + * 发送首页选中事件,重新选中默认的顶部和底部 tab + */ + fun sendReSelectDefaultTabEvent() { + CoroutineScope(SupervisorJob()).launch { + mTabSelectEventFlow.emit( + MainSelectedEvent.SelectedTab( + bottomTabIndex = defaultBottomTabIndex, + navTabId = defaultNavId, + topTabIndex = -1, + topTabId = "" + ) + ) + } + } + + /** + * 发送首页选中事件 + * @param bottomTabIndex 底部 tab 选中的 index + * @param topTabIndex 顶部 tab 选中的 index + */ + fun sendSelectTabEvent(bottomTabIndex: Int, topTabIndex: Int) { + CoroutineScope(SupervisorJob()).launch { + mTabSelectEventFlow.emit( + MainSelectedEvent.SelectedTab( + bottomTabIndex = bottomTabIndex, + topTabIndex = topTabIndex + ) + ) + } + } + + @SuppressLint("CheckResult") + fun getDataUnion() { + processBottomTabData(emptyList()) + mNewApi.dataUnion + .compose(singleToMain()) + .subscribe(object : BiResponse() { + override fun onSuccess(data: DataUnionEntity) { + processBottomTabData(data.bottomTab) + processMultiTabData(data.multiTabNav) + customPageLiveData.postValue(data.customPage?.apply { + linkPullDownPush?.isSlideBackgroundColorEnable = + customsComponents.firstOrNull()?.linkCommonCollection?.layout == CustomPageItem.COMMON_CONTENT_COLLECTION_LAYOUT_BANNER + }) + + mMainDataIsLoaded = true + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + processBottomTabData(emptyList()) + errorLiveData.postValue(exception) + } + }) + } + + private fun processBottomTabData(bottomTabList: List) { + if (bottomTabList.isNotEmpty()) { + HomeBottomBarHelper.updateDefaultHomeBottomTabData(bottomTabList) + + var preSelectedTab: BottomTab? = null + + // 如果有优先级最高的 tab type,则将优先级更高的选中 tab 设置为 default + if (mHighPrioritySelectedBottomType.isNotEmpty()) { + preSelectedTab = bottomTabList.firstOrNull { + it.link?.type == mHighPrioritySelectedBottomType + } + + mHighPrioritySelectedBottomType = "" + } + + // 如果有优先级高的选中 tab id,则将优先级更高的选中 tab 设置为 default + if (preSelectedTab == null && mHighPrioritySelectedBottomTabId.isNotEmpty()) { + preSelectedTab = bottomTabList.firstOrNull { + it.id == mHighPrioritySelectedBottomTabId + } + + mHighPrioritySelectedBottomTabId = "" + } + + // 将优先级高的选中 tab 设置为 default + if (preSelectedTab != null) { + bottomTabList.forEach { it.default = false } + preSelectedTab.default = true + } + + bottomTabList.forEachIndexed { index, bottomTab -> + if (bottomTab.default) { + defaultBottomTabIndex = index + } + if (bottomTab.link?.type == ViewPagerFragmentHelper.TYPE_VIDEO_STREAM) { + bottomTab.isTransparentStyle = true + } + } + + bottomTabListLiveData.value = 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 { + bottomTabListLiveData.value = 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 ?: "" + } + } + } + + private fun processMultiTabData(multiTabNav: MultiTabNav?) { + // 如果有优先级更高的选中 tab id,则将优先级更高的选中 tab 设置为 default + if (mHighPrioritySelectedTopTabId.isNotEmpty() && mHighPrioritySelectedNavId == multiTabNav?.id) { + val selectedTab = multiTabNav.linkMultiTabNav.firstOrNull { + it.link?.link == mHighPrioritySelectedTopTabId + } + + // 将优先级更高的选中 tab 设置为 default + if (selectedTab != null) { + multiTabNav.linkMultiTabNav.forEach { it.default = false } + selectedTab.default = true + } + + mHighPrioritySelectedTopTabId = "" + } + + defaultCustomPageId = + multiTabNav?.linkMultiTabNav?.find { it.link?.type == ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE && it.default }?.link?.link ?: "" + multiTabNavLiveData.postValue(multiTabNav) + } + + companion object : SingletonHolder({ MainWrapperRepository() }) +} + +sealed class MainSelectedEvent { + + /** + * 首页子页面选中 + * @param bottomTabIndex 首页底部 tab 选中的 index + * @param navTabId 首页顶部 tab 的 id + * @param topTabIndex 首页顶部 tab 选中的 index + * @param topTabId 首页顶部 tab 的 id + */ + data class SelectedTab( + val bottomTabIndex: Int = -1, + val navTabId: String = "", + val topTabIndex: Int = -1, + val topTabId: String = "" + ) : MainSelectedEvent() + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperViewModel.kt b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperViewModel.kt new file mode 100644 index 0000000000..3d6809cce5 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/MainWrapperViewModel.kt @@ -0,0 +1,105 @@ +package com.gh.gamecenter.wrapper + +import android.app.Application +import androidx.lifecycle.* +import com.gh.common.util.CheckLoginUtils +import com.gh.common.util.RealNameHelper +import com.gh.gamecenter.common.retrofit.Response +import com.gh.gamecenter.entity.BottomTab +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 io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.transformWhile +import okhttp3.MediaType +import okhttp3.RequestBody +import okhttp3.ResponseBody +import org.json.JSONObject +import retrofit2.HttpException + +class MainWrapperViewModel(application: Application, private val mRepository: MainWrapperRepository) : + AndroidViewModel(application) { + private val mApi = RetrofitManager.getInstance().api + + val defaultBottomTabIndex + get() = mRepository.defaultBottomTabIndex + val bottomTabListLiveData = mRepository.bottomTabListLiveData + val errorLiveData = mRepository.errorLiveData + + val accelerateNotificationPopup = MutableLiveData?>() + + private var lastAccelerateNotificationPopupRequestedTime: Long = 0 + + val tabSelectedLiveData = mRepository.tabSelectEventFlow + .map { it as? MainSelectedEvent.SelectedTab } + .transformWhile { originalEvent -> + emit(Event(originalEvent)) + true + } + .asLiveData() + + val realNameInfoUpdateLiveData = RealNameHelper.realNameInfoUpdateLiveData + + val bottomDoubleTabAction = MutableLiveData() + + /** + * 请求各种弹窗的数据 + */ + fun requestAllDialogData() { + requestAccelerateNotificationPopup() + } + + fun requestAccelerateNotificationPopup() { + if (CheckLoginUtils.isLogin()) { + if (lastAccelerateNotificationPopupRequestedTime != 0L + && System.currentTimeMillis() - lastAccelerateNotificationPopupRequestedTime < 15 * 1000L) { + return // 15 秒内不重复请求 + } + + lastAccelerateNotificationPopupRequestedTime = System.currentTimeMillis() + + mApi.getAccelerateNotificationPopup(UserManager.getInstance().userId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response>() { + override fun onResponse(response: List?) { + super.onResponse(response) + accelerateNotificationPopup.postValue(response) + } + + override fun onFailure(e: HttpException?) { + super.onFailure(e) + accelerateNotificationPopup.postValue(null) + } + }) + } else { + accelerateNotificationPopup.postValue(null) + } + } + + fun postMessageRead(messageId: String) { + val jsonObject = JSONObject() + jsonObject.put("type", "system_message") + val body = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString()) + RetrofitManager.getInstance().api.postMessageRead(UserManager.getInstance().userId, messageId, body) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Response() { + }) + } + + class Factory( + private val mApplication: Application, + ) : ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T { + return MainWrapperViewModel(mApplication, MainWrapperRepository.getInstance()) as T + } + } + + companion object { + const val SHOULD_SHOW_OPENING_DIALOG = "show_opening_dialog" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/SearchToolbarTabWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/wrapper/SearchToolbarTabWrapperFragment.kt new file mode 100644 index 0000000000..60a266f84f --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/SearchToolbarTabWrapperFragment.kt @@ -0,0 +1,1585 @@ +package com.gh.gamecenter.wrapper + +import android.graphics.Color +import android.graphics.Typeface +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.os.Message +import android.view.View +import android.view.ViewGroup +import android.view.ViewGroup.MarginLayoutParams +import android.widget.ImageView +import android.widget.RelativeLayout +import android.widget.TextView +import androidx.core.graphics.ColorUtils +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import com.gh.common.constant.Config +import com.gh.common.databind.BindingAdapters +import com.gh.common.exposure.ExposureManager +import com.gh.common.iinterface.ISearchToolbarTab +import com.gh.common.iinterface.ISmartRefresh +import com.gh.common.iinterface.ISmartRefreshContent +import com.gh.common.iinterface.ISuperiorChain +import com.gh.common.prioritychain.AccelerateNotificationHandler +import com.gh.common.prioritychain.GlobalPriorityChainHelper +import com.gh.common.prioritychain.PriorityChain +import com.gh.common.prioritychain.PullDownPushHandler +import com.gh.common.util.* +import com.gh.common.util.LogUtils +import com.gh.common.util.NewLogUtils +import com.gh.download.DownloadManager +import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.amway.AmwayFragment +import com.gh.gamecenter.common.base.adapter.FragmentAdapter +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.common.eventbus.EBReuse +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.json.json +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.common.view.TabIndicatorView +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.MD5Utils +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.core.utils.TimeElapsedHelper +import com.gh.gamecenter.databinding.FragmentSearchToolbarTabWrapperBinding +import com.gh.gamecenter.entity.BottomTab +import com.gh.gamecenter.entity.GameUpdateEntity +import com.gh.gamecenter.entity.MultiTabNav +import com.gh.gamecenter.entity.PullDownPush +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.eventbus.EBPackage +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.time.TimeUtil +import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment +import com.gh.gamecenter.home.custom.CustomPageFragment +import com.gh.gamecenter.home.video.ScrollCalculatorHelper +import com.gh.gamecenter.message.MessageUnreadViewModel +import com.gh.gamecenter.packagehelper.PackageViewModel +import com.gh.gamecenter.teenagermode.TeenagerModeActivity +import com.gh.gamecenter.wrapper.SearchToolbarTabWrapperFragment.Style +import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.tabs.TabLayout +import com.halo.assistant.HaloApp +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity +import com.scwang.smartrefresh.layout.api.RefreshHeader +import com.scwang.smartrefresh.layout.api.RefreshLayout +import com.scwang.smartrefresh.layout.constant.RefreshState +import com.scwang.smartrefresh.layout.listener.SimpleMultiPurposeListener +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder +import com.shuyu.gsyvideoplayer.video.base.GSYVideoView +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import kotlin.math.abs +import kotlin.math.roundToInt + +/** + * 一级页面,带搜索栏的多tab导航页,支持下拉推送, + * 支持三种样式:搜索栏+无Tab、单行搜索栏+Tab、双行搜索栏+Tab + * @see Style + */ +class SearchToolbarTabWrapperFragment : BaseTabWrapperFragment(), ISearchToolbarTab, ISmartRefresh, ISuperiorChain { + private val mBinding by lazy { FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater) } + private val mViewModel: SearchToolbarTabWrapperViewModel by lazy { + viewModelProviderFromParent( + SearchToolbarTabWrapperViewModel.Factory(mMultiTabNavId, mNoTabLinkEntity?.link ?: ""), + mMultiTabNavId + ) + } + + private var mDownloadHintTv: TextView? = null + private var mSearchHintTv: TextView? = null + private var mSearchContainerView: View? = null + private var mDownloadView: View? = null + private var mMessageUnread: TextView? = null + private var mSearchRightView: ImageView? = null + private var mTeenagerModelView: View? = null + + private var mCurrentAppBarColor: Int = 0 + private var mCurrentTabSelectedColor: Int = 0 + private var mCurrentTabDefaultColor: Int = 0 + private var mCurrentIndicatorDrawable: Int = 0 + + private var mMainWrapperViewModel: MainWrapperViewModel? = null + private val mUnreadViewModel: MessageUnreadViewModel by lazy { + viewModelProvider( + MessageUnreadViewModel.Factory( + HaloApp.getInstance().application + ) + ) + } + private val mPackageViewModel: PackageViewModel by lazy { viewModelProvider(PackageViewModel.Factory()) } + private lateinit var mElapsedHelper: TimeElapsedHelper + + private var mStyle = Style.TWO_LINES_TAB + + private val mStatusBarHeight by lazy { DisplayUtils.getStatusBarHeight(requireContext().resources) } + private var mIsDisplayingLightContent = false + private var mOffsetCritical = 0.7F + private var mPullDownPushAction = "" // 触发行为(主动展开/自动展开) + private var mShowTwoLevel = false + private var mIsDragging = false + private var mPullDownPushClick = false + private var mHeaderOffset = 0 + private var mClipChildren = true + private var mTwoLevelOpenOffset = 0 + private var mTwoLevelOpenCount = 0 + private var mShowTwoLevelStartOffset = 0 + private var mPullDownPush: PullDownPush? = null + private var mPullDownPushExposureEvent: ExposureEvent? = null + private var mGameViewHolder: GameViewHolder? = null + + private var mNoTabLinkEntity: LinkEntity? = null + private var mLightStatusBar: Boolean? = null + private var mLightSearchToolbarStyle: Boolean? = null + private var mLightRefreshHeaderStyle: Boolean? = null + + private var mInferiorChain: PriorityChain? = null + private val mPriorityChain by lazy { PriorityChain { mInferiorChain?.start() } } + private var mAutoFinishTwoLevelHandler: Handler? = null + + private val mSingleLineTabToolbarHeight = 56F.dip2px() + private val mTwoLinesTabToolbarHeight = 48F.dip2px() + + private var mSearchStyle: BottomTab.SearchStyle? = null + set(value) { + field = value + setSearchHint() + } + + private val dataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + if (downloadEntity.gameId == mPullDownPush?.game?.id) { + updatePullDownPushDownloadBtn() + } + } + + override fun onDataInit(downloadEntity: DownloadEntity) { + onDataChanged(downloadEntity) + } + } + + override fun getLayoutId(): Int = 0 + override fun getInflatedLayout(): View = mBinding.root + override fun provideViewModel(): TabWrapperViewModel = mViewModel + + override fun provideTabLayout(): TabLayout? { + return if (mStyle == Style.SINGLE_LINE_TAB) { + mBinding.tabSearchToolbar.tabLayout + } else { + super.provideTabLayout() + } + } + + override fun provideIndicatorView(): TabIndicatorView? { + return if (mStyle == Style.SINGLE_LINE_TAB) { + mBinding.tabSearchToolbar.indicatorView + } else { + super.provideIndicatorView() + } + } + + private fun getToolbarHeight() = if (mStyle == Style.SINGLE_LINE_TAB) mSingleLineTabToolbarHeight else mTwoLinesTabToolbarHeight + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mNoTabLinkEntity = arguments?.getParcelable(LinkEntity::class.java.simpleName) + mSearchStyle = arguments?.getParcelable(BottomTab.SearchStyle::class.java.simpleName) ?: BottomTab.SearchStyle() + if (mSearchStyle?.styleType == BottomTab.SearchStyle.STYLE_APPOSITION) { + mStyle = Style.SINGLE_LINE_TAB + } else if (mMultiTabNavId.isEmpty() && mNoTabLinkEntity != null) { + mStyle = Style.NO_TAB + } + } + + private fun initSearchToolbar() { + mBinding.contentBgView.isVisible = mStyle != Style.TWO_LINES_TAB + mBinding.tabContainer.isVisible = mStyle == Style.TWO_LINES_TAB + val showDownload = Config.isShow() + if (mStyle == Style.SINGLE_LINE_TAB) { + mBinding.tabSearchToolbar.root.visibility = View.VISIBLE + mDownloadHintTv = mBinding.tabSearchToolbar.menuDownloadCountHint + mSearchHintTv = mBinding.tabSearchToolbar.searchTv + mSearchContainerView = mBinding.tabSearchToolbar.searchContainer + mDownloadView = mBinding.tabSearchToolbar.downloadContainer + + mBinding.collapsingToolbar.updateLayoutParams { + scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL + } + mBinding.tabSearchToolbar.searchContainer.setOnClickListener(this) + mBinding.tabSearchToolbar.downloadContainer.setOnClickListener(this) + } else { + mBinding.searchToolbar.root.visibility = View.VISIBLE + mDownloadHintTv = mBinding.searchToolbar.actionTip + mSearchHintTv = mBinding.searchToolbar.actionbarSearchInput + mSearchContainerView = mBinding.searchToolbar.actionbarSearchRl + mDownloadView = mBinding.searchToolbar.actionbarRlDownload + mMessageUnread = mBinding.searchToolbar.messageUnreadHint + mSearchRightView = mBinding.searchToolbar.actionbarSearchRight + mTeenagerModelView = mBinding.searchToolbar.actionbarTeenagerModel + + mTeenagerModelView?.isVisible = !showDownload + mSearchContainerView?.isVisible = showDownload + mSearchRightView?.isVisible = !showDownload + + mDownloadView?.setOnClickListener(this) + mBinding.searchToolbar.actionbarNotification.setOnClickListener(this) + mBinding.searchToolbar.actionbarSearchRl.setOnClickListener(this) + mTeenagerModelView?.setOnClickListener(this) + mSearchHintTv?.setOnClickListener(this) + mSearchRightView?.setOnClickListener(this) + } + mDownloadView?.isVisible = showDownload + mDownloadHintTv?.typeface = Typeface.createFromAsset(requireContext().assets, Constants.DIN_FONT_PATH) + mMessageUnread?.typeface = Typeface.createFromAsset(requireContext().assets, Constants.DIN_FONT_PATH) + mPackageViewModel.filterSameUpdateLiveData.observe(this) { updateList: List -> + setDownloadHint(updateList) + } + mUnreadViewModel.messageCenterUnreadCountLiveData.observe(this) { unreadCount: Int -> + setMessageUnread(unreadCount) + } + + setSearchHint() + } + + private fun setDownloadHint(updateList: List) { + mDownloadHintTv?.also { + val count = DownloadManager.getInstance().getDownloadOrUpdateCount(updateList) + it.goneIf(count == null) { + it.text = count!! + setHintLayoutParams(it, count.isEmpty()) + } + } + } + + private fun setMessageUnread(unreadCount: Int) { + mMessageUnread?.isVisible = unreadCount != 0 + if (unreadCount == -1) { + mMessageUnread?.text = "" + mMessageUnread?.also { setHintLayoutParams(it, true) } + } else { + mMessageUnread?.also { BindingAdapters.setMessageUnread(it, unreadCount) } + mMessageUnread?.also { setHintLayoutParams(it, false) } + } + } + + private fun setHintLayoutParams(textView: TextView, isRedDot: Boolean) { + val params = textView.layoutParams + if (isRedDot) { + params.width = 6F.dip2px() + params.height = 6F.dip2px() + (params as MarginLayoutParams).rightMargin = (-4F).dip2px() + params.topMargin = 0 + textView.setPadding(0, 0, 0, 0) + textView.minWidth = 0 + } else { + params.width = RelativeLayout.LayoutParams.WRAP_CONTENT + params.height = 14F.dip2px() + (params as MarginLayoutParams).rightMargin = (-8F).dip2px() + params.topMargin = (-4F).dip2px() + textView.setPadding(4F.dip2px(), 0, 4F.dip2px(), 0) + textView.minWidth = 14F.dip2px() + } + textView.layoutParams = params + } + + private fun setSearchHint() { + if (isSupportVisible) { + when (mSearchStyle?.searchType) { + BottomTab.SearchStyle.TYPE_HALO_GAME -> { + mSearchHintTv?.let { DefaultSearchHintHelper.setSearchHint(it) } + } + BottomTab.SearchStyle.TYPE_QQ_MINI_GAME -> { + mSearchHintTv?.hint = if (mSearchStyle?.styleType == BottomTab.SearchStyle.STYLE_APPOSITION) "搜索" else "请输入小游戏关键词" + } + BottomTab.SearchStyle.TYPE_BBS -> { + mSearchHintTv?.hint = if (mSearchStyle?.styleType == BottomTab.SearchStyle.STYLE_APPOSITION) "搜索" else "搜索论坛内容、用户" + } + } + } + } + + override fun onClick(v: View) { + when (v.id) { + R.id.downloadContainer, + R.id.actionbar_rl_download -> { + DataCollectionUtils.uploadClick(activity, "下载图标", "主页") + DirectUtils.directToDownloadManager( + requireContext(), + "底部tab", + mBottomTabName, + multiTabName = mMultiTabNavName, + multiTabId = mMultiTabNavId + ) + } + + R.id.searchIv, + R.id.searchContainer, + R.id.actionbar_search_input, + R.id.actionbar_search_rl, + R.id.actionbar_search_right -> { + val searchBoxPattern = if (v.id == R.id.searchIv) "靠右" else "置顶且展开" + DirectUtils.directToSearch( + requireContext(), + mSearchStyle?.searchType ?: "halo_game", + mSearchHintTv?.hint.toString(), + "搜索栏", + "多tab导航页", + mBottomTabName, + multiTabId = mMultiTabNavId, + multiTabName = mMultiTabNavName, + searchBoxPattern = searchBoxPattern + ) + } + + R.id.actionbar_notification -> { + DataCollectionUtils.uploadClick(activity, "消息图标", "主页") + CheckLoginUtils.checkLogin(requireContext(), "(工具栏)") { + NewLogUtils.logMessageInformBellClick( + mMessageUnread?.visibility == View.VISIBLE, + "首页" + ) + SensorsBridge.trackMessageCenterClick() + // 优先进入有数字提醒的消息tab,其次是有红点提醒的游戏动态,最后是没有提醒的消息tab + var defaultTabIndex = 1 + val messageUnreadCount = mUnreadViewModel.messageUnreadCountLiveData.value + val isConcernUnread = mUnreadViewModel.zixunConcernLiveData.value + if ((messageUnreadCount == null || messageUnreadCount.message < 1) && isConcernUnread == true) { + defaultTabIndex = 0 + } + DirectUtils.directToMessageCenter(defaultTabIndex) + } + } + + R.id.actionbar_teenager_model -> { + startActivity(TeenagerModeActivity.getIntent(requireContext())) + } + } + } + + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() + mMainWrapperViewModel = viewModelProviderFromParent(MainWrapperViewModel.Factory(HaloApp.getInstance())) + mElapsedHelper = TimeElapsedHelper() + + initSearchToolbar() + buildPriorityChain() + + // 记录 appBarOffset 供其它子页面使用 + mBinding.appbar.addOnOffsetChangedListener { _, verticalOffset -> + mViewModel.appBarOffset = abs(verticalOffset) + } + + mBinding.searchIv.setOnClickListener(this) + mBinding.collapsingToolbar.scrimShownAction = { + if (mBinding.searchIv.isVisible != it) { + mBinding.searchIv.isVisible = it + mBinding.indicatorView.post { + mBinding.indicatorView.generatePath(mBinding.viewPager.currentItem, 0F) + } + } + } + + initSmartRefresh() + + if (mStyle == Style.NO_TAB) { + (mBinding.collapsingToolbar.layoutParams as? AppBarLayout.LayoutParams)?.let { lp -> + lp.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL + } + initNoTabViewPager() + } + + mViewModel.tabSelectLiveData.observe(viewLifecycleOwner) { + val selectTab = it.getContentWithHandled() + if (selectTab is MainSelectedEvent.SelectedTab) { + if (selectTab.topTabIndex != -1) { + mViewPager?.setCurrentItem(selectTab.topTabIndex, false) + } + } + } + } + + override fun hideTab() { + mBinding.tabContainer.visibility = View.GONE + (mBinding.collapsingToolbar.layoutParams as? AppBarLayout.LayoutParams)?.let { lp -> + lp.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL + } + } + + /** + * 构建 PriorityChain,使用优先队列来决定当前需要显示哪一个弹窗 + * 插件化通知 popup (4) + * 首页自动下拉二楼 (5) + */ + private fun buildPriorityChain() { + val accelerateNotificationHandler = AccelerateNotificationHandler(4) + mPriorityChain.addHandler(accelerateNotificationHandler) + + mMainWrapperViewModel?.accelerateNotificationPopup?.observe(this) { + + if (mPriorityChain.isHandlerQueueEmpty()) { + if (it == null) return@observe + + val accelerateSet = HashSet(SPUtils.getStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET)) + if (it.isNotEmpty() && !accelerateSet.contains(it[0].messageId)) { + AccelerateNotificationHandler.showAccelerateNotificationPopupWindow( + requireActivity(), + mMainWrapperViewModel, + mBaseHandler as BaseHandler, + it[0], + null + ) + accelerateSet.add(it[0].messageId) + SPUtils.setStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET, accelerateSet) + } + } else { + accelerateNotificationHandler.doPreProcess( + requireActivity(), + mBaseHandler!! as BaseHandler, + it, + mMainWrapperViewModel!! + ) + } + } + } + + private fun initNoTabViewPager() { + mFragmentList.clear() + mViewPager = provideViewPager() + mLoadingContainer?.visibility = View.GONE + mNoTabLinkEntity?.let { + if (it.type == ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE) { + val bundle = Bundle() + bundle.putAll(arguments) + bundle.putString(EntranceConsts.KEY_CUSTOM_PAGE_ID, it.link) + bundle.putString(EntranceConsts.KEY_CUSTOM_PAGE_NAME, it.text) + mFragmentList.add(CustomPageFragment().setSuperiorChain(this).with(bundle)) + } + } + mViewPager?.run { + offscreenPageLimit = mFragmentList.size + mDefaultSelectedTab = 0 + setRestoredCurItem(this) + adapter = FragmentAdapter(childFragmentManager, mFragmentList) + } + } + + private fun initSmartRefresh() { + mBinding.run { + mGameViewHolder = GameViewHolder(headerContainer).also { + it.gameDownloadBtn = downloadBtn + it.multiVersionDownloadTv = multiVersionDownloadTv + it.gameDownloadTips = downloadTipsLottie + } + topMaskView.updateLayoutParams { + height = mStatusBarHeight + getToolbarHeight() + } + mTwoLevelOpenOffset = DisplayUtils.getScreenWidth() - getToolbarHeight() - mStatusBarHeight + mShowTwoLevelStartOffset = 139F.dip2px() - mStatusBarHeight + classicsHeader.setArrowResource(R.drawable.icon_arrow) + statusBar.post { + twoLevelHeader.updateLayoutParams { + topMargin = -(mStatusBarHeight + getToolbarHeight()) + } + } + refreshLayout.setFloorHeight(DisplayUtils.getScreenWidth() - getToolbarHeight() - mStatusBarHeight) + refreshLayout.setOnMultiPurposeListener(object : SimpleMultiPurposeListener() { + override fun onRefresh(refreshLayout: RefreshLayout) { + val currentTabEntity = getCurrentTabEntity() + SensorsBridge.trackEvent("CustomPageFlush", json { + "bottom_tab" to mBottomTabName + "several_tab_page_name" to mMultiTabNavName + "several_tab_page_id" to mMultiTabNavId + "position" to mLastSelectedPosition + "tab_content" to currentTabEntity?.name + "custom_page_name" to currentTabEntity?.link?.text + "custom_page_id" to currentTabEntity?.link?.link + }) + getValidSmartRefreshContent()?.onRefresh() + } + + override fun onHeaderMoving( + header: RefreshHeader?, + isDragging: Boolean, + percent: Float, + offset: Int, + headerHeight: Int, + maxDragHeight: Int + ) { + mIsDragging = isDragging + mHeaderOffset = offset + updateStyleOnHeaderMoving(offset) + headerContainer.translationY = -(mTwoLevelOpenOffset - offset).toFloat() + } + + override fun onStateChanged( + refreshLayout: RefreshLayout, + oldState: RefreshState, + newState: RefreshState + ) { + val isAutoScrollToTwoLevel = oldState == RefreshState.None && newState == RefreshState.TwoLevel + val isDragToTwoLevel = + oldState == RefreshState.ReleaseToRefresh && newState == RefreshState.ReleaseToTwoLevel + val isScrollToTwoLevel = isDragToTwoLevel || isAutoScrollToTwoLevel + val isShowTwoLevelByDrag = + oldState == RefreshState.TwoLevelReleased && newState == RefreshState.TwoLevel + val isFinishTwoLevel = oldState == RefreshState.TwoLevel && newState == RefreshState.TwoLevelFinish + + val currentTabEntity = getCurrentTabEntity() + mPullDownPush?.run { + if (isAutoScrollToTwoLevel) { + mPullDownPushAction = "自动展开" + mTwoLevelOpenCount++ + SensorsBridge.trackEvent( + "DropDownPushShow", + json { + "action" to "自动展开" + "drop_down_push_id" to id + "game_name" to game?.name + "game_id" to game?.id + "bottom_tab" to mBottomTabName + "several_tab_page_name" to mMultiTabNavName + "several_tab_page_id" to mMultiTabNavId + "position" to mLastSelectedPosition + "tab_content" to currentTabEntity?.name + "custom_page_name" to currentTabEntity?.link?.text + "custom_page_id" to currentTabEntity?.link?.link + } + ) + com.gh.common.util.NewFlatLogUtils.logHomePushShow( + id, + "自动展开", + mTwoLevelOpenCount, + game?.id ?: "", + game?.name ?: "" + ) + } + if (isShowTwoLevelByDrag) { + mPullDownPushAction = "主动展开" + SensorsBridge.trackEvent( + "DropDownPushShow", + json { + "action" to "主动展开" + "drop_down_push_id" to id + "game_name" to game?.name + "game_id" to game?.id + "bottom_tab" to mBottomTabName + "several_tab_page_name" to mMultiTabNavName + "several_tab_page_id" to mMultiTabNavId + "position" to mLastSelectedPosition + "tab_content" to currentTabEntity?.name + "custom_page_name" to currentTabEntity?.link?.text + "custom_page_id" to currentTabEntity?.link?.link + } + ) + com.gh.common.util.NewFlatLogUtils.logHomePushShow( + id, + "主动展开", + mTwoLevelOpenCount, + game?.id ?: "", + game?.name ?: "" + ) + } + if (isDragToTwoLevel) { + mTwoLevelOpenCount++ + + SensorsBridge.trackEvent( + "DropDownPushTrigger", + json { + "drop_down_push_id" to id + "game_name" to game?.name + "game_id" to game?.id + "bottom_tab" to mBottomTabName + "several_tab_page_name" to mMultiTabNavName + "several_tab_page_id" to mMultiTabNavId + "position" to mLastSelectedPosition + "tab_content" to currentTabEntity?.name + "custom_page_name" to currentTabEntity?.link?.text + "custom_page_id" to currentTabEntity?.link?.link + } + ) + com.gh.common.util.NewFlatLogUtils.logHomePushTrigger( + id, + mTwoLevelOpenCount, + game?.id ?: "", + game?.name ?: "" + ) + } + } + + if (isScrollToTwoLevel) { + mShowTwoLevel = true + refreshStatusBarStyle() + } + + if (isFinishTwoLevel) { + pausePullDownPushVideo() + mShowTwoLevel = false + + if (mIsDragging) { + mAutoFinishTwoLevelHandler?.removeMessages(KEY_AUTO_FINISH_TWO_LEVEL) + mElapsedHelper.pauseCounting() + mPullDownPush?.run { + SensorsBridge.trackEvent( + "DropDownPushClick", + json { + "action" to mPullDownPushAction + "button_name" to "关闭推送" + "drop_down_push_id" to id + "game_name" to game?.name + "game_id" to game?.id + "bottom_tab" to mBottomTabName + "several_tab_page_name" to mMultiTabNavName + "several_tab_page_id" to mMultiTabNavId + "position" to mLastSelectedPosition + "tab_content" to currentTabEntity?.name + "custom_page_name" to currentTabEntity?.link?.text + "custom_page_id" to currentTabEntity?.link?.link + } + ) + com.gh.common.util.NewFlatLogUtils.logHomePushClose( + id, + "主动收起", + mTwoLevelOpenCount, + game?.id ?: "", + game?.name ?: "", + mElapsedHelper.elapsedTime + ) + } + } + } + + when (newState) { + RefreshState.TwoLevel -> getValidSmartRefreshContent()?.setScrollEnabled(false) + RefreshState.None -> { + getValidSmartRefreshContent()?.setScrollEnabled(true) + + mShowTwoLevel = false + mIsDragging = false + twoLevelHeader.setFloorDuration(1000) + mBinding.refreshLayout.setFloorDuration(1000) + classicsHeader.visibility = View.VISIBLE + } + + RefreshState.PullDownToRefresh -> { + mShowTwoLevel = false + mBinding.classicsHeader.updateLayoutParams { + topMargin = getToolbarHeight() + mStatusBarHeight - 59F.dip2px() + } + } + + RefreshState.PullDownCanceled, + RefreshState.ReleaseToRefresh -> mShowTwoLevel = false + + else -> { + // do nothing + } + } + + val clipChildren: Boolean + when (newState) { + RefreshState.TwoLevel, + RefreshState.TwoLevelFinish -> { + clipChildren = false + toolbarBackground.alpha = 0F + statusBarBackground.alpha = 0F + contentContainer.radius = 8F.dip2px().toFloat() + } + + RefreshState.ReleaseToTwoLevel, + RefreshState.TwoLevelReleased -> { + clipChildren = false + toolbarBackground.alpha = 0F + statusBarBackground.alpha = 0F + headerBackground.alpha = 0F + contentContainer.radius = 8F.dip2px().toFloat() + } + + else -> { + clipChildren = true + toolbarBackground.alpha = 1F + statusBarBackground.alpha = 1F + headerBackground.alpha = 1F + contentContainer.radius = 0F + } + } + + if (mClipChildren != clipChildren) { + mClipChildren = clipChildren + wrapperLl.clipChildren = mClipChildren + mBinding.refreshLayout.clipChildren = mClipChildren + } + } + }) + + twoLevelHeader.setOnTwoLevelListener { + mPullDownPushExposureEvent?.timeInMillisecond = System.currentTimeMillis() + mPullDownPushExposureEvent?.time = TimeUtil.currentTime() + mPullDownPushExposureEvent?.payload?.sequence = mTwoLevelOpenCount + mPullDownPushExposureEvent?.let { + ExposureManager.log(it) + } + mElapsedHelper.resetCounting() + mElapsedHelper.resumeCounting() + playPullDownPushVideo() + true + } + } + } + + private fun updatePullDownPushDownloadBtn() { + mBinding.run { + mPullDownPush?.game?.run { + mGameViewHolder?.let { + DownloadItemUtils.updateItem(requireContext(), this, it) + } + DownloadItemUtils.setOnClickListener( + requireContext(), + downloadBtn, + this, + 0, + null, + "自定义页面-下拉推送", + location = "", + traceEvent = mPullDownPushExposureEvent, + clickCallback = { + updatePullDownPushDownloadBtn() + val currentTabEntity = getCurrentTabEntity() + SensorsBridge.trackEvent( + "DropDownPushClick", + json { + "action" to mPullDownPushAction + "button_name" to "下载按钮" + "drop_down_push_id" to id + "game_name" to name + "game_id" to id + "bottom_tab" to mBottomTabName + "several_tab_page_name" to mMultiTabNavName + "several_tab_page_id" to mMultiTabNavId + "position" to mLastSelectedPosition + "tab_content" to currentTabEntity?.name + "custom_page_name" to currentTabEntity?.link?.text + "custom_page_id" to currentTabEntity?.link?.link + } + ) + }, + refreshCallback = { updatePullDownPushDownloadBtn() }, + allStateClickCallback = null + ) + } + } + } + + // 下拉进入/退出下拉推送时更新背景透明度 + private fun updateStyleOnHeaderMoving(offset: Int) { + if (context == null) return + mBinding.run { + if (offset >= mShowTwoLevelStartOffset) { + val oneLevelAlpha = + 1 - (offset - mShowTwoLevelStartOffset) / (mTwoLevelOpenOffset - mShowTwoLevelStartOffset).toFloat() + tabBgView.alpha = oneLevelAlpha + contentBgView.alpha = oneLevelAlpha + mViewModel.updateBackgroundAlpha(oneLevelAlpha) + + if (mViewModel.isTabCustomPage(mLastSelectedPosition)) { + refreshStatusBarStyle() + updateSearchToolbarStyle(true) + updateRefreshHeaderStyle() + if (getCurrentTabStyle()?.isSlideBackgroundColorEnable == false) { + if (mStyle == Style.SINGLE_LINE_TAB) { + updateTabStyleOnHeaderMoving( + TAB_DEFAULT_COLOR_LIGHT.toColor(requireContext()), + TAB_DEFAULT_COLOR_LIGHT.toColor(requireContext()), + R.drawable.ic_home_tab_indicator_white + ) + } + } else { + val lightStyle = + (mIsDisplayingLightContent && oneLevelAlpha >= PULL_TAB_TEXT_STYLE_UPDATE_PERCENT) || mStyle == Style.SINGLE_LINE_TAB + val tabSelectedColor = if (lightStyle) { + TAB_DEFAULT_COLOR_LIGHT.toColor(requireContext()) + } else { + TAB_SELECTED_COLOR.toColor(requireContext()) + } + val tabDefaultColor = if (lightStyle) { + TAB_DEFAULT_COLOR_LIGHT.toColor(requireContext()) + } else { + TAB_DEFAULT_COLOR.toColor(requireContext()) + } + val indicatorDrawable = if (lightStyle) { + R.drawable.ic_home_tab_indicator_white + } else { + R.drawable.ic_commodity_selected + } + updateTabStyleOnHeaderMoving(tabSelectedColor, tabDefaultColor, indicatorDrawable) + } + } + } else { + tabBgView.alpha = 1F + contentBgView.alpha = 1F + mViewModel.updateBackgroundAlpha(1F) + + if (mViewModel.isTabCustomPage(mLastSelectedPosition)) { + refreshStatusBarStyle() + updateSearchToolbarStyle(mIsDarkModeOn || mIsDisplayingLightContent) + updateRefreshHeaderStyle() + if (getCurrentTabStyle()?.isSlideBackgroundColorEnable == false && mStyle == Style.SINGLE_LINE_TAB) { + updateTabStyleOnHeaderMoving( + TAB_SELECTED_COLOR.toColor(requireContext()), + TAB_DEFAULT_COLOR.toColor(requireContext()), + R.drawable.ic_commodity_selected + ) + } + } + } + + if (refreshLayout.state == RefreshState.TwoLevelFinish || refreshLayout.state == RefreshState.TwoLevel) { + headerBackground.alpha = 1 - offset / mTwoLevelOpenOffset.toFloat() + } + } + } + + private fun updateTabStyleOnHeaderMoving(tabSelectedColor: Int, tabDefaultColor: Int, indicatorDrawable: Int) { + if (mCurrentTabSelectedColor != tabSelectedColor || mCurrentTabDefaultColor != tabDefaultColor) { + mCurrentTabSelectedColor = tabSelectedColor + mCurrentTabDefaultColor = tabDefaultColor + updateTabStyle(mLastSelectedPosition, 0F, mCurrentTabDefaultColor, mCurrentTabSelectedColor) + } + if (mCurrentIndicatorDrawable != indicatorDrawable) { + mCurrentIndicatorDrawable = indicatorDrawable + mIndicatorView?.updateIndicatorDrawable(indicatorDrawable.toDrawable(requireContext())) + } + } + + override fun popupPullDownPush(finishCallback: () -> Unit) { + if (!isSupportVisible) { + setResumeAndPauseListener { + if (it) { + popupPullDownPush(finishCallback) + setResumeAndPauseListener(null) + } + } + return + } + mBinding.run { + if (mMultiTabNavId == MainWrapperRepository.getInstance().defaultNavId) { + mPullDownPush = + mPullDownPush ?: MainWrapperRepository.getInstance().customPageLiveData.value?.linkPullDownPush + } + + if ((mPullDownPush == null) + || mFragmentList.isEmpty() + || mFragmentList.safelyGetInRelease(viewPager.currentItem) !is ISmartRefreshContent + ) { + finishCallback.invoke() + return + } + + // 自动弹出,5s后自动收起 + mBaseHandler.post { + mPullDownPush?.run { + val pullDownPushSet = HashSet(SPUtils.getStringSet(Constants.SP_PULL_DOWN_PUSH_POP_UP_SET)) + twoLevelHeader.openTwoLevel(true) + wrapperLl.clipChildren = false + refreshLayout.clipChildren = false + headerBackground.animate().alpha(0F).duration = AUTO_OPEN_TWO_LEVEL_DURATION + statusBarBackground.animate().alpha(0F).duration = AUTO_OPEN_TWO_LEVEL_DURATION + toolbarBackground.animate().alpha(0F).duration = AUTO_OPEN_TWO_LEVEL_DURATION + classicsHeader.visibility = View.INVISIBLE + pullDownPushSet.add(id) + SPUtils.setStringSet(Constants.SP_PULL_DOWN_PUSH_POP_UP_SET, pullDownPushSet) + mAutoFinishTwoLevelHandler = object : Handler(Looper.getMainLooper()) { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + finishTwoLevel("自动收起") + finishCallback.invoke() + } + } + mAutoFinishTwoLevelHandler?.sendEmptyMessageDelayed(KEY_AUTO_FINISH_TWO_LEVEL, AUTO_FINISH_TWO_LEVEL_DELAY) + } + } + } + } + + override fun setPullDownPush(pullDownPush: PullDownPush?, pullDownPushHandler: PullDownPushHandler?) { + if (pullDownPush != null) { + setSmartRefreshEnabled(true) + if ((mPullDownPush?.id ?: "") == pullDownPush.id) return + + mPullDownPush = pullDownPush + mPullDownPush?.run { + if (game != null) { + game.sequence = 1 + mPullDownPushExposureEvent = ExposureEvent.createEventWithSourceConcat( + game, + basicSource = listOf(ExposureSource("自定义页面", "$customPageName+$customPageId")), + source = listOf(ExposureSource("下拉推送", id)) + ) + } + + mBinding.run { + if (game != null) { + gameNameTv.text = game.name + GameItemViewHolder.initGameSubtitleAndAdLabel(game, gameSubtitleTv) + updatePullDownPushDownloadBtn() + headerContainer.setOnClickListener { + if (mShowTwoLevel) { + mPullDownPushClick = true + val currentTabEntity = getCurrentTabEntity() + SensorsBridge.trackEvent( + "DropDownPushClick", + json { + "action" to mPullDownPushAction + "button_name" to "游戏详情" + "drop_down_push_id" to id + "game_name" to game.name + "game_id" to game.id + "bottom_tab" to mBottomTabName + "several_tab_page_name" to mMultiTabNavName + "several_tab_page_id" to mMultiTabNavId + "position" to mLastSelectedPosition + "tab_content" to currentTabEntity?.name + "custom_page_name" to currentTabEntity?.link?.text + "custom_page_id" to currentTabEntity?.link?.link + } + ) + GameDetailActivity.startGameDetailActivity( + requireContext(), + game.id, + "(自定义页面-下拉推送)", + mPullDownPushExposureEvent + ) + } + } + } + descTv.text = description + gameImageIv.goneIf(video != null) { + ImageUtils.display(gameImageIv, imgUrl) + } + autoVideoView.onVideoReset() + autoVideoView.goneIf(video == null) { + if (!autoVideoView.isInPlayingState) { + GSYVideoOptionBuilder() + .setIsTouchWiget(false) + .setUrl(video?.url ?: "") + .setRotateViewAuto(false) + .setCacheWithPlay(true) + .setRotateWithSystem(false) + .setReleaseWhenLossAudio(true) + .setLooping(false) + .setShowFullAnimation(false) + .build(autoVideoView) + autoVideoView.updateThumb(imgUrl) + autoVideoView.setParamsData(game, null) + autoVideoView.setOnVideoClickListener { + headerContainer.performClick() + } + } + } + } + } + } + + val pullDownPushSet = + SPUtils.getStringSet(Constants.SP_PULL_DOWN_PUSH_POP_UP_SET) + val popupPush = mPullDownPush != null + && "on" == mPullDownPush!!.popSwitch + && !pullDownPushSet.contains(mPullDownPush!!.id) + + pullDownPushHandler?.doPreProcess(this as? ISmartRefresh, popupPush) + } + + private fun updateRefreshHeaderStyle(force: Boolean = false) { + if (mLightRefreshHeaderStyle == mIsDisplayingLightContent && !force) return + mLightRefreshHeaderStyle = mIsDisplayingLightContent + mBinding.classicsHeader.run { + val arrowColorFilterColor = if (mIsDisplayingLightContent) R.color.text_aw_primary else R.color.text_tertiary + val primaryColor = if (mIsDisplayingLightContent) R.color.white else R.color.ui_surface + val accentColor = if (mIsDisplayingLightContent) R.color.white else R.color.text_tertiary + setArrowColorFilter(arrowColorFilterColor.toColor(requireContext())) + setPrimaryColor(primaryColor.toColor(requireContext())) + setAccentColor(accentColor.toColor(requireContext())) + } + } + + // 播放下拉推送视频 + private fun playPullDownPushVideo() { + mBinding.run { + if (!mPullDownPush?.video?.url.isNullOrEmpty()) { + if (autoVideoView.currentState != GSYVideoView.CURRENT_STATE_PAUSE) { + mBaseHandler.postDelayed({ + val videoOption = + SPUtils.getString(Constants.SP_HOME_OR_DETAIL_VIDEO_OPTION, Constants.VIDEO_OPTION_WIFI) + ?: Constants.VIDEO_OPTION_WIFI + + when (videoOption) { + Constants.VIDEO_OPTION_ALL -> { + autoVideoView.onVideoReset() + autoVideoView.startPlayLogic(false) + } + + Constants.VIDEO_OPTION_WIFI -> { + if (NetworkUtils.isWifiConnected(HaloApp.getInstance().application)) { + autoVideoView.onVideoReset() + autoVideoView.startPlayLogic(false) + } + } + + else -> { + //do nothing + } + } + }, PULL_DOWN_PUSH_VIDEO_PLAY_DELAY) + } else { + resumePullDownPushVideo() + } + } + } + } + + private fun pausePullDownPushVideo() { + mBinding.run { + if (mShowTwoLevel && !mPullDownPush?.video?.url.isNullOrEmpty()) { + autoVideoView.onVideoPause() + val currentPosition = autoVideoView.getCurrentPosition() + val videoUrl = autoVideoView.getUrl() + if (videoUrl.isNotEmpty()) { + ScrollCalculatorHelper.savePlaySchedule(MD5Utils.getContentMD5(videoUrl), currentPosition) + } + } + } + } + + private fun resumePullDownPushVideo() { + mBinding.run { + if (mShowTwoLevel && !mPullDownPush?.video?.url.isNullOrEmpty()) { + val videoUrl = autoVideoView.getUrl() + if (videoUrl.isNotEmpty()) { + val position = ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(videoUrl)) + //这里必须要延迟操作,否则会白屏 + mBaseHandler.postDelayed({ + if (position != 0L) { + autoVideoView.seekTo(position) + autoVideoView.onVideoResume(false) + val topVideoVoiceStatus = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) + if (topVideoVoiceStatus) { + autoVideoView.mute() + } else { + autoVideoView.unMute() + } + } else { + autoVideoView.release() + } + }, PULL_DOWN_PUSH_VIDEO_PLAY_DELAY) + } + } + } + } + + override fun onFragmentPause() { + super.onFragmentPause() + pausePullDownPushVideo() + DownloadManager.getInstance().removeObserver(dataWatcher) + + GlobalPriorityChainHelper.unregisterInferiorChain(mPriorityChain) + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(status: EBDownloadStatus) { + mPackageViewModel.filterSameUpdateLiveData.value?.let { + setDownloadHint(it) + } + // 下载被删除事件 + if ("delete" == status.status) { + if (status.gameId == mPullDownPush?.game?.id) { + updatePullDownPushDownloadBtn() + } + } + } + + // 安装/卸载 事件 + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(busFour: EBPackage) { + if (busFour.isInstalledOrUninstalled()) { + updatePullDownPushDownloadBtn() + } + } + + override fun finishRefresh() { + mBinding.refreshLayout.finishRefresh(true) + } + + override fun setSmartRefreshEnabled(isEnable: Boolean) { + mBinding.refreshLayout.setEnableRefresh(isEnable) + getValidSmartRefreshContent()?.setSwipeRefreshEnabled(!isEnable) + getCurrentTabEntity()?.showPullDownPush = isEnable + } + + override fun finishTwoLevel(action: String) { + if (mShowTwoLevel) { + mAutoFinishTwoLevelHandler?.removeMessages(KEY_AUTO_FINISH_TWO_LEVEL) + mBinding.twoLevelHeader.finishTwoLevel() + mElapsedHelper.pauseCounting() + mPullDownPush?.run { + val currentTabEntity = getCurrentTabEntity() + SensorsBridge.trackEvent( + "DropDownPushClick", + json { + "action" to mPullDownPushAction + "button_name" to "关闭推送" + "drop_down_push_id" to id + "game_name" to game?.name + "game_id" to game?.id + "bottom_tab" to mBottomTabName + "several_tab_page_name" to mMultiTabNavName + "several_tab_page_id" to mMultiTabNavId + "position" to mLastSelectedPosition + "tab_content" to currentTabEntity?.name + "custom_page_name" to currentTabEntity?.link?.text + "custom_page_id" to currentTabEntity?.link?.link + } + ) + com.gh.common.util.NewFlatLogUtils.logHomePushClose( + id, + action, + mTwoLevelOpenCount, + game?.id ?: "", + game?.name ?: "", + mElapsedHelper.elapsedTime + ) + } + } + } + + override fun onFragmentResume() { + super.onFragmentResume() + refreshStatusBarStyle(true) + resumePullDownPushVideo() + DownloadManager.getInstance().addObserver(dataWatcher) + updatePullDownPushDownloadBtn() + mMainWrapperViewModel?.requestAccelerateNotificationPopup() + updateSearchToolbarStyle(mIsDarkModeOn || mIsDisplayingLightContent || mShowTwoLevel, true) + if (CheckLoginUtils.isLogin()) { + mUnreadViewModel.retry() + } + + GlobalPriorityChainHelper.registerInferiorChain(mPriorityChain) + } + + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + getCurrentTabEntity()?.showPullDownPush?.let { setSmartRefreshEnabled(it) } + if (mShowTwoLevel) finishTwoLevel("跳转收起") + } + + private fun updateIndicatorDrawable(indicatorView: TabIndicatorView?) { + if (mIsDisplayingLightContent) { + indicatorView?.updateIndicatorDrawable(R.drawable.ic_home_tab_indicator_white.toDrawable(requireContext())) + } else { + indicatorView?.updateIndicatorDrawable(R.drawable.ic_commodity_selected.toDrawable(requireContext())) + } + } + + override fun onPageScrolled( + position: Int, + positionOffset: Float, + tabEntityList: ArrayList + ) { + val nextPosition = position + 1 + val tabStyle = tabEntityList.safelyGetInRelease(position)?.style + + // 处理当前 tab 和下一个 tab + if (nextPosition != mTabBindingList.size) { + val nextTabStyle = tabEntityList.safelyGetInRelease(nextPosition)?.style + var currentAppBarColor = tabStyle?.primaryColor ?: mBackgroundWhiteColor + var nextAppBarColor = nextTabStyle?.primaryColor ?: mBackgroundWhiteColor + + if (currentAppBarColor == Color.WHITE) currentAppBarColor = mBackgroundWhiteColor + if (nextAppBarColor == Color.WHITE) nextAppBarColor = mBackgroundWhiteColor + + if (getFragment(position) is AmwayFragment) { + currentAppBarColor = if (mIsDarkModeOn) mBackgroundColor else mAmwayPrimaryColor + tabStyle?.currentSelectColor = currentAppBarColor + } + if (getFragment(nextPosition) is AmwayFragment) { + nextAppBarColor = if (mIsDarkModeOn) mBackgroundColor else mAmwayPrimaryColor + } + + val proximatelySelectedTabStyle = if (positionOffset < 0.5) tabStyle else nextTabStyle + + val appBarColorInBetween = if (currentAppBarColor == nextAppBarColor) { + currentAppBarColor + } else { + ColorUtils.blendARGB(currentAppBarColor, nextAppBarColor, positionOffset) + } + + // 颜色显示是否变更 + val isDisplayingLightContent = proximatelySelectedTabStyle?.useLightStyle ?: false || mShowTwoLevel + val isContentStyleChanged = isDisplayingLightContent != mIsDisplayingLightContent + mIsDisplayingLightContent = isDisplayingLightContent + + if (!mShowTwoLevel) { + mCurrentTabSelectedColor = + if (mIsDisplayingLightContent) mTabDefaultLightColor else mTabSelectedColor + mCurrentTabDefaultColor = if (mIsDisplayingLightContent) mTabDefaultLightColor else mTabDefaultColor + } + + if (isContentStyleChanged) { + mBinding.searchIv.setImageResource(if (mIsDisplayingLightContent) R.drawable.toolbar_search_light else R.drawable.toolbar_search) + if (!mShowTwoLevel) updateIndicatorDrawable(mIndicatorView) + refreshStatusBarStyle() + } + + mCurrentAppBarColor = appBarColorInBetween + updateAppBarStyle(appBarColorInBetween, mIsDisplayingLightContent) + } else { + var currentAppBarColor = tabStyle?.primaryColor ?: mBackgroundWhiteColor + if (mIsDarkModeOn && currentAppBarColor == Color.WHITE) { + currentAppBarColor = mBackgroundWhiteColor + } + if (getFragment(position) is AmwayFragment) { + currentAppBarColor = if (mIsDarkModeOn) mBackgroundColor else mAmwayPrimaryColor + tabStyle?.currentSelectColor = currentAppBarColor + } + mIsDisplayingLightContent = tabStyle?.useLightStyle == true || mShowTwoLevel + if (!mShowTwoLevel) { + mCurrentTabSelectedColor = + if (mIsDisplayingLightContent) mTabDefaultLightColor else mTabSelectedColor + mCurrentTabDefaultColor = if (mIsDisplayingLightContent) mTabDefaultLightColor else mTabDefaultColor + } + + updateAppBarStyle(currentAppBarColor, mIsDisplayingLightContent) + mBinding.searchIv.setImageResource(if (mIsDisplayingLightContent) R.drawable.toolbar_search_light else R.drawable.toolbar_search) + if (!mShowTwoLevel) updateIndicatorDrawable(mIndicatorView) + refreshStatusBarStyle() + } + + // 这里的 selectedPosition 指的是应该被高亮显示的 position + val selectedPosition = try { + (position + positionOffset).roundToInt() + } catch (e: IllegalArgumentException) { + // roundToInt() 方法有时候会报 Cannot round NaN value. 错误 positionOffset 的值为 NAN + // https://sentry.shanqu.cc/organizations/lightgame/issues/301377/?project=22 + position + } + val positionOffsetOnRealSelectedPosition = if (positionOffset >= 0.5) { + positionOffset - 1 + } else { + positionOffset + } + + updateTabStyle( + selectedPosition, + positionOffsetOnRealSelectedPosition, + mCurrentTabDefaultColor, + mCurrentTabSelectedColor + ) + } + + override fun onScrollChanged(totalHeight: Int, offset: Int, isDarkModeChanged: Boolean) { + val currentTabStyle = getCurrentTabStyle() + currentTabStyle?.offsetRatio = offset / totalHeight.toFloat() + if (((currentTabStyle?.isSlideBackgroundColorShow == true || (currentTabStyle != null && isDarkModeChanged)) && offset >= totalHeight) + || (currentTabStyle?.isSlideBackgroundColorEnable == false && mViewModel.isTabCustomPage(mLastSelectedPosition)) + ) { + currentTabStyle.isSlideBackgroundColorShow = false + currentTabStyle.primaryColor = mBackgroundWhiteColor + currentTabStyle.useLightStyle = false + mIsDisplayingLightContent = mShowTwoLevel && !currentTabStyle.isSlideBackgroundColorEnable + updateAppBarColorWhenScrollChanged(mBackgroundWhiteColor) + mBaseHandler.post { refreshStatusBarStyle() } + if (!mShowTwoLevel || !currentTabStyle.isSlideBackgroundColorEnable) { + mCurrentTabSelectedColor = TAB_SELECTED_COLOR.toColor(requireContext()) + mCurrentTabDefaultColor = TAB_DEFAULT_COLOR.toColor(requireContext()) + mIndicatorView?.updateIndicatorDrawable(R.drawable.ic_commodity_selected.toDrawable()) + mBinding.searchIv.setImageResource(R.drawable.toolbar_search) + updateTabStyle(mLastSelectedPosition, 0F, mCurrentTabDefaultColor, mCurrentTabSelectedColor) + } + } else if (offset < totalHeight) { + var currentSelectColor = currentTabStyle?.currentSelectColor ?: 0 + val offsetMeetsCriticalPoint = (currentTabStyle?.offsetRatio ?: 0F) < mOffsetCritical + currentTabStyle?.isSlideBackgroundColorShow = true + if (getFragment(mBinding.viewPager.currentItem) is AmwayFragment) { + currentSelectColor = if (mIsDarkModeOn) mBackgroundColor else mAmwayPrimaryColor + } + val colorInBetween = + ColorUtils.blendARGB(currentSelectColor, mBackgroundWhiteColor, currentTabStyle?.offsetRatio ?: 0F) + updateAppBarColorWhenScrollChanged(colorInBetween) + currentTabStyle?.primaryColor = colorInBetween + currentTabStyle?.useLightStyle = offsetMeetsCriticalPoint + if (mIsDisplayingLightContent != currentTabStyle?.useLightStyle) { + mIsDisplayingLightContent = offsetMeetsCriticalPoint + } + mBaseHandler.post { refreshStatusBarStyle() } + if (!mShowTwoLevel) { + mCurrentTabSelectedColor = if (offsetMeetsCriticalPoint) mTabDefaultLightColor else mTabSelectedColor + mCurrentTabDefaultColor = if (offsetMeetsCriticalPoint) mTabDefaultLightColor else mTabDefaultColor + mIndicatorView?.updateIndicatorDrawable( + if (offsetMeetsCriticalPoint) { + R.drawable.ic_home_tab_indicator_white.toDrawable() + } else { + R.drawable.ic_commodity_selected.toDrawable() + } + ) + mBinding.searchIv.setImageResource(if (offsetMeetsCriticalPoint) R.drawable.toolbar_search_light else R.drawable.toolbar_search) + updateTabStyle(mLastSelectedPosition, 0F, mCurrentTabDefaultColor, mCurrentTabSelectedColor) + } + } + } + + private fun updateAppBarColorWhenScrollChanged(color: Int) { + val currentTabStyle = getCurrentTabStyle() + if ((currentTabStyle?.offsetRatio ?: 0F) >= 1F && color != R.color.ui_surface.toColor(requireContext())) { + currentTabStyle?.currentSelectColor = color + currentTabStyle?.primaryColor = color + } + + val useLightStyle = color != R.color.ui_surface.toColor(requireContext()) || + (mShowTwoLevel && currentTabStyle?.isSlideBackgroundColorEnable == false) + updateAppBarStyle(color, useLightStyle) + } + + override fun changeAppBarColor(color: Int, pageId: String) { + val currentTab = getCurrentTabEntity() + val currentTabStyle = getCurrentTabStyle() + //此处的判断是防止banner在滑动过程中切换tab会导致背景设置错误 + if (mStyle != Style.NO_TAB && (currentTab?.link?.type != ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE || currentTab.link.link != pageId)) { + val customPageTabStyle = mViewModel.getCustomPageTabEntity(pageId)?.style + customPageTabStyle?.currentSelectColor = color + if (customPageTabStyle?.isSlideBackgroundColorShow == true) { + val colorInBetween = + ColorUtils.blendARGB( + color, + R.color.ui_surface.toColor(requireContext()), + customPageTabStyle.offsetRatio + ) + customPageTabStyle.primaryColor = colorInBetween + } + } else { + currentTabStyle?.currentSelectColor = color + if (currentTabStyle?.isSlideBackgroundColorShow == true) { + val colorInBetween = + ColorUtils.blendARGB( + color, + R.color.ui_surface.toColor(requireContext()), + currentTabStyle.offsetRatio + ) + currentTabStyle.primaryColor = colorInBetween + updateAppBarStyle(colorInBetween, colorInBetween != R.color.ui_surface.toColor(requireContext())) + } + } + } + + override fun setSearchStyle(searchStyle: BottomTab.SearchStyle) { mSearchStyle = searchStyle } + + override fun getCurrentTabIndex(): Int? = provideViewPager()?.currentItem + + private fun getCurrentTabStyle(): MultiTabNav.LinkMultiTabNav.TabStyle? = + if (mStyle == Style.NO_TAB) mViewModel.noTabStyle else getCurrentTabEntity()?.style + + /** + * 更新 AppBar 的控件 style + */ + private fun updateAppBarStyle(color: Int, useLightStyle: Boolean) { + mBinding.toolbarBgView.setBackgroundColor(color) + mBinding.statusBar.setBackgroundColor(color) + mBinding.headerBgView.setBackgroundColor(color) + mBinding.tabBgView.setBackgroundColor(color) + mBinding.contentBgView.setBackgroundColor(color) + + updateSearchToolbarStyle(mIsDarkModeOn || useLightStyle) + } + + private fun updateSearchToolbarStyle(useLightStyle: Boolean, force: Boolean = false) { + if (mLightSearchToolbarStyle == useLightStyle && !force) return + mLightSearchToolbarStyle = useLightStyle + + mBinding.searchToolbar.actionbarIvSearch.setImageResource(if (useLightStyle) R.drawable.toolbar_search_icon_light else R.drawable.toolbar_search_icon) + mBinding.searchToolbar.actionbarDownload.setImageResource(if (useLightStyle) R.drawable.toolbar_download_light else R.drawable.toolbar_download) + mBinding.searchToolbar.actionbarMessage.setImageResource(if (useLightStyle) R.drawable.toolbar_message_light else R.drawable.toolbar_message) + mBinding.searchToolbar.actionbarTeenagerModelIv.setImageResource(if (useLightStyle) R.drawable.toolbar_teenager_light else R.drawable.toolbar_teenager) + mBinding.searchToolbar.actionbarTeenagerModelTv.setTextColor( + if (useLightStyle) Color.WHITE else R.color.text_secondary.toColor( + requireContext() + ) + ) + + mBinding.tabSearchToolbar.searchIv.setImageResource(if (useLightStyle) R.drawable.toolbar_search_icon_light else R.drawable.toolbar_search_icon) + mBinding.tabSearchToolbar.menuDownloadIv.setImageResource(if (useLightStyle) R.drawable.toolbar_download_light else R.drawable.toolbar_download) + + mSearchRightView?.setImageResource(if (useLightStyle) R.drawable.toolbar_search_light else R.drawable.toolbar_search) + mTeenagerModelView?.setBackgroundResource(if (useLightStyle) R.drawable.button_round_black_alpha_10 else R.drawable.button_round_f2f4f7) + mSearchHintTv?.setHintTextColor( + if (useLightStyle) R.color.search_text_color_light.toColor(requireContext()) else R.color.search_text_color_default.toColor( + requireContext() + ) + ) + mSearchContainerView?.background = + if (useLightStyle) R.drawable.home_search_bg_light.toDrawable(requireContext()) else R.drawable.home_search_bg.toDrawable( + requireContext() + ) + } + + override fun onBackPressed(): Boolean { + if (mShowTwoLevel) { + finishTwoLevel("主动收起") + return true + } + return super.onBackPressed() + } + + private fun updateTabAndToolbarStyleOnDarkModeChanged() { + mViewModel.tabListLiveData.value?.forEach { + if (it.link?.type != ViewPagerFragmentHelper.TYPE_AMWAY) { + it.style.primaryColor = R.color.ui_surface.toColor(requireContext()) + } + } + val currentTabEntity = getCurrentTabEntity() + if (currentTabEntity?.link?.type != ViewPagerFragmentHelper.TYPE_AMWAY && currentTabEntity?.link?.type != ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE) { + onScrollChanged(0, 0, true) + } + mBinding.run { + contentContainer.setCardBackgroundColor(R.color.ui_surface.toColor(requireContext())) + toolbarBackground.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) + if (mShowTwoLevel) { + updateStyleOnHeaderMoving(mHeaderOffset) + } + if (getCurrentTabStyle()?.isSlideBackgroundColorEnable == false) { + toolbarBgView.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) + } + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(reuse: EBReuse) { + if ("Refresh" == reuse.type) { + setSearchHint() + + // 打开下载按钮事件 + mDownloadView?.isVisible = Config.isShow() + } + if (reuse.type == EntranceConsts.HOST_HOME_GAME_COLLECTION_SQUARE) { + mFragmentList.forEachIndexed { index, fragment -> + if (fragment is GameCollectionSquareFragment) { + mBinding.viewPager.currentItem = index + } + } + } + if (reuse.type == Constants.FINISH_PULL_DOWN_PUSH) { + if (!mPullDownPushClick) { + mBaseHandler.postDelayed({ + finishTwoLevel("跳转收起") + }, FINISH_TWO_LEVEL_DELAY) + } + mPullDownPushClick = false + } + } + + override fun onTabViewClick() { + if (mShowTwoLevel) { + mBinding.twoLevelHeader.setFloorDuration(100) + mBinding.refreshLayout.setFloorDuration(100) + finishTwoLevel("跳转收起") + } + } + + /** + * 刷新状态栏风格 (亮色或暗色) + * 因为调用此方法损耗较大,所以需要在状态变更时才调用 + * @param force 是否强制刷新 + */ + private fun refreshStatusBarStyle(force: Boolean = false) { + val lightStatusBar = !mShowTwoLevel && !mIsDisplayingLightContent && !mIsDarkModeOn + if ((lightStatusBar != mLightStatusBar || force) && isSupportVisible) { + mLightStatusBar = lightStatusBar + DisplayUtils.transparentStatusBar(requireActivity()) + DisplayUtils.setLightStatusBar(requireActivity(), lightStatusBar) + } + } + + /** + * 记录 tab 被选中的日志 + */ + override fun logTabSelected(tabEntity: MultiTabNav.LinkMultiTabNav, position: Int) { + super.logTabSelected(tabEntity, position) + LogUtils.logHomeTopTabClick( + tabEntity.name, + tabEntity.link?.type, + tabEntity.link?.text, + tabEntity.link?.link, + position, + "首页" + ) + + if (tabEntity.link?.type == "common_collection") { + NewLogUtils.logAccessToCommonCollectionDetail( + tabEntity.link.link ?: "", + tabEntity.link.text ?: "", + "首页顶部Tab栏" + ) + } + } + + override fun onDarkModeChanged() { + super.onDarkModeChanged() + updateSearchToolbarStyle(mIsDarkModeOn || mIsDisplayingLightContent, true) + updateRefreshHeaderStyle(true) + updateTabAndToolbarStyleOnDarkModeChanged() + updatePullDownPushDownloadBtn() + } + + override fun registerInferiorChain(chain: PriorityChain) { + mInferiorChain = chain + if (mPriorityChain.isHandlerQueueEmpty()) { + mInferiorChain?.resume() + } + } + + override fun unregisterInferiorChain(chain: PriorityChain) { + if (mInferiorChain == chain) { + mInferiorChain = null + } + } + + override fun onDestroyView() { + super.onDestroyView() + mAutoFinishTwoLevelHandler?.removeCallbacksAndMessages(null) + mBinding.autoVideoView.release() + } + + enum class Style { + /** + * 搜索栏+无Tab + */ + NO_TAB, + + /** + * 单行搜索栏+Tab + * TAB栏与搜索栏在同一行展示,对应 search_style.style_type=apposition + */ + SINGLE_LINE_TAB, + + /** + * 两行搜索栏+Tab + * TAB栏与搜索栏分开两行展示,对应 search_style.style_type=two_lines + */ + TWO_LINES_TAB, + } + + companion object { + const val PULL_DOWN_PUSH_VIDEO_PLAY_DELAY = 100L + const val AUTO_FINISH_TWO_LEVEL_DELAY = 5000L + const val FINISH_TWO_LEVEL_DELAY = 1000L + const val AUTO_OPEN_TWO_LEVEL_DURATION = 1000L + const val PULL_TAB_TEXT_STYLE_UPDATE_PERCENT = 0.5F + const val KEY_AUTO_FINISH_TWO_LEVEL = 123 + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/SearchToolbarTabWrapperViewModel.kt b/app/src/main/java/com/gh/gamecenter/wrapper/SearchToolbarTabWrapperViewModel.kt new file mode 100644 index 0000000000..347cc0ae42 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/SearchToolbarTabWrapperViewModel.kt @@ -0,0 +1,97 @@ +package com.gh.gamecenter.wrapper + +import android.app.Application +import androidx.lifecycle.* +import com.gh.common.util.ViewPagerFragmentHelper +import com.gh.gamecenter.common.utils.safelyGetInRelease +import com.gh.gamecenter.entity.MultiTabNav +import com.gh.gamecenter.livedata.Event +import com.halo.assistant.HaloApp +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.transformWhile + +class SearchToolbarTabWrapperViewModel( + application: Application, + private val multiTabNavId: String, + private val noTabLinkId: String, + repository: MainWrapperRepository +) : TabWrapperViewModel(application, multiTabNavId, repository) { + var noTabStyle = MultiTabNav.LinkMultiTabNav.TabStyle() + var appBarOffset = 0 + + private val _backgroundAlpha = MutableLiveData() + val backgroundAlpha: LiveData = _backgroundAlpha + fun updateBackgroundAlpha(alpha: Float) { + _backgroundAlpha.value = alpha + } + + val tabSelectLiveData = + repository.tabSelectEventFlow + .map { it as? MainSelectedEvent.SelectedTab } + .transformWhile { originalEvent -> + if (originalEvent?.navTabId == multiTabNavId) { + if (originalEvent.topTabIndex == -1 && originalEvent.topTabId.isNotEmpty()) { + multiTabLoadedFlow.receiveAsFlow().collect { tabList -> + val newTopTabIndex = tabList.indexOfFirst { + it.id == originalEvent.topTabId + } + emit(Event(MainSelectedEvent.SelectedTab(topTabIndex = newTopTabIndex))) + } + } else { + emit(Event(originalEvent)) + } + } + true + } + .asLiveData() + + override fun overwriteDefaultIfNeeded(multiTabNav: MultiTabNav) { + super.overwriteDefaultIfNeeded(multiTabNav) + + if (multiTabNav.id == repository.highPrioritySelectedNavId) { + val newDefaultTabPosition = multiTabNav.linkMultiTabNav.indexOfFirst { + it.id == repository.highPrioritySelectedTopTabId + } + + if (newDefaultTabPosition != -1 && newDefaultTabPosition != defaultTabPosition) { + defaultTabPosition = newDefaultTabPosition + for ((index, tab) in multiTabNav.linkMultiTabNav.withIndex()) { + tab.default = index == defaultTabPosition + } + } + } + } + + fun isTabCustomPage(position: Int) = + tabListLiveData.value?.safelyGetInRelease(position)?.link?.type == ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE || noTabLinkId.isNotEmpty() + + fun getCustomPageTabEntity(pageId: String): MultiTabNav.LinkMultiTabNav? = + tabListLiveData.value?.find { it.link?.type == ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE && it.link.link == pageId } + + fun enableSlideBackgroundColor(pageId: String, isSlideBackgroundColorEnable: Boolean) { + if (multiTabNavId.isNotEmpty()) { + getCustomPageTabEntity(pageId)?.apply { + style.isSlideBackgroundColorEnable = isSlideBackgroundColorEnable + style.isSlideBackgroundColorShow = isSlideBackgroundColorEnable + style.useLightStyle = isSlideBackgroundColorEnable + } + } else if (noTabLinkId.isNotEmpty()) { + noTabStyle.isSlideBackgroundColorEnable = isSlideBackgroundColorEnable + noTabStyle.isSlideBackgroundColorShow = isSlideBackgroundColorEnable + noTabStyle.useLightStyle = isSlideBackgroundColorEnable + } + } + + class Factory(private val mMultiTabNavId: String, private val mNoTabLinkId: String) : + ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T { + return SearchToolbarTabWrapperViewModel( + HaloApp.getInstance(), + mMultiTabNavId, + mNoTabLinkId, + MainWrapperRepository.getInstance() + ) as T + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/TabWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/wrapper/TabWrapperFragment.kt new file mode 100644 index 0000000000..03c409bc04 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/TabWrapperFragment.kt @@ -0,0 +1,126 @@ +package com.gh.gamecenter.wrapper + +import android.os.Bundle +import android.view.View +import androidx.viewpager.widget.ViewPager +import com.gh.common.iinterface.ISmartRefresh +import com.gh.common.iinterface.ISmartRefreshContent +import com.gh.common.prioritychain.PullDownPushHandler +import com.gh.gamecenter.R +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.viewModelProvider +import com.gh.gamecenter.common.view.TabIndicatorView +import com.gh.gamecenter.core.utils.SentryHelper +import com.gh.gamecenter.databinding.FragmentTabWrapperBinding +import com.gh.gamecenter.entity.MultiTabNav +import com.gh.gamecenter.entity.PullDownPush +import com.google.android.material.tabs.TabLayout +import kotlin.math.roundToInt + +/** + * 二级页面-多tab导航页(带Tab的外层Fragment) + */ +class TabWrapperFragment: BaseTabWrapperFragment(), ISmartRefresh, ISmartRefreshContent { + private val mBinding by lazy { FragmentTabWrapperBinding.inflate(layoutInflater) } + private val mViewModel: TabWrapperViewModel by lazy { viewModelProvider(TabWrapperViewModel.Factory(mMultiTabNavId)) } + + override fun getLayoutId(): Int = 0 + override fun getInflatedLayout(): View = mBinding.root + override fun provideViewModel(): TabWrapperViewModel = mViewModel + override fun provideTabLayout(): TabLayout = mBinding.tabLayout + override fun provideViewPager(): ViewPager = mBinding.viewPager + override fun provideIndicatorView(): TabIndicatorView = mBinding.indicatorView + override fun provideIndicatorWidth(): Int = 20 + override fun provideTabTextSize(): Float = 14F + override fun provideTabDefaultColor(): Int = R.color.text_secondary + override fun provideTabSelectedColor(): Int = R.color.text_theme + override fun isTabScaleEnabled(): Boolean = false + override fun getFirstTabLeftMargin(): Int = 4F.dip2px() + override fun getLastTabRightMargin(): Int = 4F.dip2px() + + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + getCurrentTabEntity()?.showPullDownPush?.let { setSmartRefreshEnabled(it) } + finishTwoLevel("跳转收起") + } + + override fun onPageScrolled( + position: Int, + positionOffset: Float, + tabEntityList: ArrayList + ) { + // 这里的 selectedPosition 指的是应该被高亮显示的 position + val selectedPosition = try { + (position + positionOffset).roundToInt() + } catch (e: IllegalArgumentException) { + // roundToInt() 方法有时候会报 Cannot round NaN value. 错误 + // https://sentry.shanqu.cc/organizations/lightgame/issues/301377/?project=22 + SentryHelper.onEvent( + "HOME_NAN_POSITION", + "value", + "$position+$positionOffset=(${position + positionOffset})" + ) + position + } + val positionOffsetOnRealSelectedPosition = if (positionOffset >= 0.5) { + positionOffset - 1 + } else { + positionOffset + } + + updateTabStyle(selectedPosition, positionOffsetOnRealSelectedPosition, mTabDefaultColor, mTabSelectedColor) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setNavigationTitle(arguments?.getString(EntranceConsts.KEY_NAVIGATION_TITLE, "") ?: "") + } + + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() + initMenu(R.menu.menu_download) + } + + override fun hideTab() { + mBinding.tabContainer.visibility = View.GONE + } + + override fun setScrollEnabled(isScrollEnabled: Boolean) { + getValidSmartRefreshContent()?.setScrollEnabled(isScrollEnabled) + } + + override fun onRefresh() { + getValidSmartRefreshContent()?.onRefresh() + } + + override fun setSwipeRefreshEnabled(isSwipeRefreshEnabled: Boolean) { + getValidSmartRefreshContent()?.setSwipeRefreshEnabled(isSwipeRefreshEnabled) + getCurrentTabEntity()?.showPullDownPush = !isSwipeRefreshEnabled + } + + override fun setSmartRefreshEnabled(isEnable: Boolean) { + (parentFragment as? ISmartRefresh)?.setSmartRefreshEnabled(isEnable) + } + + override fun finishTwoLevel(action: String) { + (parentFragment as? ISmartRefresh)?.finishTwoLevel(action) + } + + override fun finishRefresh() { + (parentFragment as? ISmartRefresh)?.finishRefresh() + } + + override fun popupPullDownPush(finishCallback: () -> Unit) { + (parentFragment as? ISmartRefresh)?.popupPullDownPush(finishCallback) + } + + override fun setPullDownPush(pullDownPush: PullDownPush?, pullDownPushHandler: PullDownPushHandler?) { + (parentFragment as? ISmartRefresh)?.setPullDownPush(pullDownPush, pullDownPushHandler) + } + + override fun onDarkModeChanged() { + super.onDarkModeChanged() + updateTabStyle(mLastSelectedPosition, 0F, mTabDefaultColor, mTabSelectedColor) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/TabWrapperViewModel.kt b/app/src/main/java/com/gh/gamecenter/wrapper/TabWrapperViewModel.kt new file mode 100644 index 0000000000..efcdae8d0e --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/TabWrapperViewModel.kt @@ -0,0 +1,89 @@ +package com.gh.gamecenter.wrapper + +import android.annotation.SuppressLint +import android.app.Application +import androidx.lifecycle.* +import com.gh.common.util.ViewPagerFragmentHelper +import com.gh.gamecenter.R +import com.gh.gamecenter.common.utils.singleToMain +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.entity.MultiTabNav +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +open class TabWrapperViewModel( + application: Application, + private val mMultiTabNavId: String, + internal val repository: MainWrapperRepository +) : AndroidViewModel(application) { + private val mNewApi = RetrofitManager.getInstance().newApi + + var defaultTabPosition = 0 + + val tabListLiveData = MutableLiveData>() + val errorLiveData = MutableLiveData() + + internal var multiTabLoadedFlow: Channel> = Channel(Channel.CONFLATED) + + init { + // 是否是预加载完成的 multiTabNavData + val isPreloadedMultiTabNavData = + repository.defaultNavId.isNotEmpty() + && repository.defaultNavId == mMultiTabNavId + && repository.multiTabNavLiveData.value?.id == mMultiTabNavId + if (isPreloadedMultiTabNavData) { + processMultiTabNavData(repository.multiTabNavLiveData.value!!) + } else if (mMultiTabNavId.isNotEmpty()) { + getMultiTabNav() + } + } + + @SuppressLint("CheckResult") + fun getMultiTabNav() { + mNewApi.getTabNav(mMultiTabNavId) + .compose(singleToMain()) + .subscribe({ + processMultiTabNavData(it) + }, { + errorLiveData.postValue(it) + }) + } + + private fun processMultiTabNavData(multiTabNav: MultiTabNav) { + val tabList = multiTabNav.linkMultiTabNav + tabList.forEachIndexed { index, tab -> + if (tab.link?.type == ViewPagerFragmentHelper.TYPE_AMWAY) { + tab.style.primaryColor = R.color.amway_primary_color.toColor(getApplication()) + tab.style.useLightStyle = true + tab.style.currentSelectColor = tab.style.primaryColor + } + + if (tab.default) { + defaultTabPosition = index + } + } + + overwriteDefaultIfNeeded(multiTabNav) + + tabListLiveData.postValue(tabList) + + // 延迟 10ms,让 viewPager 先 setAdapter 再触发页面选中 + viewModelScope.launch { + delay(10L) + multiTabLoadedFlow.send(tabList) + } + } + + open fun overwriteDefaultIfNeeded(multiTabNav: MultiTabNav) { + + } + + class Factory(private val mMultiTabNavId: String) : ViewModelProvider.NewInstanceFactory() { + override fun create(modelClass: Class): T { + return TabWrapperViewModel(HaloApp.getInstance(), mMultiTabNavId, MainWrapperRepository.getInstance()) as T + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/ToolbarWrapperActivity.kt b/app/src/main/java/com/gh/gamecenter/wrapper/ToolbarWrapperActivity.kt new file mode 100644 index 0000000000..f33834955e --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/ToolbarWrapperActivity.kt @@ -0,0 +1,67 @@ +package com.gh.gamecenter.wrapper + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import com.gh.common.util.ViewPagerFragmentHelper +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.activity.BaseActivity +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.home.custom.CustomPageFragment +import com.lightgame.listeners.OnBackPressedListener + +/** + * 带常规标题栏Toolbar的外层Activity,支持下拉推送 + */ +class ToolbarWrapperActivity : BaseActivity() { + + private var mContentFragment: Fragment? = null + override fun getLayoutId(): Int = R.layout.activity_amway + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val bundle = Bundle() + bundle.putAll(intent.extras) + if (!intent?.getStringExtra(EntranceConsts.KEY_MULTI_TAB_NAV_ID).isNullOrEmpty()) { + bundle.putString(ViewPagerFragmentHelper.WRAPPER_FRAGMENT_NAME, TabWrapperFragment::class.java.name) + } else if (!intent?.getStringExtra(EntranceConsts.KEY_CUSTOM_PAGE_ID).isNullOrEmpty()) { + bundle.putString(ViewPagerFragmentHelper.WRAPPER_FRAGMENT_NAME, CustomPageFragment::class.java.name) + } + bundle.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, true) + mContentFragment = supportFragmentManager.findFragmentByTag(ToolbarWrapperFragment::class.java.name) + ?: ToolbarWrapperFragment().with(bundle) + // 若 placeholder 外层为 RelativeLayout 的话,会出现莫名的偏移 + supportFragmentManager.beginTransaction() + .replace(R.id.placeholder, mContentFragment!!, ToolbarWrapperFragment::class.java.name).commitAllowingStateLoss() + } + + override fun handleBackPressed(): Boolean = + (mContentFragment as? OnBackPressedListener)?.onHandleBackPressed() ?: false + + override fun updateStaticViewBackground(view: View?) { + updateStaticView(view, listOf(R.id.tabLayout)) + } + + companion object { + fun getMultiTabNavIntent(context: Context, multiTabNavId: String, multiTabNavName: String): Intent { + val intent = Intent(context, ToolbarWrapperActivity::class.java) + intent.putExtra(EntranceConsts.KEY_MULTI_TAB_NAV_ID, multiTabNavId) + intent.putExtra(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, multiTabNavName) + intent.putExtra(EntranceConsts.KEY_NAVIGATION_TITLE, multiTabNavName) + intent.putExtra(EntranceConsts.KEY_ENTRANCE, "多tab导航页") + return intent + } + + fun getCustomPageIntent(context: Context, pageId: String, pageName: String): Intent { + val intent = Intent(context, ToolbarWrapperActivity::class.java) + intent.putExtra(EntranceConsts.KEY_CUSTOM_PAGE_ID, pageId) + intent.putExtra(EntranceConsts.KEY_CUSTOM_PAGE_NAME, pageName) + intent.putExtra(EntranceConsts.KEY_NAVIGATION_TITLE, pageName) + intent.putExtra(EntranceConsts.KEY_ENTRANCE, "自定义页面") + return intent + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/wrapper/ToolbarWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/wrapper/ToolbarWrapperFragment.kt new file mode 100644 index 0000000000..f8d5611bff --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/wrapper/ToolbarWrapperFragment.kt @@ -0,0 +1,954 @@ +package com.gh.gamecenter.wrapper + +import android.graphics.Typeface +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.os.Message +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.view.ViewGroup.MarginLayoutParams +import android.view.ViewTreeObserver +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.appcompat.widget.ActionMenuView +import androidx.appcompat.widget.Toolbar +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import androidx.fragment.app.Fragment +import com.facebook.drawee.view.SimpleDraweeView +import com.gh.common.exposure.ExposureManager +import com.gh.common.iinterface.IMultiTab +import com.gh.common.iinterface.ISmartRefresh +import com.gh.common.iinterface.ISmartRefreshContent +import com.gh.common.prioritychain.PullDownPushHandler +import com.gh.common.util.DirectUtils +import com.gh.common.util.DownloadItemUtils +import com.gh.common.util.ViewPagerFragmentHelper +import com.gh.download.DownloadManager +import com.gh.gamecenter.GameDetailActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.adapter.viewholder.GameViewHolder +import com.gh.gamecenter.common.base.fragment.LazyFragment +import com.gh.gamecenter.common.base.fragment.ToolbarController +import com.gh.gamecenter.common.base.fragment.ToolbarFragment +import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.eventbus.EBReuse +import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.json.json +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.utils.DisplayUtils +import com.gh.gamecenter.core.utils.MD5Utils +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.core.utils.TimeElapsedHelper +import com.gh.gamecenter.databinding.FragmentToolbarWrapperBinding +import com.gh.gamecenter.entity.GameUpdateEntity +import com.gh.gamecenter.entity.PullDownPush +import com.gh.gamecenter.eventbus.EBDownloadStatus +import com.gh.gamecenter.feature.exposure.ExposureEvent +import com.gh.gamecenter.feature.exposure.time.TimeUtil +import com.gh.gamecenter.feature.game.GameItemViewHolder +import com.gh.gamecenter.home.video.ScrollCalculatorHelper +import com.gh.gamecenter.packagehelper.PackageViewModel +import com.halo.assistant.HaloApp +import com.lightgame.OnTitleClickListener +import com.lightgame.download.DataWatcher +import com.lightgame.download.DownloadEntity +import com.lightgame.listeners.OnBackPressedListener +import com.scwang.smartrefresh.layout.api.RefreshHeader +import com.scwang.smartrefresh.layout.api.RefreshLayout +import com.scwang.smartrefresh.layout.constant.RefreshState +import com.scwang.smartrefresh.layout.listener.SimpleMultiPurposeListener +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder +import com.shuyu.gsyvideoplayer.video.base.GSYVideoView +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import org.json.JSONObject + +/** + * 带常规标题栏Toolbar的外层Fragment,支持下拉推送 + */ +class ToolbarWrapperFragment : LazyFragment(), ToolbarController, ISmartRefresh, OnBackPressedListener { + private lateinit var mBinding: FragmentToolbarWrapperBinding + private var mContentFragment: Fragment? = null + private var mToolbarContainer: View? = null + private var mToolbar: Toolbar? = null + private var mTitleTv: TextView? = null + private var mAdLabelTv: TextView? = null + private var mTitleContainer: ConstraintLayout? = null + private var mIconTitleContainer: LinearLayout? = null + private var mBackContainer: FrameLayout? = null + private var mActionMenuView: ActionMenuView? = null + private var mBackBtn: ImageView? = null + private var mUserAvatarIv: SimpleDraweeView? = null + private var mIconTitle: TextView? = null + private var mDownloadBtn: ImageView? = null + private var mShowDownloadMenu = false + private var mDownloadCountHint: TextView? = null + private var mPackageViewModel: PackageViewModel? = null + private var mPullDownPushAction = "" // 触发行为(主动展开/自动展开) + private var mShowTwoLevel = false + private var mIsDragging = false + private var mPullDownPushClick = false + private var mHeaderOffset = 0 + private var mClipChildren = true + private var mTwoLevelOpenOffset = 0 + private var mTwoLevelOpenCount = 0 + private var mShowTwoLevelStartOffset = 0 + private var mPullDownPush: PullDownPush? = null + private var mPullDownPushExposureEvent: ExposureEvent? = null + private var mGameViewHolder: GameViewHolder? = null + private val mStatusBarHeight by lazy { DisplayUtils.getStatusBarHeight(requireContext().resources) } + private var mLightToolbar = false + private var mAutoFinishTwoLevelHandler: Handler? = null + + private lateinit var mElapsedHelper: TimeElapsedHelper + + private val mDataWatcher = object : DataWatcher() { + override fun onDataChanged(downloadEntity: DownloadEntity) { + if (downloadEntity.gameId == mPullDownPush?.game?.id) { + updatePullDownPushDownloadBtn() + } + } + + override fun onDataInit(downloadEntity: DownloadEntity) { + onDataChanged(downloadEntity) + } + } + + override fun getRealLayoutId(): Int = R.layout.fragment_toolbar_wrapper + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mShowDownloadMenu = arguments?.getBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, false) ?: false + } + + override fun onRealLayoutInflated(inflatedView: View) { + mBinding = FragmentToolbarWrapperBinding.bind(inflatedView) + mToolbarContainer = mBinding.toolbar.normalToolbarContainer + mToolbar = mBinding.toolbar.normalToolbar + mTitleTv = mBinding.toolbar.normalTitle + mAdLabelTv = mBinding.toolbar.adLabelTv + mActionMenuView = mBinding.toolbar.actionMenuView + mTitleContainer = mBinding.toolbar.titleContainer + mIconTitleContainer = mBinding.toolbar.iconTitleContainer + mBackContainer = mBinding.toolbar.backContainer + mBackBtn = mBinding.toolbar.backBtn + mUserAvatarIv = mBinding.toolbar.userAvatar + mIconTitle = mBinding.toolbar.iconTitle + + initToolbar() + + tryWithDefaultCatch { + if (arguments != null) { + val className = requireArguments().getString(ViewPagerFragmentHelper.WRAPPER_FRAGMENT_NAME, "") + mContentFragment = + childFragmentManager.findFragmentById(R.id.wrapper_main_content) ?: Class.forName(className) + .getDeclaredConstructor().newInstance() as Fragment + mContentFragment!!.arguments = requireArguments().clone() as Bundle + } + } + if (mContentFragment != null) { + childFragmentManager.beginTransaction() + .replace(R.id.wrapper_main_content, mContentFragment!!) + .commitAllowingStateLoss() + } + } + + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() + mElapsedHelper = TimeElapsedHelper() + + if (!SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) && mShowDownloadMenu) { + mPackageViewModel = viewModelProvider(PackageViewModel.Factory()) + mPackageViewModel?.filterSameUpdateLiveData?.observe(this) { updateList: List -> + updateDownloadCountHint(updateList) + } + } + + mBinding.toolbar.normalToolbar.background = null + + initSmartRefresh() + } + + override fun setPullDownPush(pullDownPush: PullDownPush?, pullDownPushHandler: PullDownPushHandler?) { + if (pullDownPush != null) { + setSmartRefreshEnabled(true) + if ((mPullDownPush?.id ?: "") == pullDownPush.id) return + mPullDownPush = pullDownPush + mPullDownPush?.run { + if (game != null) { + game.sequence = 1 + mPullDownPushExposureEvent = ExposureEvent.createEventWithSourceConcat( + game, + basicSource = listOf(ExposureSource("自定义页面", "$customPageName+$customPageId")), + source = listOf(ExposureSource("下拉推送", id)) + ) + } + + mBinding.run { + if (game != null) { + gameNameTv.text = game.name + GameItemViewHolder.initGameSubtitleAndAdLabel(game, gameSubtitleTv) + updatePullDownPushDownloadBtn() + headerContainer.setOnClickListener { + if (mShowTwoLevel) { + mPullDownPushClick = true + SensorsBridge.trackEvent( + "DropDownPushClick", + json { + "action" to mPullDownPushAction + "button_name" to "游戏详情" + "drop_down_push_id" to id + "game_name" to game.name + "game_id" to game.id + getPageInfo().forEach { (key, value) -> + key to value + } + } + ) + GameDetailActivity.startGameDetailActivity( + requireContext(), + game.id, + "(自定义页面-下拉推送)", + mPullDownPushExposureEvent + ) + } + } + } + descTv.text = description + gameImageIv.goneIf(video != null) { + ImageUtils.display(gameImageIv, imgUrl) + } + autoVideoView.goneIf(video == null) { + if (!autoVideoView.isInPlayingState) { + GSYVideoOptionBuilder() + .setIsTouchWiget(false) + .setUrl(video?.url ?: "") + .setRotateViewAuto(false) + .setCacheWithPlay(true) + .setRotateWithSystem(false) + .setReleaseWhenLossAudio(true) + .setLooping(false) + .setShowFullAnimation(false) + .build(autoVideoView) + autoVideoView.updateThumb(imgUrl) + + // 处理部分设备下拉推送自动弹出时封面图高度为1 https://jira.shanqu.cc/browse/GHZS-4986 + autoVideoView.thumbImage.viewTreeObserver.addOnGlobalLayoutListener(object : + ViewTreeObserver.OnGlobalLayoutListener { + override fun onGlobalLayout() { + if (autoVideoView.thumbImage.height == 1) { + autoVideoView.thumbImage.requestLayout() + } else { + autoVideoView.thumbImage.viewTreeObserver.removeOnGlobalLayoutListener(this) + } + } + }) + + autoVideoView.setParamsData(game, null) + autoVideoView.setOnVideoClickListener { + headerContainer.performClick() + } + } + } + } + } + } + + val pullDownPushSet = + SPUtils.getStringSet(Constants.SP_PULL_DOWN_PUSH_POP_UP_SET) + val popupPush = mPullDownPush != null + && "on" == mPullDownPush!!.popSwitch + && !pullDownPushSet.contains(mPullDownPush!!.id) + pullDownPushHandler?.doPreProcess(this as? ISmartRefresh, popupPush) + } + + override fun onFragmentPause() { + super.onFragmentPause() + DownloadManager.getInstance().removeObserver(mDataWatcher) + } + + override fun onFragmentResume() { + super.onFragmentResume() + refreshStatusBarStyle() + setTitleCenter() + DownloadManager.getInstance().addObserver(mDataWatcher) + } + + private fun inflateMenu(res: Int) { + requireActivity().menuInflater.inflate(res, mActionMenuView?.menu) + if (!SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) && mShowDownloadMenu) { + createDownloadMenu(res) + } + } + + private fun createDownloadMenu(res: Int) { + if (res != R.menu.menu_download) { + requireActivity().menuInflater.inflate(R.menu.menu_download, mActionMenuView?.menu) + } + if (mPackageViewModel != null) { + updateDownloadCountHint(mPackageViewModel?.filterSameUpdateLiveData?.value) + } + val downloadMenuView = mActionMenuView?.menu?.findItem(R.id.menu_download)?.actionView + mDownloadCountHint = downloadMenuView?.findViewById(R.id.menu_download_count_hint) + mDownloadCountHint?.typeface = + Typeface.createFromAsset(requireActivity().assets, Constants.DIN_FONT_PATH) + mDownloadBtn = downloadMenuView?.findViewById(R.id.menu_download_iv) + mDownloadBtn?.setColorFilter(R.color.text_primary.toColor(requireContext())) + } + + private fun updateDownloadCountHint(updateList: List?) { + if (mDownloadCountHint == null) return + val count = DownloadManager.getInstance().getDownloadOrUpdateCount(updateList) + mDownloadCountHint?.goneIf(count == null) { + mDownloadCountHint?.text = count!! + val params = mDownloadCountHint?.layoutParams + params?.width = if (count.isEmpty()) 6F.dip2px() else ConstraintLayout.LayoutParams.WRAP_CONTENT + params?.height = if (count.isEmpty()) 6F.dip2px() else 14F.dip2px() + (params as? MarginLayoutParams)?.setMargins( + 0, + if (count.isEmpty()) 0 else (-4F).dip2px(), + if (count.isEmpty()) (-4F).dip2px() else (-8F).dip2px(), + 0 + ) + mDownloadCountHint?.setPadding( + if (count.isEmpty()) 0 else 4F.dip2px(), + 0, + if (count.isEmpty()) 0 else 4F.dip2px(), + 0 + ) + mDownloadCountHint?.minWidth = if (count.isEmpty()) 0 else 14F.dip2px() + mDownloadCountHint?.layoutParams = params + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(status: EBDownloadStatus?) { + if (!SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) && mShowDownloadMenu && mPackageViewModel != null) { + updateDownloadCountHint(mPackageViewModel?.filterSameUpdateLiveData?.value) + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onEventMainThread(reuse: EBReuse) { + if (reuse.type == Constants.FINISH_PULL_DOWN_PUSH) { + if (!mPullDownPushClick) { + mBaseHandler.postDelayed({ + finishTwoLevel("跳转收起") + }, FINISH_TWO_LEVEL_DELAY) + } + mPullDownPushClick = false + } + } + + // 设置标题居中 + override fun setTitleCenter() { + if (mActionMenuView != null && mTitleContainer != null && mBackContainer != null) { + mActionMenuView!!.post { + val params = + mTitleContainer!!.layoutParams as LinearLayout.LayoutParams + params.setMargins(mActionMenuView!!.width - mBackContainer!!.width, 0, 0, 0) + mTitleContainer!!.layoutParams = params + } + } + } + + private fun initToolbar() { + mBackBtn?.isVisible = !requireArguments().getBoolean(EntranceConsts.KEY_IS_HOME, false) + mBackBtn?.setOnClickListener { requireActivity().onBackPressed() } + mTitleTv?.setOnClickListener { + val fragmentList = childFragmentManager.fragments + for (fragment in fragmentList) { + if (fragment is OnTitleClickListener) { + (fragment as OnTitleClickListener).onTitleClick() + } + } + } + } + + private fun getPageInfo(): Map { + val customPageName = (mContentFragment as? IMultiTab)?.provideCurrentTabEntity()?.link?.text ?: + arguments?.getString(EntranceConsts.KEY_CUSTOM_PAGE_NAME) ?: "" + val customPageId = (mContentFragment as? IMultiTab)?.provideCurrentTabEntity()?.link?.link ?: + arguments?.getString(EntranceConsts.KEY_CUSTOM_PAGE_ID) ?: "" + return mapOf( + "position" to (mContentFragment as? IMultiTab)?.provideLastSelectedPosition(), + "tab_content" to (mContentFragment as? IMultiTab)?.provideCurrentTabEntity()?.name, + "custom_page_name" to customPageName, + "custom_page_id" to customPageId, + "several_tab_page_name" to (mContentFragment as? IMultiTab)?.provideMultiTabName(), + "several_tab_page_id" to (mContentFragment as? IMultiTab)?.provideMultiTabId(), + ) + } + + private fun initSmartRefresh() { + mBinding.run { + mGameViewHolder = GameViewHolder(headerContainer).also { + it.gameDownloadBtn = downloadBtn + it.multiVersionDownloadTv = multiVersionDownloadTv + it.gameDownloadTips = downloadTipsLottie + } + topMaskView.updateLayoutParams { + height = mStatusBarHeight + TOOLBAR_HEIGHT.dip2px() + } + mTwoLevelOpenOffset = DisplayUtils.getScreenWidth() - TOOLBAR_HEIGHT.dip2px() - mStatusBarHeight + mShowTwoLevelStartOffset = 139F.dip2px() - mStatusBarHeight + backgroundContainer.setOnlyTopRoundedColorBackground(R.color.ui_surface.toColor(requireContext()), 8F) + classicsHeader.setArrowResource(R.drawable.icon_arrow) + classicsHeader.setArrowColorFilter(R.color.text_tertiary.toColor(requireContext())) + statusBar.post { + twoLevelHeader.updateLayoutParams { + topMargin = -(mStatusBarHeight + TOOLBAR_HEIGHT.dip2px()) + } + } + refreshLayout.setOnMultiPurposeListener(object : SimpleMultiPurposeListener() { + override fun onRefresh(refreshLayout: RefreshLayout) { + SensorsBridge.trackEvent("CustomPageFlush", JSONObject(getPageInfo())) + (mContentFragment as? ISmartRefreshContent)?.onRefresh() + } + + override fun onHeaderMoving( + header: RefreshHeader?, + isDragging: Boolean, + percent: Float, + offset: Int, + headerHeight: Int, + maxDragHeight: Int + ) { + mIsDragging = isDragging + mHeaderOffset = offset + updateStyleOnHeaderMoving(offset) + headerContainer.translationY = -(mTwoLevelOpenOffset - offset).toFloat() + } + + override fun onStateChanged( + refreshLayout: RefreshLayout, + oldState: RefreshState, + newState: RefreshState + ) { + val isAutoScrollToTwoLevel = oldState == RefreshState.None && newState == RefreshState.TwoLevel + val isDragToTwoLevel = + oldState == RefreshState.ReleaseToRefresh && newState == RefreshState.ReleaseToTwoLevel + val isScrollToTwoLevel = isDragToTwoLevel || isAutoScrollToTwoLevel + val isShowTwoLevelByDrag = + oldState == RefreshState.TwoLevelReleased && newState == RefreshState.TwoLevel + val isFinishTwoLevel = oldState == RefreshState.TwoLevel && newState == RefreshState.TwoLevelFinish + + mPullDownPush?.run { + if (isAutoScrollToTwoLevel) { + mPullDownPushAction = "自动展开" + mTwoLevelOpenCount++ + SensorsBridge.trackEvent( + "DropDownPushShow", + json { + "action" to "自动展开" + "drop_down_push_id" to id + "game_name" to game?.name + "game_id" to game?.id + getPageInfo().forEach { (key, value) -> + key to value + } + } + ) + com.gh.common.util.NewFlatLogUtils.logHomePushShow( + id, + "自动展开", + mTwoLevelOpenCount, + game?.id ?: "", + game?.name ?: "" + ) + } + if (isShowTwoLevelByDrag) { + mPullDownPushAction = "主动展开" + SensorsBridge.trackEvent( + "DropDownPushShow", + json { + "action" to "主动展开" + "drop_down_push_id" to id + "game_name" to game?.name + "game_id" to game?.id + getPageInfo().forEach { (key, value) -> + key to value + } + } + ) + com.gh.common.util.NewFlatLogUtils.logHomePushShow( + id, + "主动展开", + mTwoLevelOpenCount, + game?.id ?: "", + game?.name ?: "" + ) + } + if (isDragToTwoLevel) { + mTwoLevelOpenCount++ + + SensorsBridge.trackEvent( + "DropDownPushTrigger", + json { + "drop_down_push_id" to id + "game_name" to game?.name + "game_id" to game?.id + getPageInfo().forEach { (key, value) -> + key to value + } + } + ) + com.gh.common.util.NewFlatLogUtils.logHomePushTrigger( + id, + mTwoLevelOpenCount, + game?.id ?: "", + game?.name ?: "" + ) + } + } + + if (isScrollToTwoLevel) { + mShowTwoLevel = true + } + + if (isFinishTwoLevel) { + pausePullDownPushVideo() + mShowTwoLevel = false + + if (mIsDragging) { + mAutoFinishTwoLevelHandler?.removeMessages(KEY_AUTO_FINISH_TWO_LEVEL) + mElapsedHelper.pauseCounting() + mPullDownPush?.run { + SensorsBridge.trackEvent( + "DropDownPushClick", + json { + "action" to mPullDownPushAction + "button_name" to "关闭推送" + "drop_down_push_id" to id + "game_name" to game?.name + "game_id" to game?.id + getPageInfo().forEach { (key, value) -> + key to value + } + } + ) + com.gh.common.util.NewFlatLogUtils.logHomePushClose( + id, + "主动收起", + mTwoLevelOpenCount, + game?.id ?: "", + game?.name ?: "", + mElapsedHelper.elapsedTime + ) + } + } + } + + when (newState) { + RefreshState.TwoLevel -> (mContentFragment as? ISmartRefreshContent)?.setScrollEnabled(false) + RefreshState.None -> { + (mContentFragment as? ISmartRefreshContent)?.setScrollEnabled(true) + + mShowTwoLevel = false + mIsDragging = false + twoLevelHeader.setFloorDuration(1000) + mBinding.refreshLayout.setFloorDuration(1000) + classicsHeader.visibility = View.VISIBLE + } + RefreshState.PullDownToRefresh -> { + mShowTwoLevel = false + mBinding.classicsHeader.updateLayoutParams { + topMargin = mStatusBarHeight - 11F.dip2px() + } + } + RefreshState.PullDownCanceled, + RefreshState.ReleaseToRefresh -> mShowTwoLevel = false + else -> { + // do nothing + } + } + + val clipChildren: Boolean + when (newState) { + RefreshState.TwoLevel, + RefreshState.TwoLevelFinish -> { + clipChildren = false + toolbarBackground.alpha = 0F + statusBar.alpha = 0F + } + + RefreshState.ReleaseToTwoLevel, + RefreshState.TwoLevelReleased -> { + clipChildren = false + toolbarBackground.alpha = 0F + statusBar.alpha = 0F + headerBackground.alpha = 0F + } + + else -> { + clipChildren = true + toolbarBackground.alpha = 1F + statusBar.alpha = 1F + headerBackground.alpha = 1F + } + } + + if (mClipChildren != clipChildren) { + mClipChildren = clipChildren + wrapperLl.clipChildren = mClipChildren + mBinding.refreshLayout.clipChildren = mClipChildren + } + } + }) + + twoLevelHeader.setOnTwoLevelListener { + mPullDownPushExposureEvent?.timeInMillisecond = System.currentTimeMillis() + mPullDownPushExposureEvent?.time = TimeUtil.currentTime() + mPullDownPushExposureEvent?.payload?.sequence = mTwoLevelOpenCount + mPullDownPushExposureEvent?.let { + ExposureManager.log(it) + } + mElapsedHelper.resetCounting() + mElapsedHelper.resumeCounting() + playPullDownPushVideo() + true + } + } + } + + override fun popupPullDownPush(finishCallback: () -> Unit) { + mBinding.run { + if ((mPullDownPush == null) || mContentFragment == null) { + finishCallback.invoke() + return + } + if (!isSupportVisible) { + setResumeAndPauseListener { + if (it) { + popupPullDownPush(finishCallback) + setResumeAndPauseListener(null) + } + } + return + } + + // 自动弹出,5s后自动收起 + mBaseHandler.post { + mPullDownPush?.run { + val pullDownPushSet = HashSet(SPUtils.getStringSet(Constants.SP_PULL_DOWN_PUSH_POP_UP_SET)) + mShowTwoLevel = true + twoLevelHeader.openTwoLevel(true) + wrapperLl.clipChildren = false + refreshLayout.clipChildren = false + headerBackground.animate().alpha(0F).duration = AUTO_OPEN_TWO_LEVEL_DURATION + toolbarBackground.animate().alpha(0F).duration = AUTO_OPEN_TWO_LEVEL_DURATION + classicsHeader.visibility = View.INVISIBLE + pullDownPushSet.add(id) + SPUtils.setStringSet(Constants.SP_PULL_DOWN_PUSH_POP_UP_SET, pullDownPushSet) + mAutoFinishTwoLevelHandler = object : Handler(Looper.getMainLooper()) { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + finishTwoLevel("自动收起") + finishCallback.invoke() + } + } + mAutoFinishTwoLevelHandler?.sendEmptyMessageDelayed(KEY_AUTO_FINISH_TWO_LEVEL, AUTO_FINISH_TWO_LEVEL_DELAY) + } + } + } + } + + // 下拉进入/退出下拉推送时更新背景和颜色 + private fun updateStyleOnHeaderMoving(offset: Int) { + if (context == null) return + mBinding.run { + val lightToolbar = offset >= mShowTwoLevelStartOffset + if (mLightToolbar != lightToolbar) { + mLightToolbar = lightToolbar + refreshStatusBarStyle() + val color = if (lightToolbar) R.color.text_aw_primary.toColor(requireContext()) else R.color.text_primary.toColor(requireContext()) + toolbar.normalTitle.setTextColor(color) + mBackBtn?.setColorFilter(color) + if (mShowDownloadMenu) { + mDownloadBtn?.setColorFilter(color) + } + } + + if (refreshLayout.state == RefreshState.TwoLevelFinish || refreshLayout.state == RefreshState.TwoLevel) { + val offsetRatio = 1 - offset / mTwoLevelOpenOffset.toFloat() + headerBackground.alpha = offsetRatio + } + } + } + + // 播放下拉推送视频 + private fun playPullDownPushVideo() { + mBinding.run { + if (!mPullDownPush?.video?.url.isNullOrEmpty()) { + if (autoVideoView.currentState != GSYVideoView.CURRENT_STATE_PAUSE) { + mBaseHandler.postDelayed({ + val videoOption = + SPUtils.getString(Constants.SP_HOME_OR_DETAIL_VIDEO_OPTION, Constants.VIDEO_OPTION_WIFI) + ?: Constants.VIDEO_OPTION_WIFI + + when (videoOption) { + Constants.VIDEO_OPTION_ALL -> { + autoVideoView.onVideoReset() + autoVideoView.startPlayLogic(false) + } + + Constants.VIDEO_OPTION_WIFI -> { + if (NetworkUtils.isWifiConnected(HaloApp.getInstance().application)) { + autoVideoView.onVideoReset() + autoVideoView.startPlayLogic(false) + } + } + + else -> { + //do nothing + } + } + }, PULL_DOWN_PUSH_VIDEO_PLAY_DELAY) + } else { + resumePullDownPushVideo() + } + } + } + } + + private fun pausePullDownPushVideo() { + mBinding.run { + if (mShowTwoLevel && !mPullDownPush?.video?.url.isNullOrEmpty()) { + autoVideoView.onVideoPause() + val currentPosition = autoVideoView.getCurrentPosition() + val videoUrl = autoVideoView.getUrl() + if (videoUrl.isNotEmpty()) { + ScrollCalculatorHelper.savePlaySchedule(MD5Utils.getContentMD5(videoUrl), currentPosition) + } + } + } + } + + private fun resumePullDownPushVideo() { + mBinding.run { + if (mShowTwoLevel && !mPullDownPush?.video?.url.isNullOrEmpty()) { + val videoUrl = autoVideoView.getUrl() + if (videoUrl.isNotEmpty()) { + val position = ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(videoUrl)) + //这里必须要延迟操作,否则会白屏 + mBaseHandler.postDelayed({ + if (position != 0L) { + autoVideoView.seekTo(position) + autoVideoView.onVideoResume(false) + val topVideoVoiceStatus = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true) + if (topVideoVoiceStatus) { + autoVideoView.mute() + } else { + autoVideoView.unMute() + } + } else { + autoVideoView.release() + } + }, PULL_DOWN_PUSH_VIDEO_PLAY_DELAY) + } + } + } + } + + private fun updatePullDownPushDownloadBtn() { + mBinding.run { + mPullDownPush?.game?.run { + mGameViewHolder?.let { + DownloadItemUtils.updateItem(requireContext(), this, it) + } + DownloadItemUtils.setOnClickListener( + requireContext(), + downloadBtn, + this, + 0, + null, + "自定义页面-下拉推送", + location = "", + traceEvent = mPullDownPushExposureEvent, + clickCallback = { + updatePullDownPushDownloadBtn() + SensorsBridge.trackEvent( + "DropDownPushClick", + json { + "action" to mPullDownPushAction + "button_name" to "下载按钮" + "drop_down_push_id" to id + "game_name" to name + "game_id" to id + getPageInfo().forEach { (key, value) -> + key to value + } + } + ) + }, + refreshCallback = { updatePullDownPushDownloadBtn() }, + allStateClickCallback = null + ) + } + } + } + + override fun finishTwoLevel(action: String) { + if (mShowTwoLevel) { + mAutoFinishTwoLevelHandler?.removeMessages(KEY_AUTO_FINISH_TWO_LEVEL) + mBinding.twoLevelHeader.finishTwoLevel() + mElapsedHelper.pauseCounting() + mPullDownPush?.run { + SensorsBridge.trackEvent( + "DropDownPushClick", + json { + "action" to mPullDownPushAction + "button_name" to "关闭推送" + "drop_down_push_id" to id + "game_name" to game?.name + "game_id" to game?.id + getPageInfo().forEach { (key, value) -> + key to value + } + } + ) + com.gh.common.util.NewFlatLogUtils.logHomePushClose( + id, + action, + mTwoLevelOpenCount, + game?.id ?: "", + game?.name ?: "", + mElapsedHelper.elapsedTime + ) + } + } + } + + override fun setToolbarMenu(res: Int) { + // 青少年模式下要隐藏下载按钮 + if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) && res == R.menu.menu_download) return + if (mActionMenuView == null) return + inflateMenu(res) + + mActionMenuView?.setOnMenuItemClickListener { + onMenuItemClick(it) + false + } + mActionMenuView?.menu?.let { menu -> + for (i in 0 until menu.size()) { + val menuItem = menu.getItem(i) + // menu设置actionLayout后,无法捕捉点击事件,以icon为tag,如果icon is null 手动设置menuItem点击事件 + if (menuItem != null && menuItem.icon == null) { + if (menuItem.actionView != null) { + menuItem.actionView!!.setOnClickListener { + onMenuItemClick(menuItem) + } + } + } + } + } + setTitleCenter() + } + + override fun onMenuItemClick(menuItem: MenuItem?) { + if (menuItem?.itemId == R.id.menu_download) { + DirectUtils.directToDownloadManager( + requireContext(), + mEntrance, + customPageName = arguments?.getString(EntranceConsts.KEY_CUSTOM_PAGE_NAME, "") ?: "", + customPageId = arguments?.getString(EntranceConsts.KEY_CUSTOM_PAGE_ID, "") ?: "", + multiTabId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: "", + multiTabName = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, "") ?: "", + ) + } else if (mContentFragment is ToolbarFragment) { + (mContentFragment as ToolbarFragment).onMenuItemClick(menuItem) + } + } + + override fun getMenuItem(res: Int): MenuItem? { + return if (mToolbar == null) null else mActionMenuView?.menu?.findItem(res) //后续页面做好判断 + } + + override fun hideToolbar(isHide: Boolean) { + mToolbarContainer?.isVisible = !isHide + } + + override fun showAdLabel(isAd: Boolean) { + mAdLabelTv?.isVisible = isAd + } + + override fun setNavigationTitle(title: Int) { + mTitleTv?.text = getString(title) + mIconTitle?.text = getString(title) + } + + override fun setNavigationTitle(title: String?) { + mTitleTv?.text = title + mIconTitle?.text = title + } + + override fun onHandleBackPressed(): Boolean = onBackPressed() + + override fun onBackPressed(): Boolean { + if (mShowTwoLevel) { + finishTwoLevel("主动收起") + return true + } + return super.onBackPressed() + } + + override fun finishRefresh() { + mBinding.refreshLayout.finishRefresh(true) + } + + override fun setSmartRefreshEnabled(isEnable: Boolean) { + mBinding.refreshLayout.setEnableRefresh(isEnable) + (mContentFragment as? ISmartRefreshContent)?.setSwipeRefreshEnabled(!isEnable) + } + + private fun refreshStatusBarStyle() { + DisplayUtils.transparentStatusBar(requireActivity()) + DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && !mShowTwoLevel) + } + + override fun onDarkModeChanged() { + super.onDarkModeChanged() + if (!::mBinding.isInitialized) return + + refreshStatusBarStyle() + mBinding.toolbarBackground.setBackgroundColor(R.color.ui_surface.toColor(requireContext())) + mBinding.backgroundContainer.setOnlyTopRoundedColorBackground(R.color.ui_surface.toColor(requireContext()), 8F) + + val color = if (mShowTwoLevel) R.color.text_aw_primary.toColor(requireContext()) else R.color.text_primary.toColor(requireContext()) + mTitleTv?.setTextColor(color) + mBackBtn?.setColorFilter(color) + if (mShowDownloadMenu) { + mDownloadBtn?.setColorFilter(color) + } + mBinding.classicsHeader.run { + setArrowColorFilter(R.color.text_tertiary.toColor(requireContext())) + setPrimaryColor(R.color.ui_surface.toColor(requireContext())) + setAccentColor(R.color.text_tertiary.toColor(requireContext())) + } + } + + override fun onDestroyView() { + super.onDestroyView() + mAutoFinishTwoLevelHandler?.removeCallbacksAndMessages(null) + if (::mBinding.isInitialized) { + mBinding.autoVideoView.release() + } + } + + companion object { + const val TOOLBAR_HEIGHT = 48F + + const val PULL_DOWN_PUSH_VIDEO_PLAY_DELAY = 100L + const val AUTO_FINISH_TWO_LEVEL_DELAY = 5000L + const val FINISH_TWO_LEVEL_DELAY = 1000L + const val AUTO_OPEN_TWO_LEVEL_DURATION = 1000L + const val KEY_AUTO_FINISH_TWO_LEVEL = 456 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/ndownload/NDownloadService.java b/app/src/main/java/com/gh/ndownload/NDownloadService.java index 8b59b8cc34..ef42c70add 100644 --- a/app/src/main/java/com/gh/ndownload/NDownloadService.java +++ b/app/src/main/java/com/gh/ndownload/NDownloadService.java @@ -67,7 +67,6 @@ public class NDownloadService extends Service { // 看不懂的操作 DownloadStatus status = DownloadStatus.valueOf(statusString); DownloadEntity entry = (DownloadEntity) intent.getSerializableExtra(DownloadConfig.KEY_DOWNLOAD_ENTRY); - DownloadEntity downloadEntity = DownloadDao.getInstance(this).get(entry.getUrl()); if (downloadEntity == null) { // 设置开始下载时间(排序用到) diff --git a/app/src/main/java/com/gh/vspace/VDownloadManagerActivity.kt b/app/src/main/java/com/gh/vspace/VDownloadManagerActivity.kt index 92979c77e5..3fb04915dc 100644 --- a/app/src/main/java/com/gh/vspace/VDownloadManagerActivity.kt +++ b/app/src/main/java/com/gh/vspace/VDownloadManagerActivity.kt @@ -6,7 +6,10 @@ import android.os.Bundle import com.gh.gamecenter.R import com.gh.gamecenter.common.base.activity.ToolBarActivity import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.constant.EntranceConsts.KEY_CUSTOM_PAGE_LOCATION +import com.gh.gamecenter.common.constant.EntranceConsts.KEY_ENTRANCE import com.gh.gamecenter.common.utils.updateStatusBarColor +import com.gh.gamecenter.feature.entity.PageLocation class VDownloadManagerActivity : ToolBarActivity() { @@ -49,6 +52,18 @@ class VDownloadManagerActivity : ToolBarActivity() { intent.putExtra(EntranceConsts.KEY_IS_FROM_HOME_RECENT, isFromHomeRecent) return intent } + + @JvmStatic + fun getIntent( + context: Context, + entrance: String, + pageLocation: PageLocation? = null + ): Intent { + val intent = Intent(context, VDownloadManagerActivity::class.java) + intent.putExtra(KEY_ENTRANCE, entrance) + intent.putExtra(KEY_CUSTOM_PAGE_LOCATION, pageLocation) + return intent + } } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/vspace/VDownloadManagerAdapter.kt b/app/src/main/java/com/gh/vspace/VDownloadManagerAdapter.kt index 948f4d773d..4d1c7f6167 100644 --- a/app/src/main/java/com/gh/vspace/VDownloadManagerAdapter.kt +++ b/app/src/main/java/com/gh/vspace/VDownloadManagerAdapter.kt @@ -141,7 +141,7 @@ class VDownloadManagerAdapter( } } - BindingAdapters.setGameName(holder.binding.nameTv, gameEntity, false, true) + BindingAdapters.setGameName(holder.binding.nameTv, gameEntity, false) holder.binding.iconIv.displayGameIcon(gameEntity) holder.binding.descTv.goneIf(mViewModel.isTypeDownloaded() && gameEntity.des?.isEmpty() == true) diff --git a/app/src/main/java/com/gh/vspace/VDownloadManagerWrapperFragment.kt b/app/src/main/java/com/gh/vspace/VDownloadManagerWrapperFragment.kt index 203d2b8861..c991181121 100644 --- a/app/src/main/java/com/gh/vspace/VDownloadManagerWrapperFragment.kt +++ b/app/src/main/java/com/gh/vspace/VDownloadManagerWrapperFragment.kt @@ -9,9 +9,12 @@ import androidx.fragment.app.Fragment import com.gh.gamecenter.R import com.gh.gamecenter.common.base.fragment.BaseLazyTabFragment import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.common.constant.EntranceConsts.KEY_CUSTOM_PAGE_LOCATION +import com.gh.gamecenter.common.constant.EntranceConsts.KEY_ENTRANCE import com.gh.gamecenter.common.utils.SensorsBridge import com.gh.gamecenter.common.utils.viewModelProvider import com.gh.gamecenter.databinding.FragmentVdownloadManagerWrapperBinding +import com.gh.gamecenter.feature.entity.PageLocation import com.gh.gamecenter.history.IBatchDelete import com.gh.gamecenter.history.ManageOption @@ -27,7 +30,18 @@ class VDownloadManagerWrapperFragment : BaseLazyTabFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - SensorsBridge.trackHaloFunGameManagerEnter() + val entrance = arguments?.getString(KEY_ENTRANCE) ?: "" + val pageLocation = arguments?.getParcelable(KEY_CUSTOM_PAGE_LOCATION) ?: PageLocation() + SensorsBridge.trackHaloFunGameManagerEnter( + entrance, + pageLocation.bottomTab, + pageLocation.severalTabPageId, + pageLocation.severalTabPageName, + pageLocation.tabPosition, + pageLocation.tabContent, + pageLocation.pageId, + pageLocation.pageName + ) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -50,9 +64,11 @@ class VDownloadManagerWrapperFragment : BaseLazyTabFragment() { ManageOption.OPTION_MANAGER -> { mViewModel.currentOptionLiveData.value = ManageOption.OPTION_CANCEL_SELECT } + ManageOption.OPTION_CANCEL_SELECT -> { mViewModel.currentOptionLiveData.value = ManageOption.OPTION_MANAGER } + else -> { // do nothing } diff --git a/app/src/main/java/com/gh/vspace/VHelper.kt b/app/src/main/java/com/gh/vspace/VHelper.kt index 692226ea61..6226caa04a 100644 --- a/app/src/main/java/com/gh/vspace/VHelper.kt +++ b/app/src/main/java/com/gh/vspace/VHelper.kt @@ -34,11 +34,15 @@ import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.core.runOnIoThread import com.gh.gamecenter.core.runOnUiThread import com.gh.gamecenter.core.utils.* -import com.gh.gamecenter.entity.* +import com.gh.gamecenter.entity.AppEntity +import com.gh.gamecenter.entity.GameUpdateEntity +import com.gh.gamecenter.entity.SubjectRecommendEntity +import com.gh.gamecenter.entity.VSetting import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.feature.entity.ApkEntity import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.InstallGameEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent import com.gh.gamecenter.manager.PackagesManager import com.gh.gamecenter.packagehelper.PackageRepository import com.gh.gamecenter.retrofit.RetrofitManager @@ -1318,6 +1322,7 @@ object VHelper { try { vGameDao.insert(it) refreshVGameSnapshot() + PackageRepository.changeRecentVaPlayed() } catch (e: SQLiteFullException) { ToastUtils.toast("设备存储空间不足,请清理后重试") } @@ -1624,10 +1629,10 @@ object VHelper { /** * 打开畅玩广场(版块) */ - fun startVSpaceSquare(context: Context) { + fun startVSpaceSquare(context: Context, exposureEvent: ExposureEvent? = null) { val entity = SubjectRecommendEntity(link = "62fc8047b07e0c0bb63058c2", nameNormal = "畅玩广场", text = "畅玩广场") - DirectUtils.directToBlock(context, entity, "") + DirectUtils.directToBlock(context, entity, "", exposureEvent) } /** diff --git a/app/src/main/java/com/halo/assistant/HaloApp.java b/app/src/main/java/com/halo/assistant/HaloApp.java index f0c3ca051f..c21cef7790 100644 --- a/app/src/main/java/com/halo/assistant/HaloApp.java +++ b/app/src/main/java/com/halo/assistant/HaloApp.java @@ -6,7 +6,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.net.ConnectivityManager; -import android.net.Uri; import android.os.Build; import android.os.StrictMode; import android.preference.PreferenceManager; @@ -32,6 +31,8 @@ import com.gh.base.GlobalActivityLifecycleObserver; import com.gh.common.FixedRateJobHelper; import com.gh.common.filter.RegionSettingHelper; import com.gh.common.util.ActivationHelper; +import com.gh.common.prioritychain.GlobalPriorityChainHelper; +import com.gh.common.util.ActivationHelper; import com.gh.common.util.DataUtils; import com.gh.common.util.DeviceTokenUtils; import com.gh.common.util.DownloadNotificationHelper; @@ -64,8 +65,6 @@ import com.gh.gamecenter.core.provider.IFlavorProvider; import com.gh.gamecenter.core.utils.DisplayUtils; import com.gh.gamecenter.core.utils.GsonUtils; import com.gh.gamecenter.core.utils.SPUtils; -import com.gh.gamecenter.entity.SubjectRecommendEntity; -import com.gh.gamecenter.fragment.MainWrapperRepository; import com.gh.gamecenter.login.user.UserRepository; import com.gh.gamecenter.oaid.OAIDHelper; import com.gh.gamecenter.packagehelper.PackageRepository; @@ -75,6 +74,7 @@ import com.gh.gamecenter.receiver.DownloadReceiver; import com.gh.gamecenter.receiver.InstallAndUninstallReceiver; import com.gh.gamecenter.receiver.InstallReceiver; import com.gh.gamecenter.receiver.NetworkStateReceiver; +import com.gh.gamecenter.wrapper.MainWrapperRepository; import com.gh.vspace.VHelper; import com.github.piasy.biv.BigImageViewer; import com.github.piasy.biv.loader.fresco.FrescoImageLoader; @@ -336,7 +336,9 @@ public class HaloApp extends MultiDexApplication { // 必须放在外面,否则不能及时刷新用户数据 UserRepository.getInstance().getLoginUserInfo(); - MainWrapperRepository.Companion.getInstance().getHomeNavBar(); + GlobalPriorityChainHelper.INSTANCE.preStart(); + + MainWrapperRepository.Companion.getInstance().getDataUnion(); AppExecutor.getUiExecutor().executeWithDelay(() -> { FixedRateJobHelper.begin(); @@ -460,15 +462,6 @@ public class HaloApp extends MultiDexApplication { LoadedApkHuaWei.hookHuaWeiVerifier(this); DownloadMessageHandler.INSTANCE.init(SimpleDownloadDatabase.getInstance().downloadDao()); - - // 预加载游戏库图标 - SubjectRecommendEntity barData = HomeBottomBarHelper.getDefaultGameBarData(); - if (!TextUtils.isEmpty(barData.getIconSelect())) { - ImageUtils.getPicasso().load(Uri.parse(barData.getIconSelect())).fetch(); - } - if (!TextUtils.isEmpty(barData.getIconUnselect())) { - ImageUtils.getPicasso().load(Uri.parse(barData.getIconUnselect())).fetch(); - } }); } diff --git a/app/src/main/java/com/halo/assistant/fragment/ApkCleanerFragment.java b/app/src/main/java/com/halo/assistant/fragment/ApkCleanerFragment.java index b322fcbdbd..de30de6f28 100644 --- a/app/src/main/java/com/halo/assistant/fragment/ApkCleanerFragment.java +++ b/app/src/main/java/com/halo/assistant/fragment/ApkCleanerFragment.java @@ -17,18 +17,19 @@ import androidx.lifecycle.Lifecycle; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.gh.gamecenter.common.utils.DialogHelper; -import com.gh.gamecenter.common.constant.EntranceConsts; -import com.gh.gamecenter.core.utils.MtaHelper; -import com.gh.gamecenter.core.utils.ToastUtils; -import com.gh.gamecenter.common.view.VerticalItemDecoration; +import com.gh.common.util.DirectUtils; import com.gh.download.DownloadManager; import com.gh.gamecenter.MainActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.adapter.CleanApkAdapter; -import com.gh.gamecenter.feature.entity.InstallGameEntity; -import com.gh.gamecenter.fragment.MainWrapperFragment; import com.gh.gamecenter.common.base.fragment.ToolbarFragment; +import com.gh.gamecenter.common.constant.EntranceConsts; +import com.gh.gamecenter.common.utils.DialogHelper; +import com.gh.gamecenter.common.view.VerticalItemDecoration; +import com.gh.gamecenter.core.utils.MtaHelper; +import com.gh.gamecenter.core.utils.ToastUtils; +import com.gh.gamecenter.feature.entity.InstallGameEntity; +import com.gh.gamecenter.wrapper.MainWrapperFragment; import com.lightgame.utils.Utils; import java.io.File; @@ -193,7 +194,7 @@ public class ApkCleanerFragment extends ToolbarFragment implements CleanApkAdapt deleteApk(apkList, selectPosition); } } else if (id == R.id.reuseResetLoadTv) { - MainActivity.skipToMainActivity(getActivity(), MainWrapperFragment.INDEX_HOME); + DirectUtils.directToHomeDefaultTab(requireContext()); } } diff --git a/app/src/main/java/com/halo/assistant/fragment/comment/CommentDetailFragment.java b/app/src/main/java/com/halo/assistant/fragment/comment/CommentDetailFragment.java index 7c28c61f56..213040ee18 100644 --- a/app/src/main/java/com/halo/assistant/fragment/comment/CommentDetailFragment.java +++ b/app/src/main/java/com/halo/assistant/fragment/comment/CommentDetailFragment.java @@ -20,23 +20,23 @@ import androidx.recyclerview.widget.RecyclerView; import com.gh.common.util.CheckLoginUtils; import com.gh.common.util.DialogUtils; -import com.gh.gamecenter.core.utils.DisplayUtils; -import com.gh.gamecenter.common.constant.EntranceConsts; import com.gh.common.util.ErrorHelper; -import com.gh.gamecenter.core.utils.KeyboardHeightObserver; -import com.gh.gamecenter.core.utils.KeyboardHeightProvider; import com.gh.common.util.PostCommentUtils; -import com.gh.gamecenter.common.utils.TextHelper; import com.gh.gamecenter.CommentDetailActivity; -import com.gh.gamecenter.NewsDetailActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.adapter.CommentDetailAdapter; import com.gh.gamecenter.adapter.OnCommentCallBackListener; +import com.gh.gamecenter.common.base.fragment.ToolbarFragment; +import com.gh.gamecenter.common.constant.EntranceConsts; +import com.gh.gamecenter.common.utils.TextHelper; +import com.gh.gamecenter.core.utils.DisplayUtils; +import com.gh.gamecenter.core.utils.KeyboardHeightObserver; +import com.gh.gamecenter.core.utils.KeyboardHeightProvider; import com.gh.gamecenter.feature.entity.CommentEntity; import com.gh.gamecenter.feature.entity.MessageEntity; -import com.gh.gamecenter.common.base.fragment.ToolbarFragment; import com.gh.gamecenter.feature.eventbus.EBAddComment; import com.gh.gamecenter.feature.eventbus.EBDeleteComment; +import com.gh.gamecenter.newsdetail.NewsDetailActivity; import com.lightgame.utils.Util_System_Keyboard; import org.greenrobot.eventbus.EventBus; diff --git a/app/src/main/java/com/halo/assistant/fragment/game/GameNewsSearchResultFragment.java b/app/src/main/java/com/halo/assistant/fragment/game/GameNewsSearchResultFragment.java index 8dbade5f41..01188d19c9 100644 --- a/app/src/main/java/com/halo/assistant/fragment/game/GameNewsSearchResultFragment.java +++ b/app/src/main/java/com/halo/assistant/fragment/game/GameNewsSearchResultFragment.java @@ -2,12 +2,6 @@ package com.halo.assistant.fragment.game; import android.os.Bundle; import android.os.Handler; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - import android.text.Html; import android.view.LayoutInflater; import android.view.View; @@ -16,21 +10,25 @@ import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; -import com.gh.common.constant.Config; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.gh.common.util.DataCollectionUtils; -import com.gh.gamecenter.common.constant.EntranceConsts; -import com.gh.gamecenter.common.utils.EnvHelper; import com.gh.common.util.NewsUtils; -import com.gh.gamecenter.core.utils.UrlFilterUtils; -import com.gh.gamecenter.common.view.VerticalItemDecoration; -import com.gh.gamecenter.NewsDetailActivity; import com.gh.gamecenter.R; -import com.gh.gamecenter.common.viewholder.FooterViewHolder; import com.gh.gamecenter.adapter.viewholder.NewsTextViewHolder; +import com.gh.gamecenter.common.base.fragment.ToolbarFragment; +import com.gh.gamecenter.common.constant.EntranceConsts; +import com.gh.gamecenter.common.retrofit.Response; +import com.gh.gamecenter.common.utils.EnvHelper; +import com.gh.gamecenter.common.view.VerticalItemDecoration; +import com.gh.gamecenter.common.viewholder.FooterViewHolder; +import com.gh.gamecenter.core.utils.UrlFilterUtils; import com.gh.gamecenter.databinding.NewsTextItemBinding; import com.gh.gamecenter.feature.entity.NewsEntity; -import com.gh.gamecenter.common.base.fragment.ToolbarFragment; -import com.gh.gamecenter.common.retrofit.Response; +import com.gh.gamecenter.newsdetail.NewsDetailActivity; import com.gh.gamecenter.retrofit.RetrofitManager; import com.lightgame.utils.Util_System_Keyboard; @@ -171,7 +169,6 @@ public class GameNewsSearchResultFragment extends ToolbarFragment { .getSearchNews(EnvHelper.getHost() + "articles:search?keyword=" + mSearchKey + "&view=digest&filter=" + UrlFilterUtils.getFilterQuery("game_id", mGameId) + "&page=" + mPage) .map(list -> { - Config.filterPluginArticle(list); // 去掉重复数据 return NewsUtils.removeDuplicateData(mNewsEntities, list); }) diff --git a/app/src/main/java/com/halo/assistant/fragment/game/GamePluginAdapter.java b/app/src/main/java/com/halo/assistant/fragment/game/GamePluginAdapter.java index a5e78d5fca..13879c2cde 100644 --- a/app/src/main/java/com/halo/assistant/fragment/game/GamePluginAdapter.java +++ b/app/src/main/java/com/halo/assistant/fragment/game/GamePluginAdapter.java @@ -109,7 +109,7 @@ public class GamePluginAdapter extends BaseRecyclerAdapter { GamePluggableItemBinding binding = holder.getBinding(); binding.gameItemIncluded.gameIconView.displayGameIcon(gameEntity); binding.gameItemIncluded.gameRating.setTextSize(gameEntity.getCommentCount() > 3 ? 12 : 10); - BindingAdapters.setGameName(binding.gameItemIncluded.gameName, gameEntity, true, null); + BindingAdapters.setGameName(binding.gameItemIncluded.gameName, gameEntity, true); BindingAdapters.setGameTags(binding.gameItemIncluded.labelList, gameEntity); ExtensionsKt.setDrawableStart(binding.gameItemIncluded.gameRating, gameEntity.getCommentCount() > 3 ? ExtensionsKt.toDrawable(R.drawable.game_horizontal_rating) : null, null, null); binding.gameItemIncluded.gameRating.setPadding(0, 0, gameEntity.getCommentCount() > 3 ? DisplayUtils.dip2px(8) : 0, 0); diff --git a/app/src/main/java/com/halo/assistant/fragment/user/RealNameInfoViewModel.kt b/app/src/main/java/com/halo/assistant/fragment/user/RealNameInfoViewModel.kt index 5b30be000a..94b7b74484 100644 --- a/app/src/main/java/com/halo/assistant/fragment/user/RealNameInfoViewModel.kt +++ b/app/src/main/java/com/halo/assistant/fragment/user/RealNameInfoViewModel.kt @@ -152,7 +152,7 @@ class RealNameInfoViewModel(application: Application) : AndroidViewModel(applica * 安装因为实名延迟的游戏 */ private fun resumeInstallationIfNeeded() { - val pendingInstallation = RealNameHelper.pendingInstallPkgPath + val pendingInstallation = RealNameHelper.getPendingInstallPkgPath() if (pendingInstallation.isNotEmpty()) { val pendingDownloadEntity = DownloadManager.getInstance().allDownloadEntity.firstOrNull { @@ -171,7 +171,7 @@ class RealNameInfoViewModel(application: Application) : AndroidViewModel(applica "自动安装" ) PackageInstaller.install(HaloApp.getInstance(), pendingDownloadEntity) - RealNameHelper.pendingInstallPkgPath = "" + RealNameHelper.updatePendingInstallPkgPath("") } } diff --git a/app/src/main/res/drawable-hdpi/home_plugin_upgrade.png b/app/src/main/res/drawable-hdpi/home_plugin_upgrade.png new file mode 100644 index 0000000000..84dc2bfca6 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/home_plugin_upgrade.png differ diff --git a/app/src/main/res/drawable-hdpi/home_plugin_upgrade.webp b/app/src/main/res/drawable-hdpi/home_plugin_upgrade.webp deleted file mode 100644 index 43d6fdfe8e..0000000000 Binary files a/app/src/main/res/drawable-hdpi/home_plugin_upgrade.webp and /dev/null differ diff --git a/app/src/main/res/drawable-night/toolbar_download.xml b/app/src/main/res/drawable-night/toolbar_download.xml deleted file mode 100644 index f9ba706480..0000000000 --- a/app/src/main/res/drawable-night/toolbar_download.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable-night/toolbar_message.xml b/app/src/main/res/drawable-night/toolbar_message.xml deleted file mode 100644 index 53c164bc2a..0000000000 --- a/app/src/main/res/drawable-night/toolbar_message.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable-nodpi/home_plugin_bottom_close.png b/app/src/main/res/drawable-nodpi/home_plugin_bottom_close.png new file mode 100644 index 0000000000..bb478b9b60 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/home_plugin_bottom_close.png differ diff --git a/app/src/main/res/drawable-nodpi/home_plugin_bottom_open.png b/app/src/main/res/drawable-nodpi/home_plugin_bottom_open.png new file mode 100644 index 0000000000..8cbd5a01fb Binary files /dev/null and b/app/src/main/res/drawable-nodpi/home_plugin_bottom_open.png differ diff --git a/app/src/main/res/drawable-xxhdpi/home_plugin_bottom_close.webp b/app/src/main/res/drawable-xxhdpi/home_plugin_bottom_close.webp deleted file mode 100644 index 35a00eb15a..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/home_plugin_bottom_close.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/home_plugin_bottom_open.webp b/app/src/main/res/drawable-xxhdpi/home_plugin_bottom_open.webp deleted file mode 100644 index b2fa5c74be..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/home_plugin_bottom_open.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_bubble.9.png b/app/src/main/res/drawable-xxxhdpi/bg_bubble.9.png new file mode 100644 index 0000000000..345b6dd0ef Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_bubble.9.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_custom_game_item.png b/app/src/main/res/drawable-xxxhdpi/bg_custom_game_item.png new file mode 100644 index 0000000000..6bf55a1a84 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_custom_game_item.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/bg_popup_accelerate_notification.webp b/app/src/main/res/drawable-xxxhdpi/bg_popup_accelerate_notification.webp index 5e86ef57c9..079a9acae6 100644 Binary files a/app/src/main/res/drawable-xxxhdpi/bg_popup_accelerate_notification.webp and b/app/src/main/res/drawable-xxxhdpi/bg_popup_accelerate_notification.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_fold_slide_large_image_tips.png b/app/src/main/res/drawable-xxxhdpi/ic_fold_slide_large_image_tips.png new file mode 100644 index 0000000000..7c2bc491e3 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_fold_slide_large_image_tips.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/toolbar_search.webp b/app/src/main/res/drawable-xxxhdpi/toolbar_search.webp deleted file mode 100644 index 06aa2a00f3..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/toolbar_search.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/toolbar_search_light.webp b/app/src/main/res/drawable-xxxhdpi/toolbar_search_light.webp deleted file mode 100644 index 21324e3cd4..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/toolbar_search_light.webp and /dev/null differ diff --git a/app/src/main/res/drawable/bg_home_recent_vgame.xml b/app/src/main/res/drawable/bg_home_recent_vgame.xml index b305f0267a..75606ba0da 100644 --- a/app/src/main/res/drawable/bg_home_recent_vgame.xml +++ b/app/src/main/res/drawable/bg_home_recent_vgame.xml @@ -1,10 +1,5 @@ - - - - - - - - - \ No newline at end of file + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_shape_f8_radius_8.xml b/app/src/main/res/drawable/bg_shape_f8_radius_8.xml new file mode 100644 index 0000000000..854ef7a52a --- /dev/null +++ b/app/src/main/res/drawable/bg_shape_f8_radius_8.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/home_amway_select_custom.xml b/app/src/main/res/drawable/home_amway_select_custom.xml new file mode 100644 index 0000000000..35eed0b499 --- /dev/null +++ b/app/src/main/res/drawable/home_amway_select_custom.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/home_amway_selector_custom.xml b/app/src/main/res/drawable/home_amway_selector_custom.xml new file mode 100644 index 0000000000..48727911ec --- /dev/null +++ b/app/src/main/res/drawable/home_amway_selector_custom.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/home_amway_unselect_custom.xml b/app/src/main/res/drawable/home_amway_unselect_custom.xml new file mode 100644 index 0000000000..23fba4f4d2 --- /dev/null +++ b/app/src/main/res/drawable/home_amway_unselect_custom.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/toolbar_download.xml b/app/src/main/res/drawable/toolbar_download.xml index 1bab0b37b5..b2d78b5b84 100644 --- a/app/src/main/res/drawable/toolbar_download.xml +++ b/app/src/main/res/drawable/toolbar_download.xml @@ -5,5 +5,5 @@ android:viewportHeight="24"> + android:fillColor="@color/text_primary" /> diff --git a/app/src/main/res/drawable/toolbar_download_light.xml b/app/src/main/res/drawable/toolbar_download_light.xml index f9ba706480..c199b1ebe2 100644 --- a/app/src/main/res/drawable/toolbar_download_light.xml +++ b/app/src/main/res/drawable/toolbar_download_light.xml @@ -5,5 +5,5 @@ android:viewportHeight="24"> + android:fillColor="@color/text_aw_primary" /> diff --git a/app/src/main/res/drawable/toolbar_message.xml b/app/src/main/res/drawable/toolbar_message.xml index d9f809a16a..1f4b975416 100644 --- a/app/src/main/res/drawable/toolbar_message.xml +++ b/app/src/main/res/drawable/toolbar_message.xml @@ -8,10 +8,10 @@ android:strokeLineJoin="round" android:strokeWidth="2" android:fillColor="#00000000" - android:strokeColor="#404040" + android:strokeColor="@color/text_primary" android:strokeLineCap="round" /> diff --git a/app/src/main/res/drawable/toolbar_message_light.xml b/app/src/main/res/drawable/toolbar_message_light.xml index 53c164bc2a..a7bdb0bd09 100644 --- a/app/src/main/res/drawable/toolbar_message_light.xml +++ b/app/src/main/res/drawable/toolbar_message_light.xml @@ -8,10 +8,10 @@ android:strokeLineJoin="round" android:strokeWidth="2" android:fillColor="#00000000" - android:strokeColor="#ffffff" + android:strokeColor="@color/text_aw_primary" android:strokeLineCap="round" /> diff --git a/app/src/main/res/drawable/toolbar_search.xml b/app/src/main/res/drawable/toolbar_search.xml new file mode 100644 index 0000000000..95cc5be9d6 --- /dev/null +++ b/app/src/main/res/drawable/toolbar_search.xml @@ -0,0 +1,17 @@ + + + + diff --git a/app/src/main/res/drawable/toolbar_search_light.xml b/app/src/main/res/drawable/toolbar_search_light.xml new file mode 100644 index 0000000000..5de2f3dc3d --- /dev/null +++ b/app/src/main/res/drawable/toolbar_search_light.xml @@ -0,0 +1,17 @@ + + + + diff --git a/app/src/main/res/layout/activity_community.xml b/app/src/main/res/layout/activity_community.xml new file mode 100644 index 0000000000..501b97150d --- /dev/null +++ b/app/src/main/res/layout/activity_community.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_libaodetail.xml b/app/src/main/res/layout/activity_libaodetail.xml deleted file mode 100644 index d2c87e80e4..0000000000 --- a/app/src/main/res/layout/activity_libaodetail.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_news_detail.xml b/app/src/main/res/layout/activity_news_detail.xml deleted file mode 100644 index 4eff4c3053..0000000000 --- a/app/src/main/res/layout/activity_news_detail.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/app_update_hint_dialog.xml b/app/src/main/res/layout/app_update_hint_dialog.xml index 57f3eb9b10..c9e1d7e3fe 100644 --- a/app/src/main/res/layout/app_update_hint_dialog.xml +++ b/app/src/main/res/layout/app_update_hint_dialog.xml @@ -2,7 +2,7 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/common_collection_1_2_item_custom.xml b/app/src/main/res/layout/common_collection_1_2_item_custom.xml new file mode 100644 index 0000000000..be92010b98 --- /dev/null +++ b/app/src/main/res/layout/common_collection_1_2_item_custom.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/common_collection_detail_two_item.xml b/app/src/main/res/layout/common_collection_detail_two_item.xml index d342c7f00b..199c348ce7 100644 --- a/app/src/main/res/layout/common_collection_detail_two_item.xml +++ b/app/src/main/res/layout/common_collection_detail_two_item.xml @@ -1,66 +1,72 @@ - + android:layout_height="wrap_content" + app:cardCornerRadius="8dp" + app:cardElevation="0dp"> - - - + android:layout_height="wrap_content"> - + - + + + android:orientation="vertical" + android:paddingLeft="12dp" + android:paddingTop="19dp" + android:paddingRight="12dp" + android:paddingBottom="12dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="@+id/common_collection_image" + app:layout_constraintStart_toStartOf="@+id/common_collection_image"> - - - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/common_collection_detail_two_item_horizontal_custom.xml b/app/src/main/res/layout/common_collection_detail_two_item_horizontal_custom.xml new file mode 100644 index 0000000000..dff67fafc1 --- /dev/null +++ b/app/src/main/res/layout/common_collection_detail_two_item_horizontal_custom.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/common_collection_item_custom.xml b/app/src/main/res/layout/common_collection_item_custom.xml new file mode 100644 index 0000000000..4eae1273e9 --- /dev/null +++ b/app/src/main/res/layout/common_collection_item_custom.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/common_collection_list_custom.xml b/app/src/main/res/layout/common_collection_list_custom.xml new file mode 100644 index 0000000000..4611e32f00 --- /dev/null +++ b/app/src/main/res/layout/common_collection_list_custom.xml @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_sertification.xml b/app/src/main/res/layout/dialog_sertification.xml deleted file mode 100644 index 4b44cdf385..0000000000 --- a/app/src/main/res/layout/dialog_sertification.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_update.xml b/app/src/main/res/layout/dialog_update.xml new file mode 100644 index 0000000000..30e38e4471 --- /dev/null +++ b/app/src/main/res/layout/dialog_update.xml @@ -0,0 +1,15 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_welcome_custom.xml b/app/src/main/res/layout/dialog_welcome_custom.xml new file mode 100644 index 0000000000..a3a15c699f --- /dev/null +++ b/app/src/main/res/layout/dialog_welcome_custom.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/empty_control_video.xml b/app/src/main/res/layout/empty_control_video.xml index fa35b80264..8f569ad5f5 100644 --- a/app/src/main/res/layout/empty_control_video.xml +++ b/app/src/main/res/layout/empty_control_video.xml @@ -42,7 +42,12 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" - android:scaleType="centerCrop" /> + android:scaleType="centerCrop" + app:roundAsCircle="false" + app:roundTopLeft="false" + app:roundTopRight="false" + app:roundBottomLeft="false" + app:roundBottomRight="false"/> diff --git a/app/src/main/res/layout/fragment_article_detail.xml b/app/src/main/res/layout/fragment_article_detail.xml index 9991bfbfb5..62b89e03f7 100644 --- a/app/src/main/res/layout/fragment_article_detail.xml +++ b/app/src/main/res/layout/fragment_article_detail.xml @@ -7,11 +7,18 @@ android:background="@color/ui_surface" android:descendantFocusability="blocksDescendants"> + + + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toBottomOf="@+id/statusBar"> - + - - + - - @@ -140,10 +119,10 @@ android:id="@+id/viewPager" android:layout_width="0dp" android:layout_height="0dp" - app:layout_constraintTop_toBottomOf="@+id/tabContainer" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toRightOf="parent" /> + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toBottomOf="@+id/tabContainer" /> + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintRight_toRightOf="parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_custom.xml b/app/src/main/res/layout/fragment_custom.xml new file mode 100644 index 0000000000..b266a1ad10 --- /dev/null +++ b/app/src/main/res/layout/fragment_custom.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_forum.xml b/app/src/main/res/layout/fragment_forum.xml index b02be79984..054a6e6dd0 100644 --- a/app/src/main/res/layout/fragment_forum.xml +++ b/app/src/main/res/layout/fragment_forum.xml @@ -329,8 +329,7 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_forum_list.xml b/app/src/main/res/layout/fragment_forum_list.xml index de2ebaf52b..64677d7979 100644 --- a/app/src/main/res/layout/fragment_forum_list.xml +++ b/app/src/main/res/layout/fragment_forum_list.xml @@ -82,7 +82,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:overScrollMode="never" - android:paddingBottom="54dp" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> diff --git a/app/src/main/res/layout/fragment_forum_video_detail.xml b/app/src/main/res/layout/fragment_forum_video_detail.xml index 216f72a2ae..ddd6fbc1d3 100644 --- a/app/src/main/res/layout/fragment_forum_video_detail.xml +++ b/app/src/main/res/layout/fragment_forum_video_detail.xml @@ -173,7 +173,8 @@ android:layout_height="44dp" android:background="@android:color/transparent" app:contentInsetStartWithNavigation="0dp" - app:layout_collapseMode="pin" /> + app:layout_collapseMode="pin" + app:navigationIcon="@null" /> diff --git a/app/src/main/res/layout/fragment_game_collection_hot_list_wrapper.xml b/app/src/main/res/layout/fragment_game_collection_hot_list_wrapper.xml index d66463a132..04c7627990 100644 --- a/app/src/main/res/layout/fragment_game_collection_hot_list_wrapper.xml +++ b/app/src/main/res/layout/fragment_game_collection_hot_list_wrapper.xml @@ -50,8 +50,7 @@ android:layout_width="match_parent" android:layout_height="@dimen/appbar_height" android:background="@color/transparent" - app:layout_collapseMode="pin" - app:navigationIcon="@drawable/ic_bar_back_light" /> + app:layout_collapseMode="pin" /> + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_game_collection_square.xml b/app/src/main/res/layout/fragment_game_collection_square.xml index c5bbe356c6..a14627a2ab 100644 --- a/app/src/main/res/layout/fragment_game_collection_square.xml +++ b/app/src/main/res/layout/fragment_game_collection_square.xml @@ -230,6 +230,7 @@ android:background="@color/transparent" /> + android:fitsSystemWindows="false"> + app:tabIndicatorHeight="0dp" + app:tabMode="fixed" + app:tabSelectedTextColor="@color/white" + app:tabTextAppearance="@style/HomeVideoTabTextAppearance" + app:tabTextColor="@color/white_alpha_50" /> + + + android:layout_marginRight="10dp" + android:padding="15dp" + android:src="@drawable/ic_video_detail_menu_more" + android:visibility="gone" /> @@ -108,19 +120,19 @@ android:id="@+id/adBannerLeft" android:layout_width="52dp" android:layout_height="wrap_content" - android:layout_marginTop="12dp" - android:layout_marginLeft="12dp" android:layout_below="@+id/marquee_ad" + android:layout_marginLeft="12dp" + android:layout_marginTop="12dp" android:visibility="gone" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_libao_detail.xml b/app/src/main/res/layout/fragment_libao_detail.xml new file mode 100644 index 0000000000..9747b626c0 --- /dev/null +++ b/app/src/main/res/layout/fragment_libao_detail.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 74402a6c92..4b4fea0e09 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -1,29 +1,37 @@ - - + + android:background="@drawable/shadow" + app:layout_constraintBottom_toTopOf="@+id/bottomTabContainer" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + android:visibility="gone" + app:layout_constraintBottom_toTopOf="@+id/bottomTabContainer" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + android:orientation="horizontal" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_news_detail.xml b/app/src/main/res/layout/fragment_news_detail.xml new file mode 100644 index 0000000000..4b5ffcb636 --- /dev/null +++ b/app/src/main/res/layout/fragment_news_detail.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_reload.xml b/app/src/main/res/layout/fragment_reload.xml new file mode 100644 index 0000000000..95b52252f3 --- /dev/null +++ b/app/src/main/res/layout/fragment_reload.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search_toolbar_tab_wrapper.xml b/app/src/main/res/layout/fragment_search_toolbar_tab_wrapper.xml new file mode 100644 index 0000000000..0789fe95e0 --- /dev/null +++ b/app/src/main/res/layout/fragment_search_toolbar_tab_wrapper.xml @@ -0,0 +1,360 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_subject_collection_double_row_skeleton.xml b/app/src/main/res/layout/fragment_subject_collection_double_row_skeleton.xml deleted file mode 100644 index 608d1d047d..0000000000 --- a/app/src/main/res/layout/fragment_subject_collection_double_row_skeleton.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_subject_collection_single_row_skeleton.xml b/app/src/main/res/layout/fragment_subject_collection_single_row_skeleton.xml deleted file mode 100644 index 3edc97fcda..0000000000 --- a/app/src/main/res/layout/fragment_subject_collection_single_row_skeleton.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_tab_wrapper.xml b/app/src/main/res/layout/fragment_tab_wrapper.xml new file mode 100644 index 0000000000..7c215156b4 --- /dev/null +++ b/app/src/main/res/layout/fragment_tab_wrapper.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_toolbar_wrapper.xml b/app/src/main/res/layout/fragment_toolbar_wrapper.xml new file mode 100644 index 0000000000..318d5a1556 --- /dev/null +++ b/app/src/main/res/layout/fragment_toolbar_wrapper.xml @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_toolbox.xml b/app/src/main/res/layout/fragment_toolbox.xml new file mode 100644 index 0000000000..fe7a175cb7 --- /dev/null +++ b/app/src/main/res/layout/fragment_toolbox.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_double_card_item_al_custom.xml b/app/src/main/res/layout/game_double_card_item_al_custom.xml new file mode 100644 index 0000000000..b9b5fae0b5 --- /dev/null +++ b/app/src/main/res/layout/game_double_card_item_al_custom.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_double_card_item_custom.xml b/app/src/main/res/layout/game_double_card_item_custom.xml new file mode 100644 index 0000000000..6b46357887 --- /dev/null +++ b/app/src/main/res/layout/game_double_card_item_custom.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_gallery_item_custom.xml b/app/src/main/res/layout/game_gallery_item_custom.xml new file mode 100644 index 0000000000..0bb0dcaa4f --- /dev/null +++ b/app/src/main/res/layout/game_gallery_item_custom.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_gallery_slide_item_custom.xml b/app/src/main/res/layout/game_gallery_slide_item_custom.xml new file mode 100644 index 0000000000..f079afc24d --- /dev/null +++ b/app/src/main/res/layout/game_gallery_slide_item_custom.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_horizontal_item_custom.xml b/app/src/main/res/layout/game_horizontal_item_custom.xml new file mode 100644 index 0000000000..0bcbbf4bca --- /dev/null +++ b/app/src/main/res/layout/game_horizontal_item_custom.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/game_horizontal_simple_item_custom.xml b/app/src/main/res/layout/game_horizontal_simple_item_custom.xml new file mode 100644 index 0000000000..3d8e6f8e74 --- /dev/null +++ b/app/src/main/res/layout/game_horizontal_simple_item_custom.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/game_item_custom.xml b/app/src/main/res/layout/game_item_custom.xml new file mode 100644 index 0000000000..d72be20a14 --- /dev/null +++ b/app/src/main/res/layout/game_item_custom.xml @@ -0,0 +1,324 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/game_plugin_item_custom.xml b/app/src/main/res/layout/game_plugin_item_custom.xml new file mode 100644 index 0000000000..f4baaa3e1d --- /dev/null +++ b/app/src/main/res/layout/game_plugin_item_custom.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/game_vertical_slide_item_custom.xml b/app/src/main/res/layout/game_vertical_slide_item_custom.xml new file mode 100644 index 0000000000..074cae98ec --- /dev/null +++ b/app/src/main/res/layout/game_vertical_slide_item_custom.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/app/src/main/res/layout/gamedetail_body.xml b/app/src/main/res/layout/gamedetail_body.xml index 2072ef6e0e..5e44e99cc0 100644 --- a/app/src/main/res/layout/gamedetail_body.xml +++ b/app/src/main/res/layout/gamedetail_body.xml @@ -250,6 +250,7 @@ app:layout_collapseMode="pin"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/home_amway_list_custom.xml b/app/src/main/res/layout/home_amway_list_custom.xml new file mode 100644 index 0000000000..7894e1a82c --- /dev/null +++ b/app/src/main/res/layout/home_amway_list_custom.xml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/app/src/main/res/layout/home_discover_card_item_custom.xml b/app/src/main/res/layout/home_discover_card_item_custom.xml new file mode 100644 index 0000000000..d48c36af4b --- /dev/null +++ b/app/src/main/res/layout/home_discover_card_item_custom.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/home_game_collection_card_item_custom.xml b/app/src/main/res/layout/home_game_collection_card_item_custom.xml new file mode 100644 index 0000000000..a347f896ed --- /dev/null +++ b/app/src/main/res/layout/home_game_collection_card_item_custom.xml @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/home_game_collection_item_custom.xml b/app/src/main/res/layout/home_game_collection_item_custom.xml new file mode 100644 index 0000000000..8b43f65e35 --- /dev/null +++ b/app/src/main/res/layout/home_game_collection_item_custom.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/home_game_collection_refresh_item_custom.xml b/app/src/main/res/layout/home_game_collection_refresh_item_custom.xml new file mode 100644 index 0000000000..0ac000fe94 --- /dev/null +++ b/app/src/main/res/layout/home_game_collection_refresh_item_custom.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/home_game_collection_slide_item_custom.xml b/app/src/main/res/layout/home_game_collection_slide_item_custom.xml new file mode 100644 index 0000000000..a6074aa733 --- /dev/null +++ b/app/src/main/res/layout/home_game_collection_slide_item_custom.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/home_game_item_custom.xml b/app/src/main/res/layout/home_game_item_custom.xml new file mode 100644 index 0000000000..a13176aece --- /dev/null +++ b/app/src/main/res/layout/home_game_item_custom.xml @@ -0,0 +1,270 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/home_horizontal_slide_video_list_custom.xml b/app/src/main/res/layout/home_horizontal_slide_video_list_custom.xml new file mode 100644 index 0000000000..6845e63248 --- /dev/null +++ b/app/src/main/res/layout/home_horizontal_slide_video_list_custom.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/app/src/main/res/layout/home_slide_card_item_custom.xml b/app/src/main/res/layout/home_slide_card_item_custom.xml new file mode 100644 index 0000000000..59ac513ea5 --- /dev/null +++ b/app/src/main/res/layout/home_slide_card_item_custom.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/home_slide_list_custom.xml b/app/src/main/res/layout/home_slide_list_custom.xml new file mode 100644 index 0000000000..6573cffac3 --- /dev/null +++ b/app/src/main/res/layout/home_slide_list_custom.xml @@ -0,0 +1,31 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/home_slide_list_item_custom.xml b/app/src/main/res/layout/home_slide_list_item_custom.xml new file mode 100644 index 0000000000..58871b6580 --- /dev/null +++ b/app/src/main/res/layout/home_slide_list_item_custom.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/home_slide_with_cards_custom.xml b/app/src/main/res/layout/home_slide_with_cards_custom.xml new file mode 100644 index 0000000000..dbbdecda06 --- /dev/null +++ b/app/src/main/res/layout/home_slide_with_cards_custom.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/home_sub_slide_list_item_custom.xml b/app/src/main/res/layout/home_sub_slide_list_item_custom.xml new file mode 100644 index 0000000000..83a5e6a883 --- /dev/null +++ b/app/src/main/res/layout/home_sub_slide_list_item_custom.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_game_navigation_custom.xml b/app/src/main/res/layout/item_game_navigation_custom.xml new file mode 100644 index 0000000000..7505d7f69e --- /dev/null +++ b/app/src/main/res/layout/item_game_navigation_custom.xml @@ -0,0 +1,47 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_game_collection_big_slide_card_custom.xml b/app/src/main/res/layout/item_home_game_collection_big_slide_card_custom.xml new file mode 100644 index 0000000000..b8dccb8a94 --- /dev/null +++ b/app/src/main/res/layout/item_home_game_collection_big_slide_card_custom.xml @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_game_collection_small_slide_card_custom.xml b/app/src/main/res/layout/item_home_game_collection_small_slide_card_custom.xml new file mode 100644 index 0000000000..7e26af674f --- /dev/null +++ b/app/src/main/res/layout/item_home_game_collection_small_slide_card_custom.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_game_test_v2_custom.xml b/app/src/main/res/layout/item_home_game_test_v2_custom.xml new file mode 100644 index 0000000000..cd4db10e01 --- /dev/null +++ b/app/src/main/res/layout/item_home_game_test_v2_custom.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_game_test_v2_recommend_label_custom.xml b/app/src/main/res/layout/item_home_game_test_v2_recommend_label_custom.xml new file mode 100644 index 0000000000..908beed062 --- /dev/null +++ b/app/src/main/res/layout/item_home_game_test_v2_recommend_label_custom.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_game_test_v2_time_point_custom.xml b/app/src/main/res/layout/item_home_game_test_v2_time_point_custom.xml new file mode 100644 index 0000000000..b4cce91964 --- /dev/null +++ b/app/src/main/res/layout/item_home_game_test_v2_time_point_custom.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_horizontal_slide_video_custom.xml b/app/src/main/res/layout/item_home_horizontal_slide_video_custom.xml new file mode 100644 index 0000000000..8b994378cc --- /dev/null +++ b/app/src/main/res/layout/item_home_horizontal_slide_video_custom.xml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_home_recent_vgame_custom.xml b/app/src/main/res/layout/item_home_recent_vgame_custom.xml new file mode 100644 index 0000000000..32700bebae --- /dev/null +++ b/app/src/main/res/layout/item_home_recent_vgame_custom.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_recommend_list_custom.xml b/app/src/main/res/layout/item_home_recommend_list_custom.xml new file mode 100644 index 0000000000..63c25cdb59 --- /dev/null +++ b/app/src/main/res/layout/item_home_recommend_list_custom.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_recommend_list_item_custom.xml b/app/src/main/res/layout/item_home_recommend_list_item_custom.xml new file mode 100644 index 0000000000..c8c6dd77b8 --- /dev/null +++ b/app/src/main/res/layout/item_home_recommend_list_item_custom.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_vgame_refactor.xml b/app/src/main/res/layout/item_home_vgame_refactor.xml new file mode 100644 index 0000000000..6b3cf93781 --- /dev/null +++ b/app/src/main/res/layout/item_home_vgame_refactor.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_skeleton_subject_collection_double_row.xml b/app/src/main/res/layout/item_skeleton_subject_collection_double_row.xml deleted file mode 100644 index 5f92a0d395..0000000000 --- a/app/src/main/res/layout/item_skeleton_subject_collection_double_row.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/layout/item_skeleton_subject_collection_single_row.xml b/app/src/main/res/layout/item_skeleton_subject_collection_single_row.xml deleted file mode 100644 index e1946a1f93..0000000000 --- a/app/src/main/res/layout/item_skeleton_subject_collection_single_row.xml +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_within_game_gallery_slide_custom.xml b/app/src/main/res/layout/item_within_game_gallery_slide_custom.xml new file mode 100644 index 0000000000..2e9f38300c --- /dev/null +++ b/app/src/main/res/layout/item_within_game_gallery_slide_custom.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/app/src/main/res/layout/layout_floating_window_custom.xml b/app/src/main/res/layout/layout_floating_window_custom.xml new file mode 100644 index 0000000000..ac427d8f32 --- /dev/null +++ b/app/src/main/res/layout/layout_floating_window_custom.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_game_collection_filter_guide.xml b/app/src/main/res/layout/layout_game_collection_filter_guide.xml index 9cfc21b326..97a7ecd0ca 100644 --- a/app/src/main/res/layout/layout_game_collection_filter_guide.xml +++ b/app/src/main/res/layout/layout_game_collection_filter_guide.xml @@ -14,7 +14,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" - android:layout_marginTop="96dp" android:background="@drawable/background_shape_white_radius_8" android:padding="8dp"> diff --git a/app/src/main/res/layout/layout_search_bar.xml b/app/src/main/res/layout/layout_search_bar.xml index b87778a7cf..559c8e61de 100644 --- a/app/src/main/res/layout/layout_search_bar.xml +++ b/app/src/main/res/layout/layout_search_bar.xml @@ -40,6 +40,25 @@ app:layout_constraintRight_toRightOf="@id/search_background" app:layout_constraintTop_toTopOf="@id/search_background" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_tab_search_toolbar.xml b/app/src/main/res/layout/layout_tab_search_toolbar.xml new file mode 100644 index 0000000000..b7ec8ca3de --- /dev/null +++ b/app/src/main/res/layout/layout_tab_search_toolbar.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_title_custom.xml b/app/src/main/res/layout/layout_title_custom.xml new file mode 100644 index 0000000000..6576b42e92 --- /dev/null +++ b/app/src/main/res/layout/layout_title_custom.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/piece_bottom_tab.xml b/app/src/main/res/layout/piece_bottom_tab.xml new file mode 100644 index 0000000000..2f16695e24 --- /dev/null +++ b/app/src/main/res/layout/piece_bottom_tab.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/rank_collection_item_custom.xml b/app/src/main/res/layout/rank_collection_item_custom.xml new file mode 100644 index 0000000000..2e737e62f4 --- /dev/null +++ b/app/src/main/res/layout/rank_collection_item_custom.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/rank_collection_list_custom.xml b/app/src/main/res/layout/rank_collection_list_custom.xml new file mode 100644 index 0000000000..a2e935f843 --- /dev/null +++ b/app/src/main/res/layout/rank_collection_list_custom.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_child_icon_matrix_custom.xml b/app/src/main/res/layout/recycler_child_icon_matrix_custom.xml new file mode 100644 index 0000000000..dc9eba5e20 --- /dev/null +++ b/app/src/main/res/layout/recycler_child_icon_matrix_custom.xml @@ -0,0 +1,32 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_fold_slide_large_image.xml b/app/src/main/res/layout/recycler_fold_slide_large_image.xml new file mode 100644 index 0000000000..c0de25b1ec --- /dev/null +++ b/app/src/main/res/layout/recycler_fold_slide_large_image.xml @@ -0,0 +1,44 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_fold_slide_large_image_custom.xml b/app/src/main/res/layout/recycler_fold_slide_large_image_custom.xml new file mode 100644 index 0000000000..c6e055028c --- /dev/null +++ b/app/src/main/res/layout/recycler_fold_slide_large_image_custom.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_fold_slide_large_image_item.xml b/app/src/main/res/layout/recycler_fold_slide_large_image_item.xml new file mode 100644 index 0000000000..4ccd1f94d8 --- /dev/null +++ b/app/src/main/res/layout/recycler_fold_slide_large_image_item.xml @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_horizontal_slide_list_custom.xml b/app/src/main/res/layout/recycler_horizontal_slide_list_custom.xml new file mode 100644 index 0000000000..741df681e7 --- /dev/null +++ b/app/src/main/res/layout/recycler_horizontal_slide_list_custom.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_icon_matrix_custom.xml b/app/src/main/res/layout/recycler_icon_matrix_custom.xml new file mode 100644 index 0000000000..879728e936 --- /dev/null +++ b/app/src/main/res/layout/recycler_icon_matrix_custom.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_navigation_custom.xml b/app/src/main/res/layout/recycler_navigation_custom.xml new file mode 100644 index 0000000000..c333bcdc82 --- /dev/null +++ b/app/src/main/res/layout/recycler_navigation_custom.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_refresh_icon_item_custom.xml b/app/src/main/res/layout/recycler_refresh_icon_item_custom.xml new file mode 100644 index 0000000000..7d2fab71cf --- /dev/null +++ b/app/src/main/res/layout/recycler_refresh_icon_item_custom.xml @@ -0,0 +1,52 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/search_game_first_item.xml b/app/src/main/res/layout/search_game_first_item.xml index 5393efeb13..2c5c661ac8 100644 --- a/app/src/main/res/layout/search_game_first_item.xml +++ b/app/src/main/res/layout/search_game_first_item.xml @@ -34,7 +34,7 @@ android:paddingBottom="20dp" /> diff --git a/app/src/main/res/layout/tab_item_main.xml b/app/src/main/res/layout/tab_item_main.xml index 4ba06b5c32..bb3ca3f391 100644 --- a/app/src/main/res/layout/tab_item_main.xml +++ b/app/src/main/res/layout/tab_item_main.xml @@ -25,6 +25,7 @@ android:layout_height="match_parent" android:layout_centerInParent="true" android:gravity="center" + android:lines="1" android:textColor="@color/text_tabbar_style" android:visibility="gone" /> @@ -32,11 +33,11 @@ android:id="@+id/titleIv" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingTop="2dp" - android:paddingBottom="2dp" android:layout_centerInParent="true" android:layout_marginStart="12dp" android:layout_marginEnd="12dp" + android:paddingTop="2dp" + android:paddingBottom="2dp" android:visibility="gone" /> \ No newline at end of file diff --git a/app/src/main/res/menu/menu_forum_video_detail.xml b/app/src/main/res/menu/menu_forum_video_detail.xml index f85c1266ce..0808d26d36 100644 --- a/app/src/main/res/menu/menu_forum_video_detail.xml +++ b/app/src/main/res/menu/menu_forum_video_detail.xml @@ -3,7 +3,7 @@ diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 97bbc152ad..066bcf1741 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -534,5 +534,11 @@ 解鎖更多精彩內容 幫助與反饋 打開外部瀏覽器失敗,已復製鏈接到粘貼板,請自行打開鏈接 + 換一批 + 更多 + 遊戲單廣場 + 全部 + 新遊開測 + 查看所有遊戲 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index aa24594f19..39eedcc0fb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -538,4 +538,10 @@ 分享交流游戏生活~ 草稿箱 打开外部浏览器失败,已复制链接到粘贴板,请自行打开链接 + 换一批 + 更多 + 游戏单广场 + 全部 + 新游开测 + 查看全部游戏 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e01bc59d33..00aa717911 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -281,4 +281,14 @@ 12sp center + + \ No newline at end of file diff --git a/feature/floating-window/src/demo/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt b/feature/floating-window/src/demo/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt index d8897541d6..992aae09eb 100644 --- a/feature/floating-window/src/demo/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt +++ b/feature/floating-window/src/demo/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt @@ -100,10 +100,6 @@ class ConfigProviderImpl : IConfigProvider { return false } - override fun isShowPlugin(gameId: String): Boolean { - return false - } - override fun init(context: Context?) { // Do nothing } diff --git a/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowEntity.kt b/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowEntity.kt index 6ec9252107..deafcfa59b 100644 --- a/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowEntity.kt +++ b/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowEntity.kt @@ -14,4 +14,9 @@ class FloatingWindowEntity( var expandable: Boolean = false, // 悬浮窗是否展示为大弹窗(true/false) @SerializedName("big_popup_img") var expandedImage: String = "", // 大弹窗图片 -) \ No newline at end of file + @SerializedName("big_popup_notice") + val bigPopupNotice: String = "" +) { + + var autoExpandBigPopup = false +} \ No newline at end of file diff --git a/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowManager.kt b/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowManager.kt index d9318fe42c..9f394d714c 100644 --- a/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowManager.kt +++ b/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowManager.kt @@ -16,6 +16,7 @@ import com.alibaba.android.arouter.launcher.ARouter import com.gh.gamecenter.common.base.fragment.BaseLazyFragment import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.exposure.ExposureSource +import com.gh.gamecenter.common.utils.SensorsBridge import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.view.HorizontalScrollableView import com.gh.gamecenter.feature.entity.WelcomeDialogEntity @@ -24,7 +25,6 @@ import com.gh.gamecenter.feature.provider.IExposureManagerProvider import com.lzf.easyfloat.EasyFloat import com.lzf.easyfloat.enums.ShowPattern import com.lzf.easyfloat.enums.SidePattern -import kotlin.collections.ArrayList /** * 右下角悬浮窗管理类,应用于首页和游戏详情页 @@ -85,7 +85,8 @@ internal object FloatingWindowManager { activity: Activity?, windowList: ArrayList, gameId: String, - gameName: String + gameName: String, + location: String ) { if (activity == null) return if (mDismissedIdSet.contains(activity.getUniqueId())) return @@ -102,14 +103,15 @@ internal object FloatingWindowManager { // 当前 activity 已经有了相关 window,将其设置为展开可见并返回 if (originalWindowView != null) return - showWindow(activity, windowList, gameId, gameName) + showWindow(activity, windowList, gameId, gameName, location) } private fun showWindow( activity: Activity, windowEntityList: ArrayList, gameId: String, - gameName: String + gameName: String, + location: String ) { mActivityAndBusinessIdPair = Pair(activity.getUniqueId(), windowEntityList.firstOrNull()?.id ?: "unknown") @@ -132,6 +134,7 @@ internal object FloatingWindowManager { FloatingWindowViewAdapter( gameId, gameName, + location, windowEntityList, object : FloatingWindowViewAdapter.Callback { override fun onItemExpandCallback(position: Int) { @@ -152,6 +155,17 @@ internal object FloatingWindowManager { linkType = windowEntity.link.type ?: "", linkText = windowEntity.link.text ?: "" ) + + SensorsBridge.trackSuspendedWindowClick( + "收起", + "展开", + location, + gameId = gameId, + gameName = gameName, + linkId = windowEntity.link.link ?: "", + linkType = windowEntity.link.type ?: "", + linkText = windowEntity.link.text ?: "" + ) } catch (e: Exception) { e.printStackTrace() } @@ -166,7 +180,18 @@ internal object FloatingWindowManager { } } ) - ) + ) { + val link = windowEntityList.getOrNull(it)?.link + SensorsBridge.trackSuspendedWindowShow( + "收起", + location, + gameId = gameId, + gameName = gameName, + linkText = link?.text ?: "", + linkType = link?.type ?: "", + linkId = link?.link ?: "" + ) + } } } .show() @@ -281,6 +306,7 @@ internal object FloatingWindowManager { windowEntityList: ArrayList, gameId: String, gameName: String, + location: String, fragment: BaseLazyFragment, recyclerView: RecyclerView, welcomeDialogCallback: ((welcomeDialogEntity: WelcomeDialogEntity) -> Unit)? @@ -299,12 +325,12 @@ internal object FloatingWindowManager { ) if (fragment.isCurrentlyVisible()) { - showWindowIfNeeded(fragment.activity, windowEntityList, gameId, gameName) + showWindowIfNeeded(fragment.activity, windowEntityList, gameId, gameName, location) } fragment.setResumeAndPauseListener { isResume -> if (isResume) { - showWindowIfNeeded(fragment.activity, windowEntityList, gameId, gameName) + showWindowIfNeeded(fragment.activity, windowEntityList, gameId, gameName, location) } else { hideWindow(fragment.activity) } diff --git a/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowProviderImpl.kt b/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowProviderImpl.kt index 7fffe37f68..3884b5792e 100644 --- a/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowProviderImpl.kt +++ b/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowProviderImpl.kt @@ -19,7 +19,6 @@ import com.gh.gamecenter.floatingwindow.retrofit.RetrofitManager import com.lightgame.utils.Utils import io.reactivex.disposables.Disposable import java.util.* -import kotlin.collections.ArrayList @Route(path = RouteConsts.provider.floatingwindow, name = "FloatingWindow 暴露服务") class FloatingWindowProviderImpl : IFloatingWindowProvider { @@ -27,6 +26,7 @@ class FloatingWindowProviderImpl : IFloatingWindowProvider override fun getAndShowFloatingWindow( gameId: String, gameName: String, + location: String, fragment: Fragment, recyclerView: RecyclerView, welcomeDialogCallback: ((welcomeDialogEntity: WelcomeDialogEntity) -> Unit)? @@ -41,6 +41,7 @@ class FloatingWindowProviderImpl : IFloatingWindowProvider filterValidWindow(data), gameId, gameName, + location, fragment, recyclerView, welcomeDialogCallback @@ -75,6 +76,7 @@ class FloatingWindowProviderImpl : IFloatingWindowProvider windowEntityList as ArrayList, "", "", + "", fragment as BaseLazyFragment, recyclerView, welcomeDialogCallback diff --git a/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowViewAdapter.kt b/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowViewAdapter.kt index 4398b66cd7..82d3c71510 100644 --- a/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowViewAdapter.kt +++ b/feature/floating-window/src/main/java/com/gh/gamecenter/floatingwindow/FloatingWindowViewAdapter.kt @@ -8,10 +8,7 @@ import com.alibaba.android.arouter.launcher.ARouter import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.constant.RouteConsts -import com.gh.gamecenter.common.utils.ImageUtils -import com.gh.gamecenter.common.utils.dip2px -import com.gh.gamecenter.common.utils.enlargeTouchArea -import com.gh.gamecenter.common.utils.toBinding +import com.gh.gamecenter.common.utils.* import com.gh.gamecenter.common.view.WrapContentDraweeView import com.gh.gamecenter.feature.provider.ILinkDirectUtilsProvider import com.gh.gamecenter.floatingwindow.databinding.LayoutFloatingWindowItemBinding @@ -19,6 +16,7 @@ import com.gh.gamecenter.floatingwindow.databinding.LayoutFloatingWindowItemBind class FloatingWindowViewAdapter( private var mGameId: String, private var mGameName: String, + private var mLocation: String, private var mDataList: ArrayList = arrayListOf(), private var mCallback: Callback ) : RecyclerView.Adapter() { @@ -72,6 +70,16 @@ class FloatingWindowViewAdapter( linkType = windowEntity.link.type ?: "", linkText = windowEntity.link.text ?: "" ) + SensorsBridge.trackSuspendedWindowClick( + "收起", + "关闭", + mLocation, + gameId = mGameId, + gameName = mGameName, + linkId = windowEntity.link.link ?: "", + linkType = windowEntity.link.type ?: "", + linkText = windowEntity.link.text ?: "" + ) } } @@ -122,6 +130,16 @@ class FloatingWindowViewAdapter( linkType = windowEntity.link.type ?: "", linkText = windowEntity.link.text ?: "" ) + SensorsBridge.trackSuspendedWindowClick( + "收起", + "跳转", + mLocation, + gameId = mGameId, + gameName = mGameName, + linkId = windowEntity.link.link ?: "", + linkType = windowEntity.link.type ?: "", + linkText = windowEntity.link.text ?: "" + ) } } diff --git a/feature/new_feedback/src/feedback/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt b/feature/new_feedback/src/feedback/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt index a0657b846f..28fb1cd6d7 100644 --- a/feature/new_feedback/src/feedback/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt +++ b/feature/new_feedback/src/feedback/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt @@ -100,10 +100,6 @@ class ConfigProviderImpl : IConfigProvider { return false } - override fun isShowPlugin(gameId: String): Boolean { - return false - } - override fun init(context: Context?) { // Do nothing } diff --git a/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/HelpAndFeedbackActivity.kt b/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/HelpAndFeedbackActivity.kt index 2bdb6480d6..9cf3429707 100644 --- a/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/HelpAndFeedbackActivity.kt +++ b/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/HelpAndFeedbackActivity.kt @@ -5,7 +5,6 @@ import androidx.fragment.app.Fragment import com.alibaba.android.arouter.facade.annotation.Route import com.gh.gamecenter.common.base.activity.BaseActivity import com.gh.gamecenter.common.constant.RouteConsts -import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.feedback.R @Route(path = RouteConsts.activity.helpAndFeedbackActivity) @@ -19,7 +18,6 @@ class HelpAndFeedbackActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - DisplayUtils.transparentStatusBar(this) mTargetFragment = supportFragmentManager.findFragmentByTag(HelpAndFeedbackFragment::class.java.name) ?: HelpAndFeedbackFragment().with(intent.extras) diff --git a/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/HelpAndFeedbackFragment.kt b/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/HelpAndFeedbackFragment.kt index 96bf8cb59b..7bd1ff378e 100644 --- a/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/HelpAndFeedbackFragment.kt +++ b/feature/new_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/HelpAndFeedbackFragment.kt @@ -11,13 +11,15 @@ import android.widget.LinearLayout import android.widget.PopupWindow import android.widget.TextView import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.SimpleOnItemTouchListener import androidx.viewpager2.widget.ViewPager2 -import com.gh.gamecenter.common.base.fragment.BaseFragment +import com.gh.gamecenter.common.base.fragment.BaseLazyFragment import com.gh.gamecenter.common.baselist.LoadStatus import com.gh.gamecenter.common.databinding.ItemIconTabBinding import com.gh.gamecenter.common.databinding.PopupAllTabsBinding @@ -36,7 +38,7 @@ import com.gh.gamecenter.feedback.view.suggest.SuggestionActivity import splitties.views.bottomPadding import kotlin.math.abs -class HelpAndFeedbackFragment : BaseFragment() { +class HelpAndFeedbackFragment : BaseLazyFragment() { private lateinit var mBinding: FragmentHelpAndFeedbackBinding private lateinit var mViewModel: HelpAndFeedbackViewModel private var mSnapHelper: LeftPagerSnapHelper? = null @@ -66,12 +68,12 @@ class HelpAndFeedbackFragment : BaseFragment() { mViewModel = viewModelProvider() } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && mIsCollapsed) + override fun onFragmentFirstVisible() { + super.onFragmentFirstVisible() initMenu() changeToolbarStyle(mIsCollapsed) mBinding.run { + val statusBarHeight = DisplayUtils.getStatusBarHeight(requireContext().resources) swipeRefreshLayout.setColorSchemeResources(R.color.primary_theme) swipeRefreshLayout.setOnRefreshListener { mViewModel.initData() @@ -82,7 +84,7 @@ class HelpAndFeedbackFragment : BaseFragment() { swipeRefreshLayout.setProgressViewOffset( false, 0, - 118F.dip2px() + DisplayUtils.getStatusBarHeight(requireContext().resources) + 118F.dip2px() + statusBarHeight ) helpVideoLoading.root.gravity = Gravity.CENTER helpContentLoading.root.gravity = Gravity.CENTER @@ -91,29 +93,50 @@ class HelpAndFeedbackFragment : BaseFragment() { helpContentNoConnection.root.visibility = View.GONE helpContentLoading.root.visibility = View.VISIBLE } - - ViewCompat.setOnApplyWindowInsetsListener(appbar) { _, insets -> - (toolbar.layoutParams as MarginLayoutParams).topMargin = - insets.systemWindowInsetTop - insets.consumeSystemWindowInsets() + if (!mIsFromTabWrapper) { + ViewCompat.setOnApplyWindowInsetsListener(appbar) { _, insets -> + (toolbar.layoutParams as MarginLayoutParams).topMargin = + insets.getInsets(WindowInsetsCompat.Type.systemBars()).top + WindowInsetsCompat.CONSUMED + } } - val statusBarHeight = DisplayUtils.getStatusBarHeight(requireContext().resources) + if (mIsFromMainWrapper) { + (toolbar.layoutParams as MarginLayoutParams).topMargin = statusBarHeight + } + if (mIsFromTabWrapper) { + statusBar.visibility = View.GONE + headIv.post { + collapsingToolbar.updateLayoutParams { + height = mBinding.headIv.height + } + } + } + val collapsingTrigger = 97F.dip2px() + statusBarHeight + if (mIsFromMainWrapper || mIsFromTabWrapper) { + toolbar.navigationIcon = null + } else { + toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) + } toolbar.setNavigationOnClickListener { requireActivity().finish() } collapsingToolbar.scrimVisibleHeightTrigger = collapsingTrigger collapsingToolbar.scrimShownAction = { if (mIsCollapsed != it) { mIsCollapsed = it - DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && it) + updateStatusBar() changeToolbarStyle(it) } } appbar.addOnOffsetChangedListener { appBarLayout, verticalOffset -> val absOffset = abs(verticalOffset) - val appBarScrollMaxHeight = appBarLayout.height - statusBarHeight - 96F.dip2px() + val appBarScrollMaxHeight = if (!mIsFromTabWrapper) { + appBarLayout.height - statusBarHeight - 96F.dip2px() + } else { + appBarLayout.height - 96F.dip2px() + } val percent = absOffset.toFloat() / appBarScrollMaxHeight searchLl.bottomPadding = (8F.dip2px() + (1 - percent) * 24F.dip2px()).toInt() mBinding.swipeRefreshLayout.isEnabled = absOffset <= 2 @@ -194,13 +217,14 @@ class HelpAndFeedbackFragment : BaseFragment() { observeData() } - override fun onPause() { + override fun onFragmentPause() { super.onPause() mHelpVideoAdapter?.stopScroll() } - override fun onResume() { + override fun onFragmentResume() { super.onResume() + updateStatusBar() mHelpVideoAdapter?.startScroll() } @@ -337,19 +361,30 @@ class HelpAndFeedbackFragment : BaseFragment() { } } + private fun updateStatusBar() { + if (!mIsFromTabWrapper && isSupportVisible) { + DisplayUtils.transparentStatusBar(requireActivity()) + DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && mIsCollapsed) + } + } + private fun changeToolbarStyle(isCollapsed: Boolean) { if (isCollapsed) { + if (!mIsFromMainWrapper && !mIsFromTabWrapper) { + mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back) + } mBinding.titleTv.alpha = 1F mBinding.titleTv.visibility = View.VISIBLE mBinding.titleTv.setTextColor(R.color.text_black.toColor(requireContext())) - mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back) - mBinding.actionMenuView.menu.findItem(R.id.menu_feedback).setIcon(R.drawable.ic_feedback) + mBinding.actionMenuView.menu.findItem(R.id.menu_feedback)?.setIcon(R.drawable.ic_feedback) mBinding.collapsingToolbar.setContentScrimColor(R.color.ui_surface.toColor(requireContext())) mBinding.searchContainer.background = R.drawable.bg_shape_f5_radius_999.toDrawable(requireContext()) } else { + if (!mIsFromMainWrapper && !mIsFromTabWrapper) { + mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) + } mBinding.titleTv.visibility = View.GONE - mBinding.toolbar.setNavigationIcon(R.drawable.ic_bar_back_light) - mBinding.actionMenuView.menu.findItem(R.id.menu_feedback).setIcon(R.drawable.ic_feedback_light) + mBinding.actionMenuView.menu.findItem(R.id.menu_feedback)?.setIcon(R.drawable.ic_feedback_light) mBinding.collapsingToolbar.setContentScrimColor(R.color.ui_background.toColor(requireContext())) mBinding.searchContainer.background = R.drawable.background_shape_white_radius_999.toDrawable(requireContext()) @@ -358,7 +393,7 @@ class HelpAndFeedbackFragment : BaseFragment() { override fun onDarkModeChanged() { super.onDarkModeChanged() - DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn && mIsCollapsed) + updateStatusBar() changeToolbarStyle(mIsCollapsed) mBinding.run { helpVideoCv.setCardBackgroundColor(R.color.ui_surface.toColor(requireContext())) diff --git a/feature/new_feedback/src/main/res/layout/fragment_help_and_feedback.xml b/feature/new_feedback/src/main/res/layout/fragment_help_and_feedback.xml index cd6b114f9b..1012cae299 100644 --- a/feature/new_feedback/src/main/res/layout/fragment_help_and_feedback.xml +++ b/feature/new_feedback/src/main/res/layout/fragment_help_and_feedback.xml @@ -9,6 +9,7 @@ + app:layout_collapseMode="pin" /> (fragment: Fragment) : FragmentStateAdapter(fragment) { + protected var mDataList = ArrayList() + + fun getFragmentTag(position: Int) = "f${getItemId(position)}" + + fun submitList(dataList: List) { + if (mDataList.isEmpty()) { + mDataList.addAll(dataList) + notifyDataSetChanged() + } else { + val originData = ArrayList(mDataList) + val updateDataCopy = ArrayList(dataList) + + if (originData.containsAll(updateDataCopy) && originData.size == updateDataCopy.size) return + + val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() { + override fun getOldListSize(): Int { + return originData.size + } + + override fun getNewListSize(): Int { + return updateDataCopy.size + } + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldItem = originData.safelyGetInRelease(oldItemPosition) + val newItem = updateDataCopy.safelyGetInRelease(newItemPosition) + return areItemsTheSame(oldItem, newItem) + } + + override fun areContentsTheSame( + oldItemPosition: Int, + newItemPosition: Int + ): Boolean { + val oldItem = originData.safelyGetInRelease(oldItemPosition) + val newItem = updateDataCopy.safelyGetInRelease(newItemPosition) + return areContentsTheSame(oldItem, newItem) + } + }) + + mDataList = updateDataCopy + diffResult.dispatchUpdatesTo(this) + } + } + + protected open fun areItemsTheSame(oldItem: T?, newItem: T?): Boolean = oldItem == newItem + protected open fun areContentsTheSame(oldItem: T?, newItem: T?): Boolean = oldItem == newItem + + override fun getItemCount(): Int = mDataList.size + + override fun createFragment(position: Int): Fragment = createFragment(mDataList.safelyGetInRelease(position), position) ?: Fragment() + + abstract fun createFragment(data: T?, position: Int): Fragment? + + override fun getItemId(position: Int): Long { + if (mDataList.size < position) { + return 0L + } + return mDataList[position].hashCode().toLong() + } + + override fun containsItem(itemId: Long): Boolean { + mDataList.forEach { + if (it.hashCode().toLong() == itemId) { + return true + } + } + return false + } +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/adapter/FragmentAdapter.java b/module_common/src/main/java/com/gh/gamecenter/common/base/adapter/FragmentAdapter.java index ee216bd2f2..f40486d79f 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/adapter/FragmentAdapter.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/adapter/FragmentAdapter.java @@ -18,12 +18,12 @@ public class FragmentAdapter extends FragmentPagerAdapter { private List mTitleList; public FragmentAdapter(FragmentManager fm, List fragmentList) { - super(fm); + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); this.mFragmentList = fragmentList; } public FragmentAdapter(FragmentManager fm, List fragmentList, List titleList) { - super(fm); + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); this.mFragmentList = fragmentList; this.mTitleList = titleList; } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/adapter/FragmentStateAdapter.java b/module_common/src/main/java/com/gh/gamecenter/common/base/adapter/FragmentStateAdapter.java index 570c64454d..22c298b612 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/adapter/FragmentStateAdapter.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/adapter/FragmentStateAdapter.java @@ -19,12 +19,12 @@ public class FragmentStateAdapter extends FragmentStatePagerAdapter { private List mFragmentList; public FragmentStateAdapter(FragmentManager fm, List fragmentList) { - super(fm); + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); this.mFragmentList = fragmentList; } public FragmentStateAdapter(FragmentManager fm, List fragmentList, List titleList) { - super(fm); + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); this.mFragmentList = fragmentList; this.mTitleList = titleList; } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java index ecbe2bdd62..79e88862ff 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java @@ -25,6 +25,7 @@ import com.gh.gamecenter.common.R; import com.gh.gamecenter.common.callback.OnListClickListener; import com.gh.gamecenter.common.callback.OnRequestCallBackListener; import com.gh.gamecenter.common.constant.Constants; +import com.gh.gamecenter.common.constant.EntranceConsts; import com.gh.gamecenter.common.eventbus.EBMiPush; import com.gh.gamecenter.common.syncpage.ISyncAdapterHandler; import com.gh.gamecenter.common.syncpage.SyncDataEntity; @@ -65,6 +66,9 @@ public abstract class BaseFragment extends Fragment implements OnRequestCallB @NonNull protected String mEntrance = ""; + protected boolean mIsFromMainWrapper = false; + protected boolean mIsFromTabWrapper = false; + public long startPageTime = 0; protected final Handler mBaseHandler = new BaseFragment.BaseHandler(this); @@ -136,8 +140,12 @@ public abstract class BaseFragment extends Fragment implements OnRequestCallB super.onCreate(savedInstanceState); final Intent intent = getActivity().getIntent(); mEntrance = intent.getStringExtra(KEY_ENTRANCE); - if (TextUtils.isEmpty(mEntrance) && getArguments() != null) { - mEntrance = getArguments().getString(KEY_ENTRANCE); + if (getArguments() != null) { + if (TextUtils.isEmpty(mEntrance)) { + mEntrance = getArguments().getString(KEY_ENTRANCE); + } + mIsFromMainWrapper = getArguments().getBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, false); + mIsFromTabWrapper = getArguments().getBoolean(EntranceConsts.KEY_IS_FROM_TAB_WRAPPER, false); } if (TextUtils.isEmpty(mEntrance)) { diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseLazyFragment.kt b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseLazyFragment.kt index 7dfbf0d50d..2864b06863 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseLazyFragment.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseLazyFragment.kt @@ -1,20 +1,18 @@ package com.gh.gamecenter.common.base.fragment -import android.os.Bundle - /** * 懒加载(支持多层嵌套) */ abstract class BaseLazyFragment : ToolbarFragment() { - private var mIsFirstVisible = true + private var isFirstVisible = true private var isViewCreated = false protected var isSupportVisible = false // 可见不可见回调 - private var mResumeAndPauseListener: ((isResume: Boolean) -> Unit)? = null + private var resumeAndPauseListener: ((isResume: Boolean) -> Unit)? = null /** * 用于分发可见时间的时候获取 父fragment 是否隐藏 @@ -36,7 +34,7 @@ abstract class BaseLazyFragment : ToolbarFragment() { * 设置页面可见不可见回调 */ fun setResumeAndPauseListener(listener: ((isResume: Boolean) -> Unit)?) { - mResumeAndPauseListener = listener + resumeAndPauseListener = listener } /** @@ -44,31 +42,6 @@ abstract class BaseLazyFragment : ToolbarFragment() { */ fun isCurrentlyVisible() = isSupportVisible - override fun setUserVisibleHint(isVisibleToUser: Boolean) { - super.setUserVisibleHint(isVisibleToUser) - // 对于默认 tab 和 间隔 checked tab 需要等到 isViewCreated = true 后才可以通过此通知用户可见 - // 这种情况下第一次可见不是在这里通知 因为 isViewCreated = false 成立,等从别的界面回到这里后会使用 onFragmentResume 通知可见 - // 对于非默认 tab mIsFirstVisible = true 会一直保持到选择则这个 tab 的时候,因为在 onActivityCreated 会返回 false - if (isViewCreated) { - if (isVisibleToUser && !isSupportVisible) { - dispatchUserVisibleHint(true) - } else if (!isVisibleToUser && isSupportVisible) { - dispatchUserVisibleHint(false) - } - } - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - isViewCreated = true - // !isHidden() 默认为 true 在调用 hide show 的时候可以使用 - if (!isHidden && userVisibleHint) { - dispatchUserVisibleHint(true) - } - - } - override fun onHiddenChanged(hidden: Boolean) { super.onHiddenChanged(hidden) @@ -81,10 +54,8 @@ abstract class BaseLazyFragment : ToolbarFragment() { override fun onResume() { super.onResume() - if (!mIsFirstVisible) { - if (!isHidden && !isSupportVisible && userVisibleHint) { - dispatchUserVisibleHint(true) - } + if (!isHidden && !isSupportVisible) { + dispatchUserVisibleHint(true) } } @@ -92,7 +63,7 @@ abstract class BaseLazyFragment : ToolbarFragment() { super.onPause() // 当前 Fragment 包含子 Fragment 的时候 dispatchUserVisibleHint 内部本身就会通知子 Fragment 不可见 // 子 fragment 走到这里的时候自身又会调用一遍 ? - if (isSupportVisible && userVisibleHint) { + if (isSupportVisible) { dispatchUserVisibleHint(false) } } @@ -104,9 +75,6 @@ abstract class BaseLazyFragment : ToolbarFragment() { * @param visible */ private fun dispatchUserVisibleHint(visible: Boolean) { - //当前 Fragment 是 child 时候 作为缓存 Fragment 的子 fragment getUserVisibleHint = true - //但当父 fragment 不可见所以 currentVisibleState = false 直接 return 掉 - // 这里限制则可以限制多层嵌套的时候子 Fragment 的分发 if (visible && isParentInvisible) return //此处是对子 Fragment 不可见的限制,因为 子 Fragment 先于父 Fragment回调本方法 currentVisibleState 置位 false @@ -119,51 +87,18 @@ abstract class BaseLazyFragment : ToolbarFragment() { if (visible) { - // TODO 当 fragment 重建时这里的被调用很奇怪,onActivityCreated 回调触发,但此时的 view 是空的,原因是 createView 还没被调用 - // TODO 这样就造成了 onFragmentResume 里可能用到 view 的地方出现空指针异常,所以这里遇到 view 为空的时候 return 等下一次被调用才进去 - if (view == null) { - return - } + if (view == null) return - if (mIsFirstVisible) { - mIsFirstVisible = false + if (isFirstVisible) { + isFirstVisible = false onFragmentFirstVisible() } onFragmentResume() - dispatchChildVisibleState(true) - mResumeAndPauseListener?.invoke(true) + resumeAndPauseListener?.invoke(true) } else { - // 当 fragment 重建时,这个代码块可能在第一次 view 为空的 visible 后调用导致在 onFragmentPause 里可能用到 view 的地方出现空指针异常 - if (!mIsFirstVisible) { - dispatchChildVisibleState(false) + if (!isFirstVisible) { onFragmentPause() - mResumeAndPauseListener?.invoke(false) - } - } - } - - /** - * 当前 Fragment 是 child 时候 作为缓存 Fragment 的子 fragment 的唯一或者嵌套 VP 的第一 fragment 时 getUserVisibleHint = true - * 但是由于父 Fragment 还未进入可见状态所以自身也是不可见的, 这个方法可以存在是因为庆幸的是 父 fragment 的生命周期回调总是先于子 Fragment - * 所以在父 fragment 设置完成当前不可见状态后,需要通知子 Fragment 我不可见,你也不可见, - * - * - * 因为 dispatchUserVisibleHint 中判断了 isParentInvisible 所以当 子 fragment 走到了 onActivityCreated 的时候直接 return 掉了 - * - * - * 当真正的外部 Fragment 可见的时候,走 setVisibleHint (VP 中)或者 onActivityCreated (hide show) 的时候 - * 从对应的生命周期入口调用 dispatchChildVisibleState 通知子 Fragment 可见状态 - * - * @param visible - */ - private fun dispatchChildVisibleState(visible: Boolean) { - val childFragmentManager = childFragmentManager - val fragments = childFragmentManager.fragments - if (!fragments.isEmpty()) { - for (child in fragments) { - if (child is BaseLazyFragment && !child.isHidden() && child.getUserVisibleHint()) { - child.dispatchUserVisibleHint(visible) - } + resumeAndPauseListener?.invoke(false) } } } @@ -183,7 +118,7 @@ abstract class BaseLazyFragment : ToolbarFragment() { override fun onDestroyView() { super.onDestroyView() isViewCreated = false - mIsFirstVisible = true + isFirstVisible = true } } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/ToolbarController.java b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/ToolbarController.java index 737b6035a6..4f7b7820d5 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/ToolbarController.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/ToolbarController.java @@ -24,4 +24,6 @@ public interface ToolbarController { void hideToolbar(boolean isHide); void showAdLabel(boolean isAd); + + void setTitleCenter(); } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/ToolbarFragment.java b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/ToolbarFragment.java index bde3090b1b..b08705b3a1 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/ToolbarFragment.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/ToolbarFragment.java @@ -66,6 +66,14 @@ public abstract class ToolbarFragment extends BaseFragment { } } + protected void setTitleCenter() { + if (getActivity() instanceof ToolbarController) { + ((ToolbarController) getActivity()).setTitleCenter(); + } else if (getParentFragment() instanceof ToolbarController) { + ((ToolbarController) getParentFragment()).setTitleCenter(); + } + } + public void onTouchEvent(MotionEvent event) { } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/Config.kt b/module_common/src/main/java/com/gh/gamecenter/common/constant/Config.kt index c7bc495186..ee05ed3bd2 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/Config.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/Config.kt @@ -7,11 +7,6 @@ import com.gh.gamecenter.core.utils.SPUtils.setString object Config { - const val FIX_DOWNLOAD_KEY = "isFixDownload" - const val FIX_PLUGIN_KEY = "isFixPlugin" - const val FIX_ARTICLE_KEY = "isFixArticle" - const val FIX_COMMUNITY_KEY = "isFixCommunity" - const val DEFAULT_CHANNEL = "GH_TEST3" const val DEFAULT_CHANNEL_FOR_RELEASE = "GH_LOST" // 正式包的缺省渠道,避免因渠道丢失而回落到测试渠道 private var mDefaultSharedPreferences: SharedPreferences? = null @@ -43,21 +38,4 @@ object Config { return mDefaultSharedPreferences!! } - fun isExistHideFunction(): Boolean { - if (!getPreferences().getBoolean(FIX_DOWNLOAD_KEY, false)) return true - if (!getPreferences().getBoolean(FIX_PLUGIN_KEY, false)) return true - if (!getPreferences().getBoolean(FIX_COMMUNITY_KEY, false)) return true - if (!getPreferences().getBoolean(FIX_ARTICLE_KEY, false)) return true - return false - } - - fun fixHideFunction() { - getPreferences().edit().run { - putBoolean(FIX_DOWNLOAD_KEY, true) - putBoolean(FIX_ARTICLE_KEY, true) - putBoolean(FIX_COMMUNITY_KEY, true) - putBoolean(FIX_PLUGIN_KEY, true) - apply() - } - } } \ 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 945b7e7145..3abe4352d9 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 @@ -145,8 +145,6 @@ public class Constants { public static final String SP_NO_REMIND_AGAIN = "no_remind_again"; //游戏详情过滤标签数据 public static final String SP_FILTER_TAGS = "filter_tags"; - //实名认证弹窗分类数据 - public static final String SP_AUTH_DIALOG = "auth_dialog"; //我的光环小红点提示 public static final String SP_GH_RED_POINT_REMIND = "gh_red_point_remind"; //论坛首页引导 @@ -492,7 +490,8 @@ public class Constants { public static final String SP_SENSORS_IS_FIRST_TIME_ARTICLE_SHARE = "sensors_is_first_time_article_share"; public static final String SP_HOME_PUSH_POP_UP_SET = "home_push_pop_up_map"; - public static final String FINISH_HOME_PUSH = "finish_home_push"; + public static final String SP_PULL_DOWN_PUSH_POP_UP_SET = "pull_down_push_pop_up_set"; + public static final String FINISH_PULL_DOWN_PUSH = "finish_pull_down_push"; public static final String APK_SIZE = "apk_size"; public static final String URL_QUERY_FROM_FLOATING_WINDOW = "from_floating_window"; // 来源于悬浮窗 @@ -525,4 +524,8 @@ public class Constants { public static final String SP_LAST_GAME_SEARCH_AD_SHOW_TIME = "last_game_search_ad_show_time"; public static final String SP_NEW_FIRST_LAUNCH_VERSION = "isNewFirstLaunchV"; + + public static final String SP_PLUGIN_AREA_SHOWED_LAUNCH_ID = ""; // 插件化区域显示过的 launchId (Tracker.launchId) + + public static final String DIN_FONT_PATH = "fonts/d_din_bold_only_number.ttf"; } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java index dea5cb5f23..12b5f3013a 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java @@ -203,6 +203,8 @@ public class EntranceConsts { public static final String KEY_SKIP_SUCCESS_PAGE = "skipSuccessPage"; public static final String KEY_COLLECTION_ID = "collectionId"; + public static final String KEY_COMMON_CONTENT_COLLECTION_LAYOUT = "key_common_content_collection_layout"; + public static final String KEY_COLLECTION_NAME = "collectionName"; public static final String KEY_NAVIGATION_TITLE = "navigationTitle"; public static final String KEY_IMAGE_CROP_RATIO = "imageCropRatio"; @@ -272,6 +274,9 @@ public class EntranceConsts { public static final String KEY_FORUM_NAME = "forum_name";//版块名称 public static final String KEY_GAME_COLLECTION_TITLE = "game_collection_title";//游戏单标题 public static final String KEY_GAME_COLLECTION_ID = "game_collection_id";//游戏单ID + public static final String KEY_COLUMN_COLLECTION_NAME= "column_collection_name";//专题合集名称 + public static final String KEY_COLUMN_COLLECTION_ID = "column_collection_id";//专题合集ID + public static final String KEY_COLUMN_COLLECTION_STYLE = "column_collection_style";//专题合集样式 public static final String KEY_ASSIST_RES = "assist_res"; public static final String KEY_LAST_SELECTED_POSITION = "last_selected_position"; public static final String KEY_RECOMMEND_ID = "recommend_id"; @@ -306,11 +311,35 @@ public class EntranceConsts { public static final String KEY_MY_RELATED_QA = "my_related_qa"; public static final String KEY_SHOW_DOWNLOAD_MENU = "show_download_menu"; public static final String KEY_IS_FROM_MAIN_WRAPPER = "is_from_main_wrapper"; - public static final String KEY_IS_FROM_HOME_TOOLBAR_WRAPPER = "is_from_home_toolbar_wrapper"; + public static final String KEY_IS_FROM_TAB_WRAPPER = "is_from_tab_wrapper"; public static final String KEY_SHOW_REMIND = "show_remind"; public static final String KEY_CALENDAR_YEAR = "calendar_year"; public static final String KEY_CALENDAR_MONTH = "calendar_month"; public static final String KEY_CALENDAR_DAY = "calendar_day"; public static final String KEY_SERVER_CALENDAR_ID = "server_calendar_id"; + public static final String KEY_MULTI_TAB_NAV_ID = "multi_tab_nav_id"; + public static final String KEY_MULTI_TAB_NAV_NAME = "multi_tab_nav_name"; + public static final String KEY_CUSTOM_PAGE_ID = "custom_page_id"; + public static final String KEY_CUSTOM_PAGE_NAME = "custom_page_name"; + public static final String KEY_TAB_NAME = "tab_name"; + public static final String KEY_BOTTOM_TAB_ID = "bottom_tab_id"; + public static final String KEY_BOTTOM_TAB_NAME = "bottom_tab_name"; + public static final String KEY_SEARCH_BOX_PATTERN = "search_box_pattern"; + public static final String KEY_GAME_COLUMN_NAME = "game_column_name"; + public static final String KEY_GAME_COLUMN_ID = "game_column_id"; + + public static final String KEY_SHOW_SUBJECT_TAB = "show_subject_tab"; + + public static final String KEY_CUSTOM_PAGE_LOCATION = "custom_page_location"; + + public static final String KEY_CUSTOM_PAGE_TRACK_DATA = "custom_page_track_data"; + + public static final String KEY_LAST_PAGE_ID = "last_page_id"; + + public static final String KEY_LAST_PAGE_NAME = "last_page_name"; + + public static final String KEY_LAST_PAGE_BUSINESS_ID = "last_page_business_id"; + + public static final String IS_DETAIL_PAGE = "is_detail_page"; } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt b/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt index dba0c48531..a60339a9d3 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt @@ -51,7 +51,6 @@ object RouteConsts { const val packageHelper = "/services/packageHelper" const val entranceUtils = "/services/entranceUtils" const val reservationRepository = "/services/reservationRepository" - const val gameSubstituteRepository = "/services/gameSubstituteRepository" const val dataUtils = "/services/dataUtils" const val errorHelper = "/services/errorHelper" const val messageUnreadRepository = "/services/messageUnreadRepository" diff --git a/module_common/src/main/java/com/gh/gamecenter/common/dialog/NotificationHintDialogFragment.kt b/module_common/src/main/java/com/gh/gamecenter/common/dialog/NotificationHintDialogFragment.kt index be5bfe27b7..12cf0f417e 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/dialog/NotificationHintDialogFragment.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/dialog/NotificationHintDialogFragment.kt @@ -36,6 +36,15 @@ class NotificationHintDialogFragment : BaseDialogFragment() { private var mDismissCallback: (() -> Unit)? = null private val mBinding: DialogNotificationHintBinding by lazy { DialogNotificationHintBinding.inflate(layoutInflater) } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState != null) { + dismiss() + return + } + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return mBinding.root } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/entity/CommunityEntity.kt b/module_common/src/main/java/com/gh/gamecenter/common/entity/CommunityEntity.kt index f96139c02e..facfd369e1 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/entity/CommunityEntity.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/entity/CommunityEntity.kt @@ -31,8 +31,6 @@ data class CommunityEntity( var id: String? = null, @SerializedName("name") var mName: String? = "", - @SerializedName("name_suffix") - var nameSuffix: String? = null, @SerializedName("icon") var mIcon: String? = null, @SerializedName("ori_icon") @@ -46,7 +44,7 @@ data class CommunityEntity( @IgnoredOnParcel val name: String? - get() = mName?.removeSuffix(".") + (nameSuffix ?: "") + get() = mName?.removeSuffix(".") fun getIcon(): String { return mRawIcon ?: mIcon ?: "" diff --git a/module_common/src/main/java/com/gh/gamecenter/common/entity/ExposureEntity.kt b/module_common/src/main/java/com/gh/gamecenter/common/entity/ExposureEntity.kt index 32f9189152..a0b9040d57 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/entity/ExposureEntity.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/entity/ExposureEntity.kt @@ -7,7 +7,7 @@ import kotlinx.parcelize.Parcelize @Keep @Parcelize -class ExposureEntity( +data class ExposureEntity( @SerializedName("game_id") val gameId: String? = "", @SerializedName("subject_id") diff --git a/module_common/src/main/java/com/gh/gamecenter/common/entity/LaunchRedirect.kt b/module_common/src/main/java/com/gh/gamecenter/common/entity/LaunchRedirect.kt new file mode 100644 index 0000000000..28a43b67b9 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/entity/LaunchRedirect.kt @@ -0,0 +1,15 @@ +package com.gh.gamecenter.common.entity + +import com.google.gson.annotations.SerializedName + +class LaunchRedirectWrapper( + @SerializedName("link") + val launchRedirect: LaunchRedirect? = null, +) + +class LaunchRedirect( + @SerializedName("multi_tab_nav_link") + val navTabLink: LinkEntity? = null, + @SerializedName("multi_tab_link") + val topTabLink: LinkEntity? = null, +) : LinkEntity() \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/DialogHelper.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/DialogHelper.kt index a588ab0530..02e484235e 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/DialogHelper.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/DialogHelper.kt @@ -19,6 +19,7 @@ import android.widget.RelativeLayout import android.widget.TextView import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat +import androidx.fragment.app.FragmentActivity import androidx.viewbinding.ViewBinding import com.alibaba.android.arouter.launcher.ARouter import com.gh.gamecenter.common.R @@ -793,11 +794,13 @@ object DialogHelper { confirmText = context.getString(R.string.unsupported_feature_dialog_confirm_text), cancelText = context.getString(R.string.unspported_feature_dialog_cancel_text), confirmClickCallback = { - val updateManager = ARouter - .getInstance() - .build(RouteConsts.provider.updateManager) - .navigation() as? IUpdateManagerProvider - updateManager?.checkUpdate(context, false, null) + if (context is FragmentActivity) { + val updateManager = ARouter + .getInstance() + .build(RouteConsts.provider.updateManager) + .navigation() as? IUpdateManagerProvider + updateManager?.checkUpdate(context, false) + } }, cancelClickCallback = {}, extraConfig = Config(centerTitle = true, centerContent = true) diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt index dc9b7ae732..754f649467 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/EnvHelper.kt @@ -1,9 +1,22 @@ package com.gh.gamecenter.common.utils +import android.app.AlertDialog +import android.content.Context +import android.content.Intent +import android.text.TextUtils +import android.view.Gravity +import android.widget.EditText +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.content.ContextCompat import com.alibaba.android.arouter.launcher.ARouter +import com.gh.gamecenter.common.R import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.RouteConsts +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.core.provider.IAppProvider import com.gh.gamecenter.core.provider.IBuildConfigProvider +import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.core.utils.SPUtils object EnvHelper { @@ -78,4 +91,80 @@ object EnvHelper { } } } + + fun showChangeChannelDialog(context: Context, restartAfterChanges: Boolean = false) { + val appProvider = ARouter.getInstance().build(RouteConsts.provider.app).navigation() as? IAppProvider + val layout = LinearLayout(context).apply { + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + gravity = Gravity.CENTER + orientation = LinearLayout.VERTICAL + setPadding(DisplayUtils.dip2px(16F), DisplayUtils.dip2px(16F), DisplayUtils.dip2px(16F), 0) + } + + val editText = EditText(context) + var channel = SPUtils.getString(Constants.SP_TEST_FLAVOR_CHANNEL) + if (TextUtils.isEmpty(channel)) { + channel = appProvider?.getChannel() ?: "" + SPUtils.setString(Constants.SP_TEST_FLAVOR_CHANNEL, channel) + } + editText.setText(channel, TextView.BufferType.EDITABLE) + editText.layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + layout.addView(editText) + + // 推荐渠道 + val channelContainer = LinearLayout(context).apply { + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + orientation = LinearLayout.HORIZONTAL + } + channelContainer.addView( + TextView(context).apply { text = "推荐渠道:" } + ) + val channelList = listOf("GH_REFRESH", "GH_TEST3", "GH_206") + channelList.forEach { title -> + channelContainer.addView( + TextView(context).apply { + text = title + setTextColor(ContextCompat.getColor(context, R.color.text_theme)) + setOnClickListener { + editText.setText(title, TextView.BufferType.EDITABLE) + } + }, + LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ).apply { + leftMargin = 10F.dip2px() + } + ) + } + layout.addView(channelContainer) + + val inputDialog: AlertDialog.Builder = AlertDialog.Builder(context) + inputDialog.setTitle("请输入渠道:").setView(layout) + inputDialog.setPositiveButton(context.getString(R.string.confirm)) { _, _ -> + val flavorChannel = SPUtils.getString(Constants.SP_TEST_FLAVOR_CHANNEL) + if (!TextUtils.isEmpty(editText.text) && editText.text.toString() != flavorChannel) { + appProvider?.setChannel(editText.text.toString()) + SPUtils.setString(Constants.SP_TEST_FLAVOR_CHANNEL, editText.text.toString()) + if (restartAfterChanges) { + AppExecutor.uiExecutor.executeWithDelay({ + val pm = context.packageManager + val intent = pm.getLaunchIntentForPackage(context.packageName) + val mainIntent = Intent.makeRestartActivityTask(intent!!.component) + context.startActivity(mainIntent) + Runtime.getRuntime().exit(0) + }, 300L) + } + } + }.show() + } } \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/ImageUtils.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/ImageUtils.kt index 428ac37a16..ccd49bb900 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/ImageUtils.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/ImageUtils.kt @@ -446,7 +446,6 @@ object ImageUtils { val loadAsAnimatedImage = (url.endsWith(".gif") || url.endsWith(".webp")) && shouldAutoPlayAnimatedImage && (appProvider?.getDeviceRamSize() == 0L || (appProvider?.getDeviceRamSize() ?: 0) > 2500) - && view?.getTag(R.id.tag_show_animated_image) != false if (loadAsAnimatedImage && view?.tag == url) return@execute diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/NotificationHelper.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/NotificationHelper.kt index a2fc9b5c70..05aeb4ec75 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/NotificationHelper.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/NotificationHelper.kt @@ -39,6 +39,10 @@ object NotificationHelper { SPUtils.setInt(Constants.SP_SHOWED_NOTIFICATION_NEW_VERSION, currentVersion) } +// debugOnly { +// show(AppManager.getInstance().recentActiveActivity as? AppCompatActivity, ugc, callBack) +// return +// } val isShowedToday = SPUtils.getString(Constants.SP_IS_SHOWED_NOTIFICATION_TODAY, "") // 每天最多只需提示一次 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 4892085067..34f5b4beca 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 @@ -15,8 +15,8 @@ import org.json.JSONObject */ object SensorsBridge { - private const val KEY_GAME_ID = "game_id" - private const val KEY_GAME_NAME = "game_name" + const val KEY_GAME_ID = "game_id" + const val KEY_GAME_NAME = "game_name" private const val KEY_PAGE_NAME = "page_name" private const val KEY_PAGE_ID = "page_id" private const val KEY_PAGE_BUSINESS_ID = "page_business_id" @@ -25,21 +25,21 @@ object SensorsBridge { private const val KEY_LAST_PAGE_BUSINESS_ID = "last_page_business_id" private const val KEY_DOWNLOAD_STATUS = "download_status" private const val KEY_GAME_TYPE = "game_type" - private const val KEY_POSITION = "position" - private const val KEY_TAB_CONTENT = "tab_content" + const val KEY_POSITION = "position" + const val KEY_TAB_CONTENT = "tab_content" private const val KEY_GAME_TAG = "game_tag" private const val KEY_GAME_TAG_ID = "game_tag_id" private const val KEY_TITLE = "title" - private const val KEY_LINK_TYPE = "link_type" - private const val KEY_LINK_ID = "link_id" - private const val KEY_LINK_TEXT = "link_text" + const val KEY_LINK_TYPE = "link_type" + const val KEY_LINK_ID = "link_id" + const val KEY_LINK_TEXT = "link_text" private const val KEY_ACTION = "action" private const val KEY_CLICK_GAME_ID = "click_game_id" private const val KEY_CLICK_GAME_NAME = "click_game_name" private const val KEY_CLICK_GAME_TYPE = "click_game_type" private const val KEY_TEXT = "text" - private const val KEY_GAME_COLLECT_TITLE = "game_collect_title" - private const val KEY_GAME_COLLECT_ID = "game_collect_id" + const val KEY_GAME_COLLECT_TITLE = "game_collect_title" + const val KEY_GAME_COLLECT_ID = "game_collect_id" private const val KEY_BUTTON_NAME = "button_name" private const val KEY_PLATFORM = "platform" private const val KEY_LOCATION = "location" @@ -84,6 +84,26 @@ object SensorsBridge { private const val KEY_VERIFICATION_TYPE = "verification_type" private const val KEY_VIDEO_ID = "video_id" private const val KEY_PLAY_TYPE = "play_type" + const val KEY_BOTTOM_TAB = "bottom_tab" + const val KEY_MULTI_TAB_NAME = "several_tab_page_name" + const val KEY_MULTI_TAB_ID = "several_tab_page_id" + const val KEY_CUSTOM_PAGE_ID = "custom_page_id" + const val KEY_CUSTOM_PAGE_NAME = "custom_page_name" + private const val KEY_SEARCH_BOX_PATTERN = "search_box_pattern" + private const val KEY_ENTRANCE = "entrance" + private const val KEY_BUTTON_TYPE = "button_type" + const val KEY_MODULE_TYPE = "module_type" + const val KEY_MODULE_PATTERN = "module_pattern" + const val KEY_LINK_CONTENT_NAME = "link_content_name" + const val KEY_LINK_CONTENT_ID = "link_content_id" + private const val KEY_GAME_LIST_COLLECTION_ID = "game_list_collection_id" + private const val KEY_GAME_LIST_COLLECTION_NAME = "game_list_collection_name" + const val KEY_MONGOLD_ID = "mongold_id" + private const val KEY_GAME_COLLECT_PATTERN = "game_collect_pattern" + private const val KEY_LINK_CONTENT_COLLECTIONE_PATTERN = "link_content_collectione_pattern" + private const val KEY_COLUMN_COLLECTIONE_PATTERN = "column_collectione_pattern" + private const val KEY_SEQUENCE = "sequence" + private const val EVENT_GAME_DETAIL_PAGE_TAB_SELECT = "GameDetailPageTabSelect" private const val EVENT_GAME_DETAIL_PAGE_TAG_CLICK = "GameDetailPageGameTagClick" @@ -188,6 +208,13 @@ object SensorsBridge { private const val EVENT_PERSONAL_HOME_PAGE_BROWSING = "PersonalHomepageBrowsing" private const val EVENT_FORUM_BROWSING_DURATION = "ForumBrowsingDuration" private const val EVENT_COMMUNITY_BROWSING_DURATION = "CommunityBrowsingDuration" + private const val EVENT_COLUMN_CLICK = "ColumnClick" + private const val EVENT_QQ_GAME_CLICK = "QqGameClick" + private const val EVENT_GAME_LIST_COLLECTION_CLICK = "GameListCollectionClick" + private const val EVENT_CONTENT_CARD_CLICK = "ContentCardClick" + private const val EVENT_VIEW_CUSTOM_PAGE = "ViewCustomPage" + private const val EVENT_SUSPENDED_WINDOW_SHOW = "SuspendedWindowShow" + private const val EVENT_SUSPENDED_WINDOW_CLICK = "SuspendedWindowClick" private var mIsSensorsEnabled = false @@ -267,17 +294,35 @@ object SensorsBridge { * @param pageId 页面ID * @param pageName 页面名称 * @param sourceEntrance 来源入口 + * @param bottomTab 来源入口 + * @param multiTabId 多tab导航页ID + * @param multiTabName 多tab导航页名称 + * @param customPageId 自定义页面ID + * @param customPageName 自定义页面名称 + * @param searchBoxPattern 搜索框样式 */ @JvmStatic fun trackSearchPageShow( pageId: String, pageName: String, - sourceEntrance: String + sourceEntrance: String, + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + customPageId: String = "", + customPageName: String = "", + searchBoxPattern: String = "" ) { val json = json { KEY_PAGE_ID to pageId KEY_PAGE_NAME to pageName KEY_SOURCE_ENTRANCE to sourceEntrance + KEY_BOTTOM_TAB to bottomTab + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName + KEY_SEARCH_BOX_PATTERN to searchBoxPattern } trackEvent(EVENT_SEARCH_PAGE_SHOW, json) } @@ -1756,11 +1801,35 @@ object SensorsBridge { * 序号:90 * 事件ID:DownloadManagerEnter * 事件名称:进入下载管理事件 + * @param entrance 入口 + * @param bottomTab 底部tab + * @param gameColumnId 游戏专题ID + * @param gameColumnName 游戏专题名称 + * @param customPageId 自定义页面ID + * @param customPageName 自定义页面名称 * @see EVENT_DOWNLOAD_MANAGER_ENTER */ @JvmStatic - fun trackDownloadManagerEnter() { - val json = json {} + fun trackDownloadManagerEnter( + entrance: String = "", + bottomTab: String = "", + gameColumnId: String = "", + gameColumnName: String = "", + customPageId: String = "", + customPageName: String = "", + multiTabId: String = "", + multiTabName: String = "" + ) { + val json = json { + KEY_ENTRANCE to entrance + KEY_BOTTOM_TAB to bottomTab + KEY_GAME_COLUMN_ID to gameColumnId + KEY_GAME_COLUMN_NAME to gameColumnName + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + } trackEvent(EVENT_DOWNLOAD_MANAGER_ENTER, json) } @@ -1790,9 +1859,26 @@ object SensorsBridge { * @see EVENT_HALO_FUN_GAME_MANAGER_ENTER */ @JvmStatic - fun trackHaloFunGameManagerEnter() { - val json = json {} - + fun trackHaloFunGameManagerEnter( + entrance: String, + bottomTab: String, + multiTabId: String, + multiTabName: String, + position: Int, + tabContent: String, + customPageId: String, + customPageName: String, + ) { + val json = json { + KEY_ENTRANCE to entrance + KEY_BOTTOM_TAB to bottomTab + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + KEY_POSITION to position + KEY_TAB_CONTENT to tabContent + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName + } trackEvent(EVENT_HALO_FUN_GAME_MANAGER_ENTER, json) } @@ -1841,7 +1927,15 @@ object SensorsBridge { linkType: String, linkId: String, linkText: String, - text: String + text: String, + linkContentCollectionePattern: String = "", + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + sequence: Int = -1, + tabContent: String = "", + customPageId: String = "", + customPageName: String = "" ) { val json = json { KEY_LOCATION to location @@ -1849,11 +1943,21 @@ object SensorsBridge { KEY_BLOCK_ID to blockId KEY_LINK_CONTENT_COLLECTION_NAME to linkContentCollectionName KEY_LINK_CONTENT_COLLECTION_ID to linkContentCollectionId - KEY_POSITION to position + KEY_POSITION to sequence KEY_LINK_TYPE to linkType KEY_LINK_ID to linkId KEY_LINK_TEXT to linkText KEY_TEXT to text + KEY_LINK_CONTENT_COLLECTIONE_PATTERN to linkContentCollectionePattern + KEY_BOTTOM_TAB to bottomTab + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + if (position != -1) { + KEY_SEQUENCE to position + } + KEY_TAB_CONTENT to tabContent + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName } trackEvent(EVENT_LINK_CONTENT_COLLECTION_CLICK, json) @@ -1877,14 +1981,27 @@ object SensorsBridge { @JvmStatic fun trackColumnCollectionClick( location: String, - blockName: String, - blockId: String, + blockName: String = "", + blockId: String = "", columnCollectionName: String, columnCollectionId: String, - position: Int, + position: Int = -1, gameColumnName: String, gameColumnId: String, - text: String + text: String, + columnCollectionPattern: String = "", + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + sequence: Int = -1, + tabContent: String = "", + customPageId: String = "", + customPageName: String = "", + gameId: String = "", + gameName: String = "", + linkType: String = "", + linkId: String = "", + linkText: String = "", ) { val json = json { KEY_LOCATION to location @@ -1892,10 +2009,25 @@ object SensorsBridge { KEY_BLOCK_ID to blockId KEY_COLUMN_COLLECTION_NAME to columnCollectionName KEY_COLUMN_COLLECTION_ID to columnCollectionId - KEY_POSITION to position + KEY_SEQUENCE to position KEY_GAME_COLUMN_NAME to gameColumnName KEY_GAME_COLUMN_ID to gameColumnId KEY_TEXT to text + KEY_COLUMN_COLLECTIONE_PATTERN to columnCollectionPattern + KEY_BOTTOM_TAB to bottomTab + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + if (position != -1) { + KEY_POSITION to sequence + } + KEY_TAB_CONTENT to tabContent + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + KEY_LINK_ID to linkId + KEY_LINK_TYPE to linkType + KEY_LINK_TEXT to linkText } trackEvent(EVENT_COLUMN_COLLECTION_CLICK, json) @@ -3089,4 +3221,298 @@ object SensorsBridge { } trackEvent(EVENT_COMMUNITY_BROWSING_DURATION, json) } + /** + * 事件ID:ColumnClick + * 事件名称:游戏专题点击事件 + * @param bottomTab 底部tab + * @param multiTabId 多tab导航页ID + * @param multiTabName 多tab导航页名称 + * @param position 序号 + * @param tabContent 选中tab的名称 + * @param customPageId 自定义页面ID + * @param customPageName 自定义页面名称 + * @param linkType 链接类型 + * @param linkId 跳转链接 + * @param linkText 链接文案 + * @param gameColumnId 游戏专题ID + * @param gameColumnName 游戏专题名称 + * @param gameId 游戏ID + * @param gameName 游戏名称 + * @param location 位置 + * @param columnPattern 游戏专题样式 + * @param text 点击内容 + * @param buttonType 右上角文案 + * @see EVENT_COLUMN_CLICK + */ + fun trackColumnClick( + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + position: Int = -1, + tabContent: String = "", + customPageId: String = "", + customPageName: String = "", + linkType: String = "", + linkId: String = "", + linkText: String = "", + gameColumnId: String = "", + gameColumnName: String = "", + gameId: String = "", + gameName: String = "", + location: String = "", + columnPattern: String = "", + text: String = "", + buttonType: String = "" + ) { + val json = json { + KEY_BOTTOM_TAB to bottomTab + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + KEY_TAB_CONTENT to tabContent + if (position != -1) { + KEY_POSITION to position + } + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName + KEY_LINK_TYPE to linkType + KEY_LINK_ID to linkId + KEY_LINK_TEXT to linkText + KEY_GAME_COLUMN_ID to gameColumnId + KEY_GAME_COLUMN_NAME to gameColumnName + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + KEY_LOCATION to location + "column_pattern" to columnPattern + KEY_TEXT to text + KEY_BUTTON_TYPE to buttonType + } + trackEvent(EVENT_COLUMN_CLICK, json) + } + + /** + * 事件ID:QqGameClick + * 事件名称:QQ小游戏点击事件 + * @param bottomTab 底部tab + * @param multiTabId 多tab导航页ID + * @param multiTabName 多tab导航页名称 + * @param position 序号 + * @param tabContent 选中tab的名称 + * @param customPageId 自定义页面ID + * @param customPageName 自定义页面名称 + * @param gameColumnId 游戏专题ID + * @param gameColumnName 游戏专题名称 + * @param location 位置 + * @param columnPattern 游戏专题样式 + * @param searchContent 搜索内容 + * @see EVENT_QQ_GAME_CLICK + */ + fun trackQQGameClick( + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + position: Int = -1, + tabContent: String = "", + customPageId: String = "", + customPageName: String = "", + gameColumnId: String = "", + gameColumnName: String = "", + location: String = "", + columnPattern: String = "", + searchContent: String = "", + gameId: String = "", + gameName: String = "" + ) { + val json = json { + KEY_BOTTOM_TAB to bottomTab + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + KEY_TAB_CONTENT to tabContent + if (position != -1) { + KEY_POSITION to position + } + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName + KEY_GAME_COLUMN_ID to gameColumnId + KEY_GAME_COLUMN_NAME to gameColumnName + KEY_LOCATION to location + "column_pattern" to columnPattern + KEY_SEARCH_CONTENT to searchContent + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + } + trackEvent(EVENT_QQ_GAME_CLICK, json) + } + + /** + * 事件ID GameListCollectionClick + * 事件名称 游戏单合集点击事件 + */ + fun trackGameListCollectionClick( + location: String, + gameListCollectionId: String, + gameListCollectionName: String, + text: String, + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + position: Int = -1, + tabContent: String = "", + customPageId: String = "", + customPageName: String = "", + gameCollectPattern: String = "", + otherParams: HashMap + ) { + val json = json { + KEY_LOCATION to location + KEY_GAME_LIST_COLLECTION_ID to gameListCollectionId + KEY_GAME_LIST_COLLECTION_NAME to gameListCollectionName + KEY_TEXT to text + KEY_BOTTOM_TAB to bottomTab + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + KEY_POSITION to position + KEY_TAB_CONTENT to tabContent + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName + KEY_GAME_COLLECT_PATTERN to gameCollectPattern + otherParams.forEach { (key, value) -> + key to value + } + } + trackEvent(EVENT_GAME_LIST_COLLECTION_CLICK, json) + } + + /** + * ContentCardClick 自定义页面:内容卡片点击事件 + */ + fun trackContentCardClick( + bottomTab: String, + multiTabId: String, + multiTabName: String, + position: Int, + tabContent: String, + customPageId: String, + customPageName: String, + linkId: String, + linkType: String, + linkText: String + ) { + val json = json { + KEY_BOTTOM_TAB to bottomTab + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + KEY_POSITION to position + KEY_TAB_CONTENT to tabContent + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName + KEY_LINK_ID to linkId + KEY_LINK_TYPE to linkType + KEY_LINK_TEXT to linkText + } + trackEvent(EVENT_CONTENT_CARD_CLICK, json) + } + + /** + * ViewCustomPage 自定义页面浏览事件 + */ + fun trackViewCustomPage( + bottomTab: String, + multiTabId: String, + multiTabName: String, + position: Int, + tabContent: String, + customPageId: String, + customPageName: String + ) { + val json = json { + KEY_BOTTOM_TAB to bottomTab + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + KEY_POSITION to position + KEY_TAB_CONTENT to tabContent + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName + } + trackEvent(EVENT_VIEW_CUSTOM_PAGE, json) + } + + /** + * 事件ID:SuspendedWindowShow + * 事件名称:右下悬浮窗展示事件 + */ + fun trackSuspendedWindowShow( + pattern: String = "", + location: String = "", + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + position: Int = -1, + tabContent: String = "", + customPageId: String = "", + customPageName: String = "", + linkId: String = "", + linkType: String = "", + linkText: String = "", + gameId: String = "", + gameName: String = "" + ) { + val json = json { + "suspended_window_pattern" to pattern + KEY_LOCATION to location + KEY_BOTTOM_TAB to bottomTab + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + KEY_POSITION to position + KEY_TAB_CONTENT to tabContent + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName + KEY_LINK_ID to linkId + KEY_LINK_TYPE to linkType + KEY_LINK_TEXT to linkText + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + } + trackEvent(EVENT_SUSPENDED_WINDOW_SHOW, json) + } + + /** + * 事件ID:SuspendedWindowClick + * 事件名称:右下悬浮窗点击事件 + */ + fun trackSuspendedWindowClick( + pattern: String = "", + action: String = "", + location: String = "", + bottomTab: String = "", + multiTabId: String = "", + multiTabName: String = "", + position: Int = -1, + tabContent: String = "", + customPageId: String = "", + customPageName: String = "", + linkId: String = "", + linkType: String = "", + linkText: String = "", + gameId: String = "", + gameName: String = "" + ) { + val json = json { + "suspended_window_pattern" to pattern + KEY_ACTION to action + KEY_LOCATION to location + KEY_BOTTOM_TAB to bottomTab + KEY_MULTI_TAB_ID to multiTabId + KEY_MULTI_TAB_NAME to multiTabName + KEY_POSITION to position + KEY_TAB_CONTENT to tabContent + KEY_CUSTOM_PAGE_ID to customPageId + KEY_CUSTOM_PAGE_NAME to customPageName + KEY_LINK_ID to linkId + KEY_LINK_TYPE to linkType + KEY_LINK_TEXT to linkText + KEY_GAME_ID to gameId + KEY_GAME_NAME to gameName + } + trackEvent(EVENT_SUSPENDED_WINDOW_CLICK, json) + } } \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/AsyncCell.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/AsyncCell.kt index 0c6f287913..3a96d2d6b1 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/view/AsyncCell.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/AsyncCell.kt @@ -26,18 +26,14 @@ open class AsyncCell @JvmOverloads constructor(context: Context, attrs: Attribut open val switchToWrapContentWhenInflated = false open var isInflated = false - private val bindingFunctions: MutableList Unit> = mutableListOf() + private var bindingFunction: (AsyncCell.() -> Unit)? = null open fun inflate() { AsyncLayoutInflater(context).inflate(layoutId, this) { view, _, _ -> isInflated = true addView(createDataBindingView(view)) - if (delayFirstTimeBindView) { - post { bindView() } - } else { - bindView() - } + bindView() if (switchToWrapContentWhenInflated) { postDelayed({ switchToWrapContent() }, 50) @@ -46,17 +42,15 @@ open class AsyncCell @JvmOverloads constructor(context: Context, attrs: Attribut } open fun bindView() { - with(bindingFunctions) { - forEach { it() } - clear() - } + bindingFunction?.invoke(this) + bindingFunction = null } fun bindWhenInflated(bindFunc: AsyncCell.() -> Unit) { if (isInflated) { bindFunc() } else { - bindingFunctions.add(bindFunc) + bindingFunction = bindFunc } } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/AsyncUi.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/AsyncUi.kt deleted file mode 100644 index f4dffa8b08..0000000000 --- a/module_common/src/main/java/com/gh/gamecenter/common/view/AsyncUi.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.gh.gamecenter.common.view - -import android.content.Context -import com.gh.gamecenter.core.runOnIoThread -import com.gh.gamecenter.core.runOnUiThread -import splitties.views.dsl.core.Ui - -open class AsyncUi(context: Context) : - AsyncCell(context) { - - var ui: T? = null - - override fun inflate() { - runOnIoThread(true) { - ui = provideUi(context) - val view = ui!!.root - runOnUiThread { - isInflated = true - addView(createDataBindingView(view)) - - if (delayFirstTimeBindView) { - post { bindView() } - } else { - bindView() - } - post { bindView() } - } - } - } - - open fun provideUi(context: Context): T? = null - -} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/AvatarBorderView.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/AvatarBorderView.kt index b71729e68e..d196008b95 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/view/AvatarBorderView.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/AvatarBorderView.kt @@ -74,7 +74,8 @@ class AvatarBorderView : ConstraintLayout { val params = layoutParams params.width = mAvatarWidth.toInt() params.height = mAvatarWidth.toInt() - layoutParams = params + // 避免我的光环头像在首页底部Tab变更时没有正确修改LayoutParams + post { layoutParams = params } val roundingParams = RoundingParams.fromCornersRadius(mAvatarWidth) roundingParams.borderColor = mAvatarBorderColor roundingParams.borderWidth = mAvatarBorderWidth @@ -84,7 +85,8 @@ class AvatarBorderView : ConstraintLayout { val params = layoutParams params.width = mAvatarWidth.toInt() params.height = mAvatarWidth.toInt() - layoutParams = params + // 避免我的光环头像在首页底部Tab变更时没有正确修改LayoutParams + post { layoutParams = params } val drawable = GradientDrawable().apply { setColor(R.color.transparent.toColor()) shape = GradientDrawable.OVAL @@ -101,13 +103,15 @@ class AvatarBorderView : ConstraintLayout { params.width = (mAvatarWidth * 3 / 2).toInt() params.height = (mAvatarWidth * 3 / 2).toInt() } - layoutParams = params + // 避免我的光环头像在首页底部Tab变更时没有正确修改LayoutParams + post { layoutParams = params } } badgeView?.apply { val params = layoutParams params.width = mBadgeWidth.toInt() params.height = mBadgeWidth.toInt() - layoutParams = params + // 避免我的光环头像在首页底部Tab变更时没有正确修改LayoutParams + post { layoutParams = params } } } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/CustomAutoScrollRecyclerViewContainerView.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/CustomAutoScrollRecyclerViewContainerView.kt new file mode 100644 index 0000000000..ba9201ece8 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/CustomAutoScrollRecyclerViewContainerView.kt @@ -0,0 +1,114 @@ +package com.gh.gamecenter.common.view + +import android.content.Context +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.util.AttributeSet +import android.view.MotionEvent +import android.widget.LinearLayout +import androidx.annotation.Nullable +import androidx.core.view.children +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener +import java.lang.ref.WeakReference +import java.util.* + +class CustomAutoScrollRecyclerViewContainerView(context: Context, @Nullable attrs: AttributeSet?) : + LinearLayout(context, attrs), DefaultLifecycleObserver { + + private var isActive = false + private var isAttached = false + + private val handler = Handler(Looper.getMainLooper()) + private val autoScrollTask = AutoScrollTask(this, handler) + + private var onItemTouchListener: OnItemTouchListener? = null + + private fun resumeScrolling() { + handler.removeCallbacksAndMessages(null) + handler.postDelayed(autoScrollTask, AUTO_SCROLL_INTERVAL) + } + + + private fun pauseScrolling() { + handler.removeCallbacksAndMessages(null) + } + + override fun onResume(owner: LifecycleOwner) { + isActive = true + if (isAttached) { + resumeScrolling() + } + } + + override fun onPause(owner: LifecycleOwner) { + isActive = false + pauseScrolling() + } + + private fun startAutoScroll() { + if (isActive && isAttached) { + resumeScrolling() + } + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + if (onItemTouchListener == null) { + onItemTouchListener = object : RecyclerView.OnItemTouchListener { + override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {} + override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent) = + rv.scrollState == RecyclerView.SCROLL_STATE_DRAGGING + + override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {} + } + } + children.forEach { child -> + if (child is RecyclerView) { + onItemTouchListener?.let { + child.removeOnItemTouchListener(it) + child.addOnItemTouchListener(it) + } + } + } + isAttached = true + startAutoScroll() + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + isAttached = false + pauseScrolling() + } + + companion object { + private const val AUTO_SCROLL_INTERVAL: Long = 10 + } + + class AutoScrollTask( + reference: CustomAutoScrollRecyclerViewContainerView?, + private val handler: Handler + ) : Runnable { + private val mReference: WeakReference? + private val mScrollSlop = if (Build.MODEL.toLowerCase(Locale.getDefault()) == "mumu") 1 else 2 + + init { + mReference = WeakReference(reference) + } + + override fun run() { + val containerView: CustomAutoScrollRecyclerViewContainerView? = mReference?.get() + if (containerView != null) { + for (child in containerView.children) { + if (child is RecyclerView) { + child.scrollBy(mScrollSlop, mScrollSlop) + } + } + handler.postDelayed(this, AUTO_SCROLL_INTERVAL) + } + } + } +} diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/HomeRefreshHeader.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/HomeRefreshHeader.kt index 3e099240b8..fc891bf91e 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/view/HomeRefreshHeader.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/HomeRefreshHeader.kt @@ -3,6 +3,7 @@ package com.gh.gamecenter.common.view import android.content.Context import android.util.AttributeSet import android.view.View +import androidx.annotation.ColorInt import com.scwang.smartrefresh.layout.api.RefreshLayout import com.scwang.smartrefresh.layout.constant.RefreshState import com.scwang.smartrefresh.layout.header.ClassicsHeader @@ -57,4 +58,8 @@ class HomeRefreshHeader @JvmOverloads constructor( else -> {} } } + + fun setArrowColorFilter(@ColorInt color: Int) { + mArrowView.setColorFilter(color) + } } \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/HomeSmartRefreshLayout.java b/module_common/src/main/java/com/gh/gamecenter/common/view/HomeSmartRefreshLayout.java index e5e348a4fe..330a5a1cb3 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/view/HomeSmartRefreshLayout.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/HomeSmartRefreshLayout.java @@ -14,7 +14,7 @@ import com.scwang.smartrefresh.layout.constant.RefreshState; import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener; public class HomeSmartRefreshLayout extends SmartRefreshLayout { - private final int mFloorHeight = DisplayUtils.getScreenWidth() - DisplayUtils.dip2px(48F) - DisplayUtils.getStatusBarHeight(getContext().getResources()); + private int mFloorHeight = DisplayUtils.getScreenWidth() - DisplayUtils.dip2px(48F) - DisplayUtils.getStatusBarHeight(getContext().getResources()); public HomeSmartRefreshLayout(Context context) { super(context); @@ -30,6 +30,10 @@ public class HomeSmartRefreshLayout extends SmartRefreshLayout { this.mFloorDuration = duration; } + public void setFloorHeight(int height) { + this.mFloorHeight = height; + } + @Override protected void overSpinner() { if (mState == RefreshState.TwoLevel) { diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/HorizontalScrollableView.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/HorizontalScrollableView.kt index 18427b6c34..247013c64a 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/view/HorizontalScrollableView.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/HorizontalScrollableView.kt @@ -7,10 +7,10 @@ import android.widget.LinearLayout import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 -import com.gh.gamecenter.core.utils.DisplayUtils -import com.gh.gamecenter.common.utils.rxTimer import com.gh.gamecenter.common.R import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.rxTimer +import com.gh.gamecenter.core.utils.DisplayUtils import io.reactivex.disposables.Disposable import kotlin.math.abs @@ -35,6 +35,8 @@ class HorizontalScrollableView : LinearLayout { private var mAutoScrollEnabled = true + private var onPageSelectedAction: ((Int) -> Unit)? = null + constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) @@ -67,6 +69,7 @@ class HorizontalScrollableView : LinearLayout { if (mDataList.isNotEmpty()) { slideIndicator(currentPage % mDataList.size) } + onPageSelectedAction?.invoke(position) } }) } @@ -89,10 +92,12 @@ class HorizontalScrollableView : LinearLayout { fun start( datas: ArrayList<*>, startFromCenter: Boolean, - newAdapter: RecyclerView.Adapter<*> + newAdapter: RecyclerView.Adapter<*>, + onPageSelectedAction: ((Int) -> Unit)? = null ) { if (datas.isEmpty()) throw IllegalArgumentException("列表不能为空") val adapter = mViewPager2.adapter + this.onPageSelectedAction = onPageSelectedAction mDataList = datas as ArrayList if (adapter == null) { mViewPager2.adapter = newAdapter diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/MaterializedConstraintLayout.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/MaterializedConstraintLayout.kt new file mode 100644 index 0000000000..2c01f9dc84 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/MaterializedConstraintLayout.kt @@ -0,0 +1,50 @@ +package com.gh.gamecenter.common.view + +import android.content.Context +import android.os.Build +import android.util.AttributeSet +import androidx.constraintlayout.widget.ConstraintLayout +import com.gh.gamecenter.common.R + +class MaterializedConstraintLayout : ConstraintLayout { + + private var mConsumeWindowInset: Boolean = false + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + val ta = context.obtainStyledAttributes(attrs, R.styleable.MaterializedRelativeLayout) + mConsumeWindowInset = ta.getBoolean(R.styleable.MaterializedRelativeLayout_consumeWindowInsets, false) + ta.recycle() + } + + /** + * 将 windowInsets 传递给子 view [https://medium.com/androiddevelopers/why-would-i-want-to-fitssystemwindows-4e26d9ce1eec#.raoa9t506] + */ + override fun onAttachedToWindow() { + super.onAttachedToWindow() + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + setOnApplyWindowInsetsListener { _, insets -> + if (mConsumeWindowInset) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { + insets.replaceSystemWindowInsets( + insets.systemWindowInsetLeft, + 0, + insets.systemWindowInsetRight, + insets.systemWindowInsetBottom + ) + insets.consumeSystemWindowInsets() + } + } else { + val childCount = childCount + for (index in 0 until childCount) { + getChildAt(index).dispatchApplyWindowInsets(insets) + } + } + insets + } + } + } + +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/OffsetLinearLayoutManager.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/OffsetLinearLayoutManager.kt index 9ff4a703f6..9162bfce8d 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/view/OffsetLinearLayoutManager.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/OffsetLinearLayoutManager.kt @@ -1,33 +1,33 @@ package com.gh.gamecenter.common.view import android.content.Context +import android.view.ViewGroup.MarginLayoutParams import androidx.recyclerview.widget.RecyclerView /** * 重写computeVerticalScrollOffset,精确计算RecyclerView滑动距离 */ -class OffsetLinearLayoutManager(context: Context?) : FixLinearLayoutManager(context) { +class OffsetLinearLayoutManager(context: Context?) : ScrollableLinearLayoutManager(context) { val heightMap = linkedMapOf() - var isScrollEnabled = true override fun onLayoutCompleted(state: RecyclerView.State) { super.onLayoutCompleted(state) val count = childCount for (i in 0 until count) { val view = getChildAt(i) - heightMap[i] = view?.height ?: 0 + if (view?.layoutParams is MarginLayoutParams) { + heightMap[i] = view.height + (view.layoutParams as MarginLayoutParams).topMargin + (view.layoutParams as MarginLayoutParams).bottomMargin + } else { + heightMap[i] = view?.height ?: 0 + } } } - override fun canScrollVertically(): Boolean { - return isScrollEnabled && super.canScrollVertically() - } - override fun computeVerticalScrollOffset(state: RecyclerView.State): Int { return if (childCount != 0) { try { val firstVisiblePosition = findFirstVisibleItemPosition() val firstVisibleView = findViewByPosition(firstVisiblePosition) - var offsetY = -(firstVisibleView?.y ?: 0).toInt() + var offsetY = -(firstVisibleView?.y ?: 0).toInt() + ((firstVisibleView?.layoutParams as? MarginLayoutParams)?.topMargin ?: 0) for (i in 0 until firstVisiblePosition) { offsetY += (if (heightMap[i] == null) 0 else heightMap[i] ?: 0) } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/ScrollableLinearLayoutManager.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/ScrollableLinearLayoutManager.kt new file mode 100644 index 0000000000..233c52fa00 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/ScrollableLinearLayoutManager.kt @@ -0,0 +1,16 @@ +package com.gh.gamecenter.common.view + +import android.content.Context + +open class ScrollableLinearLayoutManager(context: Context?): FixLinearLayoutManager(context) { + var isScrollVerticallyEnabled = true + var isScrollHorizontallyEnabled = true + + override fun canScrollVertically(): Boolean { + return isScrollVerticallyEnabled && super.canScrollVertically() + } + + override fun canScrollHorizontally(): Boolean { + return isScrollHorizontallyEnabled && super.canScrollHorizontally() + } +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/stacklayoutmanager2/DefaultLayoutAnimation.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/stacklayoutmanager2/DefaultLayoutAnimation.kt new file mode 100644 index 0000000000..eb1b8b2e30 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/stacklayoutmanager2/DefaultLayoutAnimation.kt @@ -0,0 +1,30 @@ +package com.gh.gamecenter.common.view.stacklayoutmanager2 + +import android.animation.TimeInterpolator +import android.view.View + +class DefaultLayoutAnimation : StackAnimation2 { + + private val alphaInterceptor = TimeInterpolator { x -> + x * x * x * x + } + + override fun layoutAnimations(child: View, layoutPosition: Int, layoutManager: StackLayoutManagerV2) { + // 相邻卡片间透明度差距变化 + val alphaSpace = 1f / layoutManager.visibleCount + val alpha: Float + var scale = 1f + if (layoutPosition == layoutManager.selectedPosition) { + alpha = 1 - alphaInterceptor.getInterpolation(layoutManager.scrollPercent) + } else { + alpha = + 1 - (layoutPosition - layoutManager.selectedPosition) * alphaSpace + alphaSpace * layoutManager.scrollPercent + scale = 1 - (layoutPosition - layoutManager.selectedPosition) * 0.1f + 0.1f * layoutManager.scrollPercent + } + child.alpha = alpha + child.pivotY = child.measuredHeight / 2f + child.pivotX = child.width.toFloat() + child.scaleX = scale + child.scaleY = scale + } +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/stacklayoutmanager2/StackAnimation2.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/stacklayoutmanager2/StackAnimation2.kt new file mode 100644 index 0000000000..919a66befb --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/stacklayoutmanager2/StackAnimation2.kt @@ -0,0 +1,8 @@ +package com.gh.gamecenter.common.view.stacklayoutmanager2 + +import android.view.View + +interface StackAnimation2 { + + fun layoutAnimations(child: View, layoutPosition: Int,layoutManager: StackLayoutManagerV2) +} \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/view/stacklayoutmanager2/StackLayoutManagerV2.kt b/module_common/src/main/java/com/gh/gamecenter/common/view/stacklayoutmanager2/StackLayoutManagerV2.kt new file mode 100644 index 0000000000..6df084e1a5 --- /dev/null +++ b/module_common/src/main/java/com/gh/gamecenter/common/view/stacklayoutmanager2/StackLayoutManagerV2.kt @@ -0,0 +1,367 @@ +package com.gh.gamecenter.common.view.stacklayoutmanager2 + +import android.content.res.Resources +import android.util.TypedValue +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import kotlin.math.abs + +private val Float.dpF: Float + get() = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + this, + Resources.getSystem().displayMetrics + ) + +private val Int.dpI: Int + get() = toFloat().dpI + +private val Float.dpI: Int + get() = this.dpF.toInt() + +/** + * @param ratioH + * @param ratioW + * 卡片的宽高比,不一定是真是宽高 + */ +class StackLayoutManagerV2( + private val ratioH: Int, + private val ratioW: Int, + private val layoutAnimation: StackAnimation2 = DefaultLayoutAnimation() +) : RecyclerView.LayoutManager() { + + private var _recyclerView: RecyclerView? = null + + /** + * 是否初始化过 + */ + private var isInit = false + + /** + * 当前获取的View的位置 + */ + private var currentPosition: Int = 0 + + private val marginStart = 16.dpI + + private val marginEnd = 5.dpI + + /** + * 相临两张卡片之间的偏移量 + */ + private val offset = 9.dpI + + /** + * 显示的卡片数量 + */ + val visibleCount = 3 + + /** + * 顶层卡片的偏移量 + */ + private var scrollOffset = 0 + + /** + * 滚动的偏移量 + */ + private var scrollX = 0 + + /** + * 每张卡片的宽度 + */ + private var childWidth = 0 + + /** + * 最上面卡片 + */ + private var _selectedPosition = 0 + + val selectedPosition: Int + get() = _selectedPosition + + /** + * 是否正在滚动 + */ + private var isScrolling = false + + val scrollPercent: Float + get() = + if (scrollOffset >= (childWidth + marginStart)) { + 1f + } else { + scrollOffset.toFloat() / (childWidth + marginStart) + } + + /** + * 卡片停止滚动的时候,可能处于中间状态,此时,需要在这个回调中修正状态 + */ + private val onScrollListener = object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + if (scrollX != 0) { + if (scrollX > 0f) { + //正在往左滑动 + if (scrollPercent >= 0.5f) { + recyclerView.smoothScrollToPosition(_selectedPosition + 1) + } else { + recyclerView.smoothScrollToPosition(_selectedPosition) + } + } else { + // 正在往右滑动 + if (scrollPercent >= 0.5f) { + // 上一页回到最上面 + recyclerView.smoothScrollToPosition(_selectedPosition + 1) + } else { + // 恢复 + recyclerView.smoothScrollToPosition(_selectedPosition) + } + } + } else { + isScrolling = false + onStackListener?.onScrollStateChanged(false) + } + } + } + } + + private val onFlingListener = object : RecyclerView.OnFlingListener() { + override fun onFling(velocityX: Int, velocityY: Int): Boolean { + if (velocityX >= 0) { + _recyclerView?.smoothScrollToPosition(_selectedPosition + 1) + } else { + _recyclerView?.smoothScrollToPosition(_selectedPosition) + } + return true + } + + } + + override fun isAutoMeasureEnabled(): Boolean { + return true + } + + override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams = + RecyclerView.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + + override fun onAttachedToWindow(recyclerView: RecyclerView) { + super.onAttachedToWindow(recyclerView) + _recyclerView = recyclerView + recyclerView.addOnScrollListener(onScrollListener) + recyclerView.onFlingListener = onFlingListener + } + + override fun onDetachedFromWindow(recyclerView: RecyclerView, recycler: RecyclerView.Recycler) { + super.onDetachedFromWindow(recyclerView, recycler) + _recyclerView = null + recyclerView.removeOnScrollListener(onScrollListener) + recyclerView.onFlingListener = null + } + + override fun canScrollHorizontally(): Boolean { + return true + } + + override fun smoothScrollToPosition(recyclerView: RecyclerView, state: RecyclerView.State, position: Int) { + val dx = if (scrollX >= 0) { + if (position > _selectedPosition) { + childWidth - scrollX + marginStart + } else { + -scrollX + } + } else { + if (position > _selectedPosition) { + -scrollX + } else { + -(childWidth + marginStart + scrollX) + } + } + + recyclerView.smoothScrollBy(dx, 0) + } + + override fun scrollToPosition(position: Int) { + _selectedPosition = position + requestLayout() + + } + + override fun scrollHorizontallyBy( + dx: Int, + recycler: RecyclerView.Recycler, + state: RecyclerView.State + ): Int { + if (!isScrolling) { + isScrolling = true + onStackListener?.onScrollStateChanged(true) + } + return scrollBy(dx, recycler, state) + } + + override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) { + detachAndScrapAttachedViews(recycler) + if (state.itemCount == 0) { + return + } + fill(recycler, state) + } + + /** + * 实际添加到RecyclerView中的卡片喂 visibleCount + 1 + * 最底下有一个完全透明的卡片:方便滑动时渐变出来 + */ + private fun fill(recycler: RecyclerView.Recycler, state: RecyclerView.State) { + currentPosition = _selectedPosition + var showCount = 0 + while (showCount <= visibleCount) { + layoutChunk(recycler, state, _selectedPosition + showCount) + showCount++ + } + if (state.itemCount > 0 && !isInit) { + isInit = true + onStackListener?.onSelectedPositionChanged(_selectedPosition) + } + + } + + private fun layoutChunk( + recycler: RecyclerView.Recycler, + state: RecyclerView.State, + layoutPosition: Int + ) { + + val child = next(recycler, state) + + // 添加到卡片底部 + addView(child, 0) + + measureChild(child, 0, 0) + + // 根据卡片位置重新计算卡片的宽度 + calculateChild(child) + + layoutChild(child, layoutPosition) + + layoutAnimations(child, layoutPosition) + } + + private fun calculateChild(child: View) { + childWidth = width - marginStart - marginEnd - visibleCount * offset + val layoutParams = child.layoutParams + layoutParams.width = childWidth + + // 高度也需要等比缩放,保证跟布局宽高比例不变 + layoutParams.height = (childWidth.toFloat() * ratioH / ratioW).toInt() + child.layoutParams = layoutParams + } + + private fun layoutChild(child: View, layoutPosition: Int) { + var left = marginStart + (layoutPosition - _selectedPosition) * offset + left -= if (layoutPosition == _selectedPosition) { + // 第一张卡片偏移量 + scrollOffset + } else { + (offset * scrollPercent).toInt() + } + val top = 0 + val end = left + child.layoutParams.width + val bottom = top + child.layoutParams.height + child.layout(left, top, end, bottom) + } + + /** + * 每相差一张卡片 透明度和缩放比例相差0.2 + */ + private fun layoutAnimations(child: View, layoutPosition: Int) { + layoutAnimation.layoutAnimations(child, layoutPosition, this) + } + + private fun scrollBy( + delta: Int, + recycler: RecyclerView.Recycler, + state: RecyclerView.State, + ): Int { + if (state.itemCount == 0 || delta == 0) { + return 0 + } + val lastScrollOffset = scrollX + scrollX += delta + when { + lastScrollOffset >= 0 && scrollX < 0 -> { + // 1.滚动距离由非负变成负 + _selectedPosition-- + if (_selectedPosition < 0) { + _selectedPosition = state.itemCount - 1 + } + } + + lastScrollOffset < 0 && scrollX >= 0 -> { + // 2.滚动距离由负变成非负 + _selectedPosition++ + if (_selectedPosition >= state.itemCount) { + _selectedPosition = 0 + } + } + + else -> { + // 滚动距离由0变成正,selectedPosition保持不变 + } + } + + scrollOffset = + if (scrollX >= 0) { + scrollX + } else { + marginStart + childWidth + scrollX + } + + if (scrollX > 0) { + // 向左滑动 + if (scrollX >= childWidth + marginStart) { + // 往左划过一个屏幕,当前卡片滚动结束 + scrollX = 0 + scrollOffset = 0 + _selectedPosition++ + if (_selectedPosition >= state.itemCount) { + _selectedPosition = 0 + } + onStackListener?.onSelectedPositionChanged(_selectedPosition) + } + } else { + // 向右滑动 + if (abs(scrollX) >= childWidth + marginStart) { + // 往右划过一个屏幕,当前卡片滚动结束 + scrollX = 0 + scrollOffset = 0 + onStackListener?.onSelectedPositionChanged(_selectedPosition) + } + } + detachAndScrapAttachedViews(recycler) + fill(recycler, state) + return delta + } + + private fun next(recycler: RecyclerView.Recycler, state: RecyclerView.State): View { + if (currentPosition >= state.itemCount) { + currentPosition = 0 + } + val view = recycler.getViewForPosition(currentPosition) + currentPosition++ + return view + } + + private var onStackListener: OnStackListener? = null + fun setOnItemSelectedListener(listener: OnStackListener) { + onStackListener = listener + } + + interface OnStackListener { + + fun onSelectedPositionChanged(position: Int) + + fun onScrollStateChanged(isScrolling: Boolean) + } +} \ No newline at end of file diff --git a/module_common/src/main/res/drawable-night/bg_divider_game_expand_item_custom.xml b/module_common/src/main/res/drawable-night/bg_divider_game_expand_item_custom.xml new file mode 100644 index 0000000000..aebc72ca24 --- /dev/null +++ b/module_common/src/main/res/drawable-night/bg_divider_game_expand_item_custom.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/module_common/src/main/res/drawable/bg_divider_game_expand_item_custom.xml b/module_common/src/main/res/drawable/bg_divider_game_expand_item_custom.xml new file mode 100644 index 0000000000..05534007cf --- /dev/null +++ b/module_common/src/main/res/drawable/bg_divider_game_expand_item_custom.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/module_common/src/main/res/layout/nested_scroll_data_exception.xml b/module_common/src/main/res/layout/nested_scroll_data_exception.xml new file mode 100644 index 0000000000..64e8737c7e --- /dev/null +++ b/module_common/src/main/res/layout/nested_scroll_data_exception.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/module_common/src/main/res/layout/nested_scroll_no_connection.xml b/module_common/src/main/res/layout/nested_scroll_no_connection.xml new file mode 100644 index 0000000000..53f7410cfe --- /dev/null +++ b/module_common/src/main/res/layout/nested_scroll_no_connection.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/module_common/src/main/res/layout/nested_scroll_none_data.xml b/module_common/src/main/res/layout/nested_scroll_none_data.xml new file mode 100644 index 0000000000..6aa0ffcc78 --- /dev/null +++ b/module_common/src/main/res/layout/nested_scroll_none_data.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/module_common/src/main/res/values-night/colors.xml b/module_common/src/main/res/values-night/colors.xml index 990ab55662..9659274c12 100644 --- a/module_common/src/main/res/values-night/colors.xml +++ b/module_common/src/main/res/values-night/colors.xml @@ -154,7 +154,7 @@ #FFB048 - #F7F7F7 + @color/ui_container_2 @color/white_alpha_20 @@ -257,7 +257,7 @@ @color/ui_background #F8F8F8 #b2b2b2 - #999999 + @color/text_tertiary @color/text_primary #3A3A3A #5d5d5d diff --git a/module_common/src/main/res/values/colors.xml b/module_common/src/main/res/values/colors.xml index 06fa46d8d6..148d7f86e4 100644 --- a/module_common/src/main/res/values/colors.xml +++ b/module_common/src/main/res/values/colors.xml @@ -166,7 +166,7 @@ #FFB048 - #F7F7F7 + @color/ui_container_2 @color/white_alpha_20 diff --git a/module_core/build.gradle b/module_core/build.gradle index 9bdf743a27..72cde089b4 100644 --- a/module_core/build.gradle +++ b/module_core/build.gradle @@ -61,6 +61,7 @@ dependencies { api "com.squareup.retrofit2:retrofit:${retrofit}" api "com.squareup.retrofit2:converter-gson:${retrofit}" // include gson 2.7 api "com.squareup.retrofit2:adapter-rxjava2:${retrofit}" + api "androidx.lifecycle:lifecycle-runtime-ktx:$lifeCycle" api "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifeCycle" api "androidx.lifecycle:lifecycle-livedata-ktx:$lifeCycle" api "androidx.lifecycle:lifecycle-common-java8:$lifeCycle" diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IConfigProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IConfigProvider.kt index 8de1b409d4..75887e3f0c 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/provider/IConfigProvider.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IConfigProvider.kt @@ -26,5 +26,4 @@ interface IConfigProvider : IProvider { fun getQuickLoginAppKey(): String fun getWeiboAppKey(): String fun getNightModeSetting(): Boolean - fun isShowPlugin(gameId: String):Boolean } \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IFloatingWindowProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IFloatingWindowProvider.kt index bfab9258ee..441cfbb34d 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/provider/IFloatingWindowProvider.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IFloatingWindowProvider.kt @@ -11,6 +11,7 @@ interface IFloatingWindowProvider : IProvider { fun getAndShowFloatingWindow( gameId: String, gameName: String, + location: String, fragment: Fragment, recyclerView: RecyclerView, welcomeDialogCallback: ((entity: T) -> Unit)? = null diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IGameSubstituteRepositoryProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IGameSubstituteRepositoryProvider.kt deleted file mode 100644 index d3badb765f..0000000000 --- a/module_core/src/main/java/com/gh/gamecenter/core/provider/IGameSubstituteRepositoryProvider.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.gh.gamecenter.core.provider - -import com.alibaba.android.arouter.facade.template.IProvider - -interface IGameSubstituteRepositoryProvider : IProvider { - - fun updateHistoricInstalledGameSet() - -} \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IMainProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IMainProvider.kt index 867a78739c..f41d4d6a83 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/provider/IMainProvider.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IMainProvider.kt @@ -1,9 +1,8 @@ package com.gh.gamecenter.core.provider import android.content.Context -import android.content.Intent import com.alibaba.android.arouter.facade.template.IProvider interface IMainProvider : IProvider { - fun skipToMainActivity(context: Context, position: Int) + fun skipToMainActivity(context: Context, type: String) } \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IUpdateManagerProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IUpdateManagerProvider.kt index ea1b18fe78..2c3c6a3ab1 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/provider/IUpdateManagerProvider.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IUpdateManagerProvider.kt @@ -1,9 +1,8 @@ package com.gh.gamecenter.core.provider -import android.content.Context -import android.os.Handler +import androidx.fragment.app.FragmentActivity import com.alibaba.android.arouter.facade.template.IProvider interface IUpdateManagerProvider: IProvider { - fun checkUpdate(context: Context, isAutoCheck: Boolean, handler: Handler?) + fun checkUpdate(activity: FragmentActivity, ignoreSuppressOption: Boolean) } \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/utils/GsonUtils.kt b/module_core/src/main/java/com/gh/gamecenter/core/utils/GsonUtils.kt index 123a2bbbf5..aa5de7f22a 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/utils/GsonUtils.kt +++ b/module_core/src/main/java/com/gh/gamecenter/core/utils/GsonUtils.kt @@ -4,6 +4,7 @@ import com.google.gson.Gson import com.google.gson.GsonBuilder import com.google.gson.reflect.TypeToken import org.json.JSONArray +import org.json.JSONObject /** @@ -51,4 +52,18 @@ object GsonUtils { fun toJsonIgnoreNull(any: Any?): String { return gsonThatIgnoreNull.toJson(any) } + + @JvmStatic + fun isJSONValid(json: String): Boolean { + try { + JSONObject(json) + } catch (e: Exception) { + try { + JSONArray(json) + } catch (e: Exception) { + return false + } + } + return true + } } \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AuthDialogEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AuthDialogEntity.kt deleted file mode 100644 index 872cae9593..0000000000 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AuthDialogEntity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.gh.gamecenter.feature.entity - -import android.os.Parcelable -import com.google.gson.annotations.SerializedName -import kotlinx.parcelize.Parcelize - -@Parcelize -data class AuthDialogEntity( - @SerializedName("_id") - var id: String = "", - var link: String = "", - @SerializedName("game_category") - var gameCategory: String = "",//online (网络游戏) , local (单机游戏), welfare (福利游戏) - var level: String = ""//MUST_PASS:提示且认证通过 ALWAYS_HINT:仅提示且不可关闭 OPTIONAL_HINT:仅提示且可关闭 -) : Parcelable - -enum class AuthDialogLevel(val value: String) { - MUST_PASS("MUST_PASS"), - ALWAYS_HINT("ALWAYS_HINT"), - OPTIONAL_HINT("OPTIONAL_HINT"), -} \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/CustomPageTrackData.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/CustomPageTrackData.kt new file mode 100644 index 0000000000..9b73ce6615 --- /dev/null +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/CustomPageTrackData.kt @@ -0,0 +1,41 @@ +package com.gh.gamecenter.feature.entity + +import android.os.Parcelable +import com.gh.gamecenter.common.utils.SensorsBridge +import kotlinx.parcelize.Parcelize + +@Parcelize +data class PageLocation( + val bottomTab: String = "", + val severalTabPageName: String = "", + val severalTabPageId: String = "", + val tabPosition: Int = 0, + val tabContent: String = "", + val pageName: String = "", + val pageId: String = "" +) : Parcelable + +@Parcelize +data class CustomPageTrackData( + val pageLocation: PageLocation, + val moduleType: String = "", + val modulePattern: String = "", + val linkContentName: String = "", + val linkContentId: String = "" +) : Parcelable { + + fun toKV() = + arrayOf( + SensorsBridge.KEY_BOTTOM_TAB, pageLocation.bottomTab, + SensorsBridge.KEY_MULTI_TAB_NAME, pageLocation.severalTabPageName, + SensorsBridge.KEY_MULTI_TAB_ID, pageLocation.severalTabPageId, + SensorsBridge.KEY_POSITION, "${pageLocation.tabPosition}", + SensorsBridge.KEY_TAB_CONTENT, pageLocation.tabContent, + SensorsBridge.KEY_CUSTOM_PAGE_NAME, pageLocation.pageName, + SensorsBridge.KEY_CUSTOM_PAGE_ID, pageLocation.pageId, + SensorsBridge.KEY_MODULE_TYPE, moduleType, + SensorsBridge.KEY_MODULE_PATTERN, modulePattern, + SensorsBridge.KEY_LINK_CONTENT_NAME, linkContentName, + SensorsBridge.KEY_LINK_CONTENT_ID, linkContentId + ) +} \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt index 5aa6b37a16..d9d0377aa4 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt @@ -22,6 +22,9 @@ import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize import java.util.* +/** + * 关联的后端数据库结构说明 ... + */ @Parcelize data class GameEntity( @SerializedName("_id") @@ -30,15 +33,15 @@ data class GameEntity( private var mIcon: String? = null, @SerializedName("ori_icon") private var mRawIcon: String? = null, + @SerializedName("icon_subscript") + var mIconSubscript: String? = null, @SerializedName("name") - private var mName: String? = null, // 将 name 变成私有 mName 调用 name 时直接拼接成 mName + nameSuffix - @SerializedName("name_suffix") - var nameSuffix: String = "", + private var mName: String? = null, @SerializedName("subtitle") - private var mSubtitle: String = "", + private var mSubtitle: String = "", // 副标题 @SerializedName("subtitle_style") private var mSubtitleStyle: TagStyleEntity? = null, - @SerializedName("advance_download") + @SerializedName("advance_download") // 预下载 https://git.shanqu.cc/pm/halo/halo-app-issues/-/issues/1839 private var mAdvanceDownload: Boolean = false, @SerializedName("brief") private var mBrief: String? = null, @@ -48,19 +51,15 @@ data class GameEntity( @SerializedName("appointment_button") var reserveStatus: String = "", // 预约状态供用户预约列表使用,值存在的话,有两种值 -> appointment(已预约), download(已上线) - // 插件标签 - private var tag: ArrayList? = null, private var apk: ArrayList? = null, - @SerializedName("apk_normal") - private var apkNormal: ArrayList? = null, @SerializedName("apk_search") - var apkSearch: ArrayList? = null, - + private var mApkSearch: ArrayList? = null, @SerializedName("apk_index") - var apkIndex: ArrayList? = null, + private var mApkIndex: ArrayList? = null, + // 多版本相关的东西 https://git.shanqu.cc/pm/halo/halo-app-issues/-/issues/819#note_43755 var collection: ArrayList = ArrayList(), - var slide: String? = null, + // 开测表相关的东西 var test: TestEntity? = null, @SerializedName("d_button_add_word") @@ -68,13 +67,16 @@ data class GameEntity( var image: String? = null, var type: String? = null, - var isPluggable: Boolean = false, + var isPluggable: Boolean = false, // 是否是在插件化区域中 + + // 通用跳转相关的东西 var link: String? = null, var text: String? = "", @SerializedName("link_community", alternate = ["community"]) var community: CommunityEntity? = CommunityEntity(), var display: Display? = null, + // 下载弹窗相关的东西 @SerializedName("apk_link") var apkLink: ArrayList? = null, @@ -90,12 +92,6 @@ data class GameEntity( @SerializedName("download_off_dialog") private var mDownloadOffDialog: Dialog? = null, - @SerializedName("libao_exists") - var isLibaoExists: Boolean = false, - - @SerializedName("server_remark") - var serverRemark: String? = null, - @SerializedName("server_type") var serverType: String? = null, @@ -105,9 +101,6 @@ data class GameEntity( @SerializedName("server") var serverEntity: ServerCalendarEntity? = null, - @SerializedName("server_genre") - var serverGenre: String? = null, - @SerializedName("remaining_server") var serverRemaining: List? = null, @@ -130,20 +123,6 @@ data class GameEntity( @SerializedName(value = "new_tag_style") private var mTagStyle: ArrayList = ArrayList(), var des: String? = null, - // 用来标记在专题中的序号,仅用于曝光记录 - var sequence: Int? = -1, - // 用来标记在外部页面中的序号,仅用于曝光记录,具体细节可见 https://git.ghzs.com/pm/halo-app-issues/-/issues/1087 - var outerSequence: Int? = -1, - // 用来标记平台名称,仅用于曝光记录 - var platform: String? = null, - - // 用来标记下载的类型,仅用于曝光记录 - @SerializedName("download_type") - var downloadType: String? = "", - - // 用来标记下载完成的类型,仅用于曝光记录 - @SerializedName("download_complete_type") - var downloadCompleteType: String? = "", // 用于开服表标记是否为置顶项目 @SerializedName("is_fixed_top") @@ -160,11 +139,6 @@ data class GameEntity( @SerializedName("download_dialog") var downloadDialog: ArrayList

? = arrayListOf(), - var auth: Auth? = null, - - @SerializedName("auth_dialog") - var authDialog: AuthDialogEntity? = null, - @SerializedName("is_related") var isRelated: Boolean = false, @@ -191,18 +165,9 @@ data class GameEntity( //首页设置 @SerializedName("home_setting") val homeSetting: HomeSetting = HomeSetting(), - var linkType: String = "", @SerializedName("index_plugin") var indexPlugin: String = "open", - // 游戏版本 (本地字段),供下载统计使用 - var gameVersion: String = "", - - // icon 角标 - @SerializedName("icon_subscript") - var mIconSubscript: String? = null, - - @SerializedName("info") var info: GameInfo? = null, @SerializedName("permission_dialog_status") @@ -228,10 +193,8 @@ data class GameEntity( // 是否支持不安装直接评论 @SerializedName("direct_comment") var directComment: Boolean = false, - // xxx人想玩 @SerializedName("h5_link") private var mH5Link: LinkEntity? = null, - val visit: Int = 0, @SerializedName("played_time") var playedTime: Long = 0, @SerializedName("played_game_id") @@ -246,7 +209,7 @@ data class GameEntity( var pluginDesc: String = "", //插件功能描述 var pluggableCollection: GameCollectionEntity? = null, //插件化包所在的合集 @SerializedName("assign_remark") - val assignRemark: AssignRemark = AssignRemark(), + private val _assignRemark: AssignRemark? = null, // 特殊样式显示的配置 @SerializedName("zone_setting") var zone: ZoneEntity = ZoneEntity(), @SerializedName("comment_description") @@ -293,19 +256,6 @@ data class GameEntity( @SerializedName("column_image") var columnImage: String = "", - // 本地字段,使用镜像信息 - var useMirrorInfo: Boolean = false, - - // 本地字段,最后打开时间 - var lastPlayedTime: Long = 0, - - // 本地字段,曝光用 - var displayContent: String = "", - var isPlatformRecommend: Boolean = false, - // 从启动弹窗跳转到对应游戏列表时候记录的启动弹窗数据 (ugly ugly ugly) - var welcomeDialogId: String? = null, - var welcomeDialogTitle: String? = null, - @SerializedName("google_status") var gAppsSwitch: String? = "", // 是否需要谷歌框架,目前只用在畅玩游戏里使用 @@ -330,6 +280,54 @@ data class GameEntity( @SerializedName("mirror_server") val mirrorServer: GameDetailServer? = null, + // 消息ID,用于首页求加速上架提醒弹窗 + @SerializedName("message_private_id") + var messageId: String = "", + + // 游戏ID,仅用于QQ小游戏 + @SerializedName("appid") + var qqMiniGameAppId: String = "", + // 状态(1:下架 2:秒玩),仅用于QQ小游戏 + @SerializedName("app_status") + var qqMiniGameAppStatus: Int = 0, + @SerializedName("first_setting") + var firstSetting: FirstSetting? = null, + @SerializedName("bubble_text") + var bubbleText: String = "", + + // 本地字段 + // 用来标记在专题中的序号,仅用于曝光记录 + var sequence: Int? = -1, + // 用来标记在外部页面中的序号,仅用于曝光记录,具体细节可见 https://git.ghzs.com/pm/halo-app-issues/-/issues/1087 + var outerSequence: Int? = -1, + // 用来标记平台名称,仅用于曝光记录 + var platform: String? = null, + + // 用来标记下载的类型,仅用于曝光记录 + @SerializedName("download_type") + var downloadType: String? = "", + + // 用来标记下载完成的类型,仅用于曝光记录 + @SerializedName("download_complete_type") + var downloadCompleteType: String? = "", + + // 游戏版本 (本地字段),供下载统计使用 + var gameVersion: String = "", + + // 本地字段,使用镜像信息 + var useMirrorInfo: Boolean = false, + + // 本地字段,最后打开时间 + var lastPlayedTime: Long = 0, + + // 本地字段,曝光用 + @SerializedName("display_content") + var displayContent: String = "", + var isPlatformRecommend: Boolean = false, + // 从启动弹窗跳转到对应游戏列表时候记录的启动弹窗数据 (ugly ugly ugly) + var welcomeDialogId: String? = null, + var welcomeDialogTitle: String? = null, + // 专题id,用于曝光使用 var subjectId: String? = null, // 专题名字,用于曝光使用 @@ -351,34 +349,6 @@ data class GameEntity( var gameAdSourceId: String = "", //游戏详情大家都在玩推荐类型:标签推荐/下载推荐/安装推荐,用于曝光使用 var recommendType: String = "", - - // 消息ID,用于首页求加速上架提醒弹窗 - @SerializedName("message_private_id") - var messageId: String = "", - - // 游戏ID,仅用于QQ小游戏 - @SerializedName("appid") - var qqMiniGameAppId: String = "", - // 状态(1:下架 2:秒玩),仅用于QQ小游戏 - @SerializedName("app_status") - var qqMiniGameAppStatus: Int = 0, - // 支持base_lib依赖库的最低版本,仅用于QQ小游戏 - @SerializedName("baselibMiniVersion") - var qqMiniGameBaselibMiniVersion: String = "", - // 是否支持内购,仅用于QQ小游戏 - @SerializedName("paymentState") - var qqMiniGamePaymentState: Boolean = false, - // 是否支持广告,仅用于QQ小游戏 - @SerializedName("adState") - var qqMiniGameAdState: Boolean = false, - // 应用支持平台(0:android单端,1:android&ios双端),仅用于QQ小游戏 - @SerializedName("appPlatform") - var qqMiniGamePlatform: Int = 0, - // 客户端打开小游戏需要的链接,仅用于QQ小游戏 - @SerializedName("appidLink") - var qqMiniGameAppIdLink: String = "", - @SerializedName("first_setting") - var firstSetting: FirstSetting? = null ) : Parcelable { constructor(id: String?) : this() { @@ -390,6 +360,15 @@ data class GameEntity( this.mName = name } + // 自定义页面位置信息,用于埋点 + var customPageTrackData: CustomPageTrackData? = null + + // 临时变量 + var hasBubbleShowed = false + + val assignRemark: AssignRemark + get() = _assignRemark ?: AssignRemark() + @IgnoredOnParcel private var entryMap: androidx.collection.ArrayMap? = androidx.collection.ArrayMap() @@ -407,9 +386,6 @@ data class GameEntity( @IgnoredOnParcel var briefStyle: String? = "" // 本地处理得到的简介显示配置 - @IgnoredOnParcel - var shouldShowNameSuffix = true // 专题级开关,只要这个开关为 false,实体有 suffix 也不显示 - @IgnoredOnParcel private var mValidApkList: ArrayList? = null // 有效的 apk 列表,避免重复调用 @@ -425,10 +401,9 @@ data class GameEntity( @IgnoredOnParcel var name: String? get() = if (shouldUseMirrorInfo()) { - obtainMirrorData()?.mName?.removeSuffix(".") + if (shouldShowNameSuffix) obtainMirrorData()?.nameSuffix - ?: "" else "" + obtainMirrorData()?.mName?.removeSuffix(".") } else { - mName?.removeSuffix(".") + if (shouldShowNameSuffix) nameSuffix else "" + mName?.removeSuffix(".") } set(value) { mName = value @@ -472,12 +447,6 @@ data class GameEntity( mDownloadOffDialog = value } - @IgnoredOnParcel - val nameWithoutSuffix: String? - get() = if (shouldUseMirrorInfo()) obtainMirrorData()?.mName?.removeSuffix(".") else mName?.removeSuffix( - "." - ) - @IgnoredOnParcel var isReservable: Boolean get() = if (shouldUseMirrorInfo()) obtainMirrorData()?.mReservable ?: false else mReservable @@ -660,30 +629,15 @@ data class GameEntity( welcomeDialogTitle = appProvider?.get(Constants.WELCOME_DIALOG_LINK_TITLE, false) as? String } - fun getTag(): ArrayList { - if (tag == null) tag = ArrayList() - val configProvider = ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider - if (configProvider?.isShowPlugin(id) == false) return ArrayList() - return tag!! - } - - fun setTag(tag: ArrayList?) { - this.tag = tag - } - fun getApk(): ArrayList { if (mValidApkList == null || shouldApkReCheckMirrorInfo()) { if (shouldUseMirrorInfo()) return obtainMirrorData()?.getApk() ?: arrayListOf() var rawApk = apk ?: ArrayList() - val configProvider = - ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider - if (configProvider?.isShowPlugin(id) == false) { - rawApk = getApkNormal() - } else if (gameLocation == GameLocation.INDEX && apkIndex != null) { - rawApk = apkIndex!! - } else if (gameLocation == GameLocation.SEARCH && apkSearch != null) { - rawApk = apkSearch!! + if (gameLocation == GameLocation.INDEX && mApkIndex != null) { + rawApk = mApkIndex!! + } else if (gameLocation == GameLocation.SEARCH && mApkSearch != null) { + rawApk = mApkSearch!! } mValidApkList = rawApk @@ -714,24 +668,12 @@ data class GameEntity( this.apk = apk } - fun getApkNormal(): ArrayList { - if (apkNormal == null) { - apkNormal = ArrayList() - } - return apkNormal!! - } - - fun setApkNormal(apkNormal: ArrayList?) { - this.apkNormal = apkNormal - } - /** - * 丢弃 apk 以外的 apkNormal、apkIndex、apkSearch、apkLink + * 丢弃 apk 以外的 apkIndex、apkSearch、apkLink */ fun dropOtherApk() { - apkNormal = arrayListOf() - apkIndex = arrayListOf() - apkSearch = arrayListOf() + mApkIndex = arrayListOf() + mApkSearch = arrayListOf() apkLink = arrayListOf() mValidApkList = null } @@ -948,12 +890,6 @@ data class GameEntity( return apkList[0].format == Constants.XAPK_APKS_FORMAT } - fun isNonSplitXApk(): Boolean { - val apkList = getApk() - if (apkList.isEmpty()) return false - return apkList[0].format == Constants.XAPK_FORMAT - } - fun isUseDefaultServerStyle(): Boolean { return if (serverLabel == null) { false @@ -1009,7 +945,6 @@ data class GameEntity( val simpleGame = SimpleGame() simpleGame.id = id simpleGame.mName = mName - simpleGame.nameSuffix = nameSuffix simpleGame.mIcon = mIcon simpleGame.mRawIcon = mRawIcon simpleGame.active = active @@ -1028,17 +963,18 @@ data class GameEntity( entity.iconSubscript = iconSubscript entity.name = name entity.tagStyle = tagStyle - entity.isLibaoExist = isLibaoExists - entity.tag = tag entity.simulatorType = simulatorType entity.simulator = simulator entity.category = category ?: "" entity.isRecentlyPlayed = isRecentlyPlayed entity.apk = getApk() - entity.apkNormal = getApkNormal() return entity } + val showImage: Boolean + get() = DISPLAY_CONTENT_IMAGE.equals(displayContent, true) && + homeSetting.image.isNotBlank() + companion object { const val TAG: String = "GameEntity" const val CHECK_MIRROR_INFO_INTERVAL = 10 * 1000 // 检查镜像更新间隔 10s @@ -1047,6 +983,8 @@ data class GameEntity( const val GAME_DOWNLOAD_BUTTON_MODE_DOWNLOAD = 1 const val GAME_DOWNLOAD_BUTTON_MODE_VGAME = 2 const val GAME_DOWNLOAD_BUTTON_MODE_DUAL = 3 + + private const val DISPLAY_CONTENT_IMAGE = "image" } @Parcelize @@ -1141,15 +1079,6 @@ data class GameEntity( val endTime: Long = 0 ) : Parcelable - @Parcelize - class Auth( - val switch: String? = null, // 开关 - @SerializedName("time_start") - val timeStart: String? = "", // 开始时间 - @SerializedName("time_end") - val timeEnd: String? = "" // 结束时间 - ) : Parcelable - @Parcelize data class ContentTag( val custom: List = listOf(), @@ -1193,8 +1122,6 @@ class SimpleGame( var id: String? = null, @SerializedName("name") var mName: String? = "", - @SerializedName("name_suffix") - var nameSuffix: String? = null, @SerializedName("icon") var mIcon: String? = null, @SerializedName("ori_icon") @@ -1242,7 +1169,7 @@ class SimpleGame( @IgnoredOnParcel val name: String? - get() = mName?.removeSuffix(".") + (nameSuffix ?: "") + get() = mName?.removeSuffix(".") fun getIcon(): String { return mRawIcon ?: mIcon ?: "" @@ -1264,7 +1191,6 @@ class SimpleGame( fun toGameEntity(): GameEntity { val gameEntity = GameEntity(id = id, name = name) - gameEntity.nameSuffix = nameSuffix ?: "" gameEntity.icon = mIcon gameEntity.rawIcon = mRawIcon gameEntity.iconSubscript = iconSubscript @@ -1285,7 +1211,6 @@ class SimpleGame( fun toCommunityGameEntity(): CommunityEntity.CommunityGameEntity { val gameEntity = CommunityEntity.CommunityGameEntity(id = id, mName = name) - gameEntity.nameSuffix = nameSuffix ?: "" gameEntity.mIcon = mIcon gameEntity.mRawIcon = mRawIcon gameEntity.iconSubscript = iconSubscript diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameSubjectData.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameSubjectData.kt index 43d02f59ed..c98fb39d63 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameSubjectData.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameSubjectData.kt @@ -11,5 +11,4 @@ data class GameSubjectData( val position: Int? = 0, val isOrder: Boolean, val briefStyle: String = "", - val isShowSuffix: Boolean = true ) : Parcelable 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 c11d809203..a5ae1b3c19 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 @@ -1,23 +1,17 @@ package com.gh.gamecenter.feature.entity -import android.preference.PreferenceManager -import com.gh.gamecenter.common.constant.Config import com.gh.gamecenter.common.entity.CommunityEntity import com.gh.gamecenter.common.entity.Display import com.gh.gamecenter.common.entity.LinkEntity -import com.gh.gamecenter.feature.HaloApp import com.gh.gamecenter.feature.exposure.ExposureEvent import com.google.gson.annotations.SerializedName class SettingsEntity( - var download: List? = listOf(), var suggestion: Suggestion? = null, var search: Search? = null, var community: CommunityEntity? = null, var image: Image? = null, var support: Support? = null, - @SerializedName("community_entrance") - private var communityEntrance: String? = "", @SerializedName("game_comment_blacklist") var gameCommentBlackList: List? = listOf(), @SerializedName("game_download_blacklist") @@ -38,34 +32,6 @@ class SettingsEntity( var webviewDownloadUrl: String = "",//webview下载地址 ) { - fun showCommunityEntrance(): Boolean { - val preferences = - PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance()) - val isFixCommunity = - preferences.getBoolean(Config.FIX_COMMUNITY_KEY, false) - if (isFixCommunity) return true - - if ("on" == communityEntrance) { - preferences.edit().putBoolean(Config.FIX_COMMUNITY_KEY, true).apply() - return true - } - return false - } - - fun showArticleEntrance(): Boolean { - val preferences = - PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance()) - val isFixArticle = - preferences.getBoolean(Config.FIX_ARTICLE_KEY, false) - if (isFixArticle) return true - - if ("on" == articleEntrance) { - preferences.edit().putBoolean(Config.FIX_ARTICLE_KEY, true).apply() - return true - } - return false - } - class Support( var qq: String? = "", @SerializedName("qq-group") @@ -195,8 +161,6 @@ class SettingsEntity( var id: String, var name: String, val icon: String, - @SerializedName("name_suffix") - var nameSuffix: String? = null, @SerializedName("ori_icon") var rawIcon: String? = null, @SerializedName("icon_subscript") diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/SimulatorGameRecordEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/SimulatorGameRecordEntity.kt index 24e58cc85d..bc3ac97762 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/SimulatorGameRecordEntity.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/SimulatorGameRecordEntity.kt @@ -39,14 +39,11 @@ data class SimulatorGameRecordEntity( gameEntity.iconSubscript = iconSubscript gameEntity.name = name gameEntity.tagStyle = tagStyle - gameEntity.isLibaoExists = isLibaoExist - gameEntity.setTag(tag) gameEntity.simulatorType = simulatorType gameEntity.simulator = simulator gameEntity.category = category gameEntity.isRecentlyPlayed = isRecentlyPlayed gameEntity.setApk(apk) - gameEntity.setApkNormal(apkNormal) return gameEntity } } \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/WelcomeDialogEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/WelcomeDialogEntity.kt index c08ec4efff..2e30b96830 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/WelcomeDialogEntity.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/WelcomeDialogEntity.kt @@ -1,6 +1,7 @@ package com.gh.gamecenter.feature.entity import com.gh.gamecenter.common.entity.LinkEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent import com.google.gson.annotations.SerializedName import kotlinx.parcelize.Parcelize @@ -13,5 +14,6 @@ class WelcomeDialogEntity( @SerializedName("suspended_window_id") var floatingWindowId: String? = "", // 是否需要显示退出动画,本地字段 - var shouldShowExitAnimation: Boolean = false + var shouldShowExitAnimation: Boolean = false, + var traceEvent: ExposureEvent? = null ) : LinkEntity() \ No newline at end of file diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/game/GameItemViewHolder.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/game/GameItemViewHolder.kt index 69cd10c03e..4c57ca9098 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/game/GameItemViewHolder.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/game/GameItemViewHolder.kt @@ -27,7 +27,6 @@ class GameItemViewHolder(var binding: GameItemBinding) : BaseRecyclerViewHolder< fun bindGameItem( entity: GameEntity, - isShowSuffix: Boolean = true, briefStyle: String = "", isShowPlatform: Boolean = false, forceShowSubtitle: Boolean = false @@ -42,7 +41,7 @@ class GameItemViewHolder(var binding: GameItemBinding) : BaseRecyclerViewHolder< gameIconView.displayGameIcon(entity) val provider = ARouter.getInstance().build(RouteConsts.provider.bindingAdapters).navigation() as? IBindingAdaptersProvider - provider?.setGameName(gameName, entity, isShowPlatform, isShowSuffix) + provider?.setGameName(gameName, entity, isShowPlatform) provider?.setGameTags(labelList, entity) gameRating.textSize = if (entity.commentCount > 3) 12F else 10F gameRating.setDrawableStart(if (entity.commentCount > 3) R.drawable.game_horizontal_rating.toDrawable() else null) diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/provider/IBindingAdaptersProvider.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/provider/IBindingAdaptersProvider.kt index 74fec17697..4ce0ae5506 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/provider/IBindingAdaptersProvider.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/provider/IBindingAdaptersProvider.kt @@ -8,7 +8,7 @@ import com.gh.gamecenter.feature.entity.GameEntity interface IBindingAdaptersProvider : IProvider { - fun setGameName(view: TextView, game: GameEntity, isShowPlatform: Boolean, isShowSuffix: Boolean) + fun setGameName(view: TextView, game: GameEntity, isShowPlatform: Boolean) fun setGameTags(layout: LinearLayout, gameEntity: GameEntity) diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/view/GameIconView.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/view/GameIconView.kt index ae4d214569..5d49a1ba45 100644 --- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/view/GameIconView.kt +++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/view/GameIconView.kt @@ -161,6 +161,8 @@ class GameIconView : FrameLayout { mTopLabelTv?.text = iconFloat.upperLeftText mTopLabelTv?.setBackgroundResource(if (iconFloat.upperLeftColor == "blue") R.drawable.label_upperleft_blue else R.drawable.label_upperleft_red) mTopLabelTv?.setPadding(dip(6), dip(2), dip(6), 0) + }else{ + mTopLabelContainer?.visibility = View.GONE } if (iconFloat.bottomText.isNotEmpty() && !hideBottomLabel) { mBottomLabelTv?.visibility = View.VISIBLE diff --git a/module_feedback/src/feedback/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt b/module_feedback/src/feedback/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt index 0a82c85ae3..99828882c6 100644 --- a/module_feedback/src/feedback/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt +++ b/module_feedback/src/feedback/java/com/gh/gamecenter/feedback/provider/ConfigProviderImpl.kt @@ -101,10 +101,6 @@ class ConfigProviderImpl : IConfigProvider { return false } - override fun isShowPlugin(gameId: String): Boolean { - return false - } - override fun init(context: Context?) { // Do nothing } diff --git a/module_login/src/main/java/com/gh/gamecenter/login/user/UserRepository.java b/module_login/src/main/java/com/gh/gamecenter/login/user/UserRepository.java index 07b3b97d23..5da555da36 100644 --- a/module_login/src/main/java/com/gh/gamecenter/login/user/UserRepository.java +++ b/module_login/src/main/java/com/gh/gamecenter/login/user/UserRepository.java @@ -30,7 +30,6 @@ import com.gh.gamecenter.core.provider.IAppProvider; import com.gh.gamecenter.core.provider.IDataUtilsProvider; import com.gh.gamecenter.core.provider.IDownloadManagerProvider; import com.gh.gamecenter.core.provider.IErrorHelperProvider; -import com.gh.gamecenter.core.provider.IGameSubstituteRepositoryProvider; import com.gh.gamecenter.core.provider.IReservationRepositoryProvider; import com.gh.gamecenter.core.provider.IWechatBindHelperProvider; import com.gh.gamecenter.core.utils.GsonUtils; @@ -149,11 +148,6 @@ public class UserRepository { reservationRepository.clearReservations(); } - IGameSubstituteRepositoryProvider gameSubstituteRepository = (IGameSubstituteRepositoryProvider) ARouter.getInstance().build(RouteConsts.provider.gameSubstituteRepository).navigation(); - if (gameSubstituteRepository != null) { - gameSubstituteRepository.updateHistoricInstalledGameSet(); - } - // 通知页面更新 EventBus.getDefault().post(new EBReuse(LOGOUT_TAG)); @@ -242,11 +236,6 @@ public class UserRepository { response.setId(finalUserToken); userTokenHandle(response, loginTag); - - IGameSubstituteRepositoryProvider gameSubstituteRepository = (IGameSubstituteRepositoryProvider) ARouter.getInstance().build(RouteConsts.provider.gameSubstituteRepository).navigation(); - if (gameSubstituteRepository != null) { - gameSubstituteRepository.updateHistoricInstalledGameSet(); - } IWechatBindHelperProvider wechatBindHelper = (IWechatBindHelperProvider) ARouter.getInstance().build(RouteConsts.provider.wechatHelper).navigation(); if (wechatBindHelper != null) { wechatBindHelper.getWechatConfig(null); diff --git a/module_message/src/main/java/com/gh/gamecenter/message/view/MessageWrapperActivity.kt b/module_message/src/main/java/com/gh/gamecenter/message/view/MessageWrapperActivity.kt index ef567cbc75..654ee8aecf 100644 --- a/module_message/src/main/java/com/gh/gamecenter/message/view/MessageWrapperActivity.kt +++ b/module_message/src/main/java/com/gh/gamecenter/message/view/MessageWrapperActivity.kt @@ -13,6 +13,7 @@ import androidx.fragment.app.FragmentStatePagerAdapter import androidx.viewpager.widget.PagerAdapter import com.alibaba.android.arouter.facade.annotation.Route import com.gh.gamecenter.common.base.activity.BaseActivity_TabLayout +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.utils.* @@ -112,7 +113,7 @@ class MessageWrapperActivity : BaseActivity_TabLayout() { override fun provideTabView(position: Int, tabTitle: String?): View { val tabBinding = MessageTabItemBinding.inflate(LayoutInflater.from(this)) tabBinding.run { - hintTv.typeface = Typeface.createFromAsset(assets, "fonts/d_din_bold_only_number.ttf") + hintTv.typeface = Typeface.createFromAsset(assets, Constants.DIN_FONT_PATH) titleTv.text = tabTitle } return tabBinding.root diff --git a/module_message/src/main/java/com/gh/gamecenter/message/view/message/SortedMessageListAdapter.kt b/module_message/src/main/java/com/gh/gamecenter/message/view/message/SortedMessageListAdapter.kt index 375277a2c7..fa84c54e3f 100644 --- a/module_message/src/main/java/com/gh/gamecenter/message/view/message/SortedMessageListAdapter.kt +++ b/module_message/src/main/java/com/gh/gamecenter/message/view/message/SortedMessageListAdapter.kt @@ -13,6 +13,7 @@ import com.alibaba.android.arouter.launcher.ARouter import com.gh.gamecenter.common.base.BaseRecyclerViewHolder import com.gh.gamecenter.common.base.activity.BaseActivity import com.gh.gamecenter.common.baselist.ListAdapter +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.ItemViewType import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.utils.* @@ -90,7 +91,7 @@ class SortedMessageListAdapter( contentTv.text = entity.latestContent?.text hintTv.isVisible = entity.setting.doNotDisturb == false && entity.unreadCount > 0 hintTv.text = if (entity.unreadCount < 100) entity.unreadCount.toString() else "99+" - hintTv.typeface = Typeface.createFromAsset(mContext.assets, "fonts/d_din_bold_only_number.ttf") + hintTv.typeface = Typeface.createFromAsset(mContext.assets, Constants.DIN_FONT_PATH) ovalHint.isVisible = entity.setting.doNotDisturb == true && entity.unreadCount > 0 ConstraintSet().apply { clone(root) diff --git a/module_message/src/message/java/com/gh/gamecenter/message/provider/ConfigProviderImpl.kt b/module_message/src/message/java/com/gh/gamecenter/message/provider/ConfigProviderImpl.kt index 5289bcf647..6fd0a19df3 100644 --- a/module_message/src/message/java/com/gh/gamecenter/message/provider/ConfigProviderImpl.kt +++ b/module_message/src/message/java/com/gh/gamecenter/message/provider/ConfigProviderImpl.kt @@ -100,10 +100,6 @@ class ConfigProviderImpl : IConfigProvider { return false } - override fun isShowPlugin(gameId: String): Boolean { - return false - } - override fun init(context: Context?) { // Do nothing } diff --git a/module_setting/src/main/java/com/gh/gamecenter/setting/view/AboutFragment.kt b/module_setting/src/main/java/com/gh/gamecenter/setting/view/AboutFragment.kt index e80ee4d17b..37037f2624 100644 --- a/module_setting/src/main/java/com/gh/gamecenter/setting/view/AboutFragment.kt +++ b/module_setting/src/main/java/com/gh/gamecenter/setting/view/AboutFragment.kt @@ -56,7 +56,7 @@ class AboutFragment : ToolbarFragment() { val updateManager = ARouter.getInstance().build(RouteConsts.provider.updateManager).navigation() as? IUpdateManagerProvider - updateManager?.checkUpdate(requireContext(), false, mBaseHandler) // 检查更新 + updateManager?.checkUpdate(requireActivity(), true) // 检查更新 mBinding.aboutGhIcon.setOnLongClickListener { MtaHelper.onEvent("我的光环_设置", "关于光环", "图标长按") @@ -124,7 +124,7 @@ class AboutFragment : ToolbarFragment() { root.setOnClickListener { MtaHelper.onEvent("我的光环_设置", "关于光环", "版本更新") val updateManager = ARouter.getInstance().build(RouteConsts.provider.updateManager).navigation() as? IUpdateManagerProvider - updateManager?.checkUpdate(requireContext(), false, mBaseHandler) // 检查更新 + updateManager?.checkUpdate(requireActivity(), true) // 检查更新 } } mBinding.userProtocolItem.run { diff --git a/module_setting/src/main/java/com/gh/gamecenter/setting/view/SettingsFragment.kt b/module_setting/src/main/java/com/gh/gamecenter/setting/view/SettingsFragment.kt index bde1f06131..cb59a6f202 100644 --- a/module_setting/src/main/java/com/gh/gamecenter/setting/view/SettingsFragment.kt +++ b/module_setting/src/main/java/com/gh/gamecenter/setting/view/SettingsFragment.kt @@ -111,19 +111,6 @@ class SettingsFragment : ToolbarFragment() { UserViewModel::class.java ) - // 未打开下载按钮,显示修复下载按钮 - if (Config.isExistHideFunction()) { - mBinding.settingCvFix.visibility = View.VISIBLE - mBinding.settingCvFix.setOnClickListener { - Config.fixHideFunction() - toast(getString(R.string.fix_successfully_toast)) - EventBus.getDefault().post(EBReuse("Refresh")) - val mainActivity = - ARouter.getInstance().build(RouteConsts.provider.mainActivity).navigation() as? IMainProvider - mainActivity?.skipToMainActivity(requireContext(), 0) - } - } - mViewModel.cacheSizeLiveData.observe(this) { mBinding.cacheItem.contentTv.text = it } @@ -367,7 +354,7 @@ class SettingsFragment : ToolbarFragment() { if (PackageFlavorHelper.IS_TEST_FLAVOR == true) { titleTv.text = getString(R.string.setting_about_channel) root.setOnLongClickListener({ - showChangeChannelDialog() + EnvHelper.showChangeChannelDialog(requireContext(), true) return@setOnLongClickListener true }) @@ -446,83 +433,6 @@ class SettingsFragment : ToolbarFragment() { } } - private fun showChangeChannelDialog() { - - var layout = LinearLayout(context).apply { - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - gravity = Gravity.CENTER - orientation = LinearLayout.VERTICAL - setPadding(DisplayUtils.dip2px(16f), DisplayUtils.dip2px(16f), DisplayUtils.dip2px(16f), 0) - } - - val editText = EditText(context) - var channel = SPUtils.getString(Constants.SP_TEST_FLAVOR_CHANNEL) - if (channel == null || TextUtils.isEmpty(channel)) { - val appProvider = ARouter.getInstance().build(RouteConsts.provider.app).navigation() as? IAppProvider - channel = appProvider?.getChannel() ?: "" - SPUtils.setString(Constants.SP_TEST_FLAVOR_CHANNEL, channel) - } - editText.setText(channel, TextView.BufferType.EDITABLE) - editText.layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - layout.addView(editText) - - // 推荐渠道 - val channelContainer = LinearLayout(context).apply { - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - orientation = LinearLayout.HORIZONTAL - } - channelContainer.addView( - TextView(context).apply { - text = context.getString(R.string.recommend_channel) - } - ) - val channelList = listOf("GH_REFRESH", "GH_TEST3") - channelList.forEach { title -> - channelContainer.addView( - TextView(context).apply { - text = title - setTextColor(ContextCompat.getColor(context, R.color.text_theme)) - setOnClickListener { - editText.setText(title, TextView.BufferType.EDITABLE) - } - }, - LinearLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ).apply { - leftMargin = 10F.dip2px() - } - ) - } - layout.addView(channelContainer) - - val inputDialog: AlertDialog.Builder = AlertDialog.Builder(requireActivity()) - inputDialog.setTitle(getString(R.string.please_input_channel)).setView(layout) - inputDialog.setPositiveButton(getString(R.string.confirm)) { _, _ -> - val context = this.context ?: return@setPositiveButton - val flavorChannel = SPUtils.getString(Constants.SP_TEST_FLAVOR_CHANNEL) - if (!TextUtils.isEmpty(editText.text) && editText.text.toString() != flavorChannel) { - SPUtils.setString(Constants.SP_TEST_FLAVOR_CHANNEL, editText.text.toString()) - AppExecutor.uiExecutor.executeWithDelay({ - val pm = context.packageManager - val intent = pm.getLaunchIntentForPackage(context.packageName) - val mainIntent = Intent.makeRestartActivityTask(intent!!.component) - context.startActivity(mainIntent) - Runtime.getRuntime().exit(0) - }, 300L) - } - }.show() - } - // 清除缓存 private fun clearCache() { Observable.create { emitter: ObservableEmitter -> diff --git a/module_setting/src/main/res/layout/fragment_setting.xml b/module_setting/src/main/res/layout/fragment_setting.xml index f8aea3fccb..7c43dc1dfd 100644 --- a/module_setting/src/main/res/layout/fragment_setting.xml +++ b/module_setting/src/main/res/layout/fragment_setting.xml @@ -10,26 +10,6 @@ android:layout_height="wrap_content" android:orientation="vertical"> - - - - - - 深色模式跟随系统切换 视频播放 游戏下载 - 一键修复 下载完成自动安装游戏 安装完成自动关注游戏 切换安装方式 diff --git a/module_setting/src/setting/java/com/gh/gamecenter/setting/provider/ConfigProviderImpl.kt b/module_setting/src/setting/java/com/gh/gamecenter/setting/provider/ConfigProviderImpl.kt index f1870d2047..de671a3bf9 100644 --- a/module_setting/src/setting/java/com/gh/gamecenter/setting/provider/ConfigProviderImpl.kt +++ b/module_setting/src/setting/java/com/gh/gamecenter/setting/provider/ConfigProviderImpl.kt @@ -99,10 +99,6 @@ class ConfigProviderImpl : IConfigProvider { return false } - override fun isShowPlugin(gameId: String): Boolean { - return false - } - override fun init(context: Context?) { // Do nothing } diff --git a/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeAboutActivity.kt b/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeAboutActivity.kt index de0846fa3f..5b88db3f33 100644 --- a/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeAboutActivity.kt +++ b/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeAboutActivity.kt @@ -67,8 +67,7 @@ class ComposeAboutActivity : ComposeBaseActivity() { } updateManager?.checkUpdate( this@ComposeAboutActivity, - false, - updateHandler + true, ) // 检查更新 Scaffold( @@ -148,8 +147,7 @@ class ComposeAboutActivity : ComposeBaseActivity() { ) { updateManager?.checkUpdate( this@ComposeAboutActivity, - false, - updateHandler + true, ) // 检查更新 } SettingDivider() diff --git a/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeSettingActivity.kt b/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeSettingActivity.kt index 833b0a1e9c..33106e49ab 100644 --- a/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeSettingActivity.kt +++ b/module_setting_compose/src/main/java/com/gh/gamecenter/setting/compose/activity/ComposeSettingActivity.kt @@ -111,16 +111,6 @@ class ComposeSettingActivity : ComposeBaseActivity() { backgroundColor = HaloTheme.colors.background ) { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { - if (Config.isExistHideFunction()) { - OneKeyFixButton { - Config.fixHideFunction() - Utils.toast(this@ComposeSettingActivity, "修复成功") - EventBus.getDefault().post(EBReuse("Refresh")) - val mainActivity = ARouter.getInstance().build(RouteConsts.provider.mainActivity) - .navigation() as? IMainProvider - mainActivity?.skipToMainActivity(this@ComposeSettingActivity, 0) - } - } Space(8.dp) SettingItem(getString(R.string.setting_security), showArrow = true) { ifLogin("设置-账号与安全-请先登录") { @@ -267,7 +257,7 @@ class ComposeSettingActivity : ComposeBaseActivity() { R.string.setting_about ), content = mAboutText, showUpdateHint = mIsUpdate, onLongClick = { if (PackageFlavorHelper.IS_TEST_FLAVOR) { - showChangeChannelDialog() + EnvHelper.showChangeChannelDialog(this@ComposeSettingActivity, true) } }) { val mIsUpdate = intent.getBooleanExtra(EntranceConsts.KEY_VERSION_UPDATE, false) @@ -391,80 +381,6 @@ class ComposeSettingActivity : ComposeBaseActivity() { } } - private fun showChangeChannelDialog() { - val layout = LinearLayout(this).apply { - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - gravity = Gravity.CENTER - setPadding(DisplayUtils.dip2px(16f), DisplayUtils.dip2px(16f), DisplayUtils.dip2px(16f), 0) - } - - val editText = EditText(this) - var channel = SPUtils.getString(Constants.SP_TEST_FLAVOR_CHANNEL) - if (channel == null || TextUtils.isEmpty(channel)) { - val appProvider = ARouter.getInstance().build(RouteConsts.provider.app).navigation() as? IAppProvider - channel = appProvider?.getChannel() ?: "" - SPUtils.setString(Constants.SP_TEST_FLAVOR_CHANNEL, channel) - } - editText.setText(channel, TextView.BufferType.EDITABLE) - editText.layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - layout.addView(editText) - - // 推荐渠道 - val channelContainer = LinearLayout(this).apply { - layoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - orientation = LinearLayout.HORIZONTAL - } - channelContainer.addView( - TextView(this).apply { - text = "推荐渠道:" - } - ) - val channelList = listOf("GH_REFRESH", "GH_TEST3") - channelList.forEach { title -> - channelContainer.addView( - TextView(this).apply { - text = title - setTextColor(ContextCompat.getColor(context, R.color.text_theme)) - setOnClickListener { - editText.setText(title, TextView.BufferType.EDITABLE) - } - }, - LinearLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ).apply { - leftMargin = 10F.dip2px() - } - ) - } - layout.addView(channelContainer) - - val inputDialog: AlertDialog.Builder = AlertDialog.Builder(this) - inputDialog.setTitle("请输入渠道:").setView(layout) - inputDialog.setPositiveButton("确定") { _, _ -> - val flavorChannel = SPUtils.getString(Constants.SP_TEST_FLAVOR_CHANNEL) - if (!TextUtils.isEmpty(editText.text) && editText.text.toString() != flavorChannel) { - SPUtils.setString(Constants.SP_TEST_FLAVOR_CHANNEL, editText.text.toString()) - AppExecutor.uiExecutor.executeWithDelay({ - val pm = packageManager - val intent = pm.getLaunchIntentForPackage(packageName) - val mainIntent = Intent.makeRestartActivityTask(intent!!.component) - startActivity(mainIntent) - Runtime.getRuntime().exit(0) - }, 300L) - } - }.show() - } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == USAGE_STATUS_REQUEST_CODE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {