Compare commits

...

162 Commits

Author SHA1 Message Date
0cdf7053df 多管道构建测试完毕 2023-03-30 14:03:49 +08:00
9e67be67e6 测试多管道构建 2023-03-30 14:01:28 +08:00
df2f52cf7a 重新测试多管道构建 2023-03-30 13:58:12 +08:00
14ed8e6d13 多管道构建测试完毕 2023-03-30 13:52:57 +08:00
4474991029 测试多管道构建 2023-03-30 13:51:36 +08:00
c8614ce703 测试多管道构建 2023-03-30 13:44:03 +08:00
2472a6879d 新增向下游管道传递变量 2023-03-30 11:08:48 +08:00
f040691e24 base镜像添加所需安装包 2023-03-28 11:56:17 +08:00
3648dced06 ci:测试完整流程 2023-03-27 18:20:58 +08:00
94f94f5186 ci: 测试多项目触发 2023-03-27 17:56:11 +08:00
fa03fc6d4c ci: 测试多项目触发 2023-03-27 17:54:22 +08:00
f23eb48185 ci: 测试多项目触发 2023-03-27 17:48:22 +08:00
8c5568c24d ci: 测试多项目触发 2023-03-27 17:42:56 +08:00
1bdc1a6430 ci: 测试多项目触发 2023-03-27 17:40:33 +08:00
b289e78a88 ci: Dockerfile 去掉代码复制 2023-03-27 17:09:49 +08:00
ac4143fe0a fix: 添加 outputs 参数 2023-03-24 15:01:03 +08:00
938cb1c9cb fix: 调整入参命名 2023-03-24 14:04:20 +08:00
7445f98086 修改.gitmodules url 2023-03-24 14:04:10 +08:00
c2552096e1 ci: 修改stage 2023-03-24 14:01:24 +08:00
c6c0fe771a ci:添加android,及制作基础打包镜像 2023-03-24 14:00:16 +08:00
623708fed1 fix: 修正入参命名 2023-03-24 11:27:51 +08:00
faec33658d feat: 简单对接接口 2023-03-15 18:17:53 +08:00
2fda01215a feat: 【光环助手】推广组测试打包任务 https://jira.shanqu.cc/browse/GHZS-972 2023-03-15 14:42:52 +08:00
ee6ec82d27 Merge branch 'fix-GHZS-1622' into 'dev'
fix: OPPO手机拦截安装相关优化—0314测试 (更正接口调用) https://jira.shanqu.cc/browse/GHZS-1622

See merge request halo/android/assistant-android!796
2023-03-15 09:24:07 +08:00
317cce1f56 fix: OPPO手机拦截安装相关优化—0314测试 (更正接口调用) https://jira.shanqu.cc/browse/GHZS-1622 2023-03-14 18:31:10 +08:00
f29f5089fd Merge branch 'feature-GHZS-1216' into 'dev'
feat:【光环助手】实名认证接口崩溃预案  https://jira.shanqu.cc/browse/GHZS-1216

See merge request halo/android/assistant-android!795
2023-03-14 18:06:49 +08:00
f5b71fdcb2 feat:【光环助手】实名认证接口崩溃预案 https://jira.shanqu.cc/browse/GHZS-1216 2023-03-14 18:03:45 +08:00
79c0e61a24 Merge branch 'feature-GHZS-1576' into 'dev'
feat: 游戏专题相关功能优化—客户端(补充下载完成的广告曝光埋点) https://jira.shanqu.cc/browse/GHZS-1576

See merge request halo/android/assistant-android!794
2023-03-14 17:50:50 +08:00
bfeadf8c75 feat: 游戏专题相关功能优化—客户端(补充下载完成的广告曝光埋点) https://jira.shanqu.cc/browse/GHZS-1576 2023-03-14 17:23:36 +08:00
b118309d4e Merge branch 'feature-GHZS-1498' into 'dev'
feat: 论坛详情页相关优化—客户端 https://jira.shanqu.cc/browse/GHZS-1498

See merge request halo/android/assistant-android!792
2023-03-14 15:59:39 +08:00
5aab516058 feat: 论坛详情页相关优化—客户端 https://jira.shanqu.cc/browse/GHZS-1498 2023-03-14 15:59:38 +08:00
3ef4214313 Merge branch 'fix-GHZS-1611' into 'dev'
fix: 游戏镜像&游戏屏蔽相关优化—0314测试-后端(3(2)) https://jira.shanqu.cc/browse/GHZS-1611

See merge request halo/android/assistant-android!793
2023-03-14 15:57:20 +08:00
dcca1b9ff9 fix: 游戏镜像&游戏屏蔽相关优化—0314测试-后端(3(2)) https://jira.shanqu.cc/browse/GHZS-1611 2023-03-14 15:53:26 +08:00
7bdc7a946d Merge branch 'fix-change-debug-log-class' into 'dev'
fix:更换新游开测使用的debug日志类。并补充新游开测需求地址:https://jira.shanqu.cc/browse/GHZS-1351

See merge request halo/android/assistant-android!790
2023-03-14 14:26:44 +08:00
55a92847c7 fix:更换新游开测使用的debug日志类。并补充新游开测需求地址:https://jira.shanqu.cc/browse/GHZS-1351 2023-03-14 14:26:44 +08:00
b858365c1d Merge branch 'feat-GHZS-1351' into 'dev'
feat:首页/板块-新游开测-开发完成

See merge request halo/android/assistant-android!788
2023-03-14 14:00:57 +08:00
92d2f7e701 feat:首页/板块-新游开测-开发完成 2023-03-14 14:00:57 +08:00
0967df5751 Merge branch 'feature-GHZS-1492' into 'dev'
feat: 镜像游戏-游戏更新优化—客户端 https://jira.shanqu.cc/browse/GHZS-1492

See merge request halo/android/assistant-android!789
2023-03-14 12:03:47 +08:00
d6e5b88d95 Merge remote-tracking branch 'origin/dev' into dev-5.22.0 2023-03-13 17:47:57 +08:00
b08e653b99 Merge remote-tracking branch 'origin/release' into dev
# Conflicts:
#	dependencies.gradle
#	module_common/src/main/java/com/gh/gamecenter/common/view/MaxHeightLinearLayout.kt
2023-03-13 17:41:10 +08:00
f539e45e18 feat: 镜像游戏-游戏更新优化—客户端 https://jira.shanqu.cc/browse/GHZS-1492 2023-03-13 15:50:49 +08:00
6a6ff5b12a ci: 调整最大 JVM heap size,避免编译时 OOM 2023-03-13 14:57:41 +08:00
b8ba5f85c9 Merge branch 'fix-GHZS-1587' into 'dev-5.22.0'
fix: 畅玩组件安装后自动下载游戏—0310测试 https://jira.shanqu.cc/browse/GHZS-1587

See merge request halo/android/assistant-android!787
2023-03-13 11:41:25 +08:00
67ed3cb919 fix: 畅玩组件安装后自动下载游戏—0310测试 https://jira.shanqu.cc/browse/GHZS-1587 2023-03-13 11:37:20 +08:00
c973bd6b5e Merge branch 'fix-GHZS-1591' into 'dev-5.22.0'
fix: 游戏专题新增[视频横屏滑动]样式—0313UI测试 https://jira.shanqu.cc/browse/GHZS-1591

See merge request halo/android/assistant-android!786
2023-03-13 11:29:10 +08:00
61e9bcb89d fix: 游戏专题新增[视频横屏滑动]样式—0313UI测试 https://jira.shanqu.cc/browse/GHZS-1591 2023-03-13 11:27:23 +08:00
2d3f18a302 Merge branch 'fix-vpn_setting_null_pointer' into 'dev-5.22.0'
fix: 修复安装配置为空时的空指针异常

See merge request halo/android/assistant-android!785
2023-03-13 11:17:50 +08:00
401d36a74e fix: 修复安装配置为空时的空指针异常 2023-03-13 11:15:36 +08:00
bee52ba0bd Merge branch 'feature-GHZS-1343' into 'dev-5.22.0'
fix: 【光环助手】OPPO手机拦截安装相关优化 https://jira.shanqu.cc/browse/GHZS-1343

See merge request halo/android/assistant-android!784
2023-03-13 11:01:02 +08:00
6d2e0853ff feat: 【光环助手】OPPO手机拦截安装相关优化 https://jira.shanqu.cc/browse/GHZS-1343 2023-03-13 11:01:02 +08:00
3b23f086c8 Merge branch 'fix-GHZS-1544' into 'dev-5.22.0'
fix: 【光环助手】游戏详情滑动问题 https://jira.shanqu.cc/browse/GHZS-1544

See merge request halo/android/assistant-android!783
2023-03-10 11:42:58 +08:00
d50e4e044b fix: 【光环助手】游戏详情滑动问题 https://jira.shanqu.cc/browse/GHZS-1544 2023-03-10 11:40:08 +08:00
84f56681d0 Merge remote-tracking branch 'origin/dev' into dev-5.22.0 2023-03-10 10:52:41 +08:00
7fbb3eec86 Merge branch 'feature-GHZS-1287' into 'dev-5.22.0'
feat: 游戏专题新增[视频横屏滑动]样式—客户端 https://jira.shanqu.cc/browse/GHZS-1287

See merge request halo/android/assistant-android!782
2023-03-09 15:55:11 +08:00
ef11044b50 feat: 游戏专题新增[视频横屏滑动]样式—客户端 https://jira.shanqu.cc/browse/GHZS-1287 2023-03-09 15:55:11 +08:00
26395038bd Merge branch 'feature-GHZS-1528' into 'dev-5.22.0'
fix: 【光环助手】畅玩组件安装后自动下载游戏 https://jira.shanqu.cc/browse/GHZS-1516

See merge request halo/android/assistant-android!781
2023-03-09 09:47:34 +08:00
68b0691618 fix: 【光环助手】畅玩组件安装后自动下载游戏 https://jira.shanqu.cc/browse/GHZS-1516 2023-03-09 09:36:32 +08:00
f5a4e4ca06 Merge branch 'fix-GHZS-1543' into 'dev-5.22.0'
fix: 启动广告图显示错误—0306测试 https://jira.shanqu.cc/browse/GHZS-1543

See merge request halo/android/assistant-android!780
2023-03-08 16:52:43 +08:00
c75eacaf45 fix: 启动广告图显示错误—0306测试 https://jira.shanqu.cc/browse/GHZS-1543 2023-03-08 16:37:09 +08:00
aa5ecb8e6c Merge branch 'feature-GHZS-1550' into 'dev-5.22.0'
fix: 游戏镜像&游戏屏蔽相关优化—客户端 https://jira.shanqu.cc/browse/GHZS-1550

See merge request halo/android/assistant-android!779
2023-03-08 09:30:56 +08:00
00dda64279 Merge branch 'fix-GHZS-1549' into 'dev-5.22.0'
fix: 新增用户-用户短ID显示优化—0307测试 https://jira.shanqu.cc/browse/GHZS-1549

See merge request halo/android/assistant-android!778
2023-03-08 09:20:34 +08:00
2bd3be0088 fix: 新增用户-用户短ID显示优化—0307测试 https://jira.shanqu.cc/browse/GHZS-1549 2023-03-07 21:39:15 +08:00
3bff64af27 fix: 游戏镜像&游戏屏蔽相关优化—客户端 https://jira.shanqu.cc/browse/GHZS-1550 2023-03-07 21:15:51 +08:00
32210875da chore: 版本更新至 5.19.5 2023-03-07 17:36:38 +08:00
db9d7d9d22 Merge branch 'merge_dev_to_release' into 'release'
合并 dev 部分修改到 release 分支

See merge request halo/android/assistant-android!777
2023-03-07 17:29:25 +08:00
1f211356fd fix:【光环助手】资讯文章-下载按钮显示问题(0206测试1) https://jira.shanqu.cc/browse/GHZS-1066 2023-03-07 17:20:10 +08:00
81b5b6b369 fix:【光环助手】资讯文章-下载按钮显示问题 https://jira.shanqu.cc/browse/GHZS-1066 2023-03-07 17:20:07 +08:00
98e263f99a fix:【光环助手】多版本下载面板-版本说明显示问题 https://jira.shanqu.cc/browse/GHZS-1064 2023-03-07 17:19:55 +08:00
81b74e7077 Merge branch 'feature-GHZS-1484' into 'dev-5.22.0'
feat: 游戏搜素-内容标签点击优化—客户端 https://jira.shanqu.cc/browse/GHZS-1484

See merge request halo/android/assistant-android!775
2023-03-07 16:42:35 +08:00
083a893582 feat: 游戏搜素-内容标签点击优化—客户端 https://jira.shanqu.cc/browse/GHZS-1484 2023-03-07 16:42:35 +08:00
405d4b709f Merge branch 'feature-GHZS-1491' into 'dev-5.22.0'
feat: 多版本下载面板优化—客户端 https://jira.shanqu.cc/browse/GHZS-1491

See merge request halo/android/assistant-android!776
2023-03-07 16:41:58 +08:00
36942aab5a feat: 多版本下载面板优化—客户端 https://jira.shanqu.cc/browse/GHZS-1491 2023-03-07 16:04:45 +08:00
e61b21ca49 Merge branch 'fix-dark_mode' into 'dev'
fix: 修复切换深色模式首页游戏没有更新颜色的问题

See merge request halo/android/assistant-android!774
2023-03-07 15:21:37 +08:00
b9e7f4145a fix: 修复切换深色模式首页游戏没有更新颜色的问题 2023-03-07 15:21:37 +08:00
517b474a1e Merge branch 'fix-GHZS-1440' into 'dev'
fix: 【光环助手】游戏专题夜间模式显示问题 https://jira.shanqu.cc/browse/GHZS-1440

See merge request halo/android/assistant-android!773
2023-03-07 14:24:28 +08:00
dfac1e93b7 fix: 【光环助手】游戏专题夜间模式显示问题 https://jira.shanqu.cc/browse/GHZS-1440 2023-03-07 14:02:02 +08:00
d848081f62 Merge branch 'fix-typo' into 'dev'
fix: 更正用户 id 和 gid 的命名

See merge request halo/android/assistant-android!772
2023-03-07 10:49:04 +08:00
2bac0feb2c fix: 更正用户 id 和 gid 的命名 2023-03-07 10:44:48 +08:00
a602055f98 Merge branch 'hotfix-v5.19.4-734-vector_crash' into 'release'
fix: 修复5.0以下系统矢量图相关闪退问题

See merge request halo/android/assistant-android!771
2023-03-07 10:11:00 +08:00
28ba14e9ec Merge branch 'feature-GHZS-1419' into 'dev'
feat: 神策埋点接入测试--光环埋点 https://jira.shanqu.cc/browse/GHZS-1419

See merge request halo/android/assistant-android!770
2023-03-07 09:31:20 +08:00
c36fb04f81 feat: 神策埋点接入测试--光环埋点 https://jira.shanqu.cc/browse/GHZS-1419 2023-03-07 09:21:04 +08:00
7d26a66bc3 Merge remote-tracking branch 'origin/dev' into dev-5.22.0 2023-03-06 19:57:30 +08:00
9451f9bae3 Merge remote-tracking branch 'origin/release' into dev
# Conflicts:
#	dependencies.gradle
2023-03-06 19:55:40 +08:00
ac18a678ff Merge remote-tracking branch 'origin/legacy-release' into release
# Conflicts:
#	app/proguard-rules.txt
2023-03-06 19:55:05 +08:00
bd2871788f fix: 修复5.0以下系统矢量图相关闪退问题 2023-03-06 17:16:58 +08:00
beeece1d0e Merge branch 'feature-GHZS-1417' into 'dev'
feat: 神策埋点接入测试--SDK接入 https://jira.shanqu.cc/browse/GHZS-1417

See merge request halo/android/assistant-android!769
2023-03-06 16:10:51 +08:00
1e9e39eea9 feat: 神策埋点接入测试--SDK接入 https://jira.shanqu.cc/browse/GHZS-1417 2023-03-06 16:10:51 +08:00
9162b0f67d Merge branch 'fix-news_js_error' into 'dev'
fix: 修复新闻资讯页因为没有设置 PageFinishListener 导致的方法调用问题

See merge request halo/android/assistant-android!768
2023-03-06 11:49:38 +08:00
46dd84aaf9 fix: 修复新闻资讯页因为没有设置 PageFinishListener 导致的方法调用问题 2023-03-06 11:46:24 +08:00
e5e0d99abb Merge branch 'feature-GHZS-1428' into 'dev-5.22.0'
fix: 【光环助手】游戏详情-游戏礼包显示问题 https://jira.shanqu.cc/browse/GHZS-1428

See merge request halo/android/assistant-android!767
2023-03-06 09:09:29 +08:00
6f34b16bba fix: 【光环助手】游戏详情-游戏礼包显示问题 https://jira.shanqu.cc/browse/GHZS-1428 2023-03-03 21:09:13 +08:00
5b2512f16f Merge branch 'feature-GHZS-1537' into 'dev-5.22.0'
fix: 新增用户-用户短ID显示优化—客户端 https://jira.shanqu.cc/browse/GHZS-1537

See merge request halo/android/assistant-android!766
2023-03-03 18:42:07 +08:00
9a1e0f1ae2 fix: 新增用户-用户短ID显示优化—客户端 https://jira.shanqu.cc/browse/GHZS-1537 2023-03-03 18:34:10 +08:00
a03b6b643f Merge branch 'fix-GHZS-1539' into 'dev-5.22.0'
fix: 修复缓存的启动广告图过期仍显示的问题 https://jira.shanqu.cc/browse/GHZS-1539

See merge request halo/android/assistant-android!765
2023-03-03 16:36:59 +08:00
8b2cb8b40c fix: 修复缓存的启动广告图过期仍显示的问题 https://jira.shanqu.cc/browse/GHZS-1539 2023-03-03 16:32:43 +08:00
c2402bd4d2 Merge branch 'feature-GHZS-1490' into 'dev-5.22.0'
fix: 游戏详情-顶部视频播放优化—客户端 https://jira.shanqu.cc/browse/GHZS-1490

See merge request halo/android/assistant-android!764
2023-03-03 15:53:22 +08:00
b724cde6ca fix: 游戏详情-顶部视频播放优化—客户端 https://jira.shanqu.cc/browse/GHZS-1490 2023-03-03 15:53:22 +08:00
fd3058e0be Merge branch 'fix-gdt' into 'legacy-release'
fix: 修改广点通默认数据源id和密钥

See merge request halo/android/assistant-android!762
2023-03-03 11:12:58 +08:00
03431ef3a3 Merge branch 'feature-GHZS-1502' into 'dev-5.22.0'
fix: 新增GIF图片可点击关闭—客户端 https://jira.shanqu.cc/browse/GHZS-1502

See merge request halo/android/assistant-android!761
2023-03-03 09:51:08 +08:00
23e2a79bee fix: 新增GIF图片可点击关闭—客户端 https://jira.shanqu.cc/browse/GHZS-1502 2023-03-03 09:40:06 +08:00
3aa14c9541 chore: 版本更新为 5.22.0 2023-03-02 17:54:57 +08:00
39fcc5ed63 Merge branch 'fix-GHZS-1448' into 'dev-5.21.0'
fix: 游戏礼包新增兑换码功能—0228测试-客户端部分-客户端 https://jira.shanqu.cc/browse/GHZS-1448

See merge request halo/android/assistant-android!760
2023-03-02 16:53:12 +08:00
577be922a6 fix: 游戏礼包新增兑换码功能—0228测试-客户端部分-客户端 https://jira.shanqu.cc/browse/GHZS-1448 2023-03-02 16:48:38 +08:00
19d128e98a Merge branch 'fix-typo' into 'dev-5.21.0'
fix: 更新畅玩提示文案

See merge request halo/android/assistant-android!759
2023-03-02 16:45:42 +08:00
a8acf1d6c8 fix: 更新畅玩提示文案 2023-03-02 16:44:06 +08:00
ce0a301348 Merge branch 'fix-GHZS-1485' into 'dev-5.21.0'
fix: 多版本游戏下载按钮显示问题—客户端 https://jira.shanqu.cc/browse/GHZS-1485

See merge request halo/android/assistant-android!758
2023-03-02 15:06:01 +08:00
2567e520da fix: 多版本游戏下载按钮显示问题—客户端 https://jira.shanqu.cc/browse/GHZS-1485 2023-03-02 14:57:02 +08:00
2394cf837e Merge branch 'fix-GHZS-1450' into 'dev-5.21.0'
fix: 用户更换头像功能优化—0301UI测试(第3点、添加头像边框) https://jira.shanqu.cc/browse/GHZS-1450

See merge request halo/android/assistant-android!757
2023-03-02 11:41:14 +08:00
bf692569e6 fix: 用户更换头像功能优化—0301UI测试(第3点、添加头像边框) https://jira.shanqu.cc/browse/GHZS-1450 2023-03-02 11:36:09 +08:00
090102ec5d Merge branch 'fix-GHZS-1425' into 'dev-5.21.0'
fix: 副标题前端显示优化—0228测试(修复无副标题时广告标识不显示的问题) https://jira.shanqu.cc/browse/GHZS-1425

See merge request halo/android/assistant-android!756
2023-03-02 09:57:16 +08:00
61e9966de8 fix: 副标题前端显示优化—0228测试(修复无副标题时广告标识不显示的问题) https://jira.shanqu.cc/browse/GHZS-1425 2023-03-02 09:24:44 +08:00
fe26504f43 Merge branch 'fix-video_callback_error' into 'dev-5.21.0'
fix: 修复帖子详情页的网页视频进度同步异常

See merge request halo/android/assistant-android!755
2023-03-01 19:21:40 +08:00
e4d2036cc2 chore: 版本更新至 5.19.4 2023-03-01 19:12:38 +08:00
2627a4c39c fix: 调整方法实现 2023-03-01 19:08:48 +08:00
a44d454f13 fix: 修复帖子详情页的网页视频进度同步异常 2023-03-01 18:07:35 +08:00
0600daadde Merge branch 'feature-add_download_header' into 'release'
feat: 添加新的 download header 供后端接口使用

See merge request halo/android/assistant-android!752
2023-03-01 17:55:07 +08:00
f29b1a6124 Merge branch 'fix-GHZS-1450' into 'dev-5.21.0'
fix: 用户更换头像功能优化—0301UI测试 https://jira.shanqu.cc/browse/GHZS-1450

See merge request halo/android/assistant-android!754
2023-03-01 15:41:56 +08:00
1892d10040 Merge branch 'fix-GHZS-1425' into 'dev-5.21.0'
fix: 副标题前端显示优化—0228测试 https://jira.shanqu.cc/browse/GHZS-1425

See merge request halo/android/assistant-android!753
2023-03-01 15:16:24 +08:00
9a8df13870 fix: 用户更换头像功能优化—0301UI测试 https://jira.shanqu.cc/browse/GHZS-1450 2023-03-01 15:14:11 +08:00
4eabeae5d5 fix: 副标题前端显示优化—0228测试 https://jira.shanqu.cc/browse/GHZS-1425 2023-03-01 14:34:05 +08:00
c4b1be9925 feat: 添加新的 download header 供后端接口使用 2023-03-01 13:43:37 +08:00
b2a6393afd Merge branch 'hotfix-v5.19.3-733-vspace_update_issue' into 'release'
fix: 修复畅玩组件更新后因为服务连接延迟导致的游戏游戏状态显示异常

See merge request halo/android/assistant-android!750
2023-03-01 13:38:46 +08:00
b5c15940a1 Merge branch 'hotfix-v5.19.3-733-archive_tab_error' into 'release'
fix: 修复游戏详情隐藏专区 tab 且显示云存档 tab 时,云存档红点错位的问题

See merge request halo/android/assistant-android!751
2023-03-01 13:38:01 +08:00
3f6f55b265 fix: 修复畅玩组件更新后因为服务连接延迟导致的游戏游戏状态显示异常 2023-03-01 11:19:27 +08:00
ba666da059 fix: 修复游戏详情隐藏专区 tab 且显示云存档 tab 时,云存档红点错位的问题 2023-03-01 11:16:33 +08:00
8b1f251c80 Merge remote-tracking branch 'origin/dev' into dev-5.21.0 2023-02-28 19:18:52 +08:00
01aadbd64c Merge remote-tracking branch 'origin/release' into dev
# Conflicts:
#	dependencies.gradle
2023-02-28 19:17:48 +08:00
6256b31cd9 Merge branch 'fix-ultra_short_device_display_issue' into 'dev-5.21.0'
fix: 修复部分设备使用 applicationContext 无法获取到实时 window 高度的问题

See merge request halo/android/assistant-android!749
2023-02-28 17:43:20 +08:00
8e4d1e7e6e fix: 修复部分设备使用 applicationContext 无法获取到实时 window 高度的问题 2023-02-28 17:34:28 +08:00
fa29e00864 Merge branch 'hotfix-v5.19.3-733-gamedetail_tab' into 'release'
fix: 修复游戏详情页超过 3 个 tab 时,页面变空白的问题

See merge request halo/android/assistant-android!748
2023-02-28 16:27:36 +08:00
1b412bf081 fix: 修复游戏详情页超过 3 个 tab 时,页面变空白的问题 2023-02-28 16:13:52 +08:00
0d82b9ec71 Merge branch 'feature-GHZS-1342' into 'dev-5.21.0'
feat: 帖子详情页视频播放优化 https://jira.shanqu.cc/browse/GHZS-1342

See merge request halo/android/assistant-android!747
2023-02-28 15:43:01 +08:00
1055d0c728 feat: 帖子详情页视频播放优化 https://jira.shanqu.cc/browse/GHZS-1342 2023-02-28 15:43:01 +08:00
a53183d405 Merge branch 'feature-GHZS-1304' into 'dev-5.21.0'
feat: 用户更换头像功能优化—客户端 https://jira.shanqu.cc/browse/GHZS-1304

See merge request halo/android/assistant-android!745
2023-02-28 15:07:52 +08:00
11874c3984 fix: 更换默认头像确认后添加正在修改dialog,修复更换默认头像后没有关闭所有弹窗的问题 2023-02-28 15:07:16 +08:00
47464b2864 Merge branch 'fix-GHZS-1422' into 'dev-5.21.0'
fix: 游戏专题-双列卡片 动图加载优化—0228测试 https://jira.shanqu.cc/browse/GHZS-1422

See merge request halo/android/assistant-android!746
2023-02-28 14:14:07 +08:00
a2ed4987ed Merge branch 'feature-GHZS-1300' into 'dev-5.21.0'
feat: 游戏礼包新增兑换码功能 https://jira.shanqu.cc/browse/GHZS-1300

See merge request halo/android/assistant-android!744
2023-02-28 14:06:58 +08:00
4392f9f20b feat: 游戏礼包新增兑换码功能 https://jira.shanqu.cc/browse/GHZS-1300 2023-02-28 14:06:57 +08:00
8e43d0cae3 Merge branch 'dev-5.21.0' into feature-GHZS-1304
# Conflicts:
#	app/src/main/java/com/gh/common/util/NewFlatLogUtils.kt
2023-02-28 14:06:34 +08:00
f83f67553c feat: 用户更换头像功能优化—客户端(数据埋点) https://jira.shanqu.cc/browse/GHZS-1304 2023-02-28 14:02:19 +08:00
14893acf4b fix: 游戏专题-双列卡片 动图加载优化—0228测试 https://jira.shanqu.cc/browse/GHZS-1422 2023-02-28 13:44:30 +08:00
1cbed03788 Merge branch 'feature-GHZS-1312' into 'dev-5.21.0'
fix: 跳转链接通用模块新增[发现页]—客户端 https://jira.shanqu.cc/browse/GHZS-1312

See merge request halo/android/assistant-android!743
2023-02-28 11:46:09 +08:00
0eff89acfc fix: 跳转链接通用模块新增[发现页]—客户端 https://jira.shanqu.cc/browse/GHZS-1312 2023-02-28 11:40:28 +08:00
560b6046ab Merge branch 'feature-GHZS-1307' into 'dev-5.21.0'
feat: 副标题前端显示优化—客户端 https://jira.shanqu.cc/browse/GHZS-1307

See merge request halo/android/assistant-android!740
2023-02-28 09:56:58 +08:00
e898886659 feat: 副标题前端显示优化—客户端 https://jira.shanqu.cc/browse/GHZS-1307 2023-02-28 09:56:58 +08:00
b1abadc145 chore: 版本更新至 5.19.3 2023-02-27 16:21:22 +08:00
05da2c3da4 Merge branch 'hotfix-v5.19.2-732-crashes' into 'release'
fix: 修复闪退

See merge request halo/android/assistant-android!742
2023-02-27 15:35:18 +08:00
e83ece5520 fix: 捕抓游戏详情页上报视频相关日志时的闪退 https://sentry.shanqu.cc/organizations/lightgame/issues/272842/?project=22&query=release%3Acom.gh.gamecenter%405.19.2%2B732+error.unhandled%3Atrue&sort=freq 2023-02-27 15:26:59 +08:00
08a86c778d fix: 修复游戏详情延迟选中时的闪退 https://sentry.shanqu.cc/organizations/lightgame/issues/272842/?project=22&query=release%3Acom.gh.gamecenter%405.19.2%2B732+error.unhandled%3Atrue&sort=freq 2023-02-27 14:57:03 +08:00
58119a0bdb Merge branch 'feature-GHZS-1293' into 'dev-5.21.0'
feat: 游戏详情-置顶标签功能—客户端 https://jira.shanqu.cc/browse/GHZS-1293

See merge request halo/android/assistant-android!741
2023-02-27 09:55:20 +08:00
8d9c57e972 feat: 游戏详情-置顶标签功能—客户端 https://jira.shanqu.cc/browse/GHZS-1293 2023-02-27 09:55:20 +08:00
b216d33d6b chore: 版本更新至 5.19.2 2023-02-24 16:00:21 +08:00
4fe062ea40 Merge branch 'feature-GHZS-1386' into 'release'
feat: 启动畅玩游戏时添加光环版本和渠道 https://jira.shanqu.cc/browse/GHZS-1386

See merge request halo/android/assistant-android!739
2023-02-24 11:14:43 +08:00
7d678085d5 feat: 启动畅玩游戏时添加光环版本和渠道 https://jira.shanqu.cc/browse/GHZS-1386 2023-02-24 11:09:57 +08:00
acae755461 Merge branch 'fix-add_direct_link_type' into 'dev'
fix: 添加新游开测跳转链接类型 https://jira.shanqu.cc/browse/GHZS-1127

See merge request halo/android/assistant-android!738
2023-02-23 17:39:27 +08:00
f29d3b4e29 fix: 添加新游开测跳转链接类型 https://jira.shanqu.cc/browse/GHZS-1127 2023-02-23 17:37:05 +08:00
e1907fcea8 fix: 修改广点通默认数据源id和密钥 2023-02-23 15:46:22 +08:00
76564b2414 feat: 用户更换头像功能优化—客户端 https://jira.shanqu.cc/browse/GHZS-1304 2023-02-21 14:57:05 +08:00
c72d34b4f7 Merge branch 'fix-gdt_permission' into 'legacy-release'
fix: 广点通打包时恢复READ_PHONE_STATE权限

See merge request halo/android/assistant-android!727
2023-02-20 14:51:28 +08:00
8145f389a9 fix: 广点通打包时恢复READ_PHONE_STATE权限 2023-02-20 14:45:00 +08:00
45a8f9683e Merge branch 'feature-GHZS-1254' into 'legacy-release'
feat: 对接广点通SDK—客户端 https://jira.shanqu.cc/browse/GHZS-1254

See merge request halo/android/assistant-android!711
2023-02-16 11:41:01 +08:00
572cbba819 feat: 对接广点通SDK—客户端 https://jira.shanqu.cc/browse/GHZS-1254 2023-02-16 11:41:00 +08:00
298 changed files with 9232 additions and 2089 deletions

View File

@ -1,12 +1,57 @@
stages:
- analysis
- sendmail
- android-build && analysis
- docker-build && sendmail
- deploy-trigger
## 代码检查
android_build:
tags:
- offline-test
stage: android-build && analysis
image: hub.shanqu.cc/devops/ci-android:jdk11
variables:
KUBERNETES_CPU_LIMIT: "16"
GIT_SUBMODULE_STRATEGY: recursive
Apk_Path: "app/build/outputs/apk/**/release/*.apk"
script:
- export GRADLE_USER_HOME=./.gradle
- chmod +x ./gradlew
- ./scripts/meta_build.sh --config_id 6400549c21c2c94ead074500 --sdk_platform toutiao --sdk_version 5.3.0 --channel BD-GHZS-ZP-KY --activate_reporting_ratio 60 --first_lanuch_jump e1wibGlua190eXBlXCI6XCJcIixcImxpbmtfaWRcIjpcIlwiLFwibGlua190ZXh0XCI6XCJcIixcImhvbWVfaW5kZXhcIjpcIlwiLFwiYm90dG9tX2luZGV4XCI6XCJcIn0= --unix_timestamp 1677657618 --output ./release/com.gh.gamecenter_5.17.4_694_BD-GHZS-ZP-KY_toutiao_5.3.0_1677657618.apk
- rm -rf ./.gradle/caches/build-cache-1
cache:
paths:
- .gradle
artifacts:
paths:
- Dockerfile
expire_in: 15 mins
only:
- feature-meta_build
# 构建推送docker镜像
docker-build:
tags:
- offline-test
stage: docker-build && sendmail
image: hub.shanqu.cc/library/docker:latest
variables:
GIT_STRATEGY: none
script:
- projectPath=`echo $CI_PROJECT_PATH | sed 's#/#-#g'`
- docker build -t registry.cn-shenzhen.aliyuncs.com/ghzs/$projectPath:latest .
- docker push registry.cn-shenzhen.aliyuncs.com/ghzs/$projectPath:latest
- docker run -e PROJECTKEY=$projectPath -e EMAIL=$GITLAB_USER_EMAIL -e BRANCH=$CI_COMMIT_REF_NAME --name send-email --rm hub.shanqu.cc/platform/send-sonar-report:latest
cache:
paths:
- .gradle
policy: pull
only:
- feature-meta_build
# 代码检查
sonarqube_analysis:
tags:
- offline-test
stage: analysis
stage: android-build && analysis
image: sonarsource/sonar-scanner-cli:latest
dependencies: [] #禁止传递来的artifact
script:
@ -27,17 +72,14 @@ sonarqube_analysis:
-Dsonar.gitlab.merge_request_discussion=true
-Dsonar.java.binaries=. # 如果不使用Maven或Gradle进行分析则必须手动提供测试二进制文件
only:
- dev
## 发送简易检测结果报告
send_sonar_report:
tags:
- offline-test
stage: sendmail
image: hub.shanqu.cc/library/docker:latest
dependencies: [] #禁止传递来的artifact
script:
- group=`echo $CI_PROJECT_PATH | sed 's#/#-#g'`
- docker run -e PROJECTKEY=$group -e EMAIL=$GITLAB_USER_EMAIL --name send-email --rm hub.shanqu.cc/platform/send-sonar-report:latest
only:
- dev
- feature-meta_build
## 触发多项目构建
trigger_job:
stage: deploy-trigger
variables:
BUILD_PROJECT_NAME: $CI_PROJECT_PATH
trigger:
project: devops/automation/build-eci
branch: dev

8
.gitmodules vendored
View File

@ -1,13 +1,13 @@
[submodule "libraries/LGLibrary"]
path = libraries/LGLibrary
url = git@git.shanqu.cc:android/common-library.git
url = ../../../android/common-library.git
branch = master
[submodule "vspace-bridge"]
path = vspace-bridge
url = git@git.shanqu.cc:cwzs/android/vspace-bridge.git
url = ../../../cwzs/android/vspace-bridge.git
[submodule "module_common/src/debug/assets/assistant-android-mock"]
path = module_common/src/debug/assets/assistant-android-mock
url = git@git.shanqu.cc:halo/android/assistant-android-mock.git
url = ../../../halo/android/assistant-android-mock.git
[submodule "ndownload"]
path = ndownload
url = git@git.shanqu.cc:android/ndownload.git
url = ../../../android/ndownload.git

15
Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM openjdk:11-jdk
WORKDIR /project
SHELL ["/bin/bash", "-c"]
#配置SDK环境变量
ENV ANDROID_SDK_ROOT /usr/lib/sdk
ENV ANDROID_HOME /usr/lib/sdk
ENV PATH $ANDROID_SDK_ROOT:$PATH
ENV PATH=$PATH:${ANDROID_HOME}/cmdline-tools/cmdline-tools/bin/
ENV GRADLE_USER_HOME /project/.gradle
RUN source ~/.bashrc
RUN sed -i "s@http://\(deb\|security\).debian.org@https://mirrors.aliyun.com@g" /etc/apt/sources.list \
&& apt-get --quiet update --yes \
&& apt-get --quiet install --yes lib32stdc++6 lib32z1 libncurses5 util-linux bash tzdata librdkafka-dev pkgconf \
&& rm -rf /var/lib/apt/lists/*
COPY .gradle /project/.gradle

View File

@ -9,6 +9,9 @@ import groovy.xml.XmlUtil
android {
String CONFIG_ID = ""
String FIRST_LAUNCH = ""
buildFeatures {
viewBinding true
dataBinding true
@ -67,9 +70,13 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-fresco.txt'
/**
* All third-party appid/appkey
*/
// 推广用的配置 id
buildConfigField "String", "CONFIG_ID", "\"${CONFIG_ID}\""
// 首次启动的跳转配置
buildConfigField "String", "FIRST_LAUNCH", "\"${FIRST_LAUNCH}\""
// All third-party appid/appkey
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
buildConfigField "String", "NEW_API_HOST", "\"${NEW_API_HOST}\""
buildConfigField "String", "VAPI_HOST", "\"${VAPI_HOST}\""
@ -127,7 +134,7 @@ android {
variantFilter { variant ->
def names = variant.flavors*.name
def isDebugType = variant.buildType.name == "debug"
if ((names.contains("tea") || name.contains("kuaishou")) && isDebugType) {
if ((names.contains("tea") || name.contains("kuaishou") || name.contains("gdt")) && isDebugType) {
setIgnore(true)
}
}
@ -147,6 +154,9 @@ android {
kuaishou {
java.srcDirs = ['src/main/java', 'src/kuaishou/java']
}
gdt {
java.srcDirs = ['src/main/java', 'src/gdt/java']
}
}
productFlavors {
@ -194,6 +204,16 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\""
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
}
gdt {
dimension "env"
buildConfigField "String", "DEV_API_HOST", "\"${API_HOST}\""
buildConfigField "String", "NEW_DEV_API_HOST", "\"${NEW_API_HOST}\""
buildConfigField "String", "DEV_VAPI_HOST", "\"${VAPI_HOST}\""
buildConfigField "String", "QUICK_LOGIN_APPID", "\"${QUICK_LOGIN_APPID}\""
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
}
}
}
@ -209,6 +229,7 @@ dependencies {
teaImplementation fileTree(include: ['*.jar', '*.aar'], dir: 'src/tea/libs')
kuaishouImplementation fileTree(include: ['*.jar', '*.aar'], dir: 'src/kuaishou/libs')
gdtImplementation fileTree(include: ['*.jar', '*.aar'], dir: 'src/gdt/libs')
testImplementation 'junit:junit:4.12'
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakcanary}"
@ -216,8 +237,7 @@ dependencies {
// debugImplementation "com.gu.android:toolargetool:${toolargetool}" // 需要使用调试时才启用
debugImplementation "com.github.nichbar:WhatTheStack:${whatTheStack}"
debugImplementation "io.github.didi.dokit:dokitx:${dokit}"
// debugImplementation "io.github.didi.dokit:dokitx:${dokit}"
implementation "androidx.multidex:multidex:${multiDex}"
implementation "androidx.fragment:fragment-ktx:${fragment}"
@ -289,12 +309,17 @@ dependencies {
// implementation(project(':module_setting_compose')) {
// exclude group: 'androidx.swiperefreshlayout'
// }
implementation(project(':module_core_feature')){
implementation(project(':module_core_feature')) {
exclude group: 'androidx.swiperefreshlayout'
}
implementation(project(':module_feedback')) {
exclude group: 'androidx.swiperefreshlayout'
}
implementation(project(':module_sensors_data')) {
exclude group: 'androidx.swiperefreshlayout'
}
implementation(project(':module_vpn'))
implementation(project(':module_pkg'))
// 默认不接入光能模块,提高编译速度
// debugImplementation(project(':module_energy')) {
// exclude group: 'androidx.swiperefreshlayout'

View File

@ -69,4 +69,14 @@
-keep class com.lzf.easyfloat.* {*;}
### dokit
-keep class com.didichuxing.** {*;}
-keep class com.didichuxing.** {*;}
### 广点通SDK
-dontwarn com.qq.gdt.action.**
-keep class com.qq.gdt.action.** {*;}
-keep public class com.tencent.turingfd.sdk.**
-keepclasseswithmembers class * {
native <methods>;
}

View File

@ -0,0 +1,37 @@
package com.gh.gamecenter
import android.app.Application
import com.lightgame.utils.Utils
import com.qq.gdt.action.ActionParam
import com.qq.gdt.action.ActionType
import com.qq.gdt.action.GDTAction
import org.json.JSONObject
object GdtHelper {
private const val USER_ACTION_SET_ID = "1201041887"
private const val APP_SECRET_ID = "c29cc5c48cf540c43b4b97363bb09216"
private const val KS_USER_ACTION_SET_ID = "1201032128"
private const val KS_APP_SECRET_ID = "9bdbbb81d4e0bd333a2a581f9ee36986"
@JvmStatic
fun init(application: Application, channel: String) {
if (channel == "KS_GDT_GHZS_MC01") {
GDTAction.init(application, KS_USER_ACTION_SET_ID, KS_APP_SECRET_ID, channel)
} else {
GDTAction.init(application, USER_ACTION_SET_ID, APP_SECRET_ID, channel)
}
Utils.log("init GdtHelper")
}
@JvmStatic
fun logAction(type: String) {
when (type) {
"EVENT_ACTIVE" -> GDTAction.logAction(ActionType.START_APP)
"active_register" -> GDTAction.logAction(ActionType.REGISTER)
"EVENT_NEXTDAY_STAY" -> GDTAction.logAction(ActionType.START_APP, JSONObject().apply {
put(ActionParam.Key.LENGTH_OF_STAY, 1)
})
}
}
}

View File

@ -0,0 +1,48 @@
package com.gh.gamecenter.provider
import android.app.Activity
import android.app.Application
import android.text.TextUtils
import com.gh.gamecenter.GdtHelper
import com.gh.gamecenter.core.provider.IFlavorProvider
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.TimeUtils
import com.halo.assistant.HaloApp
import com.leon.channel.helper.ChannelReaderUtil
class FlavorProviderImp : IFlavorProvider {
override fun init(application: Application, activity: Activity) {
GdtHelper.init(application, getChannelStr(application))
if (HaloApp.getInstance().isBrandNewInstall) {
logEvent("EVENT_ACTIVE")
SPUtils.setLong("TIME_OF_BRAND_NEW_INSTALL", System.currentTimeMillis() / 1000)
} else {
val shouldSendRetentionLogEvent =
SPUtils.getBoolean("SHOULD_SEND_RETENTION_EVENT", true)
val installTimeNotToday =
!TimeUtils.isToday(SPUtils.getLong("TIME_OF_BRAND_NEW_INSTALL", System.currentTimeMillis() / 1000))
if (shouldSendRetentionLogEvent && installTimeNotToday) {
logEvent("EVENT_NEXTDAY_STAY")
SPUtils.setBoolean("SHOULD_SEND_RETENTION_EVENT", false)
}
}
}
override fun getChannelStr(application: Application): String {
var channel = ChannelReaderUtil.getChannel(application)
if (channel == null || TextUtils.isEmpty(channel.trim())) {
channel = GDT_DEFAULT_CHANNEL
}
return channel
}
override fun logEvent(content: String) {
GdtHelper.logAction(content)
}
companion object {
private const val GDT_DEFAULT_CHANNEL = "GDT_GHZS_01"
}
}

Binary file not shown.

View File

@ -18,9 +18,9 @@ import kotlin.concurrent.fixedRateTimer
object FixedRateJobHelper {
private const val CHECKER_PERIOD: Long = 15 * 1000L
private const val TIME_PERIOD: Long = 600 * 1000L
private const val LOGHUB_PERIOD: Long = 120 * 1000L
private const val EXPOSURE_PERIOD: Long = 300 * 1000L
private const val TIME_PERIOD: Long = 10 * 60 * 1000L
private const val LOGHUB_PERIOD: Long = 2 * 60 * 1000L
private const val EXPOSURE_PERIOD: Long = 5 * 60 * 1000L
private const val REGION_SETTING_PERIOD: Long = 30 * 1000L
private const val VIDEO_RECORD_PERIOD: Long = 60 * 1000L
@ -35,9 +35,11 @@ object FixedRateJobHelper {
@JvmStatic
fun begin() {
doOnMainProcessOnly {
// 时间检查每15秒检查一次
fixedRateTimer("Global-Fixed-Rate-Timer", initialDelay = 100, period = CHECKER_PERIOD) {
val elapsedTime = mExecuteCount * CHECKER_PERIOD
// 时间校对10分钟一次
if ((mExecuteCount * CHECKER_PERIOD) % TIME_PERIOD == 0L) {
if (elapsedTime % TIME_PERIOD == 0L) {
RetrofitManager.getInstance().api.time
.subscribeOn(Schedulers.io())
.subscribe(object : Response<TimeEntity>() {
@ -51,14 +53,14 @@ object FixedRateJobHelper {
}
// 提交曝光数据
if ((mExecuteCount * CHECKER_PERIOD) % EXPOSURE_PERIOD == 0L) {
if (elapsedTime % EXPOSURE_PERIOD == 0L) {
runOnUiThread {
ExposureManager.commitSavedExposureEvents(true)
}
}
// 分片检测下载进度
if ((mExecuteCount * CHECKER_PERIOD) % DOWNLOAD_HEARTBEAT_SHEET_PERIOD == 0L) {
if (elapsedTime % DOWNLOAD_HEARTBEAT_SHEET_PERIOD == 0L) {
tryCatchInRelease {
val upload = (mExecuteCount * CHECKER_PERIOD) % DOWNLOAD_HEARTBEAT_PERIOD == 0L
DownloadDataHelper.uploadDownloadHeartbeat(upload)
@ -66,30 +68,29 @@ object FixedRateJobHelper {
}
// 提交普通 loghub 数据
if ((mExecuteCount * CHECKER_PERIOD) % LOGHUB_PERIOD == 0L) {
if (elapsedTime % LOGHUB_PERIOD == 0L) {
runOnUiThread {
LoghubUtils.commitSavedLoghubEvents(true)
}
}
// 更新游戏屏蔽信息
if ((mExecuteCount * CHECKER_PERIOD) % REGION_SETTING_PERIOD == 0L) {
if (elapsedTime % REGION_SETTING_PERIOD == 0L) {
if (HaloApp.getInstance().isRunningForeground) {
RegionSettingHelper.getRegionSetting()
}
}
// 提交视频浏览记录数据
if ((mExecuteCount * CHECKER_PERIOD) % VIDEO_RECORD_PERIOD == 0L) {
if (elapsedTime % VIDEO_RECORD_PERIOD == 0L) {
VideoRecordUtils.commitVideoRecord()
}
// 获取启动广告
if ((mExecuteCount * CHECKER_PERIOD) % STARTUP_AD == 0L) {
// 获取启动广告 (第一次不需要获取)
if (elapsedTime % STARTUP_AD == 0L && mExecuteCount != 0) {
AdHelper.getSettingAdCache()
}
// ExposureUtils.logADownloadCompleteExposureEvent(GameEntity(id = mExecuteCount.toString(), name = "测试曝光上传"), platform = "", trace = null, downloadType = ExposureUtils.DownloadType.DOWNLOAD)
mExecuteCount++
}
}

View File

@ -21,6 +21,7 @@ import com.gh.gamecenter.common.utils.DarkModeUtils;
import com.gh.gamecenter.common.utils.EnvHelper;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.SPUtils;
import com.gh.gamecenter.core.utils.UrlFilterUtils;
import com.gh.gamecenter.entity.GameGuidePopupEntity;
import com.gh.gamecenter.entity.NewApiSettingsEntity;
import com.gh.gamecenter.entity.NewSettingsEntity;
@ -76,8 +77,6 @@ public class Config {
public static final String FIX_DOWNLOAD_KEY = "isFixDownload";
public static final String FIX_PLUGIN_KEY = "isFixPlugin";
public static final String FIX_ARTICLE_KEY = "isFixArticle";
public static final String FIX_COMMUNITY_KEY = "isFixCommunity";
public static final int VIDEO_PAGE_SIZE = 21; // 视频列表大多都是一行3个
@ -134,6 +133,18 @@ public class Config {
return !"off".equals(getSettings().getGameSmooth());
}
/**
* VPN 开关选项是否开启
*/
public static boolean isVpnOptionEnabled() {
if (mNewApiSettingsEntity == null
|| mNewApiSettingsEntity.getInstall() == null
|| mNewApiSettingsEntity.getInstall().getVpnRequired() == null) {
return false;
}
return mNewApiSettingsEntity.getInstall().getVpnRequired().getShouldShowVpnOption();
}
public static boolean isShowPlugin(String gameId) {
SharedPreferences preferences = getPreferences();
@ -343,6 +354,10 @@ public class Config {
@SuppressLint("CheckResult")
public static void getGhzsSettings() {
String channel = HaloApp.getInstance().getChannel();
// 把请求提前,避免请求过多阻塞,首页显示广告时老是用到上一次的缓存
getNewSettings(channel);
RetrofitManager.getInstance()
.getApi().getSettings(PackageUtils.getGhVersionName(), channel)
.subscribeOn(Schedulers.io())
@ -392,27 +407,6 @@ public class Config {
});
}
if (mNewApiSettingsEntity == null) {
RetrofitManager.getInstance()
.getNewApi().getNewSettings(PackageUtils.getGhVersionName(), channel)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BiResponse<NewApiSettingsEntity>() {
@Override
public void onSuccess(NewApiSettingsEntity data) {
mNewApiSettingsEntity = data;
mNightModeSetting = data.getNightMode();
mNewSimulatorEntity = data.getSimulator();
if (HaloApp.getInstance().isNewForThisVersion && mNightModeSetting != null && mNightModeSetting.getInstall()) {
DarkModeUtils.INSTANCE.updateFollowSystemDarkModeToSp(true);
DarkModeUtils.INSTANCE.initDarkMode();
}
AdHelper.prefetchStartUpAd(mNewApiSettingsEntity);
SPUtils.setString(Constants.SP_NEW_API_SETTINGS, GsonUtils.toJson(data));
}
});
}
RetrofitManager.getInstance()
.getApi().getGameGuidePopup(Build.MANUFACTURER, Build.VERSION.RELEASE, Build.MODEL, channel, BuildConfig.VERSION_NAME)
.subscribeOn(Schedulers.io())
@ -444,4 +438,34 @@ public class Config {
});
}
}
@SuppressLint("CheckResult")
private static void getNewSettings(String channel) {
if (mNewApiSettingsEntity == null) {
String filterString = UrlFilterUtils.getFilterQuery(
"manufacturer", Build.MANUFACTURER,
"model", Build.MODEL,
"android_sdk_version", String.valueOf(Build.VERSION.SDK_INT));
RetrofitManager.getInstance()
.getNewApi().getNewSettings(PackageUtils.getGhVersionName(), channel, filterString)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BiResponse<NewApiSettingsEntity>() {
@Override
public void onSuccess(NewApiSettingsEntity data) {
mNewApiSettingsEntity = data;
mNightModeSetting = data.getNightMode();
mNewSimulatorEntity = data.getSimulator();
if (HaloApp.getInstance().isNewForThisVersion && mNightModeSetting != null && mNightModeSetting.getInstall()) {
DarkModeUtils.INSTANCE.updateFollowSystemDarkModeToSp(true);
DarkModeUtils.INSTANCE.initDarkMode();
}
AdHelper.prefetchStartUpAd(mNewApiSettingsEntity);
SPUtils.setString(Constants.SP_NEW_API_SETTINGS, GsonUtils.toJson(data));
}
});
}
}
}

View File

@ -31,7 +31,6 @@ import com.gh.common.chain.PackageCheckHandler;
import com.gh.common.chain.ValidateVSpaceHandler;
import com.gh.common.chain.VersionNumberHandler;
import com.gh.common.constant.Config;
import com.gh.gamecenter.feature.exposure.ExposureEvent;
import com.gh.common.filter.RegionSetting;
import com.gh.common.filter.RegionSettingHelper;
import com.gh.common.history.HistoryHelper;
@ -46,11 +45,7 @@ import com.gh.common.util.LogUtils;
import com.gh.common.util.NewsUtils;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.PackageUtils;
import com.gh.gamecenter.feature.utils.PlatformUtils;
import com.gh.common.util.ReservationHelper;
import com.gh.gamecenter.feature.view.DownloadButton;
import com.gh.gamecenter.feature.view.GameIconView;
import com.gh.gamecenter.common.view.NoEllipsizeSpaceTextView;
import com.gh.download.DownloadManager;
import com.gh.download.dialog.DownloadDialog;
import com.gh.gamecenter.DownloadManagerActivity;
@ -58,25 +53,29 @@ import com.gh.gamecenter.R;
import com.gh.gamecenter.WebActivity;
import com.gh.gamecenter.common.baselist.LoadStatus;
import com.gh.gamecenter.common.callback.OnViewClickListener;
import com.gh.gamecenter.common.entity.LinkEntity;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.common.utils.DarkModeUtils;
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.view.DrawableView;
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.databinding.KaifuDetailItemRowBinding;
import com.gh.gamecenter.feature.entity.ApkEntity;
import com.gh.gamecenter.feature.entity.CommunityVideoEntity;
import com.gh.gamecenter.feature.entity.GameEntity;
import com.gh.gamecenter.common.entity.LinkEntity;
import com.gh.gamecenter.feature.entity.PluginLocation;
import com.gh.gamecenter.feature.entity.ServerCalendarEntity;
import com.gh.gamecenter.feature.entity.TagStyleEntity;
import com.gh.gamecenter.feature.entity.TestEntity;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.feature.exposure.ExposureEvent;
import com.gh.gamecenter.feature.utils.PlatformUtils;
import com.gh.gamecenter.feature.view.DownloadButton;
import com.gh.gamecenter.feature.view.GameIconView;
import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment;
import com.gh.gamecenter.manager.PackagesManager;
import com.gh.gamecenter.feature.entity.CommunityVideoEntity;
import com.gh.vspace.VDownloadManagerActivity;
import com.gh.vspace.VHelper;
import com.lightgame.download.DownloadEntity;
@ -785,7 +784,7 @@ public class BindingAdapters {
}
}
public static void setGameName(NoEllipsizeSpaceTextView view, GameEntity game, boolean isShowPlatform, @Nullable Boolean isShowSuffix) {
public static void setGameName(TextView view, GameEntity game, boolean isShowPlatform, @Nullable Boolean isShowSuffix) {
if (isShowSuffix == null) isShowSuffix = true; // 默认显示
String gameName;
if (isShowPlatform && game.getApk().size() > 0) {

View File

@ -28,6 +28,7 @@ object RegionSettingHelper {
private const val SP_SETTING = "region_setting"
const val SP_SETTING_FAILURE = "region_setting_failure"
@JvmStatic
fun shouldThisGameDisplayMirrorInfo(gameId: String): Boolean {
return mDisplayMirrorIfoGameIdSet?.contains(gameId) ?: false
}

View File

@ -2,17 +2,17 @@ package com.gh.common.provider
import android.content.Context
import android.widget.LinearLayout
import android.widget.TextView
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.common.databind.BindingAdapters
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.view.NoEllipsizeSpaceTextView
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.provider.IBindingAdaptersProvider
@Route(path = RouteConsts.provider.bindingAdapters, name = "BindingAdapters暴露服务")
class BindingAdaptersProviderImpl : IBindingAdaptersProvider {
override fun setGameName(
view: NoEllipsizeSpaceTextView,
view: TextView,
game: GameEntity,
isShowPlatform: Boolean,
isShowSuffix: Boolean

View File

@ -13,8 +13,10 @@ import com.gh.base.GlobalActivityManager;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.base.activity.BaseActivity;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
import com.gh.gamecenter.common.retrofit.BiResponse;
import com.gh.gamecenter.common.utils.SensorsBridge;
import com.gh.gamecenter.core.AppExecutor;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.MtaHelper;
@ -30,6 +32,8 @@ import com.halo.assistant.HaloApp;
import com.lightgame.config.CommonDebug;
import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import io.reactivex.schedulers.Schedulers;
import io.sentry.Sentry;
import io.sentry.android.core.SentryAndroid;
@ -114,6 +118,8 @@ public class DataUtils {
String originalGid = HaloApp.getInstance().getGid();
HaloApp.getInstance().setGid(gid);
SensorsBridge.setGid(gid);
// 避免重复调用
if (!TextUtils.isEmpty(gid) && !gid.equals(originalGid)) {
GameSubstituteRepositoryHelper.updateSubstitutableGames();
@ -170,6 +176,8 @@ public class DataUtils {
values.put(GhContentProvider.KEY_IS_ADULT, false);
}
EventBus.getDefault().post(new EBReuse(Constants.EB_REALNAME_RESULT));
// new GhContentProvider().localInsert( HaloApp.getInstance().getApplication(),values);
try {
// Unknown URL content://com.gh.gamecenter.provider/certification

View File

@ -28,6 +28,7 @@ import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.constant.EntranceConsts.*
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.entity.*
import com.gh.gamecenter.common.entity.Display
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.runOnIoThread
@ -186,7 +187,8 @@ object DirectUtils {
"game_list",
"game_list_detail",
"bbs_video",
"explore_column"
"explore_column",
"column_test_v2"
)
fun directToLinkPage(

View File

@ -371,6 +371,14 @@ object DownloadObserver {
type = ExposureUtils.DownloadType.PLUGIN_DOWNLOAD
}
if (downloadEntity.isVGame()) {
SensorsBridge.trackEvent(
"HaloFunGameDownloadDone",
"game_name", downloadEntity.name,
"game_id", downloadEntity.gameId
)
}
var downloadSpeed = 0L
val elapsedTimeString = downloadEntity.meta[DownloadConfig.KEY_DOWNLOAD_ELAPSED_TIME]
if (elapsedTimeString != null) {
@ -393,7 +401,9 @@ object DownloadObserver {
id = downloadEntity.gameId,
mName = downloadEntity.name.removeSuffix(Constants.GAME_NAME_DECORATOR),
gameVersion = downloadEntity.versionName ?: "",
isPlatformRecommend = isPlatformRecommend
isPlatformRecommend = isPlatformRecommend,
adIconActive = downloadEntity.meta[Constants.AD_ICON_ACTIVE].toBoolean(),
isAdData = downloadEntity.meta[Constants.IS_AD_DATA].toBoolean()
),
downloadEntity.platform,
downloadEntity.exposureTrace,

View File

@ -318,6 +318,32 @@ public class LibaoUtils {
}
}
// 类型为复制的,不需要登录也可以直接复制
if ("copy".equals(libaoEntity.getReceiveMethod())) {
if ("finish".equals(libaoEntity.getStatus())) {
libaoBtn.setText(R.string.libao_finish);
libaoBtn.setBackgroundResource(R.drawable.button_border_round_gray);
libaoBtn.setTextColor(context.getResources().getColor(R.color.button_gray));
libaoBtn.setOnClickListener(v -> ToastUtils.toast("兑换码领取已结束"));
} else {
libaoBtn.setText(R.string.libao_copy);
libaoBtn.setTextColor(ExtensionsKt.toColor(R.color.white, context));
libaoBtn.setBackgroundResource(R.drawable.button_normal_round_style);
libaoBtn.setOnClickListener(v -> {
LogUtils.uploadReceiveGift(
"game_gift_code_successful",
libaoEntity.getId(),
libaoEntity.getName(),
"游戏详情",
libaoEntity.getGame().getId(),
libaoEntity.getGame().getName()
);
ExtensionsKt.copyTextAndToast(libaoEntity.getCode(), libaoEntity.getToast());
});
}
return;
}
libaoBtn.setOnClickListener(v -> {
String btnStatus = libaoBtn.getText().toString();
// 领取限制

View File

@ -1028,7 +1028,7 @@ public class LogUtils {
JSONObject object = new JSONObject();
JSONObject payloadObject = new JSONObject();
try {
object.put(KEY_EVENT, event);//game_gift_get_successful领取礼包、game_gift_dig_successful淘号
object.put(KEY_EVENT, event);//game_gift_get_successful领取礼包、game_gift_dig_successful淘号、game_gift_code_successful复制礼包
payloadObject.put("gift_id", giftId);
payloadObject.put("gift_name", giftName);
payloadObject.put("location", location);

View File

@ -808,13 +808,24 @@ object NewFlatLogUtils {
}
//点击兴趣推荐卡片
fun logDiscoverPageRecommendedInterestCardClick(sequence: Int, linkText: String, linkType: String, linkId: String) {
fun logDiscoverPageRecommendedInterestCardClick(
sequence: Int,
linkText: String,
linkType: String,
linkId: String,
entrance: String,
blockName: String = ""
) {
val json = json {
KEY_EVENT to "discover_page_recommended_interest_card_click"
"sequence" to sequence
"link_text" to linkText
"link_type" to linkType
"link_id" to linkId
"entrance" to entrance
if (blockName.isNotEmpty()) {
"block_name" to blockName
}
parseAndPutMeta().invoke(this)
}
log(json)
@ -1424,4 +1435,158 @@ object NewFlatLogUtils {
}
log(json, "event", false)
}
/**
* 新游开测首页/板块,点击右上角(全部/更多)
*/
fun logGameTestV2MoreClick(
text: String,
location: String,
blockId: String = "",
blockName: String = "",
linkType: String = "",
linkId: String = "",
linkText: String = ""
) {
val json = json {
"event" to "game_test_home_more_click"
"text" to text //右上角文案,包括:全部、更多
"location" to location //新游开测所处位置,包括:首页、版块
"block_id" to blockId //新游开测所处位置为“版块”上报版块ID
"block_name" to blockName //新游开测所处位置为“版块”,上报版块名称
"link_type" to linkType //右上角文案为“更多”时的链接类型
"link_id" to linkId //右上角文案为“更多”时的链接ID
"link_text" to linkText //右上角文案为“更多”时的跳转链接
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
/**
* 新游开测首页/板块,点击推荐标签
*/
fun logGameTestV2RecommendLabelClick(
location: String,
blockId: String = "",
blockName: String = "",
tagId: String = "",
text: String = "",
sequence: Int = 0,
linkType: String = "",
linkId: String = "",
linkText: String = ""
) {
val json = json {
"event" to "game_test_home_recommend_tag_click"
"location" to location //新游开测所处位置,包括:首页、版块
"block_id" to blockId //新游开测所处位置为“版块”上报版块ID
"block_name" to blockName //新游开测所处位置为“版块”,上报版块名称
"tag_id" to tagId //点击推荐标签的ID
"text" to text //点击推荐标签的文案
"sequence" to sequence //点击推荐标签在所有标签中所处位置,从左往右依次计算
"link_type" to linkType //点击推荐标签的链接类型
"link_id" to linkId //点击推荐标签的链接ID
"link_text" to linkText //点击推荐标签的跳转链接
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
/**
* 新游开测首页点击时间轴
*/
fun logGameTestV2TimeLineClick(
action: String,
type: String,
time: Long,
sequence: Int,
){
val json = json {
"event" to "game_test_home_timeline_click"
"action" to action //类型包括: 1.自动定位:滑动游戏时间轴自动更换定位 2.手动点击:手动点击时间轴的定位
"type" to type //类型包括“推荐/全部”,时间轴定位在“推荐”时记“推荐”,其余位置则记“全部”
"game_test_start" to time //节点时间字符串 2023-03-10 转时间戳
"sequence" to sequence //时间类型为“全部”时时间轴排除“推荐”从左往右依次计算记录在时间轴实际上所处的位置顺序例如12.30排在“推荐”后面则记录“1”
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
/**
* 新游开测条目完整展示时长上报
*/
fun logGameTestV2ItemViewTime(
location: String,
blockId: String = "",
blockName: String = "",
interval: Long = 0,
type: String = "",
startTime: Long = 0,
sequence: Int = 0,
){
val json = json {
"event" to "game_test_home_view"
"location" to location //新游开测所处位置,包括:首页、版块
"block_id" to blockId //新游开测所处位置为“版块”上报版块ID
"block_name" to blockName //新游开测所处位置为“版块”,上报版块名称
"interval" to interval //内容在屏幕可见范围完整展示的时长
"type" to type //触发事件时所停留的时间定位;类型包括“推荐/全部”,时间轴定位在“推荐”时记“推荐”,其余位置则记“全部”
"game_test_start" to startTime //触发事件时所停留的时间定位;时间类型为“全部”时,记录具体的月份日期 例如12.30、12.31
"sequence" to sequence //点击推荐标签在所有标签中所处位置,从左往右依次计算
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
//发现页下拉刷新
fun logDiscoverPageDropDownRefresh(refreshCount: Int) {
val json = json {
"event" to "discover_page_drop_down_refresh"
"count_num" to refreshCount
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
//更换头像弹窗点击
fun logChangeAvatarDialogClick(button: String) {
val json = json {
"event" to "profile_picture_change_dialog_click"
"button" to button
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
//默认头像弹窗点击
fun logDefaultAvatarDialogClick(button: String) {
val json = json {
"event" to "profile_picture_default_dialog_click"
"button" to button
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
fun logVpnHintDialogShow(gameId: String, gameName: String) {
val json = json {
KEY_EVENT to "profile_picture_default_dialog_click"
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
fun logVpnHintDialogClick(gameId: String, gameName: String, button: String) {
val json = json {
KEY_EVENT to "profile_picture_default_dialog_click"
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
"button" to button
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
}

View File

@ -137,7 +137,7 @@ object NewLogUtils {
}
// 游戏详情点击顶部标签
fun logGameDetailTagClick(gameId: String, gameName: String, tagId: String, tagName: String) {
fun logGameDetailTagClick(gameId: String, gameName: String, tagId: String, tagName: String, isTop: Boolean) {
val json = json {
KEY_EVENT to "game_type_tag_click_jump"
KEY_META to LogUtils.getMetaObject()
@ -145,6 +145,7 @@ object NewLogUtils {
KEY_GAME_NAME to gameName
"type_tag_id" to tagId
"type_tag_name" to tagName
"is_top_type_tag" to isTop
KEY_TIMESTAMP to System.currentTimeMillis() / 1000
}
log(json, LOG_STORE_EVENT)

View File

@ -1,11 +1,11 @@
package com.gh.common.util
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import com.gh.common.dialog.InstallPermissionDialogFragment
@ -13,25 +13,18 @@ import com.gh.common.xapk.XapkInstaller
import com.gh.download.server.BrowserInstallHelper
import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.DialogHelper
import com.gh.gamecenter.common.utils.getExtension
import com.gh.gamecenter.common.utils.getMetaExtra
import com.gh.gamecenter.common.utils.toRequestBody
import com.gh.gamecenter.core.utils.CurrentActivityHolder
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.vpn.VpnHelper
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
import com.lightgame.download.DownloadEntity
import com.lightgame.download.FileUtils
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import retrofit2.HttpException
import java.io.File
object PackageInstaller {
@ -64,15 +57,17 @@ object PackageInstaller {
return
}
// TODO 此处可能遇到 activity 是 WXEntryActivity
// TODO 当 activity 全部出栈,但是应用还在下载游戏,下载完会唤不起安装!
// 已知问题
// 1. 此处可能遇到 activity 是 WXEntryActivity因为 WXEntryActivity 不是 AppCompatActivity 调不起弹窗
// 2. 当 activity 全部出栈,但是应用还在下载游戏,下载完会唤不起安装
if (currentActivity is AppCompatActivity && !currentActivity.isFinishing) {
InstallPermissionDialogFragment.show(currentActivity, downloadEntity) { isFromPermissionGrantedCallback ->
// 取消状态栏下载完成的通知,若存在
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
if (isFromPermissionGrantedCallback && Build.VERSION.SDK_INT >= 31) {
// 安卓12后的设备需要重启应用来继续解压 XAPK 文件
if (isFromPermissionGrantedCallback && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val pm = context.packageManager
val intent = pm.getLaunchIntentForPackage(context.packageName)
val mainIntent = Intent.makeRestartActivityTask(intent!!.component)
@ -82,7 +77,7 @@ object PackageInstaller {
if (isXapk) {
XapkInstaller.install(context, downloadEntity, showUnzipToast)
} else {
install(context, downloadEntity.isPlugin, downloadEntity.path)
install(context, downloadEntity.isPlugin, downloadEntity.path, downloadEntity)
}
}
}
@ -94,9 +89,11 @@ object PackageInstaller {
*
* 除非特殊情况,请勿使用此方法进行应用安装操作
* 除非你已经确定该文件一定是Apk
*
* @param downloadEntity 仅用来记录日志用,非必须可为空
*/
@JvmStatic
fun install(context: Context, isPluggin: Boolean = false, pkgPath: String?) {
fun install(context: Context, isPluggin: Boolean = false, pkgPath: String?, downloadEntity: DownloadEntity? = null) {
if (pkgPath.isNullOrEmpty()) {
ToastUtils.toast("下载文件异常")
return
@ -112,20 +109,13 @@ object PackageInstaller {
}
if (PackageUtils.isCanLaunchSetup(context, pkgPath)) {
// val app = HaloApp.getInstance()
val currentActivity = CurrentActivityHolder.getCurrentActivity()
HaloApp.put(Constants.LAST_INSTALL_GAME, pkgPath)
// val downloadEntity =
// DownloadManager.getInstance().allDownloadEntity.firstOrNull {
// it.path == pkgPath
// }
// if (downloadEntity != null) {
// showCertificateDialogIfNeededBeforeInstall(app, downloadEntity, pkgPath)
// } else {
val installIntent = getInstallIntent(context, pkgPath)
context.startActivity(installIntent)
// }
if (VpnHelper.shouldUseVpn() && currentActivity is AppCompatActivity) {
turnOnVpnThenInstall(currentActivity, pkgPath, downloadEntity)
} else {
install(context, pkgPath)
}
} else {
if (isPluggin) {
DialogHelper.showPluginDialog(context) {
@ -143,47 +133,13 @@ object PackageInstaller {
}
}
@SuppressLint("CheckResult")
fun showCertificateDialogIfNeededBeforeInstall(context: Context, downloadEntity: DownloadEntity, pkgPath: String) {
val isOverwrite = LunchType.UPDATE.name == SPUtils.getString(Constants.SP_INSTALL_TYPE)
val hashMap = hashMapOf(Pair("url", downloadEntity.url), Pair("overwrite", isOverwrite))
RetrofitManager.getInstance().api.postCertificationCheck(hashMap.toRequestBody())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BiResponse<ResponseBody>() {
override fun onSuccess(data: ResponseBody) {
val installIntent = getInstallIntent(context, pkgPath)
context.startActivity(installIntent)
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
if (exception is HttpException) {
val resultString =
(exception as HttpException).response().errorBody()
?.string()
// 未实名
when {
resultString?.contains("403117") == true -> {
RealNameHelper.showRealNameUncertificatedDialog(
downloadEntity
)
}
resultString?.contains("403118") == true -> {
RealNameHelper.showRealNameUnqualifiedDialog(
downloadEntity
)
}
resultString?.contains("400004") == true -> {
ToastUtils.toast("安装服务异常,缺少参数")
}
}
} else {
ToastUtils.toast("安装服务异常,${exception.localizedMessage}请稍候再试")
}
}
})
/**
* 最终执行安装的方法
*/
private fun install(context: Context, pkgPath: String) {
HaloApp.put(Constants.LAST_INSTALL_GAME, pkgPath)
val installIntent = getInstallIntent(context, pkgPath)
context.startActivity(installIntent)
}
/**
@ -271,4 +227,71 @@ object PackageInstaller {
fun createDownloadId(gameName: String?): String {
return MD5Utils.getContentMD5(gameName + "_" + System.currentTimeMillis())
}
/**
* 启动 VPN 然后安装应用 (若没有授权会先提醒授权 VPN ,若拒绝授权会回落到直接执行安装)
*/
private fun turnOnVpnThenInstall(currentActivity: AppCompatActivity,
pkgPath: String,
downloadEntity: DownloadEntity?) {
if (VpnHelper.isVpnPermissionGranted(currentActivity) == true) {
VpnHelper.startVpn(currentActivity) { shouldShowVpnError ->
if (shouldShowVpnError) {
ToastUtils.toast("安装防护功能启动失败")
}
install(currentActivity, pkgPath)
}
} else {
val isTheFirstTimeToShowVpnHintDialog = VpnHelper.isTheFistTimeToShowVpnHintDialog()
downloadEntity?.let {
NewFlatLogUtils.logVpnHintDialogShow(it.gameId, it.name)
}
// 将 VPN 提示弹窗标记为已读(已读后的下一次显示"不再提醒"按钮)
VpnHelper.setVpnHintDialogShowed()
DialogHelper.showGuideDialog(
context = currentActivity,
title = "建议开启安装防护",
content = "为了帮助您安装官方的光环助手游戏包并加速安装进程避免安装游戏时被手机厂商替换导致重复下载浪费流量及手机存储空间建议您开启安装防护功能光环助手将向您申请开启VPN权限",
confirmText = "立即授权开启防护",
cancelText = "不再提醒",
confirmClickCallback = {
VpnHelper.startVpn(currentActivity) { shouldShowVpnError ->
if (shouldShowVpnError) {
ToastUtils.toast("安装防护功能启动失败")
}
install(currentActivity, pkgPath)
}
downloadEntity?.let {
NewFlatLogUtils.logVpnHintDialogClick(it.gameId, it.name, "立即授权")
}
},
cancelClickCallback = {
VpnHelper.ignoreVpnHintDialog()
install(currentActivity, pkgPath)
downloadEntity?.let {
NewFlatLogUtils.logVpnHintDialogClick(it.gameId, it.name, "不再提醒")
}
},
extraConfig = DialogHelper.Config(
showCloseIcon = true,
showAlternativeCancelStyle = !isTheFirstTimeToShowVpnHintDialog
),
uiModificationCallback = { binding, dialog ->
binding.cancelTv.visibility = View.GONE
binding.closeContainer.setOnClickListener {
install(currentActivity, pkgPath)
dialog.dismiss()
downloadEntity?.let {
NewFlatLogUtils.logVpnHintDialogClick(it.gameId, it.name, "关闭按钮")
}
}
}
)
}
}
}

View File

@ -23,6 +23,7 @@ import androidx.webkit.WebViewCompat;
import com.android.apksig.ApkVerifier;
import com.android.apksig.internal.apk.ApkSigningBlockUtilsLite;
import com.g00fy2.versioncompare.Version;
import com.gh.common.filter.RegionSettingHelper;
import com.gh.common.xapk.XapkInstaller;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.constant.Constants;
@ -139,6 +140,11 @@ public class PackageUtils {
}
}
// 镜像游戏,使用镜像 Apk 替换掉原来的 ApkNormal
if (RegionSettingHelper.shouldThisGameDisplayMirrorInfo(gameEntity.getId())) {
gameEntity.setApkNormal(gameEntity.getApk());
}
// 非插件游戏更新
for (ApkEntity apkEntity : gameEntity.getApkNormal()) {

View File

@ -12,10 +12,7 @@ import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.DarkModeUtils
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.sp2px
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.feature.entity.TagStyleEntity
import kotlin.math.ceil
@ -41,12 +38,12 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib
gravity = Gravity.CENTER_VERTICAL
val ta = context.obtainStyledAttributes(attrs, R.styleable.FlexLinearLayout)
mItemHeight = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemHeight, 20f.dip2px())
mPadding = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemPadding, 5f.dip2px())
mMargin = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemMargin, 4f.dip2px())
mTextSize = ta.getDimension(R.styleable.FlexLinearLayout_itemTextSize, 10f.sp2px().toFloat())
mLastItemWidth = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_lastItemWidth, 18f.dip2px())
mStrokeWidth = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_strokeWidth, 0.5f.dip2px())
mItemHeight = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemHeight, 20F.dip2px())
mPadding = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemPadding, 5F.dip2px())
mMargin = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemMargin, 4F.dip2px())
mTextSize = ta.getDimension(R.styleable.FlexLinearLayout_itemTextSize, 10F.sp2px().toFloat())
mLastItemWidth = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_lastItemWidth, 18F.dip2px())
mStrokeWidth = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_strokeWidth, 0.5F.dip2px())
ta.recycle()
}
@ -95,7 +92,7 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib
layoutParams = params
setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_game_detail_label_more))
if (DarkModeUtils.isDarkModeOn(context)) setColorFilter(R.color.text_title.toColor(context))
background = createBackgroundDrawable()
background = createNormalBackgroundDrawable()
scaleType = ImageView.ScaleType.CENTER
}
imageView.setOnClickListener {
@ -111,27 +108,33 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib
includeFontPadding = false
textSize = DisplayUtils.px2sp(context, mTextSize).toFloat()
gravity = Gravity.CENTER
setTextColor(R.color.text_title.toColor(context))
val params = LayoutParams(LayoutParams.WRAP_CONTENT, mItemHeight)
params.setMargins(0, 0, mMargin, 0)
setPadding(mPadding, 0, mPadding, 0)
layoutParams = params
val gradientDrawable = createBackgroundDrawable()
background = gradientDrawable
setTextColor(if (tag.isTop) "#${tag.color}".hexStringToIntColor() else R.color.text_title.toColor(context))
background = if (tag.isTop) createTopBackgroundDrawable(tag) else createNormalBackgroundDrawable()
setOnClickListener {
onClickListener?.onItemClickListener(tag)
}
}
}
private fun createBackgroundDrawable(): GradientDrawable {
private fun createNormalBackgroundDrawable(): GradientDrawable {
val gradientDrawable = GradientDrawable()
val isDarkModeOn = DarkModeUtils.isDarkModeOn(context)
gradientDrawable.setColor(Color.TRANSPARENT)
gradientDrawable.setStroke(mStrokeWidth, Color.parseColor(if (isDarkModeOn) "#33FFFFFF" else "#CCCCCC"))
gradientDrawable.cornerRadius = DisplayUtils.dip2px(2f).toFloat()
gradientDrawable.cornerRadius = 2F.dip2px().toFloat()
return gradientDrawable
}
private fun createTopBackgroundDrawable(tag: TagStyleEntity): GradientDrawable {
val gradientDrawable = GradientDrawable()
gradientDrawable.setColor("#${tag.background}".hexStringToIntColor())
gradientDrawable.cornerRadius = 2F.dip2px().toFloat()
return gradientDrawable
}

View File

@ -22,6 +22,7 @@ import com.gh.gamecenter.common.utils.DialogHelper;
import com.gh.gamecenter.common.utils.EnvHelper;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.ImageUtils;
import com.gh.gamecenter.common.utils.NetworkUtils;
import com.gh.gamecenter.core.utils.DisplayUtils;
import com.gh.gamecenter.core.utils.EmptyCallback;
import com.gh.gamecenter.core.utils.GsonUtils;
@ -139,6 +140,7 @@ public class RichEditor extends WebView {
setVerticalScrollBarEnabled(false);
setHorizontalScrollBarEnabled(false);
getSettings().setJavaScriptEnabled(true);
getSettings().setDomStorageEnabled(true);
setWebChromeClient(new WebChromeClient()); // 不要重写这个方法否则无法加载
setWebViewClient(createWebViewClient());
@ -573,6 +575,13 @@ public class RichEditor extends WebView {
exec("javascript:RE.formatBlock();");
}
/**
* 调用 JS 方法,告诉网页端该 url 对应视频播放的进度
*/
public void onVideoPlayedCallback(String url, int position) {
exec("javascript:RE.onVideoPlayedCallback('" + url + "', '" + position + "')");
}
public String getText() {
return HtmlUtils.stripHtml(mContents);
}
@ -586,7 +595,7 @@ public class RichEditor extends WebView {
return String.format("#%06X", (0xFFFFFF & color));
}
protected void exec(final String trigger) {
public void exec(final String trigger) {
if (isReady) {
load(trigger);
} else {
@ -708,7 +717,9 @@ public class RichEditor extends WebView {
@JavascriptInterface
public void onPageFinished() {
mPageFinishedListener.onPageFinished();
if (mPageFinishedListener != null) {
mPageFinishedListener.onPageFinished();
}
}
@JavascriptInterface
@ -721,6 +732,16 @@ public class RichEditor extends WebView {
return PackageUtils.getGhVersionCode();
}
@JavascriptInterface
public boolean isNetworkConnected() {
return NetworkUtils.isNetworkConnected(HaloApp.getInstance());
}
@JavascriptInterface
public boolean isWifiConnected() {
return NetworkUtils.isWifiConnected(HaloApp.getInstance());
}
/**
* 显示toast
*

View File

@ -152,7 +152,7 @@ object XapkInstaller : IXapkUnzipListener {
return@execute
}
PackageInstaller.install(mContext, downloadEntity.isPlugin, pkgPath)
PackageInstaller.install(mContext, downloadEntity.isPlugin, pkgPath, downloadEntity)
downloadEntity.meta[XAPK_UNZIP_PERCENT] = "100.0"
downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.SUCCESS.name

View File

@ -16,6 +16,7 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.gh.gamecenter.common.utils.ExtensionsKt;
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.exposure.ExposureEvent;
@ -326,6 +327,8 @@ public class DownloadManager implements DownloadStatusListener {
ExtensionsKt.addMetaExtra(downloadEntity, Constants.GAME_ICON_SUBSCRIPT, gameEntity.getIconSubscript());
ExtensionsKt.addMetaExtra(downloadEntity, Constants.IS_PLATFORM_RECOMMEND, apkEntity.getRecommend() != null ? "true" : "false");
ExtensionsKt.addMetaExtra(downloadEntity, Constants.GAME_NAME, gameEntity.getName());
ExtensionsKt.addMetaExtra(downloadEntity, Constants.AD_ICON_ACTIVE, String.valueOf(gameEntity.getAdIconActive()));
ExtensionsKt.addMetaExtra(downloadEntity, Constants.IS_AD_DATA, String.valueOf(gameEntity.isAdData()));
if (gameEntity.getIconFloat() != null) {
ExtensionsKt.addMetaExtra(downloadEntity, Constants.GAME_ICON_FLOAT_TOP_TEXT, gameEntity.getIconFloat().getUpperLeftText());
ExtensionsKt.addMetaExtra(downloadEntity, Constants.GAME_ICON_FLOAT_TOP_COLOR, gameEntity.getIconFloat().getUpperLeftColor());
@ -340,6 +343,10 @@ public class DownloadManager implements DownloadStatusListener {
ExtensionsKt.addMetaExtra(downloadEntity, Constants.SMOOTH_GAME, "true");
ExtensionsKt.addMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE, Constants.SMOOTH_GAME);
ExtensionsKt.addMetaExtra(downloadEntity, DownloadConfig.KEY_PROGRESS_CALLBACK_INTERVAL, "200");
SensorsBridge.trackEvent("HaloFunGameDownloadClick",
"game_name", gameEntity.getName(),
"game_id", gameEntity.getId());
}
HashMap<String, String> map = PageSwitchDataHelper.popLastPageData();
@ -1270,7 +1277,8 @@ public class DownloadManager implements DownloadStatusListener {
*/
public static void updateDownloadMetaMap() {
String isOverwrite;
if (LunchType.UPDATE.name().equals(SPUtils.getString(Constants.SP_INSTALL_TYPE))) {
String installType = SPUtils.getString(Constants.SP_INSTALL_TYPE);
if (LunchType.UPDATE.name().equals(installType)) {
isOverwrite = "true";
} else {
isOverwrite = "false";
@ -1285,6 +1293,7 @@ public class DownloadManager implements DownloadStatusListener {
map.put(HttpDnsManager.IMEI, MetaUtil.getBase64EncodedIMEI());
map.put(HttpDnsManager.TOKEN, UserManager.getInstance().getToken());
map.put(HttpDnsManager.IS_OVERWRITE, isOverwrite);
map.put(HttpDnsManager.INSTALL_TYPE, installType);
HttpDnsManager.metaMap = map;
}

View File

@ -83,7 +83,7 @@ class DownloadDialogAdapter(
holder.binding.root.layoutParams = this
}
val section = listData[position].section
holder.binding.title.text = if (section == DownloadDialogSectionType.OTHER) "其它版本" else "我的版本"
holder.binding.title.text = if (section == DownloadDialogSectionType.OTHER) "更多版本" else "我的版本"
holder.binding.otherVersionHint.goneIf(section != DownloadDialogSectionType.OTHER)
}
is DownloadDialogLinkItemViewHolder -> {

View File

@ -38,21 +38,21 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.core.ImagePipeline
import com.facebook.imagepipeline.request.ImageRequest
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.common.constant.Config
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.ImageUtils.getTransformedUrl
import com.gh.gamecenter.common.view.DraggableBigImageView
import com.gh.gamecenter.common.view.Gh_RelativeLayout
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.databinding.ActivityViewimageBinding
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.entity.ImageInfoEntity
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.retrofit.RetrofitManager
import com.github.piasy.biv.view.BigImageView
import com.github.piasy.biv.view.FrescoImageViewFactory
@ -849,9 +849,9 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener {
// ssiv.resetScaleAndCenter()
// }
// })
ssiv.setOnClickListener {
onBackPressed()
}
}
imageView.setOnClickListener {
onBackPressed()
}
}
})

View File

@ -36,10 +36,10 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.common.DefaultUrlHandler;
@ -307,15 +307,15 @@ public class MainActivity extends BaseActivity {
checkDialog();
}
// 默认配置为空时重试
if (Config.getSettings() == null) {
Config.getGhzsSettings();
}
// 耗时操作
AppExecutor.getIoExecutor().execute(() -> {
// 上传数据
DataCollectionManager.getInstance().upload();
// 获取默认配置
if (Config.getSettings() == null) {
Config.getGhzsSettings();
}
// 初始化PlatformUtils
PlatformUtils.getInstance(getApplicationContext());
@ -507,7 +507,27 @@ public class MainActivity extends BaseActivity {
private void observeStartUpAd() {
final StartupAdEntity startUpAd = AdHelper.getStartUpAd();
if (startUpAd != null && !TextUtils.isEmpty(startUpAd.getImg())) {
// 根据接口返回的广告时间,判断该图片广告是否还有必要显示
boolean isAdValid = false;
if (startUpAd.getTime() != null) {
long currentTimeInSecond = System.currentTimeMillis() / 1000;
if (currentTimeInSecond > startUpAd.getTime().getStart()
&& currentTimeInSecond < startUpAd.getTime().getEnd()) {
isAdValid = true;
}
} else {
// 接口没有返回开始和结束时间时,默认有效
isAdValid = true;
}
// 图片广告无效,跳过
if (!isAdValid) {
hideStartUpAd();
return;
}
final String showedTodayTimestamp = SPUtils.getString(Constants.SP_STARTUP_AD_TIMESTAMP, "");
final String rule = startUpAd.getRule();
switch (rule) {
@ -594,7 +614,7 @@ public class MainActivity extends BaseActivity {
SimpleDraweeView adImage = findViewById(R.id.adImage);
startAdContainer.setVisibility(View.VISIBLE);
jumpDetailBtn.setText(ad.getDesc());
ExtensionsKt.setDrawableEnd(jumpDetailBtn, VectorDrawableCompat.create(getResources(), R.drawable.ic_startup_ad_arrow, null), null, null);
ExtensionsKt.setDrawableEnd(jumpDetailBtn, AppCompatResources.getDrawable(this, R.drawable.ic_startup_ad_arrow), null, null);
ImageUtils.display(adImage, ad.getImg());
startAdContainer.setOnClickListener(v -> {
// do nothing 只是为了点击拦截事件,避免传递到下面的页面
@ -987,7 +1007,7 @@ public class MainActivity extends BaseActivity {
public void onEventMainThread(EBNetworkState busNetworkState) {
if (busNetworkState.isNetworkConnected()) {
if (Config.getSettings() == null) {
AppExecutor.getIoExecutor().execute(Config::getGhzsSettings);
Config.getGhzsSettings();
}
mPackageViewModel.checkData();

View File

@ -35,6 +35,7 @@ import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.entity.PrivacyPolicyEntity
import com.gh.gamecenter.feature.utils.PlatformUtils
import com.gh.gamecenter.pkg.PkgHelper
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
import com.lightgame.download.FileUtils
@ -273,6 +274,8 @@ class SplashScreenActivity : BaseActivity() {
private fun doFlavorInit() {
HaloApp.getInstance().flavorProvider.init(HaloApp.getInstance(), this)
SensorsBridge.init(HaloApp.getInstance(), HaloApp.getInstance().channel)
}
private fun getGitLogString(): String {
@ -347,7 +350,6 @@ class SplashScreenActivity : BaseActivity() {
private fun prefetchData() {
runOnIoThread {
Config.getGhzsSettings()
mViewModel?.deviceDialogSetting()
mViewModel?.filterDetailTags()
mViewModel?.authDialog()
@ -356,6 +358,10 @@ class SplashScreenActivity : BaseActivity() {
checkAndPostUsageStats()
updateGameSubstituteRepository()
if (BuildConfig.CONFIG_ID.isNotEmpty()) {
PkgHelper.requestPkgConfig(BuildConfig.CONFIG_ID)
}
// 获取自动刷新的cd获取版本对应表
val time = mSharedPreferences!!.getString("refresh_time", null)
val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
@ -438,7 +444,7 @@ class SplashScreenActivity : BaseActivity() {
if (position == mPics.size - 1) {
val tvSkip = view.findViewById<TextView>(R.id.splsh_guide_tv_skip)
// 如果屏幕特短,或者是平板的横屏显示,把图片改成按高度显示
if (DisplayUtils.isUltraShortScreen()) {
if (DisplayUtils.isUltraShortScreen(this@SplashScreenActivity)) {
ivImage.scaleType = ImageView.ScaleType.CENTER_INSIDE
}
tvSkip.setOnClickListener {

View File

@ -202,7 +202,7 @@ public class LibaoDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
if (mLibaoEntity.getGame() != null) {
holder.binding.libaodetailGameIcon.displayGameIcon(mLibaoEntity.getGame().getIcon(), mLibaoEntity.getGame().getIconSubscript(), mLibaoEntity.getGame().getIconFloat());
GameEntity gameEntity = mLibaoEntity.getGame().toGameEntity();
GameItemViewHolder.initGameSubtitle(gameEntity, holder.binding.gameSubtitleTv, null, null, false, null);
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, holder.binding.gameSubtitleTv, null, null, false, null, false, null);
} else {
holder.binding.libaodetailGameIcon.displayGameIcon(mLibaoEntity.getIcon(), null, mLibaoEntity.getGame().getIconFloat());
}

View File

@ -12,7 +12,6 @@ import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.gamecenter.R;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.view.DrawableView;
import com.gh.gamecenter.common.view.NoEllipsizeSpaceTextView;
import com.gh.gamecenter.feature.databinding.GameItemBinding;
import com.gh.gamecenter.feature.entity.ColorEntity;
import com.gh.gamecenter.feature.entity.GameEntity;
@ -22,7 +21,7 @@ import com.gh.gamecenter.feature.view.GameIconView;
public class GameViewHolder extends RecyclerView.ViewHolder {
public GameIconView gameThumb;
public NoEllipsizeSpaceTextView gameName;
public TextView gameName;
public DownloadButton gameDownloadBtn;
public TextView gameDes;
public LinearLayout gameLabelList;

View File

@ -27,8 +27,8 @@ import com.gh.gamecenter.databinding.AmwayCommentItemBinding
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureSource
import com.gh.gamecenter.game.GameAndPosition
import com.gh.gamecenter.feature.game.GameItemViewHolder
import com.gh.gamecenter.game.GameAndPosition
import com.gh.gamecenter.game.vertical.GameVerticalAdapter
import com.gh.gamecenter.gamedetail.rating.RatingFragment
import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity
@ -240,7 +240,7 @@ class AmwayAdapter(
}
itemData.exposureEvent = ExposureEvent.createEvent(gameEntity, basicExposureSource)
GameItemViewHolder.initGameSubtitle(gameEntity, binding.gameSubtitleTv)
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, binding.gameSubtitleTv)
binding.gameContainer.setOnClickListener {
GameDetailActivity.startGameDetailActivity(

View File

@ -2,8 +2,8 @@ package com.gh.gamecenter.amway.search
import android.os.Bundle
import android.view.View
import androidx.appcompat.content.res.AppCompatResources
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.databinding.FragmentAmwaySearchDefaultBinding
@ -65,11 +65,12 @@ class AmwaySearchDefaultFragment : SearchDefaultFragment() {
headTitle.textSize = 16F
headActionTv.text = "清空"
headActionTv.setTextColor(R.color.text_subtitleDesc.toColor(requireContext()))
headActionTv.setDrawableStart(VectorDrawableCompat.create(
resources,
R.drawable.search_history_delete,
null
))
headActionTv.setDrawableStart(
AppCompatResources.getDrawable(
requireContext(),
R.drawable.search_history_delete
)
)
headActionTv.setOnClickListener {
DialogHelper.showCenterWarningDialog(requireContext(), "清空记录", "确定清空历史搜索记录?", confirmClickCallback = {
mSearchDao.deleteAll()

View File

@ -221,7 +221,6 @@ class CategoryV2ListAdapter(
inner class CategoryGameItemViewHolder(val binding: CategoryGameItemBinding) :
BaseRecyclerViewHolder<Any>(binding.root) {
fun bindGameItem(gameEntity: GameEntity) {
binding.run {
gameIconView.displayGameIcon(gameEntity)
@ -240,7 +239,7 @@ class CategoryV2ListAdapter(
)
gameDes.text = gameEntity.decoratedDes
GameItemViewHolder.initGameSubtitle(gameEntity, gameSubtitleTv, gameDesSpace, root)
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitleTv)
}
}
@ -279,6 +278,8 @@ class CategoryV2ListAdapter(
recommendContainer = binding.recommendContainer
recommendTv = binding.recommendTv
recommendIv = binding.recommendIv
multiVersionDownloadTv = binding.multiVersionDownloadTv
gameDownloadTips = binding.downloadTipsLottie
}
}
}

View File

@ -7,9 +7,6 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.constant.Config
import com.gh.common.databind.BindingAdapters
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureSource
import com.gh.gamecenter.feature.exposure.ExposureType
import com.gh.common.exposure.IExposable
import com.gh.common.util.DirectUtils
import com.gh.common.util.DownloadItemUtils
@ -34,12 +31,15 @@ import com.gh.gamecenter.databinding.ItemRecommendInterestImageBinding
import com.gh.gamecenter.discovery.DiscoveryFragment.Companion.INTERESTED_GAME_REQUEST_CODE
import com.gh.gamecenter.discovery.interestedgame.InterestedGameActivity
import com.gh.gamecenter.entity.DiscoveryGameCardLabel
import com.gh.gamecenter.feature.entity.GameEntity
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.ExposureSource
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.gh.gamecenter.feature.game.GameItemViewHolder
import com.lightgame.download.DownloadEntity
import org.greenrobot.eventbus.EventBus
@ -248,7 +248,8 @@ class DiscoveryAdapter(
position,
label.text ?: "",
label.type ?: "",
label.link ?: ""
label.link ?: "",
"发现详情页"
)
}
@ -315,8 +316,6 @@ class DiscoveryAdapter(
gameKaifuType.setBackgroundColor(R.color.theme.toColor(root.context))
gameName.setTextColor(R.color.text_title.toColor(root.context))
gameDes.setTextColor(R.color.text_subtitleDesc.toColor(root.context))
downloadSpeed.setTextColor(R.color.text_subtitleDesc.toColor(root.context))
downloadPercentage.setTextColor(R.color.theme_font.toColor(root.context))
gameIconView.displayGameIcon(gameEntity)
BindingAdapters.setGameName(gameName, gameEntity, false, null)
@ -338,8 +337,6 @@ class DiscoveryAdapter(
gameDes.visibleIf(gameEntity.recommendTag.isNotEmpty()) {
gameDes.text = "根据 “${gameEntity.recommendTag}” 兴趣推荐"
}
//type: recommend 专题游戏 | ad 广告游戏 | filter 算法游戏
adLabelTv.goneIf(!(gameEntity.type == "ad" && gameEntity.adIconActive))
if (gameEntity.type == "ad" && gameEntity.adIconActive) {
recommendReasonTv.visibility = View.GONE
} else {
@ -360,11 +357,15 @@ class DiscoveryAdapter(
recommendReasonTv.visibility = View.VISIBLE
}
}
GameItemViewHolder.initGameSubtitle(gameEntity, gameSubtitleTv) {
gameDesSpace.post {
gameSubtitleTv.maxWidthExcludeZero(if (gameEntity.type == "ad" && gameEntity.adIconActive) gameDesSpace.width - 25F.dip2px() else gameDesSpace.width)
}
}
//type: recommend 专题游戏 | ad 广告游戏 | filter 算法游戏
GameItemViewHolder.initGameSubtitleAndAdLabel(
gameEntity,
gameSubtitleTv,
gameNameContainer,
gameName,
gameEntity.type == "ad" && gameEntity.adIconActive,
adLabelTv
)
}
}
}

View File

@ -57,6 +57,9 @@ class DiscoveryFragment : LazyListFragment<DiscoveryItemData, DiscoveryViewModel
private var mPageBottomVisibleCount = 0
private lateinit var mExposureListener: ExposureListener
//下拉刷新次数
private var mRefreshCount = 0
private val dataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
mAdapter?.notifyItemByDownload(downloadEntity)
@ -75,6 +78,11 @@ class DiscoveryFragment : LazyListFragment<DiscoveryItemData, DiscoveryViewModel
mCachedView.setBackgroundColor(R.color.background_white.toColor(requireContext()))
}
override fun onRefresh() {
super.onRefresh()
NewFlatLogUtils.logDiscoverPageDropDownRefresh(++mRefreshCount)
}
override fun initSkeletonScreen() {
if (mSkeletonScreenView == null) return
mSkeletonScreen = Skeleton.bind(mSkeletonScreenView)

View File

@ -0,0 +1,13 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class DefaultAvatar(
@SerializedName("_id")
val id: String = "",
val url: String = "",
val order: String = ""
) : Parcelable

View File

@ -1,11 +1,13 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.feature.entity.MeEntity
import com.gh.gamecenter.feature.entity.SimpleGame
import com.gh.gamecenter.feature.entity.UserEntity
import com.gh.gamecenter.feature.entity.ZoneEntity
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
class ForumDetailEntity(
@SerializedName("_id")
@ -21,7 +23,11 @@ class ForumDetailEntity(
var zone: ZoneEntity? = null,
var me: MeEntity = MeEntity(),
var type: String = "", //game_bbs/official_bbs
var hot: Int = 0 // 热度值
var hot: Int = 0, // 热度值
@SerializedName("hot_rank")
var hotRank: Int = 0, // 热度排名
@SerializedName("background_layer_color")
var backgroundLayerColor: String = "" // 背景图色值
) {
class TopLinkEntity(
@ -41,6 +47,13 @@ class ForumDetailEntity(
var fontColor: String = ""
)
@Parcelize
class Section(
@SerializedName("_id")
var id: String = "",
var name: String = ""
) : Parcelable
fun convertForumDetailEntityToForumEntity(): ForumEntity {
val forumEntity = ForumEntity()
forumEntity.id = id

View File

@ -24,6 +24,8 @@ data class HomeContent(
val recommendText: String = "",
@SerializedName("common_collection")
val commonCollection: CommonCollectionEntity? = null,
@SerializedName("column_test_v2_data")
val testV2Data: HomeItemTestV2Entity? = null,
val image: String = "",
@SerializedName("first_line_recommend")
val firstLineRecommend: String = "",

View File

@ -0,0 +1,91 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import androidx.annotation.Keep
import androidx.room.Entity
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
/**
* @author : liujiarui
* date : 2023/3/3
* description : 首页-推荐tab-新游开测 item 实体类
*/
@Keep
@Entity
@Parcelize
data class HomeItemTestV2Entity(
@SerializedName("action")
val action: String = "",
@SerializedName("count")
val count: Int = 0,
@SerializedName("limit")
val limit: Int = 0,
@SerializedName("page_id")
val pageId: String = "",
@SerializedName("recommend_label")
val recommendLabel: List<RecommendLabel> = listOf(),
@SerializedName("right_top")
val rightTop: RightTop = RightTop(),
@SerializedName("start_id")
val startId: String = "",
@SerializedName("start_point")
val startPoint: Map<String, String> = mapOf(), // key 时间轴名称value 当前时间轴第一个游戏id
@SerializedName("data_count")
val dataCount: Map<String, Int> = mapOf(), //key 时间轴名称value 当前时间轴所包含的游戏数量
@SerializedName("time_type")
val timeType: String = "",
// 首屏游戏列表
val data: List<GameData> = emptyList()
) : Parcelable
@Keep
@Entity
@Parcelize
data class RecommendLabel(
@SerializedName("_id")
val id: String = "",
@SerializedName("link")
val link: LinkEntity = LinkEntity(),
@SerializedName("text")
val text: String = ""
) : Parcelable
@Keep
@Entity
@Parcelize
data class RightTop(
@SerializedName("link")
val link: LinkEntity = LinkEntity(),
@SerializedName("text")
val text: String = ""
) : Parcelable
@Keep
@Entity
@Parcelize
data class GameData(
@SerializedName("start_midnight")
val startMidnight: Long = 0L,
@SerializedName("time_type")
val timeType: String = "",//时间轴recommend推荐、xxxx-xx-xx全部比如2023-01-01
var games: List<GameEntity> = emptyList()
) : Parcelable
/**
* 新游开测游戏列表包装类
*/
@Keep
data class GameDataWrapper(
var index: Int,
val pageId: String = "",
val timeType: String = "",
val gameData: GameEntity? = null,
var isPlaceHolder: Boolean = false, //占位标识
var isSpace: Boolean = false, //空条目标识
)

View File

@ -42,7 +42,10 @@ data class LibaoEntity(
@SerializedName("activity_link")
var activityLink: ActivityLink = ActivityLink(), //关联活动链接数据
@SerializedName("app_and_plugin_hide")
var appAndPluginHide: Boolean = false, //app和插件内不显示
var appAndPluginHide: Boolean = false, // app和插件内不显示
@SerializedName("receive_method")
var receiveMethod: String = "", // 当值为 copy 时,仅领取不能进入详情
var toast: String = "", // 点击游戏详情页的领取按钮时的 toast 内容
//本地字段
var clickReceiveBtnIn: Boolean = false//是否是点击列表领取按钮进入

View File

@ -11,7 +11,8 @@ class NewApiSettingsEntity(
var startAd: StartupAdEntity? = null,//开屏图片广告
var startup: StartupAdEntity? = null,//启动文案广告
@SerializedName("user_interested_game")
var userInterestedGame: Boolean = false //偏好设置状态开关
var userInterestedGame: Boolean = false, //偏好设置状态开关
var install: Install, // 安装相关的
) {
/**
*
@ -24,4 +25,17 @@ class NewApiSettingsEntity(
val setting: Boolean,
val install: Boolean
)
// VPN 配置
class Install(
@SerializedName("vpn_required")
val vpnRequired: VpnSetting? = null
)
class VpnSetting(
@SerializedName("current_device")
val shouldShowVpnOption: Boolean,
@SerializedName("packages")
val vpnMatchedPackagesName: HashSet<String>
)
}

View File

@ -11,5 +11,8 @@ class StartupAdEntity(
val jump: LinkEntity,
val img: String = "",
// 显示规则: none无, each每一次打开, everyday每一天一次, once一次
val rule: String = ""
)
val rule: String = "",
val time: TimeEntity? = null
) {
class TimeEntity(val start: Long = 0, val end: Long = 0)
}

View File

@ -43,6 +43,9 @@ data class SubjectEntity(
@SerializedName("game_list_collection")
var gameListCollection: List<GamesCollectionEntity>? = null,
@SerializedName("column_test_v2_data")
val testV2Data: HomeItemTestV2Entity? = null,
@SerializedName("show_name")
var showName: Boolean = true, // 是否显示“专题名字”true、false
@SerializedName("show_suffix")

View File

@ -17,6 +17,10 @@ import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.databinding.CommunityAnswerItemBinding
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.databinding.ItemForumArticleHeadBinding
import com.gh.gamecenter.forum.home.ForumArticleAskItemViewHolder
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.feature.entity.AnswerEntity
@ -24,15 +28,29 @@ import com.gh.gamecenter.feature.entity.Questions
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntrance: String, val path: String) :
class ForumArticleAskListAdapter(
context: Context,
val bbsId: String,
val entrance: String,
val path: String,
val viewModel: ForumArticleAskListViewModel,
val onCheckCallback: (String) -> Unit
) :
ListAdapter<AnswerEntity>(context), ISyncAdapterHandler {
private var mDefOrderList = listOf("回复", "发布")
private var mVideoOrderList = listOf("推荐", "发布")
private var mFilterPosition = 0
override fun areItemsTheSame(oldItem: AnswerEntity?, newItem: AnswerEntity?): Boolean {
return oldItem?.id == newItem?.id
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
ItemViewType.ITEM_HEADER -> {
ForumArticleHeadViewHolder(parent.toBinding())
}
ItemViewType.ITEM_FOOTER -> {
FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false))
}
@ -51,6 +69,7 @@ class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntra
}
override fun getItemViewType(position: Int): Int {
if (position == 0) return ItemViewType.ITEM_HEADER
if (position == itemCount - 1) return ItemViewType.ITEM_FOOTER
return ItemViewType.ITEM_BODY
}
@ -62,6 +81,7 @@ class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntra
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemViewType(position)) {
ItemViewType.ITEM_BODY -> {
val articlePosition = position - 1
val answer = mEntityList[position]
if (answer.type != "answer") {
val questions = Questions()
@ -80,7 +100,7 @@ class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntra
val answerViewHolder = holder as ForumArticleAskItemViewHolder
val binding = answerViewHolder.binding
binding.forumNameLl.visibility = View.GONE
binding.topLine.goneIf(position == 0)
binding.topLine.goneIf(articlePosition == 0)
val params = binding.includeVoteAndComment.root.layoutParams as LinearLayout.LayoutParams
params.width = LinearLayout.LayoutParams.MATCH_PARENT
@ -89,12 +109,12 @@ class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntra
binding.includeVoteAndComment.root.layoutParams = params
val tabInfo = "${path}tab"
answerViewHolder.bindForumAnswerItem(answer, mEntrance, path, position)
answerViewHolder.bindForumAnswerItem(answer, entrance, path, articlePosition)
answerViewHolder.itemView.setOnClickListener {
val entrance = BaseActivity.mergeEntranceAndPath(mEntrance, path)
val entrance = BaseActivity.mergeEntranceAndPath(entrance, path)
val userId = answer.user.id ?: ""
val contentId = answer.id ?: ""
val sequence = position + 1
val sequence = articlePosition + 1
val bbsType = if (answer.bbs.type == "official_bbs") "综合论坛" else "游戏论坛"
when (answer.type) {
"community_article" -> {
@ -118,7 +138,7 @@ class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntra
mContext,
CommunityEntity(bbsId),
answer.id!!,
mEntrance,
entrance,
path
)
)
@ -160,7 +180,7 @@ class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntra
mContext.startActivity(
NewQuestionDetailActivity.getIntent(
mContext, answer.id
?: "", mEntrance, path
?: "", entrance, path
)
)
}
@ -183,7 +203,7 @@ class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntra
mContext.startActivity(
NewQuestionDetailActivity.getCommentIntent(
mContext, answer.questions.id, answer.id
?: "", mEntrance, path
?: "", entrance, path
)
)
}
@ -197,6 +217,31 @@ class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntra
footerViewHolder.hint.textSize = 12f
footerViewHolder.hint.setTextColor(ContextCompat.getColor(mContext, R.color.aaaaaa))
}
ItemViewType.ITEM_HEADER -> {
if (holder is ForumArticleHeadViewHolder) {
val articleListHead = if (path == "全部") {
viewModel.selectedSection.name
} else path
holder.binding.root.setBackgroundColor(R.color.background_white.toColor(mContext))
holder.binding.articleListHeadTv.setTextColor(R.color.text_title.toColor(mContext))
holder.binding.articleListHeadTv.text = "${articleListHead}列表"
holder.binding.orderSfv.run {
setContainerBackground(R.drawable.button_round_f5f5f5.toDrawable(mContext))
setIndicatorBackground(R.drawable.bg_game_collection_sfv_indicator.toDrawable(mContext))
setTextColor(
R.color.text_subtitle.toColor(mContext),
R.color.text_subtitleDesc.toColor(mContext)
)
setItemList(if (path == "视频") mVideoOrderList else mDefOrderList, mFilterPosition)
setOnCheckedCallback { position ->
mFilterPosition = position
onCheckCallback.invoke((if (path == "视频") mVideoOrderList else mDefOrderList)[position])
}
}
}
}
}
}
@ -205,4 +250,6 @@ class ForumArticleAskListAdapter(context: Context, val bbsId: String, val mEntra
val entity = mEntityList[position]
return Pair(entity.id ?: "", entity)
}
class ForumArticleHeadViewHolder(val binding: ItemForumArticleHeadBinding) : RecyclerView.ViewHolder(binding.root)
}

View File

@ -18,6 +18,7 @@ import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.iinterface.IScrollable
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.databinding.FragmentListBaseSkeletonBinding
import com.gh.gamecenter.entity.ForumDetailEntity
import com.gh.gamecenter.eventbus.EBDeleteDetail
import com.gh.gamecenter.eventbus.EBTypeChange
import com.gh.gamecenter.eventbus.EBUserFollow
@ -39,6 +40,9 @@ class ForumArticleAskListFragment : LazyListFragment<AnswerEntity, ForumArticleA
private var bbsId: String = ""
private var mListReachTop = true
var filter = "回复"
var videoFilter = "推荐"
override fun getRealLayoutId(): Int = R.layout.fragment_list_base_skeleton
override fun onRealLayoutInflated(inflatedView: View) {
@ -47,8 +51,10 @@ class ForumArticleAskListFragment : LazyListFragment<AnswerEntity, ForumArticleA
}
override fun provideListViewModel(): ForumArticleAskListViewModel {
val factory = ForumArticleAskListViewModel.Factory(bbsId, mPath)
mViewModel = viewModelProvider(factory)
if (mViewModel == null) {
val factory = ForumArticleAskListViewModel.Factory(bbsId, mPath)
mViewModel = viewModelProvider(factory)
}
return mViewModel!!
}
@ -56,8 +62,11 @@ class ForumArticleAskListFragment : LazyListFragment<AnswerEntity, ForumArticleA
requireContext(),
bbsId,
mEntrance,
mPath
).apply { mAdapter = this }
mPath,
mViewModel!!
) {
onRefresh(it)
}.apply { mAdapter = this }
override fun getItemDecoration() = null
@ -167,9 +176,11 @@ class ForumArticleAskListFragment : LazyListFragment<AnswerEntity, ForumArticleA
fun onRefresh(filter: String) {
if (mPath == "全部" || mPath == "问答") {
this.filter = filter
mViewModel?.sort = if (filter == "发布") "time.create" else "time.reply"
}
if (mPath == "视频") {
videoFilter = filter
mViewModel?.videoSort = if (filter == "推荐") "recommend" else "time.upload"
}
onRefresh()
@ -308,6 +319,11 @@ class ForumArticleAskListFragment : LazyListFragment<AnswerEntity, ForumArticleA
}
}
fun setSection(section: ForumDetailEntity.Section) {
mViewModel?.selectedSection = section
onRefresh()
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
mAdapter?.run { notifyItemRangeChanged(0, itemCount) }

View File

@ -8,6 +8,7 @@ import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.core.utils.PageSwitchDataHelper
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.UrlFilterUtils
import com.gh.gamecenter.entity.ForumDetailEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.retrofit.RetrofitManager
@ -18,6 +19,8 @@ class ForumArticleAskListViewModel(application: Application, val bbsId: String =
ListViewModel<AnswerEntity, AnswerEntity>(application) {
var sort: String = "time.reply"
var filter: String = "section_id"
var selectedSection = ForumDetailEntity.Section("", "全部")
var videoSort: String = "recommend"
var videoList = arrayListOf<ForumVideoEntity>()
@ -27,6 +30,10 @@ class ForumArticleAskListViewModel(application: Application, val bbsId: String =
val data = PageSwitchDataHelper.popLastPageData()
val map = hashMapOf<String, Any?>()
if (selectedSection.id.isNotEmpty()) {
map["filter"] = UrlFilterUtils.getFilterQuery(filter, selectedSection.id)
}
if (data != null && data.containsKey(EntranceConsts.KEY_TOP_ID)) {
map["top_id"] = data[EntranceConsts.KEY_TOP_ID]
}
@ -79,7 +86,7 @@ class ForumArticleAskListViewModel(application: Application, val bbsId: String =
it.transformForumVideoEntity()
})
mResultLiveData.postValue(list)
mResultLiveData.postValue(ArrayList(list).apply { add(0, AnswerEntity().apply { id = "" }) })
}
}

View File

@ -52,6 +52,15 @@ class ForumDetailActivity : BaseActivity() {
intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance)
return intent
}
@JvmStatic
fun getTrendsIntent(context: Context, bbsId: String, entrance: String): Intent {
val intent = Intent(context, ForumDetailActivity::class.java)
intent.putExtra(EntranceConsts.KEY_BBS_ID, bbsId)
intent.putExtra(EntranceConsts.KEY_SCROLL_TO_TRENDS, true)
intent.putExtra(EntranceConsts.KEY_ENTRANCE, entrance)
return intent
}
}
override fun handleBackPressed(): Boolean {

View File

@ -4,7 +4,9 @@ import android.animation.ValueAnimator
import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.GradientDrawable
import android.os.Build
import android.os.Bundle
import android.view.Gravity
@ -12,14 +14,22 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.os.bundleOf
import androidx.core.view.ViewCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.PagerAdapter
import com.ethanhua.skeleton.Skeleton
import com.ethanhua.skeleton.ViewSkeletonScreen
import com.facebook.drawee.drawable.ScalingUtils
@ -32,6 +42,7 @@ import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.NewsDetailActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.TrackableDialog
import com.gh.gamecenter.common.base.adapter.FragmentStateAdapter
import com.gh.gamecenter.common.base.fragment.BaseFragment_TabLayout
import com.gh.gamecenter.common.base.fragment.BaseLazyTabFragment
import com.gh.gamecenter.common.callback.BiCallback
@ -40,11 +51,11 @@ import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.mvvm.Status
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.SegmentedFilterView
import com.gh.gamecenter.core.iinterface.IScrollable
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.databinding.FragmentForumDetailBinding
import com.gh.gamecenter.databinding.PopupForumDetailSectionsBinding
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.eventbus.EBForumFollowChange
import com.gh.gamecenter.eventbus.EBTypeChange
@ -52,15 +63,17 @@ import com.gh.gamecenter.forum.home.CommunityHomeFragment
import com.gh.gamecenter.forum.moderator.ApplyModeratorActivity
import com.gh.gamecenter.forum.moderator.ModeratorListActivity
import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity
import com.gh.gamecenter.gamedetail.GameDetailFragment
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.UserEntity
import com.gh.gamecenter.gamedetail.fuli.FuLiFragment
import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity
import com.gh.gamecenter.qa.video.publish.VideoPublishActivity
import com.google.android.material.appbar.AppBarLayout
import com.halo.assistant.fragment.WebFragment
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@ -70,24 +83,27 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
private var mAllForumArticleAskListFragment: ForumArticleAskListFragment? = null
private var mEssenceForumArticleAskListFragment: ForumArticleAskListFragment? = null
private var mTrendsFragment: Fragment? = null
private var mAskForumArticleAskListFragment: ForumArticleAskListFragment? = null
private var mVideoForumArticleAskListFragment: ForumArticleAskListFragment? = null
private var mBbsId: String = ""
private var mBbsType: String = ""
private var mViewModel: ForumDetailViewModel? = null
private var mForumDetail: ForumDetailEntity? = null
private var mForumSectionAdapter: ForumSectionAdapter? = null
private var mPopupWindow: PopupWindow? = null
private lateinit var mBinding: FragmentForumDetailBinding
private lateinit var mSkeleton: ViewSkeletonScreen
private var mFilter = "回复"
private var mVideoFilter = "推荐"
private var mLastPosition = 0
private var mAppbarReachTop = true
private var mDefOrderList = listOf("回复", "发布")
private var mVideoOrderList = listOf("推荐", "发布")
private var mIsExpand = false
private var mStatus: ApplyModeratorStatusEntity = ApplyModeratorStatusEntity()
private var mIsToolbarWhite = false
private var mShowSections = false
private var mAskIndex = INDEX_ASK
private var mVideoIndex = INDEX_VIDEO
override fun initFragmentList(fragments: MutableList<Fragment>) {
mAllForumArticleAskListFragment = ForumArticleAskListFragment().with(
@ -128,6 +144,9 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
"精华" -> {
mEssenceForumArticleAskListFragment = fragment as ForumArticleAskListFragment
}
"专区" -> {
mTrendsFragment = fragment
}
"问答" -> {
mAskForumArticleAskListFragment = fragment as ForumArticleAskListFragment
}
@ -144,6 +163,10 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
tabTitleList.add(TAB_TITLE_VIDEO)
}
override fun providePagerAdapter(): PagerAdapter {
return FragmentStateAdapter(childFragmentManager, mFragmentsList, mTabTitleList)
}
override fun getInflatedLayout(): View {
mBinding = FragmentForumDetailBinding.bind(layoutInflater.inflate(R.layout.fragment_forum_detail, null, false))
return mBinding.root
@ -202,12 +225,17 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
mAppbarReachTop = abs(verticalOffset) <= 2
if (mViewPager.currentItem < mFragmentsList.size) {
mFragmentsList.safelyGetInRelease(mViewPager.currentItem)?.run {
mBinding.refreshLayout.isEnabled = mAppbarReachTop
&& (this as ForumArticleAskListFragment).isListReachTop()
if (this is ForumArticleAskListFragment) {
mBinding.refreshLayout.isEnabled = mAppbarReachTop && isListReachTop()
} else {
mBinding.refreshLayout.isEnabled = false
}
}
}
})
initSection()
mSkeleton = Skeleton.bind(mBinding.skeletonContainer)
.shimmer(true)
.angle(Constants.SHIMMER_ANGLE)
@ -269,25 +297,35 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
})
mViewPager.doOnPageSelected {
mBinding.run {
allOrderSfv.goneIf(it != INDEX_ALL)
askOrderSfv.goneIf(it != INDEX_ASK)
videoOrderSfv.goneIf(it != INDEX_VIDEO)
sectionContainer.goneIf(it != INDEX_ALL || !mShowSections)
}
MtaHelper.onEvent("论坛详情", getKeyValue(mLastPosition), getKeyValue(it))
mLastPosition = it
if (it < mFragmentsList.size) {
(mFragmentsList.safelyGetInRelease(it) as ForumArticleAskListFragment).refreshScrollHelper()
val fragment = mFragmentsList.safelyGetInRelease(it)
if (fragment is ForumArticleAskListFragment) {
fragment.refreshScrollHelper()
mBinding.refreshLayout.isEnabled = mAppbarReachTop && fragment.isListReachTop()
} else {
mBinding.refreshLayout.isEnabled = false
}
}
when (it) {
INDEX_ALL -> NewLogUtils.logForumDetailEnterOrClick("click_all_tab", mBbsId, mBbsType, "", mFilter)
INDEX_ALL -> NewLogUtils.logForumDetailEnterOrClick(
"click_all_tab",
mBbsId,
mBbsType,
"",
mAllForumArticleAskListFragment?.filter ?: "回复"
)
INDEX_ESSENCE -> NewLogUtils.logForumDetailEnterOrClick("click_essence_tab", mBbsId, mBbsType)
INDEX_ASK -> NewLogUtils.logForumDetailEnterOrClick("click_question_tab", mBbsId, mBbsType)
INDEX_VIDEO -> NewLogUtils.logForumDetailEnterOrClick(
mAskIndex -> NewLogUtils.logForumDetailEnterOrClick("click_question_tab", mBbsId, mBbsType)
mVideoIndex -> NewLogUtils.logForumDetailEnterOrClick(
"click_video_tab",
mBbsId,
mBbsType,
"",
mVideoFilter
mVideoForumArticleAskListFragment?.videoFilter ?: "推荐"
)
}
}
@ -298,16 +336,42 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
if (mEssenceForumArticleAskListFragment != null && mViewPager.currentItem == INDEX_ESSENCE) {
mEssenceForumArticleAskListFragment?.refresh()
}
if (mAskForumArticleAskListFragment != null && mViewPager.currentItem == INDEX_ASK) {
if (mAskForumArticleAskListFragment != null && mViewPager.currentItem == mAskIndex) {
mAskForumArticleAskListFragment?.refresh()
}
if (mVideoForumArticleAskListFragment != null && mViewPager.currentItem == INDEX_VIDEO) {
if (mVideoForumArticleAskListFragment != null && mViewPager.currentItem == mVideoIndex) {
mVideoForumArticleAskListFragment?.refresh()
}
}
LogUtils.uploadAccessToBbs(mBbsId, "论坛详情")
}
private fun initSection() {
mForumSectionAdapter = ForumSectionAdapter(requireContext(), mViewModel!!) {
mAllForumArticleAskListFragment?.setSection(
mViewModel?.selectedSection ?: ForumDetailEntity.Section(
"",
"全部"
)
)
}
mBinding.sectionRv.run {
layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
adapter = mForumSectionAdapter
(itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
}
mViewModel?.sectionListLiveData?.observe(this) {
if (!it.isNullOrEmpty() && it.size >= 2) {
mShowSections = true
mBinding.sectionContainer.visibility = View.VISIBLE
mForumSectionAdapter?.submitList(it)
} else {
mShowSections = false
mBinding.sectionContainer.visibility = View.GONE
}
}
}
fun hideRefreshingLayout() {
mBinding.refreshLayout.isRefreshing = false
}
@ -320,8 +384,9 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
return when (position) {
INDEX_ALL -> "全部Tab"
INDEX_ESSENCE -> "精华Tab"
INDEX_ASK -> "问答Tab"
else -> "视频Tab"
mAskIndex -> "问答Tab"
mVideoIndex -> "视频Tab"
else -> "专区Tab"
}
}
@ -331,7 +396,7 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
mBinding.forumDefaultBackground.goneIf(background.isNotEmpty())
mBinding.forumMaskDefaultView.goneIf(background.isNotEmpty())
mBinding.moderatorContainer.visibleIf(moderator.isNotEmpty())
mBinding.gameZoneTv.visibleIf(zone != null)
mBinding.gameDetailTv.goneIf(!game.active)
mBinding.forumNameTv.text = name
mBinding.followTv.text = if (me.isFollower) "已关注" else "关注"
mBinding.titleTv.text = name
@ -407,11 +472,66 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
requireContext(),
if (me.isFollower) R.drawable.button_round_gray_light else R.drawable.download_button_normal_style
)
mBinding.allOrderSfv.setItemList(mDefOrderList, 0)
mBinding.askOrderSfv.setItemList(mDefOrderList, 0)
mBinding.videoOrderSfv.setItemList(mVideoOrderList, 0)
showModeratorGuide()
if (backgroundLayerColor.isNotEmpty()) {
val colorRGBList =
backgroundLayerColor.substring(4, backgroundLayerColor.length - 1).split(",").map { it.toInt() }
val colorHSV = colorRGBList.colorRGBToHSV().apply {
this[2] *= 0.6F
}
val startColor = Color.HSVToColor(colorHSV)
val endColor = ColorUtils.setAlphaComponent(startColor, 0)
mBinding.forumTopMaskContainer.background = GradientDrawable().apply {
gradientType = GradientDrawable.LINEAR_GRADIENT
orientation = GradientDrawable.Orientation.BOTTOM_TOP
colors = intArrayOf(startColor, endColor)
}
}
mBinding.forumHeatTv.text = "热度值:${NumberUtils.transSimpleCount(hot)}"
mBinding.forumHeatTv.setDrawableEnd(
if (hotRank <= 20) AppCompatResources.getDrawable(
requireContext(),
R.drawable.icon_heat
) else null
)
mBinding.sectionMoreIv.setOnClickListener {
showSectionsPopupWindow()
}
zone?.let {
mTabTitleList.add(INDEX_TRENDS, it.customName.ifEmpty { TAB_TRENDS })
if (it.style == "link") {//显示web页面
val webFragment = childFragmentManager.findFragmentByTag("${tag}$INDEX_TRENDS") ?: WebFragment()
val webBundle = Bundle()
webBundle.putString(EntranceConsts.KEY_ENTRANCE, "游戏专区")
webBundle.putString(EntranceConsts.KEY_URL, it.link)
webBundle.putBoolean(WebFragment.KEY_OPEN_NATIVE_PAGE, true)
webBundle.putString(EntranceConsts.KEY_PATH, TAB_TRENDS)
webFragment.arguments = webBundle
mTrendsFragment = webFragment
mFragmentsList.add(INDEX_TRENDS, webFragment)
} else {
val fuliFragment = childFragmentManager.findFragmentByTag("${tag}$INDEX_TRENDS") ?: FuLiFragment()
fuliFragment.arguments = bundleOf(
GameEntity.TAG to game.toGameEntity(),
EntranceConsts.KEY_ENTRANCE to mEntrance,
EntranceConsts.KEY_PATH to TAB_TRENDS
)
mTrendsFragment = fuliFragment
mFragmentsList.add(INDEX_TRENDS, fuliFragment)
}
mViewPager.adapter?.notifyDataSetChanged()
mAskIndex = INDEX_ASK + 1
mVideoIndex = INDEX_VIDEO + 1
if (arguments?.getBoolean(EntranceConsts.KEY_SCROLL_TO_TRENDS, false) == true) {
mViewPager.currentItem = INDEX_TRENDS
}
}
}
}
@ -462,18 +582,6 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
NewLogUtils.logForumDetailEnterOrClick("click_forum_detail_search")
requireContext().startActivity(ForumOrUserSearchActivity.getIntent(requireContext(), mBbsId, "论坛详情"))
}
mBinding.allOrderSfv.setOnCheckedCallback { position ->
mFilter = mDefOrderList[position]
mAllForumArticleAskListFragment?.onRefresh(mDefOrderList[position])
}
mBinding.askOrderSfv.setOnCheckedCallback { position ->
mFilter = mDefOrderList[position]
mAskForumArticleAskListFragment?.onRefresh(mDefOrderList[position])
}
mBinding.videoOrderSfv.setOnCheckedCallback { position ->
mVideoFilter = mVideoOrderList[position]
mVideoForumArticleAskListFragment?.onRefresh(mVideoOrderList[position])
}
mBinding.forumTopContentArrowContainer.setOnClickListener {
mForumDetail?.run {
mBinding.run {
@ -543,16 +651,17 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
mSkeleton.show()
mViewModel?.getForumDetail()
mViewModel?.getModeratorsApplyStatus()
mViewModel?.getSections()
}
mBinding.gameZoneTv.setOnClickListener {
NewLogUtils.logForumDetailEnterOrClick("click_strategy_zone", mBbsId, mBbsType)
if (mForumDetail?.zone != null) {
MtaHelper.onEvent("论坛详情", "顶部区域", "游戏专区")
GameDetailActivity.startGameDetailActivity(
requireContext(), mForumDetail?.game?.id ?: "", mEntrance, GameDetailFragment.INDEX_TRENDES
)
}
}
// mBinding.gameZoneTv.setOnClickListener {
// NewLogUtils.logForumDetailEnterOrClick("click_strategy_zone", mBbsId, mBbsType)
// if (mForumDetail?.zone != null) {
// MtaHelper.onEvent("论坛详情", "顶部区域", "游戏专区")
// GameDetailActivity.startGameDetailActivity(
// requireContext(), mForumDetail?.game?.id ?: "", mEntrance, GameDetailFragment.INDEX_TRENDES
// )
// }
// }
mBinding.forumThumbBig.setOnClickListener {
NewLogUtils.logForumDetailEnterOrClick("click_forum_detail_forum_icon", mBbsId)
MtaHelper.onEvent("论坛详情", "顶部区域", "游戏图标")
@ -613,6 +722,12 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
)
)
}
mBinding.gameDetailTv.setOnClickListener {
GameDetailActivity.startGameDetailActivity(
requireContext(), mForumDetail?.game?.id
?: "", "(论坛详情)"
)
}
}
private fun updateToolbarStyle(isToolbarWhite: Boolean) {
@ -683,30 +798,42 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
private fun initModeratorHeadView(datas: List<UserEntity>) {
mBinding.moderatorContainer.removeAllViews()
datas.forEachIndexed { index, user ->
val headView = SimpleDraweeView(requireContext())
val roundingParams = RoundingParams().apply {
roundAsCircle = true
setBorder(ContextCompat.getColor(requireContext(), R.color.white), 1f.dip2px().toFloat())
}
headView.hierarchy = GenericDraweeHierarchyBuilder(resources)
.setFadeDuration(500)
.setRoundingParams(roundingParams)
.setPressedStateOverlay(ColorDrawable(ContextCompat.getColor(requireContext(), R.color.pressed_bg)))
.setPlaceholderImage(R.drawable.occupy2, ScalingUtils.ScaleType.CENTER)
.setBackground(ColorDrawable(ContextCompat.getColor(requireContext(), R.color.placeholder_bg)))
.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)
.build()
val params = LinearLayout.LayoutParams(20f.dip2px(), 20f.dip2px())
headView.layoutParams = params
if (index != 0) {
params.leftMargin = (-8f).dip2px()
if (index < 3) {
val headView = SimpleDraweeView(requireContext())
val roundingParams = RoundingParams().apply {
roundAsCircle = true
setBorder(ContextCompat.getColor(requireContext(), R.color.white), 1f.dip2px().toFloat())
}
headView.hierarchy = GenericDraweeHierarchyBuilder(resources)
.setFadeDuration(500)
.setRoundingParams(roundingParams)
.setPressedStateOverlay(ColorDrawable(ContextCompat.getColor(requireContext(), R.color.pressed_bg)))
.setPlaceholderImage(R.drawable.occupy2, ScalingUtils.ScaleType.CENTER)
.setBackground(ColorDrawable(ContextCompat.getColor(requireContext(), R.color.placeholder_bg)))
.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP)
.build()
headView.layoutParams = params
if (index != 0) {
params.leftMargin = (-8f).dip2px()
}
ImageUtils.display(headView, user.icon)
headView.setOnClickListener {
MtaHelper.onEvent("论坛详情", "顶部区域", "版主头像")
mBinding.moderatorTv.performClick()
}
mBinding.moderatorContainer.addView(headView)
} else if (index == 3) {
params.leftMargin = (-8F).dip2px()
mBinding.moderatorContainer.addView(ImageView(requireContext()).apply {
layoutParams = params
setImageResource(R.drawable.icon_moderator_more)
setOnClickListener {
MtaHelper.onEvent("论坛详情", "顶部区域", "版主头像")
mBinding.moderatorTv.performClick()
}
})
}
ImageUtils.display(headView, user.icon)
headView.setOnClickListener {
MtaHelper.onEvent("论坛详情", "顶部区域", "版主头像")
mBinding.moderatorTv.performClick()
}
mBinding.moderatorContainer.addView(headView)
}
}
@ -812,6 +939,72 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
}
}
private fun showSectionsPopupWindow() {
val binding = PopupForumDetailSectionsBinding.inflate(LayoutInflater.from(requireContext()), null, false)
val popupWindow = PopupWindow(
binding.root,
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply { mPopupWindow = this }
mViewModel?.sectionListLiveData?.value?.forEachIndexed { index, section ->
val item = getItemTextView(section.name)
binding.flexbox.addView(item)
item.tag = section.name
toggleHighlightedTextView(item, mViewModel?.selectedSection?.id == section.id)
item.setOnClickListener {
toggleHighlightedTextView(item, true)
mViewModel?.selectedSection = section
mAllForumArticleAskListFragment?.setSection(
mViewModel?.selectedSection ?: ForumDetailEntity.Section(
"",
"全部"
)
)
mForumSectionAdapter?.run {
notifyItemRangeChanged(0, itemCount)
}
mBinding.sectionRv.smoothScrollToPosition(index)
popupWindow.dismiss()
}
}
binding.background.setOnClickListener {
popupWindow.dismiss()
mPopupWindow = null
}
binding.closeIv.setOnClickListener {
popupWindow.dismiss()
mPopupWindow = null
}
popupWindow.isTouchable = true
popupWindow.isFocusable = true
popupWindow.animationStyle = 0
popupWindow.showAsDropDown(mBinding.tabContainer, 0, 0)
}
private fun toggleHighlightedTextView(targetTextView: TextView, highlightIt: Boolean) {
if (highlightIt) {
targetTextView.background = R.drawable.button_round_primary_light.toDrawable(requireContext())
targetTextView.setTextColor(R.color.theme_font.toColor(requireContext()))
} else {
targetTextView.background = R.drawable.button_round_gray_light.toDrawable(requireContext())
targetTextView.setTextColor(R.color.text_subtitle.toColor(requireContext()))
}
}
private fun getItemTextView(name: String): TextView {
return TextView(requireContext()).apply {
text = name
includeFontPadding = false
textSize = 12F
gravity = Gravity.CENTER
setPadding(12F.dip2px(), 0, 12F.dip2px(), 0)
val params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, 24F.dip2px())
layoutParams = params
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
@ -843,7 +1036,14 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
override fun onBackPressed(): Boolean {
NewLogUtils.logForumDetailEnterOrClick("click_forum_detail_return", mBbsId, mBbsType)
return (mFragmentsList[mBinding.fragmentViewPager.currentItem] as ForumArticleAskListFragment).onBackPressed()
val fragment = mFragmentsList[mBinding.fragmentViewPager.currentItem]
return if (fragment is ForumArticleAskListFragment) {
fragment.onBackPressed()
} else if (fragment is WebFragment && fragment.isAdded) {
fragment.onBackPressed()
} else {
super.onBackPressed()
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
@ -877,30 +1077,13 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
super.onDarkModeChanged()
updateToolbarStyle(mIsToolbarWhite)
if (::mBinding.isInitialized) {
listOf(
mBinding.allOrderSfv,
mBinding.askOrderSfv,
mBinding.videoOrderSfv
).forEach {
updateOrderView(it)
}
for (i in 0 until mBinding.fragmentTabLayout.tabCount) {
val tab = mBinding.fragmentTabLayout.getTabAt(i)
if (tab != null) {
BaseFragment_TabLayout.updateTabStyle(tab, tab.isSelected)
}
}
}
}
private fun updateOrderView(view: SegmentedFilterView) {
view.run {
setContainerBackground(R.drawable.button_round_f5f5f5.toDrawable(requireContext()))
setIndicatorBackground(R.drawable.bg_game_collection_sfv_indicator.toDrawable(requireContext()))
setTextColor(
R.color.text_subtitle.toColor(requireContext()),
R.color.text_subtitleDesc.toColor(requireContext())
)
mForumSectionAdapter?.let { it.notifyItemRangeChanged(0, it.itemCount) }
}
}
@ -915,10 +1098,12 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
companion object {
const val INDEX_ALL = 0
const val INDEX_ESSENCE = 1
const val INDEX_TRENDS = 2
const val INDEX_ASK = 2
const val INDEX_VIDEO = 3
const val TAB_TITLE_ALL = "全部"
const val TAB_TITLE_ESSENCE = "精华"
const val TAB_TRENDS = "专区"
const val TAB_TITLE_ASK = "问答"
const val TAB_TITLE_VIDEO = "视频"
const val EB_SHOW_QUESTION_BUTTON = "EB_SHOW_QUESTION_BUTTON"

View File

@ -27,15 +27,20 @@ import retrofit2.HttpException
class ForumDetailViewModel(application: Application, val bbsId: String) : AndroidViewModel(application) {
private val mApi = RetrofitManager.getInstance().api
private val mNewApi = RetrofitManager.getInstance().newApi
private val mForumDao = AppDatabase.getInstance().forumDao()
var forumDetail = MutableLiveData<Resource<ForumDetailEntity>>()
val answerLiveData = MediatorLiveData<AnswerEntity>()
val statusEntity = MutableLiveData<ApplyModeratorStatusEntity?>()
val sectionListLiveData = MutableLiveData<List<ForumDetailEntity.Section>>()
var selectedSection = ForumDetailEntity.Section("", "全部")
init {
postForumRead()
getForumDetail()
getModeratorsApplyStatus()
getSections()
}
@SuppressLint("CheckResult")
@ -146,6 +151,20 @@ class ForumDetailViewModel(application: Application, val bbsId: String) : Androi
})
}
fun getSections() {
mNewApi.getForumSections(bbsId)
.compose(observableToMain())
.subscribe(object : Response<List<ForumDetailEntity.Section>>() {
override fun onResponse(response: List<ForumDetailEntity.Section>?) {
response?.run {
val sectionList = ArrayList(this)
sectionList.add(0, ForumDetailEntity.Section("", "全部"))
sectionListLiveData.postValue(sectionList)
}
}
})
}
fun convertArticleDetailToAnswer(articleDetailEntity: ArticleDetailEntity): AnswerEntity {
val answerEntity = AnswerEntity()

View File

@ -0,0 +1,56 @@
package com.gh.gamecenter.forum.detail
import android.content.Context
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import androidx.recyclerview.widget.RecyclerView
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.common.utils.toDrawable
import com.gh.gamecenter.databinding.ItemForumSectionBinding
import com.gh.gamecenter.entity.ForumDetailEntity
import com.lightgame.adapter.BaseRecyclerAdapter
class ForumSectionAdapter(context: Context, val viewModel: ForumDetailViewModel, val onClick: () -> Unit) :
BaseRecyclerAdapter<ForumSectionAdapter.ForumSectionItemViewHolder>(context) {
private var mSectionList = listOf<ForumDetailEntity.Section>()
fun submitList(sectionList: List<ForumDetailEntity.Section>) {
mSectionList = sectionList
notifyItemRangeChanged(0, itemCount)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ForumSectionItemViewHolder =
ForumSectionItemViewHolder(parent.toBinding())
override fun getItemCount(): Int = mSectionList.size
override fun onBindViewHolder(holder: ForumSectionItemViewHolder, position: Int) {
val entity = mSectionList[position]
holder.binding.root.run {
layoutParams = (layoutParams as MarginLayoutParams).apply {
leftMargin = if (position == 0) 16F.dip2px() else 6F.dip2px()
}
text = entity.name
setTextColor(
if (entity.id == viewModel.selectedSection.id) R.color.theme_font.toColor(mContext) else R.color.text_subtitle.toColor(
mContext
)
)
background =
if (entity.id == viewModel.selectedSection.id) R.drawable.button_round_primary_light.toDrawable(mContext) else R.drawable.button_round_gray_light.toDrawable(
mContext
)
setOnClickListener {
viewModel.selectedSection = entity
notifyItemRangeChanged(0, itemCount)
onClick.invoke()
}
}
}
class ForumSectionItemViewHolder(val binding: ItemForumSectionBinding) : RecyclerView.ViewHolder(binding.root)
}

View File

@ -2,8 +2,8 @@ package com.gh.gamecenter.forum.search
import android.os.Bundle
import android.view.View
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.databinding.FragmentSearchDefaultBinding
@ -75,11 +75,12 @@ class ForumOrUserSearchDefaultFragment : SearchDefaultFragment() {
headTitle.textSize = 16F
headActionTv.text = "清空"
headActionTv.setTextColor(R.color.text_subtitleDesc.toColor(requireContext()))
headActionTv.setDrawableStart(VectorDrawableCompat.create(
resources,
R.drawable.search_history_delete,
null
))
headActionTv.setDrawableStart(
AppCompatResources.getDrawable(
requireContext(),
R.drawable.search_history_delete
)
)
headActionTv.setOnClickListener {
DialogHelper.showCenterWarningDialog(requireContext(), "清空记录", "确定清空历史搜索记录?", confirmClickCallback = {
mSearchDao.deleteAll()

View File

@ -11,6 +11,7 @@ import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.entity.HomeDataEntity
import com.gh.gamecenter.entity.SubjectRecommendEntity
import com.gh.gamecenter.pkg.PkgHelper
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
@ -64,6 +65,20 @@ class HomeSearchToolWrapperViewModel(application: Application) : AndroidViewMode
if (homeTab.size == 0) {
homeTab.add(SubjectRecommendEntity(type = "home"))
}
// 从推广包配置信息里找是否需要默认选中一个 tab
PkgHelper.getPkgConfig()?.let { pkgLinkEntity ->
if (pkgLinkEntity.isHomeTopTabLink) {
PkgHelper.markConfigUsed()
for ((index, tab) in homeTab.withIndex()) {
if (pkgLinkEntity.type == tab.type && pkgLinkEntity.link == tab.link) {
defaultTabPosition = index
break
}
}
}
}
if (!isRefresh) {
tabs.postValue(homeTab)
}

View File

@ -29,22 +29,28 @@ import com.facebook.imagepipeline.image.ImageInfo;
import com.gh.common.constant.Config;
import com.gh.common.dialog.PrivacyPolicyDialogFragment;
import com.gh.common.dialog.ReserveDialog;
import com.gh.common.util.DirectUtils;
import com.gh.common.util.HomeBottomBarHelper;
import com.gh.common.util.IntegralLogHelper;
import com.gh.common.util.LogUtils;
import com.gh.common.util.NewLogUtils;
import com.gh.gamecenter.R;
import com.gh.gamecenter.ShellActivity;
import com.gh.gamecenter.category2.CategoryV2Fragment;
import com.gh.gamecenter.common.base.fragment.BaseFragment_ViewPager_Checkable;
import com.gh.gamecenter.common.callback.BiCallback;
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.entity.PkgConfigEntity;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.common.syncpage.SyncPageRepository;
import com.gh.gamecenter.common.tracker.TrackerLogger;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.ImageUtils;
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.databinding.FragmentMainBinding;
import com.gh.gamecenter.entity.SubjectData;
import com.gh.gamecenter.entity.SubjectRecommendEntity;
@ -58,9 +64,11 @@ 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;
@ -258,11 +266,47 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem
}
});
applyPkgConfig();
ViewModelProviders.of(this)
.get(MessageUnreadViewModel.class)
.getUnreadMessageTotalLiveData().observe(this, isShow -> ExtensionsKt.goneIf(mBinding.mainIvMessageHint, !isShow));
}
private void applyPkgConfig() {
PkgConfigEntity.PkgLinkEntity pkgLinkEntity = PkgHelper.INSTANCE.getPkgConfig();
if (pkgLinkEntity != null && !pkgLinkEntity.isHomeTopTabLink()) {
String bottomTab = pkgLinkEntity.getHomeBottomTab();
PkgHelper.INSTANCE.markConfigUsed();
if (!TextUtils.isEmpty(bottomTab)) {
// TODO 根据具体 tab 来作为跳转的具体位置,避免硬编码
int targetIndex = INDEX_HOME;
switch (bottomTab) {
case "game_lib":
targetIndex = INDEX_GAME;
break;
case "community":
targetIndex = INDEX_BBS;
break;
case "video":
targetIndex = INDEX_VIDEO;
break;
case "gh":
targetIndex = INDEX_PERSONAL;
break;
}
mViewPager.setCurrentItem(targetIndex);
onPageChanged(targetIndex);
changeColor(targetIndex);
} else {
DirectUtils.directToLinkPage(requireContext(), pkgLinkEntity, "推广包配置", "首页");
}
}
}
public void getDialog() {
mViewModel.requestOpeningData();
mViewModel.getPrivacyPolicyDialog().observe(this, it -> {
@ -579,6 +623,33 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem
} else {
mBinding.mainTabCommunity.setVisibility(View.VISIBLE);
}
} else if (Constants.EB_REALNAME_RESULT.equals(reuse.getType())) {
updateRealNameErrorContainer();
}
}
private void updateRealNameErrorContainer() {
String deviceCertificationInfoString =
SPUtils.getString(Constants.SP_DEVICE_CERTIFICATION_PREFIX + HaloApp.getInstance().getGid());
// 未点过关闭按钮并且处于实名认证失败状态的,显示提示 view
if (!SPUtils.getBoolean(Constants.SP_REALNAME_ERROR_HINT_IGNORED)
&& !TextUtils.isEmpty(deviceCertificationInfoString)) {
UserInfoEntity entity = GsonUtils.fromJson(deviceCertificationInfoString, UserInfoEntity.class);
if (entity.getIdCard() != null
&& entity.getIdCard().getStatus() == 2) {
mBinding.realNameErrorContainer.setVisibility(View.VISIBLE);
ExtensionsKt.enlargeTouchArea(mBinding.realNameErrorCloseIv, 100);
mBinding.realNameErrorCloseIv.setOnClickListener(v -> {
SPUtils.setBoolean(Constants.SP_REALNAME_ERROR_HINT_IGNORED, true);
mBinding.realNameErrorContainer.setVisibility(View.GONE);
});
mBinding.realNameErrorContainer.setOnClickListener(v -> {
startActivity(ShellActivity.getIntent(requireActivity(), ShellActivity.Type.REAL_NAME_INFO, null));
});
} else {
mBinding.realNameErrorContainer.setVisibility(View.GONE);
}
}
}

View File

@ -11,7 +11,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.ethanhua.skeleton.Skeleton
import com.ethanhua.skeleton.ViewSkeletonScreen
import com.gh.common.exposure.ExposureListener
import com.gh.gamecenter.feature.exposure.ExposureSource
import com.gh.common.util.DialogUtils
import com.gh.common.xapk.XapkInstaller
import com.gh.common.xapk.XapkUnzipStatus
@ -32,8 +31,10 @@ 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.feature.exposure.ExposureSource
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.video.ScrollCalculatorHelper
import com.halo.assistant.HaloApp
import com.lightgame.download.DataWatcher
@ -44,6 +45,7 @@ import org.greenrobot.eventbus.ThreadMode
class GameFragment : LazyFragment() {
private lateinit var mViewModel: GameViewModel
private lateinit var mHomeGameTestV2ViewModel: HomeGameTestV2ViewModel
private lateinit var mBinding: FragmentGameBinding
private lateinit var mSkeleton: ViewSkeletonScreen
@ -76,9 +78,13 @@ class GameFragment : LazyFragment() {
)
mViewModel = ViewModelProviders.of(this, factory).get(GameViewModel::class.java)
mViewModel.entrance = mEntrance
//新游开测模块
mHomeGameTestV2ViewModel = viewModelProvider()
mHomeGameTestV2ViewModel.location = "板块"
mHomeGameTestV2ViewModel.blockData = mViewModel.blockData
mViewModel.setHomeGameTestV2DownloadStateUpdateHelper(mHomeGameTestV2ViewModel)
super.onFragmentFirstVisible()
mScrollCalculatorHelper = ScrollCalculatorHelper(mBinding.gameList, R.id.autoVideoView, 0)
mViewModel.loadStatus.observe(this) {
if (it != null) {
@ -120,6 +126,7 @@ class GameFragment : LazyFragment() {
super.initRealView()
mBinding = FragmentGameBinding.bind(mCachedView)
mScrollCalculatorHelper = ScrollCalculatorHelper(mBinding.gameList, R.id.autoVideoView, 0)
mBinding.gameRefresh.setColorSchemeColors(ContextCompat.getColor(requireContext(), R.color.theme))
mLayoutManager = FixLinearLayoutManager(context)
val exposureSourceList = arrayListOf<ExposureSource>().apply {
@ -133,7 +140,15 @@ class GameFragment : LazyFragment() {
)
)
}
mListAdapter = GameFragmentAdapter(requireContext(), mViewModel, exposureSourceList, mLayoutManager)
mListAdapter = GameFragmentAdapter(
requireContext(),
mViewModel,
this,
mHomeGameTestV2ViewModel = mHomeGameTestV2ViewModel,
exposureSourceList,
mLayoutManager,
mScrollCalculatorHelper
)
mBinding.gameList.run {
(itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
@ -237,35 +252,30 @@ class GameFragment : LazyFragment() {
}
private fun pauseVideo() {
if (mScrollCalculatorHelper.currentPosition >= 0) {
mScrollCalculatorHelper.currentPlayer?.resetDetailMask()
mScrollCalculatorHelper.currentPlayer?.onVideoPause()
val currentPosition = mScrollCalculatorHelper.currentPlayer?.getCurrentPosition() ?: 0L
val topVideo =
mViewModel.itemDataList.value?.safelyGetInRelease(mScrollCalculatorHelper.currentPosition)?.attachGame?.linkGame?.topVideo
if (topVideo != null) {
ScrollCalculatorHelper.savePlaySchedule(
MD5Utils.getContentMD5(topVideo.url),
currentPosition
)
val currentPlayer = mScrollCalculatorHelper.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() {
if (mScrollCalculatorHelper.currentPlayer != null) {
val topVideo =
mViewModel.itemDataList.value?.safelyGetInRelease(mScrollCalculatorHelper.currentPosition)?.attachGame?.linkGame?.topVideo
if (topVideo != null) {
val position =
ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(topVideo.url))
val currentPlayer = mScrollCalculatorHelper.currentPlayer
if (currentPlayer != null) {
val videoUrl = currentPlayer.getUrl()
if (videoUrl.isNotEmpty()) {
val position = ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(videoUrl))
//这里必须要延迟操作,否则会白屏
mBaseHandler.postDelayed({
if (position != 0L) {
mScrollCalculatorHelper.currentPlayer?.seekTo(position)
mScrollCalculatorHelper.currentPlayer?.onVideoResume(false)
val topVideoVoiceStatus =
SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true)
val topVideoVoiceStatus = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true)
if (topVideoVoiceStatus) {
mScrollCalculatorHelper.currentPlayer?.mute()
} else {
@ -283,7 +293,7 @@ class GameFragment : LazyFragment() {
override fun onDestroy() {
super.onDestroy()
if (::mScrollCalculatorHelper.isInitialized) {
mScrollCalculatorHelper.currentPlayer?.release()
mScrollCalculatorHelper.release()
}
}

View File

@ -7,6 +7,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
@ -73,7 +74,14 @@ import com.gh.gamecenter.home.HomeGameItemViewHolder
import com.gh.gamecenter.home.discovercard.DiscoverCardGameAdapter
import com.gh.gamecenter.home.discovercard.HomeDiscoverCardViewHolder
import com.gh.gamecenter.home.gamecollection.HomeGameCollectionViewHolder
import com.gh.gamecenter.home.horizontalslidevideo.HomeHorizontalSlideVideoAdapter
import com.gh.gamecenter.home.horizontalslidevideo.HomeHorizontalSlideVideoListViewHolder
import com.gh.gamecenter.home.test_v2.HomeGameTestV2GameListRvAdapter
import com.gh.gamecenter.home.test_v2.HomeGameTestV2ViewModel
import com.gh.gamecenter.home.test_v2.HomeItemGameTestV2ViewHolder
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
import com.gh.gamecenter.servers.GameServersActivity
import com.gh.gamecenter.servers.gametest2.GameServerTestV2Activity
import com.gh.gamecenter.subject.SubjectActivity
import com.lightgame.adapter.BaseRecyclerAdapter
import com.lightgame.download.DownloadEntity
@ -81,8 +89,11 @@ import com.lightgame.download.DownloadEntity
class GameFragmentAdapter(
context: Context,
model: GameViewModel,
val mBasicExposureSource: List<ExposureSource>,
val layoutManager: LinearLayoutManager?
private val mLifecycleOwner: LifecycleOwner,
private val mHomeGameTestV2ViewModel: HomeGameTestV2ViewModel,
private val mBasicExposureSource: List<ExposureSource>,
private val mLayoutManager: LinearLayoutManager?,
private val mScrollCalculatorHelper: ScrollCalculatorHelper? = null
) : BaseRecyclerAdapter<ViewHolder>(context), IExposable {
private val mViewModel: GameViewModel = model
@ -135,6 +146,8 @@ class GameFragmentAdapter(
if (itemData.attachGame != null) return ItemViewType.GAME_ITEM
if (itemData.lineDivider != null) return ItemViewType.DIVIDER_ITEM
if (itemData.discoverCard != null) return ItemViewType.DISCOVER_CARD
if (itemData.gameTestV2Entity != null) return ItemViewType.COLUMN_TEST_V2
if (itemData.horizontalSlideVideo != null) return ItemViewType.HORIZONTAL_SLIDE_VIDEO
return ItemViewType.LOADING
}
@ -173,6 +186,12 @@ class GameFragmentAdapter(
ItemViewType.GAME_ITEM -> HomeGameItemViewHolder(parent.toBinding())
ItemViewType.DIVIDER_ITEM -> HomeDividerViewHolder(parent.toBinding())
ItemViewType.DISCOVER_CARD -> HomeDiscoverCardViewHolder(parent.toBinding())
ItemViewType.COLUMN_TEST_V2 -> HomeItemGameTestV2ViewHolder(
parent.toBinding(),
mHomeGameTestV2ViewModel,
mLifecycleOwner
)
ItemViewType.HORIZONTAL_SLIDE_VIDEO -> HomeHorizontalSlideVideoListViewHolder(parent.toBinding())
else -> GameItemViewHolder(GameItemBinding.bind(mLayoutInflater.inflate(R.layout.game_item, parent, false)))
}
@ -202,6 +221,8 @@ class GameFragmentAdapter(
is HomeGameItemViewHolder -> bindAttachGame(holder, position)
is HomeDividerViewHolder -> holder.bindView(mItemDataList[position].lineDivider ?: 1F)
is HomeDiscoverCardViewHolder -> bindDiscoverCard(holder, position)
is HomeItemGameTestV2ViewHolder -> bindGameTestV2ViewHolder(holder, position)
is HomeHorizontalSlideVideoListViewHolder -> bindHomeHorizontalSlideVideo(holder, position)
}
}
@ -1122,6 +1143,37 @@ class GameFragmentAdapter(
mViewModel.blockData?.name ?: ""
)
}
"column_test_v2" -> {
//跳转到新游开测页面
val link = column.indexRightTopLink
val text = if (column.indexRightTop == "all") {
"全部"
} else {
"更多"
}
val location = "板块"
//埋点上报
if (text == "全部") {
NewFlatLogUtils.logGameTestV2MoreClick(text, location)
//点击进入[新游开测]详情页面
mContext.startActivity(GameServerTestV2Activity.getIntent(mContext, "新游开测"))
} else {
//点击跳转通用链接
if (link != null) {
val blockData = mViewModel.blockData
DirectUtils.directToLinkPage(mContext, link, "新游开测", "")
NewFlatLogUtils.logGameTestV2MoreClick(
text,
location,
blockId = blockData?.link ?: "",
blockName = blockData?.name ?: "",
linkType = link.type ?: "",
linkId = link.link ?: "",
linkText = link.text ?: ""
)
}
}
}
else -> {
if (column.indexRightTopLink != null) {
val link = column.indexRightTopLink!!
@ -1239,7 +1291,7 @@ class GameFragmentAdapter(
val listExposureEventList = arrayListOf<ExposureEvent>()
val spanCount = 3
val snapHelper = holder.bindView(item.discoverCard!!, "版块", mViewModel.blockData?.name ?: "")
val snapHelper = holder.bindView(item.discoverCard!!, "版块", position, mViewModel.blockData?.name ?: "")
val exposureClosure: (Int) -> Unit = {
val gameList = discoverCard.games
@ -1277,6 +1329,21 @@ class GameFragmentAdapter(
}
private fun bindGameTestV2ViewHolder(holder: HomeItemGameTestV2ViewHolder, position: Int) {
val exposureItemData = mItemDataList.getOrNull(position)
val data = exposureItemData?.gameTestV2Entity ?: return
holder.bindData(data, exposureItemData, mBasicExposureSource, position)
}
private fun bindHomeHorizontalSlideVideo(holder: HomeHorizontalSlideVideoListViewHolder, position: Int) {
val item = mItemDataList[position]
item.horizontalSlideVideo?.run {
holder.bindView(this, "版块内容列表", mBasicExposureSource, mScrollCalculatorHelper) {
item.exposureEventList?.add(it)
}
}
}
override fun getItemCount(): Int {
return if (mItemDataList.size > 0) mItemDataList.size + 1 else mItemDataList.size
}
@ -1290,13 +1357,17 @@ class GameFragmentAdapter(
}
if (getItemViewType(gameAndPosition.position) == ItemViewType.VERTICAL_SLIDE_ITEM
|| getItemViewType(gameAndPosition.position) == ItemViewType.DISCOVER_CARD
|| getItemViewType(gameAndPosition.position) == ItemViewType.COLUMN_TEST_V2
|| getItemViewType(gameAndPosition.position) == ItemViewType.HORIZONTAL_SLIDE_VIDEO
) {
val view = layoutManager?.findViewByPosition(gameAndPosition.position)
val view = mLayoutManager?.findViewByPosition(gameAndPosition.position)
val recyclerView = view?.findViewById<RecyclerView>(R.id.recycler_view)
when (val adapter = recyclerView?.adapter) {
is GameVerticalAdapter -> adapter.notifyItemByDownload(download)
is RankCollectionAdapter -> adapter.notifyItemByDownload(download)
is DiscoverCardGameAdapter -> adapter.notifyItemByDownload(download)
is HomeGameTestV2GameListRvAdapter -> adapter.notifyItemByDownload(download)
is HomeHorizontalSlideVideoAdapter -> adapter.notifyItemByDownload(download)
}
} else {
notifyItemChanged(gameAndPosition.position)
@ -1319,12 +1390,16 @@ class GameFragmentAdapter(
if (getItemViewType(position) == ItemViewType.VERTICAL_SLIDE_ITEM
|| getItemViewType(position) == ItemViewType.RANK_COLLECTION
|| getItemViewType(position) == ItemViewType.DISCOVER_CARD
|| getItemViewType(position) == ItemViewType.COLUMN_TEST_V2
|| getItemViewType(position) == ItemViewType.HORIZONTAL_SLIDE_VIDEO
) {
val view = layoutManager?.findViewByPosition(position)
val view = mLayoutManager?.findViewByPosition(position)
val recyclerView = view?.findViewById<RecyclerView>(R.id.recycler_view)
when (val adapter = recyclerView?.adapter) {
is RankCollectionAdapter -> adapter.notifyChildItem()
is DiscoverCardGameAdapter -> adapter.notifyChildItem(packageName)
is HomeGameTestV2GameListRvAdapter -> adapter.notifyChildItem(packageName)
is HomeHorizontalSlideVideoAdapter -> adapter.notifyChildItem(packageName)
else -> recyclerView?.adapter?.notifyDataSetChanged()
}
} else {
@ -1390,6 +1465,32 @@ class GameFragmentAdapter(
}
continue
}
//新游开测相关条目
val gameTestV2 = mItemDataList[position].gameTestV2Entity
val gameTestV2List = mHomeGameTestV2ViewModel.getDataListLiveData().value
gameTestV2List?.forEach { gameDataWrapper ->
val gameEntity = gameDataWrapper.gameData
if (gameEntity != null) {
for (apkEntity in gameEntity.getApk()) {
if (apkEntity.packageName == packageName) {
positionList.add(GameAndPosition(gameEntity, position))
}
}
}
}
if (gameTestV2 != null) continue
val horizontalSlideVideo = mItemDataList[position].horizontalSlideVideo
if (horizontalSlideVideo != null) {
horizontalSlideVideo.data?.forEach {
for (apkEntity in it.getApk()) {
if (apkEntity.packageName == packageName) {
positionList.add(GameAndPosition(it, position))
}
}
}
continue
}
val image = mItemDataList[position].image
if (image != null) positionList.add(GameAndPosition(image, position))

View File

@ -30,6 +30,7 @@ import com.gh.gamecenter.game.data.GameItemData
import com.gh.gamecenter.game.rank.RankCollectionAdapter
import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData
import com.gh.gamecenter.home.BlankDividerViewHolder
import com.gh.gamecenter.home.test_v2.HomeGameTestV2DownloadStateUpdateHelper
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
@ -43,6 +44,8 @@ import kotlin.math.roundToInt
class GameViewModel(application: Application, var blockData: SubjectRecommendEntity?) : AndroidViewModel(application),
IOffsetable {
private var mHomeGameTestV2DownloadStateUpdateHelper: HomeGameTestV2DownloadStateUpdateHelper? = null
private var mSlideList = ArrayList<LinkEntity>() // 轮播图
private var mNavigationList = ArrayList<GameNavigationEntity>() // 导航栏
private var mSubjectList: MutableList<SubjectEntity> = ArrayList() // 专题
@ -656,6 +659,12 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt
iterator.remove()
}
}
//过滤掉游戏数量小于2个的视频横屏滑动专题
if (item.type == "game_video_horizontal_slide") {
if ((item.data?.size ?: 0) < 2) {
iterator.remove()
}
}
}
// 专题 "type": "image/game_vertical/game_horizontal"
@ -730,6 +739,7 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt
if ((subjectEntity.type != "gallery"
&& subjectEntity.type != "column_collection"
&& subjectEntity.type != "column_test_v2"
&& subjectEntity.type != "community_article"
&& subjectEntity.type != "question"
&& subjectEntity.type != "bbs_video"
@ -923,6 +933,17 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt
continue
}
if (subjectEntity.type == "game_video_horizontal_slide") {
val itemItemData = GameItemData().apply {
horizontalSlideVideo = subjectEntity
}
mItemDataListCache.add(itemItemData)
itemItemData.horizontalSlideVideo?.data?.forEach {
addGamePositionAndPackage(it)
}
continue
}
if (!data.isNullOrEmpty()) {
for (i in 0 until data.size) {
val game = data[i]
@ -976,6 +997,30 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt
continue
}
if (subjectEntity.type == "column_test_v2") {
//新游开测 标题和全部按钮
val head = GameItemData()
val rightTopLink = subjectEntity.testV2Data?.rightTop?.link
val indexRightTop = subjectEntity.testV2Data?.rightTop?.text
head.columnHead =
SubjectEntity(
type = subjectEntity.type,
name = "新游开测",
indexRightTop = indexRightTop,
indexRightTopLink = LinkEntity(
text = rightTopLink?.text,
link = rightTopLink?.link,
type = rightTopLink?.type
)
)
mItemDataListCache.add(head)
//新游开测 条目
val item = GameItemData()
item.gameTestV2Entity = subjectEntity.testV2Data
mItemDataListCache.add(item)
continue
}
if (subjectEntity.type == "game" || subjectEntity.type == "video") {
val itemItemData = GameItemData()
itemItemData.attachGame = subjectEntity
@ -1015,6 +1060,16 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt
itemDataList.postValue(mItemDataListCache)
}
private fun addGamePositionAndPackage(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))
}
private fun addGamePositionAndPackage(game: GameEntity) {
var packages = ""
for (apkEntity in game.getApk()) {
@ -1025,6 +1080,15 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt
game.setEntryMap(DownloadManager.getInstance().getEntryMap(game.name))
}
fun setHomeGameTestV2DownloadStateUpdateHelper(helper: HomeGameTestV2DownloadStateUpdateHelper) {
mHomeGameTestV2DownloadStateUpdateHelper = helper
mHomeGameTestV2DownloadStateUpdateHelper?.setOnGameListAddCallback { position, gameEntities ->
gameEntities.forEach {
addGamePositionAndPackage(it, position)
}
}
}
/**
* 获取填充间距的空白 item
*/

View File

@ -1,12 +1,13 @@
package com.gh.gamecenter.game.data
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData
import com.gh.gamecenter.home.ExposureItemData
class GameItemData {
class GameItemData : ExposureItemData {
var game: GameEntity? = null
var columnHead: SubjectEntity? = null
var horizontalColumn: SubjectEntity? = null
@ -35,11 +36,15 @@ class GameItemData {
var discoverCard: DiscoveryCardEntity? = null // 发现页卡片
var gameTestV2Entity: HomeItemTestV2Entity? = null// 新游开测
var horizontalSlideVideo: SubjectEntity? = null //水平可自动播放的视频专题
var blankDivider: Float? = null // 空白的空间补全item
var lineDivider: Float? = null
var offset: Int = 0
var exposureEvent: ExposureEvent? = null
var exposureEventList: ArrayList<ExposureEvent>? = null
override var offset: Int = 0
override var exposureEvent: ExposureEvent? = null
override var exposureEventList: ArrayList<ExposureEvent>? = null
}

View File

@ -2,15 +2,15 @@ package com.gh.gamecenter.game.doublecard
import android.content.Context
import android.view.ViewGroup
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.utils.display
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.core.utils.StringUtils
import com.gh.gamecenter.databinding.GameDoubleCardItemBinding
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.entity.SubjectEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.game.GameItemViewHolder
import com.lightgame.adapter.BaseRecyclerAdapter
@ -43,7 +43,7 @@ class DoubleCardListAdapter(
brief.text =
if (gameEntity.columnRecommend?.text.isNullOrBlank()) gameEntity.brief
else gameEntity.columnRecommend!!.text
GameItemViewHolder.initGameSubtitle(gameEntity, gameSubtitle)
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitle)
root.setOnClickListener {
GameDetailActivity.startGameDetailActivity(

View File

@ -45,7 +45,7 @@ class DoubleCardViewHolder(val binding: GameDoubleCardItemAlBinding) : RecyclerV
brief.text =
if (gameEntity.columnRecommend?.text.isNullOrBlank()) gameEntity.brief
else gameEntity.columnRecommend!!.text
GameItemViewHolder.initGameSubtitle(gameEntity, gameSubtitle)
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitle)
root.setOnClickListener {
GameDetailActivity.startGameDetailActivity(

View File

@ -8,11 +8,11 @@ import android.view.Gravity
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.feature.view.GameIconView
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieDrawable
import com.gh.gamecenter.R
@ -147,11 +147,7 @@ class RankGameItemUi(override val ctx: Context) : Ui {
gravity = Gravity.CENTER
text = "展开"
setTextColor(ContextCompat.getColor(context, R.color.white))
setDrawableEnd(
VectorDrawableCompat.create(
resources, R.drawable.ic_jump_universal, null
)
)
setDrawableEnd(AppCompatResources.getDrawable(context, R.drawable.ic_jump_universal))
compoundDrawablePadding = dip(2)
visibility = View.GONE
}

View File

@ -10,15 +10,14 @@ import android.view.View
import android.widget.LinearLayout
import android.widget.Space
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieDrawable
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.feature.view.GameIconView
import com.gh.gamecenter.common.view.NoEllipsizeSpaceTextView
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.setDrawableEnd
import com.gh.gamecenter.common.view.GameTagContainerView
@ -46,7 +45,8 @@ import splitties.views.startPadding
class GameItemUi(override val ctx: Context) : Ui {
var iconIv: GameIconView
var gameNameTv: NoEllipsizeSpaceTextView
var gameNameContainer: LinearLayout
var gameNameTv: TextView
var gameSubtitleTv: TextView
var adLabelTv: TextView
var downloadTv: DownloadButton
@ -70,6 +70,7 @@ class GameItemUi(override val ctx: Context) : Ui {
gameNameTv = initGameNameTv()
gameSubtitleTv = initGameSubtitleTv()
adLabelTv = initAdLabelTv()
gameNameContainer = initGameNameContainer()
downloadTv = initDownloadTv()
multiVersionDownloadTv = initMultiVersionDownloadTv()
downloadTipsLottie = initDownloadTipsLottie()
@ -103,30 +104,10 @@ class GameItemUi(override val ctx: Context) : Ui {
topMargin = dip(-8)
})
add(gameNameTv, lParams(wrapContent, wrapContent) {
constrainedWidth = true
startToStartOf(mGameDesSpace)
topToTopOf(iconIv)
endToStartOf(gameSubtitleTv)
bottomToTopOf(mGameDesSpace)
verticalChainStyle = packed
horizontalChainStyle = packed
horizontalBias = 0F
constrainedWidth = true
})
add(serverTypeTv, lParams(wrapContent, wrapContent) {
startToEndOf(gameNameTv)
topToTopOf(gameNameTv)
bottomToBottomOf(gameNameTv)
endToEndOf(mGameDesSpace)
startMargin = dip(4)
})
add(mGameDesSpace, lParams(0, dip(28)) {
startToEndOf(iconIv, dip(12))
endToStartOf(downloadTv, dip(8))
topToBottomOf(gameNameTv)
topToBottomOf(gameNameContainer)
bottomToTopOf(gameTagContainer)
})
add(gameRatingTv, lParams(wrapContent, wrapContent) {
@ -158,22 +139,14 @@ class GameItemUi(override val ctx: Context) : Ui {
endToEndOf(mGameDesSpace)
orientation = LinearLayout.HORIZONTAL
})
add(gameSubtitleTv, lParams(wrapContent, dip(14)) {
startToEndOf(gameNameTv)
topToTopOf(gameNameTv)
bottomToBottomOf(gameNameTv)
add(gameNameContainer, lParams(0, wrapContent) {
startToStartOf(mGameDesSpace)
topToTopOf(iconIv)
endToEndOf(mGameDesSpace)
bottomToTopOf(mGameDesSpace)
startMargin = dip(4)
})
add(adLabelTv, lParams(dip(21), dip(14)) {
startToEndOf(gameSubtitleTv)
topToTopOf(gameSubtitleTv)
bottomToBottomOf(gameSubtitleTv)
endToEndOf(mGameDesSpace)
horizontalBias = 0F
startMargin = dip(4)
orientation = LinearLayout.HORIZONTAL
verticalChainStyle = packed
})
}
@ -183,7 +156,21 @@ class GameItemUi(override val ctx: Context) : Ui {
}
}
private fun initGameNameTv() = NoEllipsizeSpaceTextView(ctx).apply {
private fun initGameNameContainer() = LinearLayout(ctx).apply {
gravity = Gravity.CENTER_VERTICAL
add(gameNameTv, lParams(wrapContent, wrapContent) {})
add(gameSubtitleTv, lParams(wrapContent, dip(14)) {
startMargin = dip(4)
})
add(serverTypeTv, lParams(wrapContent, wrapContent) {
startMargin = dip(4)
})
add(adLabelTv, lParams(dip(21), dip(14)) {
startMargin = dip(4)
})
}
private fun initGameNameTv() = textView {
id = R.id.game_name
setSingleLine()
includeFontPadding = false
@ -299,11 +286,7 @@ class GameItemUi(override val ctx: Context) : Ui {
gravity = Gravity.CENTER
text = "展开"
setTextColor(ContextCompat.getColor(context, R.color.white))
setDrawableEnd(
VectorDrawableCompat.create(
resources, R.drawable.ic_jump_universal, null
)
)
setDrawableEnd(AppCompatResources.getDrawable(context, R.drawable.ic_jump_universal))
compoundDrawablePadding = dip(2)
visibility = View.GONE
}

View File

@ -3,7 +3,6 @@ package com.gh.gamecenter.game.vertical
import android.content.Context
import android.graphics.drawable.Drawable
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.databind.BindingAdapters
import com.gh.common.util.DownloadItemUtils
@ -26,6 +25,7 @@ class GameVerticalAdapter(
private val mMaxWidth = mContext.resources.displayMetrics.widthPixels
private var countAndKey: Pair<Int, String>? = null
private var mDarkMode = DarkModeUtils.isDarkModeOn(context)
init {
var dataIds = ""
@ -90,23 +90,14 @@ class GameVerticalAdapter(
BindingAdapters.setGameTags(gameTagContainer, gameEntity)
GameItemViewHolder.initServerType(gameNameTv, serverTypeTv, gameEntity)
gameDesTv.text = gameEntity.decoratedDes
adLabelTv.goneIf(!gameEntity.adIconActive)
GameItemViewHolder.initGameSubtitle(gameEntity, gameSubtitleTv, mGameDesSpace)
ConstraintSet().apply {
clone(root)
connect(
R.id.game_name,
ConstraintSet.END,
if (gameEntity.serverLabel != null && !gameEntity.advanceDownload) R.id.game_server_type else R.id.gameSubtitleTv,
ConstraintSet.START
)
connect(
R.id.adLabelTv,
ConstraintSet.START,
if (gameEntity.adIconActive && gameEntity.serverLabel != null && !gameEntity.advanceDownload) R.id.game_server_type else R.id.gameSubtitleTv,
ConstraintSet.END
)
}.applyTo(root)
GameItemViewHolder.initGameSubtitleAndAdLabel(
gameEntity,
gameSubtitleTv,
gameNameContainer,
gameNameTv,
gameEntity.adIconActive,
adLabelTv
)
var gameRatingPaddingEnd = 0
var gameRatingDrawableStart: Drawable? = null
@ -172,8 +163,12 @@ class GameVerticalAdapter(
}
mSubjectEntity = updateData
if (countAndKey?.first == updateData.data?.size && countAndKey?.second != dataIds) { // 数量不变,内容发生改变
if ((countAndKey?.first == updateData.data?.size && countAndKey?.second != dataIds) || mDarkMode != DarkModeUtils.isDarkModeOn(
mContext
)
) { // 数量不变,内容发生改变
notifyItemRangeChanged(0, itemCount)
mDarkMode = DarkModeUtils.isDarkModeOn(mContext)
} else if (countAndKey?.first != updateData.data?.size) { // 数量发生改变
notifyDataSetChanged()
}

View File

@ -563,7 +563,6 @@ open class GameCollectionDetailAdapter(
inner class GameCollectionGameItemViewHolder(val binding: GameCollectionGameItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindView(gameEntity: GameEntity, position: Int, isLast: Boolean) {
binding.run {
container.background = R.drawable.reuse_listview_item_style.toDrawable(mContext)
@ -594,7 +593,7 @@ open class GameCollectionDetailAdapter(
)
gameDes.text = gameEntity.decoratedDes
recommendStar.rating = gameEntity.recommendStar.toFloat()
GameItemViewHolder.initGameSubtitle(gameEntity, gameSubtitleTv, gameDesSpace, root)
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitleTv)
}
root.setPadding(
16F.dip2px(),

View File

@ -63,6 +63,7 @@ import com.gh.gamecenter.feature.entity.TagStyleEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureSource
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.gamecenter.forum.detail.ForumDetailActivity
import com.gh.gamecenter.gamedetail.cloudarchive.CloudArchiveFragment
import com.gh.gamecenter.gamedetail.desc.DescFragment
import com.gh.gamecenter.gamedetail.dialog.GameBigEventDialog
@ -131,6 +132,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
private var mRecommendPopupEntity: RecommendPopupEntity? = null
private var mTabClickEvent: Pair<Long, String> = Pair(0L, "")
private var mContentCardServerVp: ViewPager2? = null
private var mIsPlayingWhenPause = false // 判断执行onPause时视频是否在播放中
private lateinit var mBinding: FragmentGamedetailBinding
private lateinit var mVideoBinding: PieceGameDetailVideoBinding
@ -408,7 +410,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
savedInstanceState?.let { mDestinationTab = it.getInt(LAST_SELECTED_POSITION) }
mBinding.reuseNoneData.reuseNoneDataTv.text = "页面不见了"
mBodyBinding.gamedetailVp.setScrollable(true)
mBodyBinding.gamedetailVp.offscreenPageLimit = 3
mBodyBinding.gamedetailVp.offscreenPageLimit = 5
mMaxWidth = resources.displayMetrics.widthPixels - DisplayUtils.dip2px(40f)
@ -441,27 +443,13 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
mBodyBinding.gamedetailVp.doOnPageSelected { position ->
mBinding.gamedetailKaifuHint.visibleIf(mIsShowKaifuHint && position == INDEX_TRENDES)
if (mFirstTimeSelected) {
when (position) {
INDEX_DESC -> MtaHelper.onEvent("游戏详情_新", "默认[详情]", mGameEntity!!.name)
// INDEX_TRENDES -> MtaHelper.onEvent("游戏详情_新", "默认[专区]", mGameEntity!!.name)
// else -> MtaHelper.onEvent("游戏详情_新", "默认[评论]", mGameEntity!!.name)
}
mFirstTimeSelected = false
} else {
// 这个统计有歧义,当前统计包含左右滑动的选择
when (position) {
INDEX_DESC -> MtaHelper.onEvent("游戏详情_新", "点击[详情]", mGameEntity!!.name)
INDEX_TRENDES -> MtaHelper.onEvent("游戏详情_新", "点击[专区]", mGameEntity!!.name)
INDEX_RATING -> MtaHelper.onEvent("游戏详情_新", "点击[评论]", mGameEntity!!.name)
INDEX_BBS -> MtaHelper.onEvent("游戏详情_新", "点击[论坛]", mGameEntity!!.name)
}
}
val bbsPosition = getTabPositionFromTabName(getString(R.string.game_detail_bbs))
val trendsPosition = getTabPositionFromTabName(
if (mNewGameDetailEntity?.zone?.customName.isNullOrEmpty()) getString(R.string.game_detail_dongtai) else mNewGameDetailEntity?.zone?.customName
?: ""
)
if (mNewGameDetailEntity?.bbsTab != null
&& position == mTabTitleList.size - 1
&& mTabTitleList.contains(getString(R.string.game_detail_bbs))
) {
if (mNewGameDetailEntity?.bbsTab != null && position == bbsPosition) {
DirectUtils.directToLinkPage(
requireContext(),
mNewGameDetailEntity?.bbsTab!!,
@ -474,6 +462,22 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
mBodyBinding.gamedetailVp.currentItem = mCurVpPosition
}
}, 200)
} else if (mNewGameDetailEntity?.zone != null && position == trendsPosition && mNewGameDetailEntity?.bbsTab != null) {
// 跳转论坛-专区
mNewGameDetailEntity?.bbsTab?.link?.let {
startActivity(
ForumDetailActivity.getTrendsIntent(
requireContext(),
it,
"游戏详情"
)
)
}
mBodyBinding.gamedetailVp.postDelayed({
if (mBodyBinding.gamedetailVp.currentItem == trendsPosition) {
mBodyBinding.gamedetailVp.currentItem = mCurVpPosition
}
}, 200)
} else {
mCurVpPosition = position
}
@ -552,8 +556,8 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
lastPosX = layout.getSecondaryHorizontal(text.length)
}
var remainWidth = (width - lastPosX).toInt()
val tagMaxWidth =
if (lineCount == 2 && layout.getEllipsisCount(1) == 0) remainWidth - 4F.dip2px() else width
val isFullGameName = (lineCount == 2 && layout.getEllipsisCount(1) == 0)
val tagMaxWidth = if (isFullGameName) remainWidth - 4F.dip2px() else width
val tagLayout = FrameLayout(context)
val tagView = TextView(context).apply {
layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, 14F.dip2px())
@ -581,7 +585,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
}
}
tagView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
if (tagView.layout.getEllipsisCount(0) > 0 && tagView.maxWidth != width) {
if (tagView.layout.getEllipsisCount(0) > 0 && !isFullGameName) {
tagView.maxWidth = width
tagView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
}
@ -720,7 +724,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
initViewPage(data)
if (data.contentCard.size > 1 && mGameEntity?.shouldUseMirrorInfo() == false) {
if (data.isShowContentCard(mGameEntity)) {
initGameContentCard(data.contentCard)
}
@ -830,7 +834,18 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
@SuppressLint("ClickableViewAccessibility")
private fun initViewPage(data: NewGameDetailEntity) {
val scrollToLibao = arguments?.getBoolean(EntranceConsts.KEY_SCROLL_TO_LIBAO) ?: false
val scrollToServer = arguments?.getBoolean(EntranceConsts.KEY_SCROLL_TO_SERVER) ?: false
var scrollToServer = arguments?.getBoolean(EntranceConsts.KEY_SCROLL_TO_SERVER) ?: false
// 内容卡片包含开服表时不跳转详情开服表
if (scrollToServer) {
data.contentCard.forEach {
if (it.type == "func_server" && data.isShowContentCard(mGameEntity)) {
scrollToServer = false
contentCardClick.invoke(it)
}
}
}
if (scrollToLibao || scrollToServer) {
mBodyBinding.gamedetailAppbar.setExpanded(false, true)
}
@ -857,11 +872,23 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
mFragmentsList.add(descFragment)
mTabTitleList.add(getString(R.string.game_detail_desc))
if (data.showComment) {
val ratingFragment = childFragmentManager.findFragmentByTag("${tag}${INDEX_RATING}") ?: RatingFragment()
bundle.putBoolean(EntranceConsts.KEY_SKIP_GAME_COMMENT, mSkipGameComment)
bundle.putBoolean(EntranceConsts.KEY_DIRECT_COMMENT, data.directComment)
ratingFragment.arguments = bundle
mFragmentsList.add(ratingFragment)
mTabTitleList.add(getString(R.string.game_detail_comment))
}
data.zone?.let {
data.contentCard.forEach { contentCard ->
if (data.contentCard.size > 1 && contentCard.type == "func_zone" && mGameEntity?.shouldUseMirrorInfo() == false) return@let
if (contentCard.type == "func_zone" && data.isShowContentCard(mGameEntity)) return@let
}
if (it.style == "link") {//显示web页面
if (data.bbsTab != null && mGameEntity?.shouldUseMirrorInfo() == false) {
mFragmentsList.add(Fragment())
} else if (it.style == "link") {
//显示web页面
val webFragment = childFragmentManager.findFragmentByTag("${tag}${INDEX_TRENDES}") ?: WebFragment()
val webBundle = Bundle()
webBundle.putString(EntranceConsts.KEY_ENTRANCE, "游戏专区")
@ -874,7 +901,6 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
fuliFragment.arguments = bundle
mFragmentsList.add(fuliFragment)
}
if (!TextUtils.isEmpty(it.customName)) {
mTabTitleList.add(it.customName)
} else {
@ -898,15 +924,6 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
)
}
if (data.showComment) {
val ratingFragment = childFragmentManager.findFragmentByTag("${tag}${INDEX_RATING}") ?: RatingFragment()
bundle.putBoolean(EntranceConsts.KEY_SKIP_GAME_COMMENT, mSkipGameComment)
bundle.putBoolean(EntranceConsts.KEY_DIRECT_COMMENT, data.directComment)
ratingFragment.arguments = bundle
mFragmentsList.add(ratingFragment)
mTabTitleList.add(getString(R.string.game_detail_comment))
}
var isShowBbs = true
if (data.bbsTab != null) {
data.contentCard.forEach { contentCard ->
@ -939,7 +956,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
for (i in 0 until mBodyBinding.tabLayout.tabCount) {
val tab = mBodyBinding.tabLayout.getTabAt(i) ?: continue
val tabTitle = if (tab.text != null) tab.text.toString() else ""
if (data.showArchive && i == INDEX_ARCHIVE) {
if (data.showArchive && i == getTabPositionFromTabName(getString(R.string.game_detail_cloud_archive))) {
tab.customView = getArchiveTabView(tabTitle)
tab.view.run {
clipChildren = false
@ -1064,7 +1081,13 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
}
override fun onItemClickListener(tag: TagStyleEntity) {
NewLogUtils.logGameDetailTagClick(mGameEntity?.id ?: "", mGameEntity?.name ?: "", tag.id, tag.name)
NewLogUtils.logGameDetailTagClick(
mGameEntity?.id ?: "",
mGameEntity?.name ?: "",
tag.id,
tag.name,
tag.isTop
)
MtaHelper.onEvent("游戏详情_新", "点击标签", mViewModel.game?.name ?: "")
requireContext().startActivity(
TagsActivity.getIntent(requireContext(), tag.name, tag.name, mEntrance, "游戏介绍")
@ -1373,6 +1396,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
.setSeekRatio(1f)
.setUrl(topVideo.url)
.setCacheWithPlay(true)
.setShowPauseCover(true)
.setReleaseWhenLossAudio(false)
.setVideoAllCallBack(object : GSYSampleCallBack() {
override fun onQuitFullscreen(url: String?, vararg objects: Any) {
@ -1751,39 +1775,18 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
) {
performJumpContentCard("func_zone")
} else {
mBodyBinding.gamedetailVp.currentItem = position
}
}
INDEX_RATING -> {
//跳转评论Tab需要判断是否有专区Tab和云存档Tab
val zoneEntity = mNewGameDetailEntity?.zone
val hasZone = mTabTitleList.contains(getString(R.string.game_detail_dongtai))
|| mTabTitleList.contains(zoneEntity?.customName)
val hasArchive = mTabTitleList.contains(getString(R.string.game_detail_cloud_archive))
val skipPosition = when {
hasZone && hasArchive -> INDEX_RATING
hasZone || hasArchive -> INDEX_RATING - 1
else -> INDEX_RATING - 2
}
if (mTabTitleList.contains(getString(R.string.game_detail_comment))) {
mBodyBinding.gamedetailVp.currentItem = skipPosition
mBodyBinding.gamedetailVp.currentItem = getTabPositionFromTabName(
if (mNewGameDetailEntity?.zone?.customName.isNullOrEmpty()) getString(R.string.game_detail_dongtai) else mNewGameDetailEntity?.zone?.customName
?: ""
)
}
}
INDEX_BBS -> {
if (!mTabTitleList.contains(getString(R.string.game_detail_bbs))) {
performJumpContentCard("func_bbs")
} else {
//跳转论坛tab需要判断是否有评论Tab、云存档Tab专区Tab
val zoneEntity = mNewGameDetailEntity?.zone
val hasZone = mTabTitleList.contains(getString(R.string.game_detail_dongtai))
|| mTabTitleList.contains(zoneEntity?.customName)
val hasArchive = mTabTitleList.contains(getString(R.string.game_detail_cloud_archive))
val hasRatting = mTabTitleList.contains(getString(R.string.game_detail_comment))
var skipPosition = INDEX_BBS
if (!hasZone) skipPosition--
if (!hasArchive) skipPosition--
if (!hasRatting) skipPosition--
mBodyBinding.gamedetailVp.currentItem = skipPosition
mBodyBinding.gamedetailVp.currentItem =
getTabPositionFromTabName(getString(R.string.game_detail_bbs))
}
}
else -> {
@ -1794,8 +1797,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
private fun performJumpContentCard(type: String) {
val entity = mNewGameDetailEntity?.contentCard?.find { it.type == type }
if ((mNewGameDetailEntity?.contentCard?.size ?: 0) > 1
&& mGameEntity?.shouldUseMirrorInfo() == false
if (mNewGameDetailEntity?.isShowContentCard(mGameEntity) == true
&& entity != null
) {
contentCardClick(entity)
@ -1803,6 +1805,9 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
}
private fun logTabClick(position: Int) {
// 特殊处理延时选中 tab 时可能 fragment 已被移除的情况
if (!isAdded) return
val entrance = if (mEntrance.contains("论坛详情")) "论坛" else "游戏"
mGameEntity?.run {
when (getTabContentForReal(position)) {
@ -1840,7 +1845,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
/**
* 获取真实的 tab 内容类型
* @param position tab 的位置
* @return 真实的
* @return 真实的 tab 类型
*/
private fun getTabContentForReal(position: Int): String {
return when (mTabTitleList[position]) {
@ -1859,6 +1864,15 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
}
}
/**
* 获取 tab 的位置
* @param tabName tab 名字
* @return tab 的真实位置
*/
private fun getTabPositionFromTabName(tabName: String): Int {
return mTabTitleList.indexOf(tabName)
}
private fun handleTabTouchEvent(title: String) {
if (title == mTabClickEvent.second && System.currentTimeMillis() - mTabClickEvent.first < 300) {
val fragment = mFragmentsList[mBodyBinding.gamedetailVp.currentItem]
@ -2092,7 +2106,8 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
private fun updateArchiveTabUI() {
mBodyBinding.tabLayout.run {
getTabAt(INDEX_ARCHIVE)?.customView?.findViewById<ImageView>(R.id.newIv)?.visibility = View.VISIBLE
val archivePosition = getTabPositionFromTabName(getString(R.string.game_detail_cloud_archive))
getTabAt(archivePosition)?.customView?.findViewById<ImageView>(R.id.newIv)?.visibility = View.VISIBLE
}
}
@ -2124,7 +2139,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
override fun onResume() {
super.onResume()
if (!mIsPauseTopVideo) {
if (!mIsPauseTopVideo && mIsPlayingWhenPause) {
resumeVideo()
}
if (mGameEntity != null && (mGameEntity!!.getApk().size == 1 || mGameEntity!!.isReservable)) {
@ -2139,8 +2154,12 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
override fun onPause() {
super.onPause()
pauseVideo()
if (mViewModel.displayTopVideo) {
mVideoBinding.player.postDelayed({
mVideoBinding.player.showFullPauseBitmap()
}, 50)
mIsPlayingWhenPause = CustomManager.getCustomManager(mVideoBinding.player.getKey()).isPlaying
pauseVideo()
val currentPosition = mVideoBinding.player.getCurrentPosition()
val topVideo = mNewGameDetailEntity?.topVideo
if (topVideo != null) {
@ -2270,7 +2289,7 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
mBinding.detailLlBottom.detailLlBottom.setBackgroundColor(R.color.background.toColor(requireContext()))
updateToolbarStyle(!mViewModel.displayTopVideo || mBodyBinding.gamedetailThumbSmall.visibility == View.VISIBLE)
mViewModel.gameDetailLiveData.value?.data?.let {
if (it.contentCard.size > 1 && mGameEntity?.shouldUseMirrorInfo() == false) {
if (it.isShowContentCard(mGameEntity)) {
initGameContentCard(it.contentCard)
}
}
@ -2286,9 +2305,9 @@ class GameDetailFragment : ToolbarFragment(), IScrollable {
companion object {
const val INDEX_DESC = 0
const val INDEX_TRENDES = 1
const val INDEX_RATING = 1
const val INDEX_ARCHIVE = 2
const val INDEX_RATING = 3
const val INDEX_TRENDES = 3
const val INDEX_BBS = 4
const val TAB_DESC = "详情"

View File

@ -25,6 +25,7 @@ import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.UrlFilterUtils
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.SimpleGame
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.gamecenter.gamedetail.entity.BigEvent
import com.gh.gamecenter.gamedetail.entity.CustomColumn
@ -149,7 +150,7 @@ class GameDetailViewModel(
data.contentCard.forEach {
// 显示开服内容卡片时,游戏详情-详情tab不显示“最新开服”
if (data.contentCard.size > 1 && it.type == "func_server" && game?.shouldUseMirrorInfo() == false) {
if (it.type == "func_server" && data.isShowContentCard(game)) {
removeLatestServerIfNeeded(data)
return@forEach
}
@ -162,8 +163,12 @@ class GameDetailViewModel(
// 初始化礼包按钮状态
for (entity in data.detailEntity) {
if (entity.type == "libao") {
val simpleGame = SimpleGame(gameId, mName = game?.name)
if (entity.libao != null) {
loadLiBaoStatus(data, entity.libao!!)
for (libaoEntity in entity.libao!!) {
libaoEntity.game = simpleGame
}
}
break
}
@ -247,6 +252,12 @@ class GameDetailViewModel(
if (item.type == DetailEntity.Type.GAME_INFO.value) {
item.info?.manufacturer = data.mirrorData?.manufacturer ?: ""
item.info?.privacyPolicyUrl = data.mirrorData?.privacyPolicyUrl ?: ""
item.info?.manufacturerType = data.mirrorData?.manufacturerType ?: ""
item.info?.publisher = data.mirrorData?.publisher ?: ""
item.info?.developer = data.mirrorData?.developer ?: ""
item.info?.supplier = data.mirrorData?.supplier ?: ""
item.info?.creditCode = data.mirrorData?.creditCode ?: ""
item.info?.updateTime = data.mirrorData?.updateTime ?: 0L
finalItemList.add(item)
continue
}
@ -301,6 +312,8 @@ class GameDetailViewModel(
item.des = data.mirrorData?.des
item.gallery = data.mirrorData?.gallery
item.update?.updateDes = data.mirrorData?.updateDes ?: ""
item.info?.size = data.mirrorData?.apk?.firstOrNull()?.size ?: ""
item.info?.version = data.mirrorData?.apk?.firstOrNull()?.version ?: ""
}

View File

@ -25,13 +25,12 @@ import com.gh.gamecenter.core.iinterface.IScrollable
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.databinding.FragmentDescBinding
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.entity.LibaoEntity
import com.gh.gamecenter.entity.RatingComment
import com.gh.gamecenter.eventbus.EBScroll
import com.gh.gamecenter.eventbus.EBTypeChange
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.gamedetail.GameDetailFragment
import com.gh.gamecenter.gamedetail.GameDetailFragment.Companion.OPEN_APPBAR
import com.gh.gamecenter.gamedetail.GameDetailFragment.Companion.SKIP_DESC
import com.gh.gamecenter.gamedetail.GameDetailViewModel
import com.gh.gamecenter.gamedetail.entity.DetailEntity
@ -43,7 +42,6 @@ import io.reactivex.disposables.Disposable
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import kotlin.math.abs
class DescFragment : BaseFragment<Any>(), IScrollable {
@ -190,9 +188,6 @@ class DescFragment : BaseFragment<Any>(), IScrollable {
super.onScrolled(recyclerView, dx, dy)
val firstCompletelyVisibleItemPosition = mLayoutManager!!.findFirstCompletelyVisibleItemPosition()
val lastCompletelyVisibleItemPosition = mLayoutManager!!.findLastCompletelyVisibleItemPosition()
if (firstCompletelyVisibleItemPosition == 0 && abs(dy) > 10) {
EventBus.getDefault().post(EBReuse(OPEN_APPBAR))
}
for (i in firstCompletelyVisibleItemPosition..lastCompletelyVisibleItemPosition) {
if (i < 0) continue

View File

@ -59,11 +59,30 @@ class GameLibaoAdapter(
when (holder) {
is LibaoViewHolder -> {
val libaoEntity = libaos[position]
val isTypeCopy = libaoEntity.receiveMethod == "copy"
holder.binding.libaoNameTv.text = libaoEntity.name
holder.binding.contentTv.text = libaoEntity.content?.fromHtml()
//通用码礼包/或者还未添加礼包码时,不显示进度条,显示礼包码
if (libaoEntity.universal || libaoEntity.status == "check") {
if (isTypeCopy) {
// 类型为复制,不需要登录也能直接领取
holder.binding.libaoSchedulePb.visibility = View.GONE
holder.binding.remainingTv.visibility = View.GONE
holder.binding.libaoCodeTv.visibility = View.VISIBLE
val text = "兑换码:${libaoEntity.code}"
holder.binding.libaoCodeTv.text = SpanBuilder(text).color(
holder.binding.root.context,
4,
text.length,
R.color.theme_font
).build()
holder.binding.copyLibaoCodeIv.visibility = View.VISIBLE
holder.binding.copyLibaoCodeIv.setOnClickListener {
holder.binding.receiveTv.performClick()
}
} else if (libaoEntity.universal || libaoEntity.status == "check") {
//通用码礼包/或者还未添加礼包码时,不显示进度条,显示礼包码
holder.binding.libaoSchedulePb.visibility = View.GONE
holder.binding.remainingTv.visibility = View.GONE
holder.binding.libaoCodeTv.visibility = View.VISIBLE
@ -76,7 +95,7 @@ class GameLibaoAdapter(
"ling" -> {
holder.binding.libaoCodeTv.text = "礼包码:-"
}
"linged" -> {
"linged", "repeatLing", "repeatLinged" -> {
val size = libaoEntity.me?.userDataLibaoList?.size ?: 0
val code = libaoEntity.me?.userDataLibaoList?.get(size - 1)?.code ?: ""
val text = "礼包码:$code"
@ -112,7 +131,7 @@ class GameLibaoAdapter(
initProgressUI(libaoEntity, holder)
}
"linged" -> {
"linged", "repeatLing", "repeatLinged" -> {
holder.binding.libaoSchedulePb.visibility = View.GONE
holder.binding.remainingTv.visibility = View.GONE
holder.binding.libaoCodeTv.visibility = View.VISIBLE
@ -152,8 +171,12 @@ class GameLibaoAdapter(
}
}
holder.itemView.setOnClickListener {
listListener?.onListClick(it, position, libaoEntity)
NewLogUtils.logGameDetailGiftClick(gameName, gameId, "礼包详情")
if (isTypeCopy) {
// do nothing
} else {
listListener?.onListClick(it, position, libaoEntity)
NewLogUtils.logGameDetailGiftClick(gameName, gameId, "礼包详情")
}
}
}
is MoreViewHolder -> {

View File

@ -6,10 +6,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.gh.common.databind.BindingAdapters
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.safelyGetInRelease
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.StringUtils
import com.gh.gamecenter.databinding.ItemGameDetailRelatedVersionBinding
@ -75,7 +72,7 @@ class GameRelatedVersionAdapter(
exposureEventList.safelyGetInRelease(position)
)
}
GameItemViewHolder.initGameSubtitle(gameEntity, holder.binding.gameSubtitleTv)
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, holder.binding.gameSubtitleTv)
}
}

View File

@ -10,14 +10,14 @@ import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.common.util.NewLogUtils
import com.gh.gamecenter.common.view.divider.HorizontalDividerItemDecoration
import com.gh.download.dialog.DownloadDialog
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.view.divider.HorizontalDividerItemDecoration
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.databinding.DialogGameTagsBinding
import com.gh.gamecenter.feature.entity.TagStyleEntity
import com.gh.gamecenter.tag.TagsActivity
@ -77,7 +77,7 @@ class GameTagsDialog : BaseDialogFragment() {
it.name, it.name, "", "游戏介绍"
)
)
NewLogUtils.logGameDetailTagClick(mGameId, mGameName, it.id, it.name)
NewLogUtils.logGameDetailTagClick(mGameId, mGameName, it.id, it.name, it.isTop)
MtaHelper.onEvent("游戏标签弹窗", "进入标签", "${mGameName}+${it.name}")
}
}

View File

@ -69,6 +69,27 @@ class DetailEntity(
// 仅用于镜像
@SerializedName("privacy_policy_url")
var privacyPolicyUrl: String? = null,
// 仅用于镜像
@SerializedName("manufacturer_type")
var manufacturerType: String = "",
// 仅用于镜像
var publisher: String = "",
// 仅用于镜像
var developer: String = "",
// 仅用于镜像
var supplier: String = "",
// 仅用于镜像
@SerializedName("credit_code")
var creditCode: String = "",
// 仅用于镜像
@SerializedName("update_des")
var updateDes: String = "",
// 仅用于镜像
@SerializedName("update_time")
var updateTime: Long = 0,
// 仅用于镜像
@SerializedName("content_card_status")
var contentCardStatus: String = "off", //on生效/off不生效
@SerializedName("detail_dialogs")
var detailDialogs: ArrayList<GameEntity.Dialog> = arrayListOf(),
@ -160,7 +181,7 @@ class UpdateContent(
@SerializedName("history_apk_status")
var historyApkStatus: String = "",
@SerializedName("update_des")
val updateDes: String = ""
var updateDes: String = ""
)
@Keep

View File

@ -23,7 +23,7 @@ class NewGameDetailEntity(
var event: BigEvent? = null,//游戏大事件
@SerializedName("detail_dialogs")
var detailDialogs: ArrayList<GameEntity.Dialog> = arrayListOf(),
@SerializedName("tag_style")
@SerializedName("new_tag_style")
var tagStyle: ArrayList<TagStyleEntity> = ArrayList(),
@SerializedName("detail_tab")
var detailEntity: ArrayList<DetailEntity> = ArrayList(),
@ -54,7 +54,11 @@ class NewGameDetailEntity(
var showArchive: Boolean = false, //云存档开关
@SerializedName("archive_tab")
var archiveTab: ArchiveTab = ArchiveTab()
)
) {
fun isShowContentCard(gameEntity: GameEntity?): Boolean {
return contentCard.size > 1 && (gameEntity?.shouldUseMirrorInfo() == false || (gameEntity?.shouldUseMirrorInfo() == true && mirrorData?.contentCardStatus == "on"))
}
}
@Keep
class Ranking(

View File

@ -8,19 +8,19 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.ethanhua.skeleton.Skeleton
import com.ethanhua.skeleton.ViewSkeletonScreen
import com.gh.gamecenter.common.base.fragment.LazyFragment
import com.gh.gamecenter.core.iinterface.IScrollable
import com.gh.common.util.NewLogUtils
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.utils.*
import com.gh.gamecenter.databinding.FragmentFuliBinding
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.common.eventbus.EBReuse
import com.gh.gamecenter.common.mvvm.Status
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.iinterface.IScrollable
import com.gh.gamecenter.databinding.FragmentFuliBinding
import com.gh.gamecenter.eventbus.EBTypeChange
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.gamedetail.GameDetailFragment
import com.gh.gamecenter.gamedetail.GameDetailViewModel
import com.gh.gamecenter.common.mvvm.Status
import com.halo.assistant.HaloApp
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
@ -135,14 +135,6 @@ class FuLiFragment : LazyFragment(), IScrollable {
}
mBinding?.fmFuliRv?.layoutManager = layoutManager
mBinding?.fmFuliRv?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val position = layoutManager!!.findFirstCompletelyVisibleItemPosition()
if (position == 0 && Math.abs(dy) > 10) {
EventBus.getDefault().post(EBReuse(GameDetailFragment.OPEN_APPBAR))
}
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {

View File

@ -438,6 +438,11 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
findViewById<LinearLayout>(R.id.errorContainer)?.visibility = View.GONE
}
fun showFullPauseBitmap() {
mThumbImageViewLayout.visibility = View.VISIBLE
findViewById<ImageView>(R.id.thumbImage).setImageBitmap(mFullPauseBitmap)
}
override fun changeUiToCompleteShow() {
super.changeUiToCompleteShow()
findViewById<LinearLayout>(R.id.errorContainer)?.visibility = View.GONE

View File

@ -0,0 +1,14 @@
package com.gh.gamecenter.home
import com.gh.gamecenter.feature.exposure.ExposureEvent
/**
* @author : liujiarui
* date : 2023/3/13
* description : 曝光条目公共属性
*/
interface ExposureItemData {
var exposureEvent: ExposureEvent?
var exposureEventList: ArrayList<ExposureEvent>?
var offset: Int // 偏移,供下面的实体用
}

View File

@ -1,6 +1,7 @@
package com.gh.gamecenter.home
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
@ -17,7 +18,10 @@ 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.eventbus.EBReuse
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.observeNonNull
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.common.utils.viewModelProviderFromParent
import com.gh.gamecenter.common.view.OffsetLinearLayoutManager
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.MD5Utils
@ -33,6 +37,10 @@ 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
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.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
@ -42,6 +50,7 @@ import org.greenrobot.eventbus.ThreadMode
class HomeFragment : LazyFragment() {
private lateinit var mViewModel: HomeViewModel
private lateinit var mHomeGameTestV2ViewModel: HomeGameTestV2ViewModel
private lateinit var mHomeSearchViewModel: HomeSearchToolWrapperViewModel
private lateinit var mBinding: FragmentMainHomeBinding
private lateinit var mLayoutManager: LinearLayoutManager
@ -69,6 +78,8 @@ class HomeFragment : LazyFragment() {
override fun onFragmentFirstVisible() {
mViewModel = viewModelProvider()
mHomeGameTestV2ViewModel = viewModelProvider()
mViewModel.setHomeGameTestV2DownloadStateUpdateHelper(mHomeGameTestV2ViewModel)
mHomeSearchViewModel = viewModelProviderFromParent()
mViewModel.homeOnlyWithoutOtherTab = arguments?.getInt(EntranceConsts.KEY_TAB_COUNT) == 1
super.onFragmentFirstVisible()
@ -86,7 +97,8 @@ class HomeFragment : LazyFragment() {
reuseLoading.root.goneIf(loadStatus != LoadStatus.INIT_LOADING)
}
mListAdapter.setLoadStatus(it)
if (it == LoadStatus.INIT_LOADED) {
val startAdContainerView = requireActivity().findViewById<View>(R.id.startAdContainer)
if (it == LoadStatus.INIT_LOADED && startAdContainerView == null) {
AppExecutor.uiExecutor.executeWithDelay({
scroll()
mScrollCalculatorHelper.onScrollStateChanged(RecyclerView.SCROLL_STATE_IDLE)
@ -96,14 +108,14 @@ class HomeFragment : LazyFragment() {
mHomeSearchViewModel.homeDataLiveData.observe(this) {
mViewModel.initData(it)
}
mScrollCalculatorHelper = ScrollCalculatorHelper(mBinding.gameList, R.id.autoVideoView, 0)
listenStartAdViewRemoved()
}
override fun onRealLayoutInflated(inflatedView: View) {
super.onRealLayoutInflated(inflatedView)
mBinding = FragmentMainHomeBinding.bind(inflatedView)
mScrollCalculatorHelper = ScrollCalculatorHelper(mBinding.gameList, R.id.autoVideoView, 0)
mBinding.gameRefresh.setColorSchemeColors(
ContextCompat.getColor(
requireContext(),
@ -115,7 +127,14 @@ class HomeFragment : LazyFragment() {
}
mAutomaticLayoutManager = OffsetLinearLayoutManager(requireContext())
mLayoutManager = mAutomaticLayoutManager
mListAdapter = HomeFragmentAdapter(requireContext(), mViewModel, mLayoutManager) {
mListAdapter = HomeFragmentAdapter(
requireContext(),
this,
mViewModel,
mHomeGameTestV2ViewModel,
mLayoutManager,
mScrollCalculatorHelper
) {
if (parentFragment is HomeSearchToolWrapperFragment) {
(parentFragment as HomeSearchToolWrapperFragment).changeAppBarColor(it)
}
@ -124,7 +143,11 @@ class HomeFragment : LazyFragment() {
(mBinding.gameList.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
mBinding.gameList.layoutManager = mLayoutManager
mBinding.gameList.adapter = mListAdapter
//监听新游开测条目可见性变化
mBinding.gameList.addViewHolderWatcher(
HomeItemGameTestV2ViewHolder::class.java,
HomeItemGameTestV2ViewHolderWatcher()
)
mBinding.gameList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
@ -164,6 +187,30 @@ class HomeFragment : LazyFragment() {
}
}
/**
* 监听广告view被remove时再播放视频否则会出现首页还未显示视频就开始播放了
*/
private fun listenStartAdViewRemoved() {
val startAdContainerView = requireActivity().findViewById<View>(R.id.startAdContainer)
val parentView = startAdContainerView?.parent as? ViewGroup
parentView?.setOnHierarchyChangeListener(object : ViewGroup.OnHierarchyChangeListener {
override fun onChildViewRemoved(parent: View?, child: View?) {
if (mViewModel.loadStatus.value == LoadStatus.INIT_LOADED && child == startAdContainerView) {
AppExecutor.uiExecutor.executeWithDelay({
scroll()
mScrollCalculatorHelper.onScrollStateChanged(RecyclerView.SCROLL_STATE_IDLE)
}, 100)
parentView.setOnHierarchyChangeListener(null)
}
}
override fun onChildViewAdded(parent: View?, child: View?) {
//Do nothing
}
})
}
private fun scroll() {
val firstVisibleItem = mAutomaticLayoutManager.findFirstVisibleItemPosition()
val lastVisibleItem = mAutomaticLayoutManager.findLastVisibleItemPosition()
@ -187,35 +234,30 @@ class HomeFragment : LazyFragment() {
}
private fun pauseVideo() {
if (mScrollCalculatorHelper.currentPosition >= 0) {
mScrollCalculatorHelper.currentPlayer?.resetDetailMask()
mScrollCalculatorHelper.currentPlayer?.onVideoPause()
val currentPosition = mScrollCalculatorHelper.currentPlayer?.getCurrentPosition() ?: 0L
val topVideo =
mViewModel.itemDataList.value?.safelyGetInRelease(mScrollCalculatorHelper.currentPosition)?.attachGame?.linkGame?.topVideo
if (topVideo != null) {
ScrollCalculatorHelper.savePlaySchedule(
MD5Utils.getContentMD5(topVideo.url),
currentPosition
)
val currentPlayer = mScrollCalculatorHelper.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() {
if (mScrollCalculatorHelper.currentPlayer != null) {
val topVideo =
mViewModel.itemDataList.value?.safelyGetInRelease(mScrollCalculatorHelper.currentPosition)?.attachGame?.linkGame?.topVideo
if (topVideo != null) {
val position =
ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(topVideo.url))
val currentPlayer = mScrollCalculatorHelper.currentPlayer
if (currentPlayer != null) {
val videoUrl = currentPlayer.getUrl()
if (videoUrl.isNotEmpty()) {
val position = ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(videoUrl))
//这里必须要延迟操作,否则会白屏
mBaseHandler.postDelayed({
if (position != 0L) {
mScrollCalculatorHelper.currentPlayer?.seekTo(position)
mScrollCalculatorHelper.currentPlayer?.onVideoResume(false)
val topVideoVoiceStatus =
SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true)
val topVideoVoiceStatus = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true)
if (topVideoVoiceStatus) {
mScrollCalculatorHelper.currentPlayer?.mute()
} else {
@ -233,7 +275,7 @@ class HomeFragment : LazyFragment() {
override fun onDestroy() {
super.onDestroy()
if (::mScrollCalculatorHelper.isInitialized) {
mScrollCalculatorHelper.currentPlayer?.release()
mScrollCalculatorHelper.release()
}
}

View File

@ -4,6 +4,7 @@ import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.exposure.IExposable
@ -36,8 +37,13 @@ import com.gh.gamecenter.home.amway.HomeAmwayListViewHolder
import com.gh.gamecenter.home.discovercard.DiscoverCardGameAdapter
import com.gh.gamecenter.home.discovercard.HomeDiscoverCardViewHolder
import com.gh.gamecenter.home.gamecollection.HomeGameCollectionViewHolder
import com.gh.gamecenter.home.horizontalslidevideo.HomeHorizontalSlideVideoAdapter
import com.gh.gamecenter.home.slide.HomeSlideListAdapter
import com.gh.gamecenter.home.slide.HomeSlideListViewHolder
import com.gh.gamecenter.home.test_v2.HomeGameTestV2GameListRvAdapter
import com.gh.gamecenter.home.test_v2.HomeGameTestV2ViewModel
import com.gh.gamecenter.home.test_v2.HomeItemGameTestV2ViewHolder
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
import com.gh.gamecenter.setting.SettingBridge
import com.gh.vspace.HomeRecentVGameAdapter
import com.gh.vspace.HomeRecentVGameViewHolder
@ -47,8 +53,11 @@ import com.lightgame.download.DownloadEntity
class HomeFragmentAdapter(
context: Context,
private val lifecycleOwner: LifecycleOwner,
val viewModel: HomeViewModel,
private val mHomeGameTestV2ViewModel: HomeGameTestV2ViewModel,
private val mLayoutManager: LinearLayoutManager,
private val mScrollCalculatorHelper: ScrollCalculatorHelper,
private val mCallback: (colorStr: Int) -> Unit
) : DiffUtilAdapter<HomeItemData>(context), IExposable {
@ -150,6 +159,14 @@ class HomeFragmentAdapter(
return oldItem.discoverCard == newItem.discoverCard
}
if (oldItem?.gameTestV2Entity != null && newItem?.gameTestV2Entity != null) {
return oldItem.gameTestV2Entity?.pageId == newItem.gameTestV2Entity?.pageId
}
if (oldItem?.horizontalSlideVideo != null && newItem?.horizontalSlideVideo != null) {
return oldItem.horizontalSlideVideo?.id == newItem.horizontalSlideVideo?.id
}
return false
}
@ -175,6 +192,7 @@ class HomeFragmentAdapter(
if (itemData.recentVGame != null) return RECENT_V_GAME
if (itemData.discoverCard != null) return DISCOVER_CARD
if (itemData.lineDivider != null) return DIVIDER_ITEM
if (itemData.gameTestV2Entity != null) return COLUMN_TEST_V2
if (itemData.unknownData != null) return UNKNOWN_ITEM
return mLegacyHomeFragmentAdapterAssistant.getLegacyItemType(itemData)
@ -190,6 +208,7 @@ class HomeFragmentAdapter(
DIVIDER_ITEM -> HomeDividerViewHolder(parent.toBinding())
RECENT_V_GAME -> HomeRecentVGameViewHolder(parent.toBinding())
DISCOVER_CARD -> HomeDiscoverCardViewHolder(parent.toBinding())
COLUMN_TEST_V2 -> HomeItemGameTestV2ViewHolder(parent.toBinding(), mHomeGameTestV2ViewModel, lifecycleOwner)
FOOTER_ITEM -> FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false))
UNKNOWN_ITEM -> ReuseViewHolder(mLayoutInflater.inflate(R.layout.home_unknown_item, parent, false))
@ -221,8 +240,14 @@ class HomeFragmentAdapter(
is HomeGameCollectionViewHolder -> bindGameCollection(holder, position)
is HomeRecentVGameViewHolder -> bindRecentVGame(holder, position)
is HomeDiscoverCardViewHolder -> bindDiscoverCard(holder, position)
is HomeItemGameTestV2ViewHolder -> bindGameTestV2ViewHolder(holder, position)
else -> mLegacyHomeFragmentAdapterAssistant.bindLegacyViewHolder(holder, mDataList[position], position)
else -> mLegacyHomeFragmentAdapterAssistant.bindLegacyViewHolder(
holder,
mDataList[position],
position,
mScrollCalculatorHelper
)
}
}
@ -344,7 +369,7 @@ class HomeFragmentAdapter(
val listExposureEventList = arrayListOf<ExposureEvent>()
val spanCount = 3
val snapHelper = holder.bindView(discoverCard, "首页")
val snapHelper = holder.bindView(discoverCard, "首页", position)
val exposureClosure: (Int) -> Unit = {
val gameList = discoverCard.games
@ -398,6 +423,12 @@ class HomeFragmentAdapter(
}
}
private fun bindGameTestV2ViewHolder(holder: HomeItemGameTestV2ViewHolder, position: Int) {
val exposureItemData = mDataList.getOrNull(position)
val data = exposureItemData?.gameTestV2Entity ?: return
holder.bindData(data, exposureItemData, mBasicExposureSource, position)
}
private fun bindFooterView(holder: FooterViewHolder) {
holder.initFooterViewHolder(
mLoadStatus == LoadStatus.LIST_LOADING,
@ -429,6 +460,8 @@ class HomeFragmentAdapter(
|| getItemViewType(gameAndPosition.position) == ItemViewType.GAME_SUBJECT
|| getItemViewType(gameAndPosition.position) == ItemViewType.GAME_SUBJECT_SLIDE
|| getItemViewType(gameAndPosition.position) == DISCOVER_CARD
|| getItemViewType(gameAndPosition.position) == ItemViewType.HORIZONTAL_SLIDE_VIDEO
|| getItemViewType(gameAndPosition.position) == COLUMN_TEST_V2
) {
val view = mLayoutManager.findViewByPosition(gameAndPosition.position)
val recyclerView = view?.findViewById<RecyclerView>(R.id.recycler_view)
@ -441,6 +474,8 @@ class HomeFragmentAdapter(
is GameHorizontalSlideAdapter -> adapter.notifyItemByDownload(download)
is HomeRecentVGameAdapter -> adapter.notifyItemByDownload(download)
is DiscoverCardGameAdapter -> adapter.notifyItemByDownload(download)
is HomeGameTestV2GameListRvAdapter -> adapter.notifyItemByDownload(download)
is HomeHorizontalSlideVideoAdapter -> adapter.notifyItemByDownload(download)
}
} else {
notifyItemChanged(gameAndPosition.position)
@ -466,6 +501,8 @@ class HomeFragmentAdapter(
|| getItemViewType(position) == ItemViewType.GAME_SUBJECT
|| getItemViewType(position) == ItemViewType.GAME_SUBJECT_SLIDE
|| getItemViewType(position) == DISCOVER_CARD
|| getItemViewType(position) == COLUMN_TEST_V2
|| getItemViewType(position) == ItemViewType.HORIZONTAL_SLIDE_VIDEO
) {
val view = mLayoutManager.findViewByPosition(position)
val recyclerView = view?.findViewById<RecyclerView>(R.id.recycler_view)
@ -475,6 +512,8 @@ class HomeFragmentAdapter(
is GameHorizontalAdapter -> adapter.notifyChildItem(packageName)
is GameHorizontalSlideAdapter -> adapter.notifyChildItem(packageName)
is DiscoverCardGameAdapter -> adapter.notifyChildItem(packageName)
is HomeGameTestV2GameListRvAdapter -> adapter.notifyChildItem(packageName)
is HomeHorizontalSlideVideoAdapter -> adapter.notifyChildItem(packageName)
else -> recyclerView?.adapter?.notifyDataSetChanged()
}
} else {
@ -527,6 +566,20 @@ class HomeFragmentAdapter(
}
continue
}
//新游开测相关条目
val gameTestV2 = itemData.gameTestV2Entity
val gameTestV2List = mHomeGameTestV2ViewModel.getDataListLiveData().value
gameTestV2List?.forEach { gameDataWrapper ->
val gameEntity = gameDataWrapper.gameData
if (gameEntity != null) {
for (apkEntity in gameEntity.getApk()) {
if (apkEntity.packageName == packageName) {
positionList.add(GameAndPosition(gameEntity, position))
}
}
}
}
if (gameTestV2 != null) continue
// 原游戏板块样式
mLegacyHomeFragmentAdapterAssistant.getLegacyGameEntityByPackage(
@ -559,5 +612,6 @@ class HomeFragmentAdapter(
const val GAME_COLLECTION_ITEM: Int = 116
const val RECENT_V_GAME: Int = 117
const val DISCOVER_CARD: Int = 118
const val COLUMN_TEST_V2: Int = 119 //新游开测
}
}

View File

@ -12,7 +12,6 @@ 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.DisplayUtils
import com.gh.gamecenter.core.utils.RandomUtils
import com.gh.gamecenter.databinding.HomeGameItemBinding
import com.gh.gamecenter.entity.SubjectEntity
@ -124,9 +123,9 @@ class HomeGameItemViewHolder(val binding: HomeGameItemBinding) : BaseRecyclerVie
) {
val game = subjectEntity.linkGame!!
binding.gameIcon.displayGameIcon(game)
binding.gameName.text = game.name
binding.gameName.setTextColor(R.color.text_title.toColor(binding.root.context))
binding.gameBrief.setTextColor(R.color.text_title.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())
@ -137,11 +136,14 @@ class HomeGameItemViewHolder(val binding: HomeGameItemBinding) : BaseRecyclerVie
View.VISIBLE
} else View.GONE
}, 5)
binding.adLabelTv.goneIf(!subjectEntity.adIconActive)
GameItemViewHolder.initGameSubtitle(game, binding.gameSubtitleTv, null, binding.root, false) {
binding.gameSubtitleTv.maxWidth =
DisplayUtils.getScreenWidth() - (if (game.adIconActive) 131F.dip2px() else 156F.dip2px())
}
GameItemViewHolder.initGameSubtitleAndAdLabel(
game,
binding.gameSubtitleTv,
binding.gameNameContainer,
binding.gameName,
subjectEntity.adIconActive,
binding.adLabelTv
)
val hierarchy = binding.gameImage.hierarchy
try {
hierarchy.setPlaceholderImage(ColorDrawable(game.homeSetting.placeholderColor.hexStringToIntColor()))
@ -173,17 +175,17 @@ class HomeGameItemViewHolder(val binding: HomeGameItemBinding) : BaseRecyclerVie
binding.gameRating.goneIf(!(game.showComment && game.commentCount >= 3 && game.star >= 7 && game.homeSetting.downloadBtnSwitch == "on"))
binding.gameRating2.goneIf(!(game.showComment && game.commentCount >= 3 && game.star >= 7 && game.homeSetting.downloadBtnSwitch != "on"))
ConstraintSet().apply {
clone(binding.gameSubtitleTv.parent as ConstraintLayout)
clear(binding.gameSubtitleTv.id, ConstraintSet.END)
clone(binding.gameNameContainer.parent as ConstraintLayout)
clear(binding.gameNameContainer.id, ConstraintSet.END)
if (binding.downloadBtn.visibility == View.VISIBLE) {
connect(binding.gameSubtitleTv.id, ConstraintSet.END, binding.downloadBtn.id, ConstraintSet.START)
connect(binding.gameNameContainer.id, ConstraintSet.END, binding.downloadBtn.id, ConstraintSet.START)
} else if (binding.gameRating2.visibility == View.VISIBLE) {
connect(binding.gameSubtitleTv.id, ConstraintSet.END, binding.gameRating2.id, ConstraintSet.START)
connect(binding.gameNameContainer.id, ConstraintSet.END, binding.gameRating2.id, ConstraintSet.START)
} else {
connect(binding.gameSubtitleTv.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END)
connect(binding.gameNameContainer.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END)
}
setMargin(binding.gameSubtitleTv.id, ConstraintSet.END, 8F.dip2px())
applyTo(binding.gameSubtitleTv.parent as ConstraintLayout)
setMargin(binding.gameNameContainer.id, ConstraintSet.END, 8F.dip2px())
applyTo(binding.gameNameContainer.parent as ConstraintLayout)
}
}
}

View File

@ -12,6 +12,7 @@ data class HomeItemData(
var attachGame: HomeContent? = null,
var recentVGame: ArrayList<VGameItemData>? = null,
var discoverCard: DiscoveryCardEntity? = null,
var gameTestV2Entity: HomeItemTestV2Entity? = null, // 新游开测
var lineDivider: Float? = null,
var unknownData: Any? = null
) : LegacyHomeItemData()

View File

@ -13,6 +13,7 @@ import com.gh.download.DownloadManager
import com.gh.gamecenter.BuildConfig
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
@ -25,6 +26,7 @@ import com.gh.gamecenter.feature.entity.PluginLocation
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.gamecenter.game.rank.RankCollectionAdapter
import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData
import com.gh.gamecenter.home.test_v2.HomeGameTestV2DownloadStateUpdateHelper
import com.gh.gamecenter.packagehelper.PackageRepository
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.vspace.VGameItemData
@ -41,6 +43,8 @@ import kotlin.math.roundToInt
class HomeViewModel(application: Application) : AndroidViewModel(application) {
private var mHomeGameTestV2DownloadStateUpdateHelper: HomeGameTestV2DownloadStateUpdateHelper? = null
private val mApi = RetrofitManager.getInstance().api
private var mSnapshotItemList = ArrayList<HomeItemData>()
@ -530,6 +534,13 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
iterator.remove()
}
}
//过滤掉游戏数量小于2个的视频横屏滑动专题
if (item.linkType == "column" && item.linkColumn?.type == "game_video_horizontal_slide") {
if ((item.linkColumn.data?.size ?: 0) < 2) {
iterator.remove()
}
}
}
if (mSmartSubject != null && homeContents.size > mSmartSubject!!.sort) {
@ -777,6 +788,28 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
}
mSnapshotItemList.add(LegacyHomeSubjectTransformer.getBlankSpacingItem(HomeItemData()) as HomeItemData)
}
} else if (linkType == "column_test_v2") {
//新游开测 标题和全部按钮
val head = HomeItemData()
val rightTopLink = homeContent.testV2Data?.rightTop?.link
val indexRightTop = homeContent.testV2Data?.rightTop?.text
head.columnHead =
SubjectEntity(
type = linkType,
name = "新游开测",
indexRightTop = indexRightTop,
indexRightTopLink = LinkEntity(
text = rightTopLink?.text,
link = rightTopLink?.link,
type = rightTopLink?.type
)
)
mSnapshotItemList.add(LegacyHomeSubjectTransformer.getBlankSpacingItem(HomeItemData()) as HomeItemData)
mSnapshotItemList.add(head)
//新游开测 条目
val item = HomeItemData()
item.gameTestV2Entity = homeContent.testV2Data
mSnapshotItemList.add(item)
} else {
val unknown = HomeItemData()
unknown.blockPosition = i + 1
@ -799,6 +832,26 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
}
}
fun setHomeGameTestV2DownloadStateUpdateHelper(helper: HomeGameTestV2DownloadStateUpdateHelper) {
mHomeGameTestV2DownloadStateUpdateHelper = helper
mHomeGameTestV2DownloadStateUpdateHelper?.setOnGameListAddCallback { position, gameEntities ->
gameEntities.forEach {
addGamePositionAndPackage(it, position)
}
positionAndPackageMap.value = HashMap(mPositionAndPackageMap)
}
}
private fun addGamePositionAndPackage(game: GameEntity, position: Int) {
var packages = ""
for (apkEntity in game.getApk()) {
packages += apkEntity.packageName
}
mPositionAndPackageMap[packages + position] = position
game.gameLocation = GameEntity.GameLocation.INDEX
game.setEntryMap(DownloadManager.getInstance().getEntryMap(game.name))
}
private fun addGamePositionAndPackage(game: GameEntity) {
var packages = ""
for (apkEntity in game.getApk()) {

View File

@ -47,6 +47,9 @@ import com.gh.gamecenter.game.rank.RankCollectionViewHolder
import com.gh.gamecenter.game.vertical.GameVerticalAdapter
import com.gh.gamecenter.game.vertical.GameVerticalSlideViewHolder
import com.gh.gamecenter.game.vertical.OnPagerSnapScrollListener
import com.gh.gamecenter.servers.gametest2.GameServerTestV2Activity
import com.gh.gamecenter.home.horizontalslidevideo.HomeHorizontalSlideVideoListViewHolder
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
import com.gh.gamecenter.subject.SubjectActivity
import com.halo.assistant.fragment.game.GamePluginAdapter
import com.halo.assistant.fragment.game.GamePluginViewHolder
@ -85,6 +88,7 @@ class LegacyHomeFragmentAdapterAssistant(
if (itemData.rankCollection != null) return ItemViewType.RANK_COLLECTION
if (itemData.doubleCardColumn != null) return ItemViewType.DOUBLE_CARD_COLUMN
if (itemData.bigImageRecommend != null) return ItemViewType.BIG_IMAGE_RECOMMEND
if (itemData.horizontalSlideVideo != null) return ItemViewType.HORIZONTAL_SLIDE_VIDEO
return 0
}
@ -112,6 +116,7 @@ class LegacyHomeFragmentAdapterAssistant(
ItemViewType.RANK_COLLECTION -> RankCollectionViewHolder(parent.toBinding())
ItemViewType.DOUBLE_CARD_COLUMN -> DoubleCardViewHolder(parent.toBinding())
ItemViewType.BIG_IMAGE_RECOMMEND -> BigImageRecommendViewHolder(parent.toBinding())
ItemViewType.HORIZONTAL_SLIDE_VIDEO -> HomeHorizontalSlideVideoListViewHolder(parent.toBinding())
else -> throw NullPointerException()
}
@ -120,7 +125,8 @@ class LegacyHomeFragmentAdapterAssistant(
fun bindLegacyViewHolder(
holder: ViewHolder,
item: LegacyHomeItemData,
position: Int
position: Int,
scrollCalculatorHelper: ScrollCalculatorHelper? = null,
) {
when (holder) {
is GameHeadViewHolder -> bindHeadView(holder, item)
@ -147,6 +153,11 @@ class LegacyHomeFragmentAdapterAssistant(
is RankCollectionViewHolder -> bindRankCollection(holder, item)
is DoubleCardViewHolder -> bindGameDoubleCard(holder, item)
is BigImageRecommendViewHolder -> bindBigImageRecommend(holder, item)
is HomeHorizontalSlideVideoListViewHolder -> bindHomeHorizontalSlideVideo(
holder,
item,
scrollCalculatorHelper
)
}
}
@ -228,6 +239,18 @@ class LegacyHomeFragmentAdapterAssistant(
return
}
val horizontalSlideVideo = itemData.horizontalSlideVideo
if (horizontalSlideVideo != null) {
horizontalSlideVideo.data?.forEach {
for (apkEntity in it.getApk()) {
if (apkEntity.packageName == packageName) {
positionList.add(GameAndPosition(it, position))
}
}
}
return
}
val image = itemData.image
if (image != null) {
positionList.add(GameAndPosition(image, position))
@ -628,6 +651,34 @@ class LegacyHomeFragmentAdapterAssistant(
NewLogUtils.logCommonCategoryHomeButtonClick(buttonType, column.name ?: "", column.id ?: "", "新首页")
} else if (column.type == "game_list_collection") {
DirectUtils.directToGameCollectionSquare(mContext, "首页内容列表", column.name ?: "")
} else if (column.type == "column_test_v2") {
//跳转到新游开测页面
val link = column.indexRightTopLink
val text = if (column.indexRightTop == "all") {
"全部"
} else {
"更多"
}
val location = "首页"
//埋点上报
if (text == "全部") {
NewFlatLogUtils.logGameTestV2MoreClick(text, location)
//点击进入[新游开测]详情页面
mContext.startActivity(GameServerTestV2Activity.getIntent(mContext, "新游开测"))
} else {
//点击跳转通用链接
if (link != null) {
DirectUtils.directToLinkPage(mContext, link, "新游开测", "")
NewFlatLogUtils.logGameTestV2MoreClick(
text,
location,
linkType = link.type ?: "",
linkId = link.link ?: "",
linkText = link.text ?: ""
)
}
}
} else {
if (column.indexRightTopLink != null) {
val link = column.indexRightTopLink!!
@ -914,6 +965,18 @@ class LegacyHomeFragmentAdapterAssistant(
}
}
private fun bindHomeHorizontalSlideVideo(
holder: HomeHorizontalSlideVideoListViewHolder,
item: LegacyHomeItemData,
scrollCalculatorHelper: ScrollCalculatorHelper? = null
) {
item.horizontalSlideVideo?.run {
holder.bindView(this, "新首页", mBasicExposureSources, scrollCalculatorHelper) {
item.exposureEventList?.add(it)
}
}
}
private fun setPageSwitchData() {
PageSwitchDataHelper.pushCurrentPageData(hashMapOf(Pair(PageSwitchDataHelper.PAGE_BUSINESS_TYPE, "首页-内容列表")))
}

View File

@ -1,17 +1,17 @@
package com.gh.gamecenter.home
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.entity.SubjectEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
// 原游戏板块样式
open class LegacyHomeItemData(
// 非实体 (统计数据时用)
var blockPosition: Int = -1, // 统计会用到块的位置(相当于一个专题就是1)
var exposureEvent: ExposureEvent? = null,
var exposureEventList: ArrayList<ExposureEvent>? = arrayListOf(),
var offset: Int = -1, // 偏移,供下面的实体用
override var exposureEvent: ExposureEvent? = null,
override var exposureEventList: ArrayList<ExposureEvent>? = arrayListOf(),
override var offset: Int = -1, // 偏移,供下面的实体用
// 实体
var game: GameEntity? = null,
@ -30,5 +30,6 @@ open class LegacyHomeItemData(
var rankCollection: SubjectEntity? = null, // 排行榜样式专题合集
var doubleCardColumn: SubjectEntity? = null, // 双列卡片专题
var bigImageRecommend: SubjectEntity? = null, //提问、帖子、视频帖、文章
var horizontalSlideVideo: SubjectEntity? = null, //水平可自动播放的视频专题
var blankDivider: Float? = null // 空白填充内容
)
):ExposureItemData

View File

@ -1,8 +1,8 @@
package com.gh.gamecenter.home
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.entity.SubjectEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.GameSubjectData
object LegacyHomeSubjectTransformer {
@ -236,6 +236,30 @@ object LegacyHomeSubjectTransformer {
return
}
if (subjectEntity.type == "game_video_horizontal_slide") {
val itemItemData = newItemInstance()
itemItemData.horizontalSlideVideo = subjectEntity
appendAdditionalInfoToSubjectGame(subjectEntity, blockPosition)
itemList.add(itemItemData)
for (i in 0 until data.size) {
val game = data[i]
game.subjectData = GameSubjectData(
id = subjectEntity.id,
name = subjectEntity.name,
tag = subjectEntity.tag,
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
game.isAdData = subjectEntity.adIconActive
addGamePositionAndPackage(game)
}
return
}
for (i in 0 until data.size) {
val game = data[i]
game.sequence = i

View File

@ -5,10 +5,7 @@ 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.ImageUtils
import com.gh.gamecenter.common.utils.TextHelper
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.AsyncCell
import com.gh.gamecenter.databinding.HomeAmwayItemBinding
import com.gh.gamecenter.entity.AmwayCommentEntity
@ -54,7 +51,7 @@ class HomeAmwayItemViewHolder(val binding: HomeAmwayItemBinding) : BaseRecyclerV
ratingName.setTextColor(R.color.text_subtitleDesc.toColor(root.context))
all.setTextColor(R.color.text_title.toColor(root.context))
}
GameItemViewHolder.initGameSubtitle(gameEntity, binding.gameSubtitleTv)
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, binding.gameSubtitleTv)
}
}

View File

@ -25,7 +25,9 @@ class HomeDiscoverCardViewHolder(val binding: HomeDiscoverCardItemBinding) : Bas
fun bindView(
entity: DiscoveryCardEntity,
entrance: String, blockName: String = ""
entrance: String,
position: Int,
blockName: String = ""
): SpanCountPagerSnapHelper {
val spanCount = 3
val snapHelper = SpanCountPagerSnapHelper(spanCount, false)
@ -67,10 +69,26 @@ class HomeDiscoverCardViewHolder(val binding: HomeDiscoverCardItemBinding) : Bas
entity.label.forEachIndexed { index, label ->
if (index < childCount) {
val labelView = labelContainer.getChildAt(index) as TextView
updateLabelView(context, labelView, label, index == entity.label.size - 1)
updateLabelView(
context,
labelView,
label,
index == entity.label.size - 1,
entrance,
blockName,
position
)
} else {
val newLabelView = TextView(context)
updateLabelView(context, newLabelView, label, index == entity.label.size - 1)
updateLabelView(
context,
newLabelView,
label,
index == entity.label.size - 1,
entrance,
blockName,
position
)
labelContainer.addView(newLabelView)
}
}
@ -82,7 +100,10 @@ class HomeDiscoverCardViewHolder(val binding: HomeDiscoverCardItemBinding) : Bas
context: Context,
textView: TextView,
label: DiscoveryGameCardLabel,
isLast: Boolean
isLast: Boolean,
entrance: String,
blockName: String,
position: Int
) {
textView.background = R.drawable.bg_shape_space_radius_4.toDrawable(context)
textView.setTextColor(R.color.text_subtitle.toColor(context))
@ -102,6 +123,14 @@ class HomeDiscoverCardViewHolder(val binding: HomeDiscoverCardItemBinding) : Bas
text = label.title
label.text = label.linkText
setOnClickListener {
NewFlatLogUtils.logDiscoverPageRecommendedInterestCardClick(
position,
label.text ?: "",
label.type ?: "",
label.link ?: "",
entrance,
blockName
)
DirectUtils.directToLinkPage(context, label, "发现页卡片", "")
}
}

View File

@ -0,0 +1,109 @@
package com.gh.gamecenter.home.horizontalslidevideo
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.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureSource
import com.lightgame.adapter.BaseRecyclerAdapter
import com.lightgame.download.DownloadEntity
import splitties.views.bottomPadding
import splitties.views.leftPadding
import splitties.views.rightPadding
import splitties.views.topPadding
class HomeHorizontalSlideVideoAdapter(
mContext: Context,
private var mGames: MutableList<GameEntity>,
private val mSubjectName: String,
private val mEntrance: String,
private val mBasicExposureSources: List<ExposureSource>,
private val mExposureClosure: (ExposureEvent) -> Unit
) :
BaseRecyclerAdapter<HomeHorizontalSlideVideoItemViewHolder>(mContext) {
private var mCountAndKey: Pair<Int, String>? = null
init {
var dataIds = ""
mGames.forEach {
dataIds += it.id
}
if (dataIds.isNotEmpty()) mCountAndKey = Pair(mGames.size, dataIds)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeHorizontalSlideVideoItemViewHolder {
return HomeHorizontalSlideVideoItemViewHolder(parent.toBinding())
}
override fun getItemCount(): Int = mGames.size
override fun onBindViewHolder(holder: HomeHorizontalSlideVideoItemViewHolder, position: Int) {
val gameEntity = mGames[position]
val event = ExposureEvent.createEventWithSourceConcat(
gameEntity = gameEntity,
basicSource = mBasicExposureSources,
source = listOf(ExposureSource("专题", mSubjectName))
)
gameEntity.exposureEvent = event
mExposureClosure.invoke(event)
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(gameEntity, this, mEntrance, event)
}
fun checkResetData(update: MutableList<GameEntity>) {
var dataIds = ""
update.forEach { dataIds += it.id }
mGames = update
if (mCountAndKey?.first == update.size && mCountAndKey?.second != dataIds) { // 数量不变,内容发生改变
notifyItemRangeChanged(0, itemCount)
} else if (mCountAndKey?.first != update.size) { // 数量发生改变
notifyDataSetChanged()
}
// 重新刷新数据标识
mCountAndKey = Pair(update.size, dataIds)
}
fun notifyItemByDownload(downloadEntity: DownloadEntity?) {
if (downloadEntity == null) {
notifyDataSetChanged()
} else {
mGames.forEachIndexed { position, gameEntity ->
if (downloadEntity.gameId == gameEntity.id) {
notifyItemChanged(position)
}
}
}
}
fun notifyChildItem(packageName: String) {
mGames.forEachIndexed { position, gameEntity ->
gameEntity.getApk().forEach { apkEntity ->
if (apkEntity.packageName == packageName) {
notifyItemChanged(position)
return
}
}
}
}
}

View File

@ -0,0 +1,115 @@
package com.gh.gamecenter.home.horizontalslidevideo
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.GameDetailActivity
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.ItemHomeHorizontalSlideVideoBinding
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.game.GameItemViewHolder
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
class HomeHorizontalSlideVideoItemViewHolder(val binding: ItemHomeHorizontalSlideVideoBinding) :
BaseRecyclerViewHolder<Any>(binding.root) {
fun bindAttachGame(
gameEntity: GameEntity,
adapter: RecyclerView.Adapter<out RecyclerView.ViewHolder>,
entrance: String,
exposureEvent: ExposureEvent
) {
bindGameInfo(gameEntity, adapter, exposureEvent, entrance)
binding.gameImage.goneIf(gameEntity.topVideo != null || gameEntity.homeSetting.image.isEmpty()) {
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.topVideo == null) {
if (!binding.autoVideoView.isInPlayingState) {
GSYVideoOptionBuilder()
.setIsTouchWiget(false)
.setUrl(gameEntity.topVideo?.url ?: "")
.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 {
GameDetailActivity.startGameDetailActivity(
binding.root.context,
gameEntity.id,
"(${entrance}-游戏[" + gameEntity.name + "])",
exposureEvent
)
}
}
private fun bindGameInfo(
gameEntity: GameEntity,
adapter: RecyclerView.Adapter<out RecyclerView.ViewHolder>,
exposureEvent: ExposureEvent?,
entrance: String
) {
binding.gameIcon.displayGameIcon(gameEntity)
binding.gameName.text = gameEntity.name
binding.gameName.setTextColor(R.color.text_title.toColor(binding.root.context))
BindingAdapters.setGameTags(binding.gameTags, gameEntity)
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, binding.gameSubtitleTv)
DownloadItemUtils.setOnClickListener(
binding.root.context,
binding.downloadBtn,
gameEntity,
position,
adapter,
entrance,
"",
exposureEvent
)
DownloadItemUtils.updateItem(
binding.root.context,
gameEntity,
GameViewHolder(binding.root).apply {
gameDownloadBtn = binding.downloadBtn
multiVersionDownloadTv = binding.multiVersionDownloadTv
gameDownloadTips = binding.downloadTipsLottie
},
true
)
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)
}
}
}

View File

@ -0,0 +1,53 @@
package com.gh.gamecenter.home.horizontalslidevideo
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.RecyclerView
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.view.FixLinearLayoutManager
import com.gh.gamecenter.databinding.HomeHorizontalSlideVideoListBinding
import com.gh.gamecenter.entity.SubjectEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureSource
import com.gh.gamecenter.home.amway.LeftPagerSnapHelper
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
class HomeHorizontalSlideVideoListViewHolder(val binding: HomeHorizontalSlideVideoListBinding) :
BaseRecyclerViewHolder<Any>(binding.root) {
fun bindView(
subject: SubjectEntity,
entrance: String,
basicExposureSource: List<ExposureSource>,
scrollCalculatorHelper: ScrollCalculatorHelper? = null,
exposureClosure: (ExposureEvent) -> Unit
) {
val context = binding.recyclerView.context
var adapter = binding.recyclerView.adapter
val games = subject.data ?: return
if (adapter is HomeHorizontalSlideVideoAdapter) {
adapter.checkResetData(games)
return
}
adapter = HomeHorizontalSlideVideoAdapter(context, games, subject.name ?: "", entrance, basicExposureSource) {
exposureClosure.invoke(it)
}
binding.recyclerView.layoutManager =
FixLinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
binding.recyclerView.adapter = adapter
(binding.recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
val snapHelper = LeftPagerSnapHelper()
snapHelper.attachToRecyclerView(binding.recyclerView)
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)
}
})
}
}

View File

@ -6,10 +6,7 @@ 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.common.utils.*
import com.gh.gamecenter.core.utils.RandomUtils
import com.gh.gamecenter.databinding.HomeSlideListItemBinding
import com.gh.gamecenter.entity.HomeSlide
@ -42,7 +39,7 @@ class HomeSlideListItemViewHolder(val binding: HomeSlideListItemBinding) : BaseR
binding.includeGame.gameName.setTextColor(R.color.white.toColor())
homeSlide.linkGame?.let {
GameItemViewHolder.initGameSubtitle(it, binding.includeGame.gameSubtitleTv, null, binding.includeGame.root)
GameItemViewHolder.initGameSubtitleAndAdLabel(it, binding.includeGame.gameSubtitleTv)
}
binding.bottomGradient.visibility = View.GONE

View File

@ -0,0 +1,42 @@
package com.gh.gamecenter.home.test_v2
import com.gh.gamecenter.feature.entity.GameEntity
/**
* @author : liujiarui
* date : 2023/3/10
* description : 首页-新游开测-游戏下载进度更新辅助
*/
interface HomeGameTestV2DownloadStateUpdateHelper {
/**
* 模块所处的父列表索引
*/
var mParentPosition: Int
/**
* 游戏列表新增回调
*/
var mOnGameListAddCallback: (Int, List<GameEntity>) -> Unit
/**
* 设置模块在父列表的索引
*/
fun setParentPosition(parentPosition: Int) {
mParentPosition = parentPosition
}
/**
* 设置游戏列表新增回调
*/
fun setOnGameListAddCallback(callback: (Int, List<GameEntity>) -> Unit) {
mOnGameListAddCallback = callback
}
/**
* 通知外部有游戏列表添加
*/
fun notifyParentAddGameList(gameList: List<GameEntity>) {
mOnGameListAddCallback(mParentPosition, gameList)
}
}

View File

@ -0,0 +1,46 @@
package com.gh.gamecenter.home.test_v2
import android.view.View
import androidx.core.view.updateLayoutParams
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.databinding.ItemSkeletonHomeGameTestV2PlaceHolderBinding
/**
* @author : liujiarui
* date : 2023/3/6
* description : 新游开测,游戏列表 item space holder
*/
class HomeGameTestV2GameListPlaceHolderViewHolder(
private val mBinding: ItemSkeletonHomeGameTestV2PlaceHolderBinding
) : BaseRecyclerViewHolder<Any>(mBinding.root) {
fun bind(isLastRow: Boolean) {
mBinding.root.post {
mBinding.resetItemWidth(isLastRow)
}
}
/**
* 右侧条目露出一小部分
*/
private fun ItemSkeletonHomeGameTestV2PlaceHolderBinding.resetItemWidth(isLastRow: Boolean) {
root.parent?.let {
if (it is View) {
root.updateLayoutParams {
width = it.width - if (isLastRow) {
8F.dip2px()
} else {
24F.dip2px()
}
}
}
}
val padding = 16F.dip2px()
if (isLastRow) {
root.setPadding(padding, 0, padding, 12F.dip2px())
} else {
root.setPadding(padding, 0, 0, 12F.dip2px())
}
}
}

View File

@ -0,0 +1,208 @@
package com.gh.gamecenter.home.test_v2
import android.content.Context
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.gh.common.exposure.IExposable
import com.gh.common.util.DownloadItemUtils
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.core.utils.StringUtils
import com.gh.gamecenter.entity.GameDataWrapper
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureSource
import com.gh.gamecenter.home.ExposureItemData
import com.lightgame.adapter.BaseRecyclerAdapter
import com.lightgame.download.DownloadEntity
/**
* @author : liujiarui
* date : 2023/3/6
* description : 新游开测,游戏列表 adapter
*/
class HomeGameTestV2GameListRvAdapter(
context: Context,
private val mViewModel: HomeGameTestV2ViewModel,
private val mBasicExposureSource: List<ExposureSource>,
private val mOuterSequence: Int,
var exposureItemData: ExposureItemData
) : BaseRecyclerAdapter<ViewHolder>(context), IExposable {
private val mEntrance: String = "新游开测"
private val mExposureEventMap = mutableMapOf<Int, ExposureEvent>()
var mDataList = listOf<GameDataWrapper>()
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 getItemCount(): Int {
return mDataList.size
}
override fun getItemViewType(position: Int): Int {
val data = mDataList[position]
return when {
data.isSpace -> 0
data.isPlaceHolder -> 1
else -> 2
}
}
fun submitData(data: List<GameDataWrapper>) {
val oldList = mDataList
mDataList = data
DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return data.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition]
val newItem = data[newItemPosition]
return oldItem.index == newItem.index
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition]
val newItem = data[newItemPosition]
return oldItem.isSpace == newItem.isSpace
&& oldItem.isPlaceHolder == newItem.isPlaceHolder
&& oldItem.gameData?.id == newItem.gameData?.id
&& oldItem.gameData?.name == newItem.gameData?.name
}
}, false).dispatchUpdatesTo(this)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val data = mDataList.getOrNull(position) ?: return
var lastModPosition = itemCount % 3
if (lastModPosition == 0) lastModPosition = 3
val isLastRow = (itemCount - position) <= lastModPosition
if (holder is HomeGameTestV2GameListViewHolder) {
val gameData = data.gameData ?: return
holder.bindGameItem(gameData, isLastRow)
initGameHolder(holder, gameData, position)
} else if (holder is HomeGameTestV2GameListPlaceHolderViewHolder) {
holder.bind(isLastRow)
}
}
/**
* 初始化上报事件,下载按钮点击事件,游戏详情点击事件,
* 更新下载状态等
*/
private fun initGameHolder(
viewHolder: HomeGameTestV2GameListViewHolder,
gameEntity: GameEntity,
position: Int
) {
val exposureEvent = putExposureEvent(gameEntity, position)
//游戏下载事件上报
DownloadItemUtils.setOnClickListener(
mContext,
viewHolder.gameDownloadBtn, gameEntity, viewHolder.bindingAdapterPosition, this,
StringUtils.buildString(mEntrance, "+(新游开测[", position.toString(), "])"),
StringUtils.buildString("新游开测:", gameEntity.name),
exposureEvent
)
DownloadItemUtils.updateItem(mContext, gameEntity, viewHolder, true)
//游戏详情点击事件上报
viewHolder.itemView.setOnClickListener {
GameDetailActivity.startGameDetailActivity(
mContext,
gameEntity,
StringUtils.buildString(mEntrance, "+(新游开测[", viewHolder.adapterPosition.toString(), "])"),
traceEvent = exposureEvent
)
}
}
fun notifyItemByDownload(downloadEntity: DownloadEntity?) {
if (downloadEntity == null) {
notifyDataSetChanged()
} else {
mDataList.forEachIndexed { position, gameEntity ->
if (downloadEntity.gameId == gameEntity.gameData?.id) {
notifyItemChanged(position)
}
}
}
}
fun notifyChildItem(packageName: String) {
mDataList.forEachIndexed { position, gameEntity ->
gameEntity.gameData?.getApk()?.forEach { apkEntity ->
if (apkEntity.packageName == packageName) {
notifyItemChanged(position)
return
}
}
}
}
private fun putExposureEvent(
gameEntity: GameEntity,
position: Int
): ExposureEvent {
val exposureSources = ArrayList<ExposureSource>()
var timeType = mViewModel.getTimePointLiveData().value ?: ""
if (timeType == "recommend") {
timeType = "推荐"
}
exposureSources.add(
ExposureSource(
"新游开测",
timeType
)
)
val exposureEvent = ExposureEvent.createEventWithSourceConcat(
gameEntity = gameEntity.apply {
outerSequence = mOuterSequence
sequence = position
},
basicSource = mBasicExposureSource,
source = exposureSources
)
mExposureEventMap.put(position, exposureEvent)
//游戏曝光事件交给外层的adapter处理
val list = mExposureEventMap.values.toList()
val arrayList = arrayListOf(*list.toTypedArray())
exposureItemData.exposureEventList = arrayList
return exposureEvent
}
/**
* 左右滑动曝光条目
*/
fun exposureItem(firstItemPosition: Int) {
val gameList = mDataList
for (position in firstItemPosition..firstItemPosition + 2) {
val gameEntity = gameList.getOrNull(position)?.gameData ?: continue
putExposureEvent(gameEntity, position)
}
}
override fun getEventByPosition(pos: Int): ExposureEvent? {
return mExposureEventMap[pos]
}
override fun getEventListByPosition(pos: Int): List<ExposureEvent>? {
return null
}
}

View File

@ -0,0 +1,13 @@
package com.gh.gamecenter.home.test_v2
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.databinding.ItemHomeGameTestV2ItemSpaceBinding
/**
* @author : liujiarui
* date : 2023/3/6
* description : 新游开测,游戏列表 item space holder
*/
class HomeGameTestV2GameListSpaceViewHolder(binding: ItemHomeGameTestV2ItemSpaceBinding) :
BaseRecyclerViewHolder<Any>(binding.root) {
}

View File

@ -0,0 +1,121 @@
package com.gh.gamecenter.home.test_v2
import android.view.View
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import com.gh.common.databind.BindingAdapters
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.setDrawableStart
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.databinding.ItemHomeGameTestV2ItemBinding
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.game.GameItemViewHolder
/**
* @author : liujiarui
* date : 2023/3/6
* description :
*/
class HomeGameTestV2GameListViewHolder(
private val mBinding: ItemHomeGameTestV2ItemBinding
) : GameViewHolder(mBinding.root) {
init {
gameName = mBinding.gameName
gameDownloadBtn = mBinding.downloadBtn
gameDes = mBinding.gameDes
gameRating = mBinding.gameRating
recommendContainer = mBinding.recommendContainer
recommendIv = mBinding.recommendIv
multiVersionDownloadTv = mBinding.multiVersionDownloadTv
gameDownloadTips = mBinding.downloadTipsLottie
}
fun bindGameItem(gameEntity: GameEntity, isLastRow: Boolean) {
mBinding.run {
gameIconView.displayGameIcon(gameEntity)
BindingAdapters.setGameName(gameName, gameEntity, false, null)
BindingAdapters.setTextSize(gameRating, if (gameEntity.commentCount > 3) 12 else 10)
BindingAdapters.setGameTags(labelList, gameEntity)
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()
} else ""
gameRating.setPadding(0, 0, if (gameEntity.commentCount > 3) 8F.dip2px() else 0, 0)
gameRating.setTextColor(
if (gameEntity.commentCount > 3) {
R.color.theme_font.toColor(gameRating.context)
} else {
R.color.theme.toColor(gameRating.context)
}
)
//开服日期
val gameTimeText = getGameTimeText(gameEntity)
gameDes.text = gameTimeText
gameDes.isVisible = gameTimeText.isNotEmpty()
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitleTv)
fixViewHolderTheme(gameEntity)
root.post { resetItemWidth(isLastRow) }
}
}
/**
* 修复切换深色模式后,条目复用颜色没有变化的问题
*/
private fun fixViewHolderTheme(gameEntity: GameEntity) {
val context = mBinding.root.context
mBinding.root.setBackgroundColor(R.color.background_white.toColor(context))
gameName.setTextColor(R.color.text_title.toColor(context))
gameDownloadBtn.background = R.drawable.download_button_normal_style.toDrawable(context)
gameDes.setTextColor(R.color.text_subtitleDesc.toColor(context))
var gameRatingTextColor = R.color.theme.toColor(context)
if (gameEntity.commentCount > 3) {
gameRatingTextColor = R.color.theme_font.toColor(context)
}
gameRating?.setTextColor(gameRatingTextColor)
}
private fun getGameTimeText(gameEntity: GameEntity): String {
val time = gameEntity.time?.time ?: return ""
val timeText = TimeUtils.getFormatDate(time)
val typeText = when (gameEntity.eventType) {
"first" -> "首发上线"
"test" -> "开启测试"
"update" -> "更新"
else -> ""
}
return "$timeText $typeText"
}
/**
* 右侧条目露出一小部分
*/
private fun ItemHomeGameTestV2ItemBinding.resetItemWidth(isLastRow: Boolean) {
root.parent?.let {
if (it is View) {
root.updateLayoutParams {
width = it.width - if (isLastRow) {
8F.dip2px()
} else {
24F.dip2px()
}
}
}
}
val padding = 16F.dip2px()
if (isLastRow) {
root.setPadding(padding, 0, padding, 12F.dip2px())
} else {
root.setPadding(padding, 0, 0, 12F.dip2px())
}
}
}

View File

@ -0,0 +1,34 @@
package com.gh.gamecenter.home.test_v2
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.entity.SubjectRecommendEntity
import com.lightgame.adapter.BaseRecyclerAdapter
/**
* @author : liujiarui
* date : 2023/3/6
* description : 新游开测,推荐标签
*/
class HomeGameTestV2RecommendRvAdapter(
context: Context,
var data: List<RecommendLabel>,
private val mLocation: String,
private val mBlockData: SubjectRecommendEntity? = null
) : BaseRecyclerAdapter<HomeGameTestV2RecommendViewHolder>(context) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeGameTestV2RecommendViewHolder {
return HomeGameTestV2RecommendViewHolder(parent.toBinding(), mLocation, mBlockData)
}
override fun getItemCount(): Int {
return data.size
}
override fun onBindViewHolder(holder: HomeGameTestV2RecommendViewHolder, position: Int) {
val label = data.getOrElse(position) { RecommendLabel() }
holder.bind(label)
}
}

View File

@ -0,0 +1,54 @@
package com.gh.gamecenter.home.test_v2
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.ItemHomeGameTestV2RecommendLabelBinding
import com.gh.gamecenter.entity.RecommendLabel
import com.gh.gamecenter.entity.SubjectRecommendEntity
/**
* @author : liujiarui
* date : 2023/3/3
* description : 新游开测 推荐标签 holder
*/
class HomeGameTestV2RecommendViewHolder(
private val mBinding: ItemHomeGameTestV2RecommendLabelBinding,
private val mLocation: String,
private val mBlockData: SubjectRecommendEntity? = null
) : BaseRecyclerViewHolder<Any>(mBinding.root) {
fun bind(data: RecommendLabel) {
val context = mBinding.tvLabel.context
mBinding.tvLabel.text = data.text
mBinding.tvLabel.setTextColor(R.color.text_subtitle.toColor(context))
mBinding.tvLabel.background =
ContextCompat.getDrawable(
context,
R.drawable.bg_game_test_v2_recommend_label
)
mBinding.tvLabel.setDebouncedClickListener {
DirectUtils.directToLinkPage(
context,
data.link,
"新游开测",
""
)
//埋点上报
NewFlatLogUtils.logGameTestV2RecommendLabelClick(
location = mLocation,
blockId = mBlockData?.link ?: "",
blockName = mBlockData?.name ?: "",
tagId = data.id,
text = data.text,
sequence = this.bindingAdapterPosition,
linkType = data.link.type ?: "",
linkId = data.link.link ?: "",
linkText = data.link.text ?: ""
)
}
}
}

View File

@ -0,0 +1,127 @@
package com.gh.gamecenter.home.test_v2
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.lightgame.adapter.BaseRecyclerAdapter
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
/**
* @author : liujiarui
* date : 2023/3/3
* description :
*/
class HomeGameTestV2TimePointRvAdapter(
context: Context,
private val mViewModel: HomeGameTestV2ViewModel,
var data: List<String>
) : BaseRecyclerAdapter<HomeGameTestV2TimePointViewHolder>(context) {
private var mSelectedPosition: Int = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeGameTestV2TimePointViewHolder {
return HomeGameTestV2TimePointViewHolder(parent.toBinding())
}
override fun getItemCount(): Int {
return data.size
}
override fun onBindViewHolder(holder: HomeGameTestV2TimePointViewHolder, position: Int) {
val text = data.getOrElse(position) { "" }
holder.binding.root.setOnClickListener {
val bindingAdapterPosition = holder.bindingAdapterPosition
if (bindingAdapterPosition == mSelectedPosition) return@setOnClickListener
selectPoint(bindingAdapterPosition, true)
mViewModel.onTimePointClick(text)
}
val isSelected = position == mSelectedPosition
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 = mSelectedPosition
mSelectedPosition = 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 = data.getOrElse(mSelectedPosition) { "" }
return if (text == "recommend") "推荐" else "全部"
}
fun getStartTime(): Long {
val text = data.getOrElse(mSelectedPosition) { "" }
return getDateTimeStamp(text)
}
fun getSequence(): Int {
return mSelectedPosition
}
}

View File

@ -0,0 +1,62 @@
package com.gh.gamecenter.home.test_v2
import android.view.ViewGroup.MarginLayoutParams
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.ItemHomeGameTestV2TimePointBinding
/**
* @author : liujiarui
* date : 2023/3/3
* description :
*/
class HomeGameTestV2TimePointViewHolder(
val binding: ItemHomeGameTestV2TimePointBinding
) : BaseRecyclerViewHolder<Any>(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.setBackgroundResource(R.drawable.ic_home_game_test_v2_time_point_dot)
binding.tvTimePoint.isChecked = false
binding.tvTimePoint.textSize = 12f
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
}
private fun isStartNode() {
binding.lineLeft.isInvisible = true
binding.root.updateLayoutParams {
(this as? MarginLayoutParams)?.leftMargin = 16f.dip2px()
}
}
private fun isEndNode() {
binding.lineRight.isInvisible = true
}
}

View File

@ -0,0 +1,411 @@
package com.gh.gamecenter.home.test_v2
import android.app.Application
import androidx.lifecycle.AndroidViewModel
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.GameData
import com.gh.gamecenter.entity.GameDataWrapper
import com.gh.gamecenter.entity.HomeItemTestV2Entity
import com.gh.gamecenter.entity.SubjectRecommendEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import retrofit2.HttpException
/**
* @author : liujiarui
* date : 2023/3/3
* description : 首页-推荐tab-新游开测 item 的分页加载 ViewModel
*/
class HomeGameTestV2ViewModel(application: Application) : AndroidViewModel(application),
HomeGameTestV2DownloadStateUpdateHelper {
override var mParentPosition: Int = 0
override var mOnGameListAddCallback: (Int, List<GameEntity>) -> Unit = { _, _ -> }
//埋点上报的信息,别的板块使用修改这里
var location: String = "首页"
var blockData: SubjectRecommendEntity? = null
private val mApi = RetrofitManager.getInstance().api
/**
* 加载状态livedata
*/
private val mLoadStateLiveData = MutableLiveData<LoadStatus>()
/**
* 加载数据返回livedata
*/
private val mDataLiveData = MutableLiveData<List<GameDataWrapper>>()
/**
* 时间线节点事件通知livedata
*/
private val mTimePointLiveData = MutableLiveData<String>()
/**
* 游戏列表滑动到指定position位置通知
*/
private val mGameListScrollLiveData = MutableLiveData<Int>()
/**
* 时间轴数据
*/
private var mTimePoint: Map<String, String>? = null
/**
* 时间轴节点对应游戏总数
*/
private var mDataCount: Map<String, Int>? = null
/**
* 设置时间轴数据
*/
fun setTimePoint(timePoint: Map<String, String>) {
this.mTimePoint = timePoint
}
/**
* 设置时间轴节点游戏总数
* 用占位和空条目填充数据列表
*/
fun setDataCount(dataCount: Map<String, Int>) {
this.mDataCount = dataCount
if (dataCount.isEmpty()) return
val rawCount = 3
val gameList = mutableListOf<GameDataWrapper>()
var index = 0
dataCount.asSequence().forEach {
val (timeType, count) = it
repeat(count) {
gameList.add(GameDataWrapper(index++, timeType = timeType, isPlaceHolder = true))
}
val limit = rawCount - (count % rawCount)
repeat(limit) {
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 {
values().forEach {
if (it.command == command) return it
}
return LEFT
}
}
}
/**
* 分页加载
* @param isRefresh 是否是刷新
* @param action 分页加载方向
* @param pageId 分页id 加载下一页为上一页最后一个id加载上一页为下一页第一个id
* @param timeType 时间轴类型 time_typerecommend推荐、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 = 9
) {
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_typerecommend推荐、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<HomeItemTestV2Entity>() {
override fun onResponse(response: HomeItemTestV2Entity?) {
mLoadStateLiveData.postValue(LoadStatus.LIST_LOADED)
val data = response?.data ?: return
addGameList(
list = data,
timeType = response.timeType,
pageId = response.pageId,
startId = response.startId,
action = Action.getActionByCommand(response.action),
isRefresh = false
)
}
override fun onFailure(e: HttpException?) {
mLoadStateLiveData.postValue(LoadStatus.INIT_FAILED)
}
})
}
fun getDataListLiveData(): LiveData<List<GameDataWrapper>> {
return mDataLiveData
}
fun getLoadStateLiveData(): LiveData<LoadStatus> {
return mLoadStateLiveData
}
fun getTimePointLiveData(): LiveData<String> {
return mTimePointLiveData
}
fun getGameListScrollLiveData(): LiveData<Int> {
return mGameListScrollLiveData
}
private fun getCurrentData(): List<GameDataWrapper> {
return mDataLiveData.value ?: emptyList()
}
/**
* 根据当前条目索引向前和向后查找6个条目如果有占位条目则加载数据
*/
fun parseActionWithPosition(position: Int) {
val forwardPosition = maxOf(position - 6, 0)
if (forwardPosition != position) {
val list = getCurrentData().subList(forwardPosition, position + 1)
val lastPlaceHolderItem = list.lastOrNull { it.isPlaceHolder }
val firstHasDataItem = list.firstOrNull { it.gameData != null }
if (firstHasDataItem != null && lastPlaceHolderItem != null &&
firstHasDataItem.index > lastPlaceHolderItem.index
) {
loadData(
action = Action.RIGHT,
pageId = firstHasDataItem.gameData?.id ?: firstHasDataItem.pageId,
timeType = firstHasDataItem.timeType,
loadTimeType = lastPlaceHolderItem.timeType
)
}
}
val backwardPosition = minOf(position + 7, getCurrentData().size)
if (backwardPosition != position) {
val list = getCurrentData().subList(position, backwardPosition)
val firstPlaceHolderItem = list.firstOrNull { it.isPlaceHolder }
val lastHasDataItem = list.lastOrNull { it.gameData != null }
if (firstPlaceHolderItem != null && lastHasDataItem != null &&
lastHasDataItem.index < firstPlaceHolderItem.index
) {
loadData(
action = Action.LEFT,
pageId = lastHasDataItem.gameData?.id ?: lastHasDataItem.pageId,
timeType = lastHasDataItem.timeType,
loadTimeType = firstPlaceHolderItem.timeType
)
}
}
}
//添加数据到列表
@Synchronized //可能会有多请求同时修改数据问题,以防万一
fun addGameList(
list: List<GameData>,
timeType: String,
pageId: String,
startId: String,
action: Action,
isRefresh: Boolean = false
) {
val currentData = getCurrentData().toMutableList()
val gameList = mutableListOf<GameDataWrapper>()
list.forEach { gameData ->
val gameTimeType = gameData.timeType
//过滤被屏蔽的游戏 todo 占位也要同步减少
val filterGames = RegionSettingHelper.filterGame(gameData.games)
//注册到外部model用于下载按钮状态更新
notifyParentAddGameList(filterGames)
filterGames.forEach { gameEntity ->
gameList.add(
GameDataWrapper(
-1, //还未分配位置的数据
pageId,
gameTimeType,
gameEntity
)
)
}
}
if (currentData.size < gameList.size) {
//出现错误,没有获取到时间轴的游戏数量, 或者数量不正确
return
}
//给数据分配位置
var index = 0
if (isRefresh) {
//初始化分配
while (gameList.isNotEmpty()) {
val gameDataWrapper = gameList.removeFirst()
while (currentData.getOrNull(index)?.isPlaceHolder == false) {
index++
}
if (index >= currentData.size) {
//出现错误,没有获取到时间轴的游戏数量, 或者数量不正确
return
}
currentData[index] = gameDataWrapper.also { it.index = index }
}
} else {
//分页加载逻辑
val findPageIdItemPosition = currentData.indexOfFirst {
it.timeType == timeType && it.gameData?.id == pageId
}
when (action) {
Action.LEFT -> {
val firstItemWithTimeTypePosition = currentData.indexOfFirst {
it.timeType == timeType
}
//获取分页标志的条目索引
index = if (findPageIdItemPosition != -1) {
//找得到pageId那条item就以此item的索引为基准
findPageIdItemPosition
} else {
//找不到分页标志item就以时间轴第一个条目作为基准
firstItemWithTimeTypePosition
}
//左滑,向右加载
if (findPageIdItemPosition != -1) index++
while (gameList.isNotEmpty()) {
val gameDataWrapper = gameList.removeFirst()
while (currentData.getOrNull(index)?.isSpace == true) {
index++
}
if (index >= currentData.size) {
//出现错误,没有获取到时间轴的游戏数量, 或者数量不正确
return
}
currentData[index] = gameDataWrapper.also { it.index = index }
index++
}
}
Action.RIGHT -> {
//右滑,向左加载
val lastItemWithTimeTypePosition = currentData.indexOfLast {
it.timeType == timeType
}
//获取分页标志的条目索引
index = if (findPageIdItemPosition != -1) {
//找得到pageId那条item就以此item的索引为基准
findPageIdItemPosition
} else {
//找不到分页标志item就以时间轴第一个条目作为基准
lastItemWithTimeTypePosition
}
if (findPageIdItemPosition != -1) index--
while (gameList.isNotEmpty()) {
val gameDataWrapper = gameList.removeLast()
while (currentData.getOrNull(index)?.isSpace == true) {
index--
}
if (index < 0) {
//出现错误,没有获取到时间轴的游戏数量, 或者数量不正确
return
}
currentData[index] = gameDataWrapper.also { it.index = index }
index--
}
}
}
}
mDataLiveData.value = currentData.toMutableList()
}
/**
* 选中时间轴,如果列表内有相关数据则滑动到指定位置
* @param timeType 时间轴类型 time_typerecommend推荐、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?.get(timeType) ?: ""
if (firstItemWithTimeType.isPlaceHolder) {
//加载当前页数据 , 不足limit 则用下一页补足
val timeTypeList = mTimePoint?.keys?.toList()
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
)
}
//判断前一页是否有数据
if (index == -1) return
val preRow = getCurrentData().take(index).takeLast(3)
val preRowNoData = preRow.all { it.isPlaceHolder || it.isSpace }
//前一列没有数据,需要加载
if (preRowNoData && preRow.isNotEmpty()) {
//前一列如果有空条目则需要计算空条目的数量作为limit
val space = preRow.count { it.isSpace }
val limit = 9 - space
loadData(
isRefresh = false,
action = Action.RIGHT,
pageId = startId,
timeType = timeType,
loadTimeType = preRow.firstOrNull()?.timeType ?: timeType,
limit = limit
)
}
}
}

Some files were not shown because too many files have changed in this diff Show More