Compare commits

...

136 Commits

Author SHA1 Message Date
684dbcfd87 chore: 版本更新至 5.27.1 2023-06-16 17:06:45 +08:00
4f8b961e2c Merge branch 'hotfix/v5.27.0-890/GHZS-2719' into 'release'
fix: 首页顶部tab栏图片显示问题 https://jira.shanqu.cc/browse/GHZS-2719

See merge request halo/android/assistant-android!1134
2023-06-16 14:21:55 +08:00
58a7f7c3fa Merge branch 'hotfix/v5.27.0-890/crashes' into 'release'
fix: 捕抓获取是否已安装情况时的闪退...

See merge request halo/android/assistant-android!1135
2023-06-16 14:21:50 +08:00
ae8267d96d fix: 首页顶部tab栏图片显示问题 https://jira.shanqu.cc/browse/GHZS-2719 2023-06-16 14:18:33 +08:00
e20af462b7 fix: 捕抓获取是否已安装情况时的闪退 https://sentry.shanqu.cc/organizations/lightgame/issues/304548/?project=22&query=dist%3A890+level%3Afatal&statsPeriod=14d 2023-06-16 14:17:36 +08:00
e2773ed8c7 Merge branch 'feat/update_gdt_sdk' into 'release'
feat: 更新广点通 SDK

See merge request halo/android/assistant-android!1132
2023-06-16 11:33:19 +08:00
c77e5ca56b Merge branch 'pack/v5.25.3-853/change_kuaishou_channel_sdk' into 'release'
更新快手 SDK ,对接快手渠道 SDK,调整快手 APP ID https://jira.shanqu.cc/browse/GHZS-2306

See merge request halo/android/assistant-android!1131
2023-06-16 11:26:44 +08:00
e78c23ca4f Merge branch 'release' into 'pack/v5.25.3-853/change_kuaishou_channel_sdk'
# Conflicts:
#   app/src/kuaishou/java/com/gh/gamecenter/KuaishouHelper.kt
2023-06-16 03:26:17 +00:00
b790d71aa5 feat: 更新快手 SDK,更换默认帐号 2023-06-15 17:22:51 +08:00
5c91023a68 Merge branch 'hotfix/v5.27.0-890/wrong_time_issue' into 'release'
fix: 我的收藏-帖子时间显示问题 https://jira.shanqu.cc/browse/GHZS-2675

See merge request halo/android/assistant-android!1119
2023-06-14 14:40:35 +08:00
2eae71bc96 fix: 我的收藏-帖子时间显示问题 https://jira.shanqu.cc/browse/GHZS-2675 2023-06-14 14:33:54 +08:00
909477ec7f Merge branch 'hotfix/v5.27.0-890/vertical_slide_data_issue' into 'release'
fix: 游戏专题数据显示问题 https://jira.shanqu.cc/browse/GHZS-2665

See merge request halo/android/assistant-android!1116
2023-06-14 13:50:28 +08:00
a5da376f62 fix: 游戏专题数据显示问题 https://jira.shanqu.cc/browse/GHZS-2665 2023-06-14 11:54:38 +08:00
d084cb1c34 Merge branch 'fix/GHZS-2516' into 'release'
fix: 神策首次事件取值的相关排查(添加开发校验埋点) https://jira.shanqu.cc/browse/GHZS-2516

See merge request halo/android/assistant-android!1110
2023-06-12 15:17:41 +08:00
411bf3561f fix: 神策首次事件取值的相关排查(添加开发校验埋点) https://jira.shanqu.cc/browse/GHZS-2516 2023-06-12 15:11:58 +08:00
610c006aeb Merge branch 'fix/pad_display_issue' into 'dev'
fix: 低版本系统不支持新 API 导致获取不到软键盘高度的问题 https://jira.shanqu.cc/browse/GHZS-2639

See merge request halo/android/assistant-android!1107
2023-06-09 16:40:49 +08:00
ffa81bc4c1 fix: 低版本系统不支持新 API 导致获取不到软键盘高度的问题 https://jira.shanqu.cc/browse/GHZS-2639 2023-06-09 16:31:43 +08:00
67477ac20a feat: 更新广点通 SDK 2023-06-09 15:39:40 +08:00
a6a16e4694 Merge branch 'fix/GHZS-2651' into 'dev'
fix: 【光环助手】首页/版块内容列表-游戏单图片显示(0609测试) https://jira.shanqu.cc/browse/GHZS-2651

See merge request halo/android/assistant-android!1105
2023-06-09 13:51:56 +08:00
8f8352dc12 fix: 【光环助手】首页/版块内容列表-游戏单图片显示(0609测试) https://jira.shanqu.cc/browse/GHZS-2651 2023-06-09 13:49:06 +08:00
1bbbe575f2 Merge branch 'fix/oaid_crash' into 'dev'
ci: 移除 byteX 的 access_inline 插件

See merge request halo/android/assistant-android!1104
2023-06-09 10:08:53 +08:00
5975d9850f Merge branch 'fix/webview_multi_process_crash' into 'dev'
fix: 修复部分设备出现的多进程访问 WebView 数据闪退

See merge request halo/android/assistant-android!1103
2023-06-09 10:08:47 +08:00
ef001b0a77 ci: 移除 byteX 的 access_inline 插件 2023-06-09 09:58:04 +08:00
fd49ecbc8a fix: 修复部分设备出现的多进程访问 WebView 数据闪退 2023-06-08 17:25:33 +08:00
d683969eda Merge branch 'feat/CWZS-76' into 'dev'
feat: 下载弹窗卖点icon更换 https://jira.shanqu.cc/browse/CWZS-74

See merge request halo/android/assistant-android!1101
2023-06-08 16:07:13 +08:00
f8527da9df feat: 下载弹窗卖点icon更换 https://jira.shanqu.cc/browse/CWZS-74 2023-06-08 16:00:06 +08:00
f50d901171 Merge branch 'fix/GHZS-2639' into 'dev'
fix: 华为pad简易适配—0606测试 https://jira.shanqu.cc/browse/GHZS-2639

See merge request halo/android/assistant-android!1099
2023-06-08 14:54:01 +08:00
71db870bd2 Merge branch 'fix/GHZS-2651' into 'dev'
fix: 【光环助手】首页/版块内容列表-游戏单图片显示 https://jira.shanqu.cc/browse/GHZS-2651

See merge request halo/android/assistant-android!1098
2023-06-08 14:49:03 +08:00
b4c550f71b fix: 华为pad简易适配—0606测试 https://jira.shanqu.cc/browse/GHZS-2639 2023-06-08 14:43:14 +08:00
91bde61054 fix: 【光环助手】首页/版块内容列表-游戏单图片显示 https://jira.shanqu.cc/browse/GHZS-2651 2023-06-08 14:26:25 +08:00
d7940121ee Merge branch 'release' into 'dev'
Release

See merge request halo/android/assistant-android!1096
2023-06-08 09:19:32 +08:00
ef9c473301 Merge branch 'fix/gallery_slide_crash' into 'release'
fix: 修复滚动图集在游戏数量较少时的数组越界闪退

See merge request halo/android/assistant-android!1094
2023-06-07 16:24:18 +08:00
2c82437449 Merge branch 'fix/v2_test_crash' into 'release'
fix: 捕抓新游开测快速滑动时偶发的空指针异常

See merge request halo/android/assistant-android!1095
2023-06-07 16:24:04 +08:00
311e5e4225 fix: 捕抓新游开测快速滑动时偶发的空指针异常 2023-06-07 16:16:53 +08:00
91864e3681 fix: 修复滚动图集在游戏数量较少时的数组越界闪退 2023-06-07 16:07:31 +08:00
5e66db7435 Merge branch 'release' into 'dev'
Release

See merge request halo/android/assistant-android!1091
2023-06-07 11:51:04 +08:00
e7c74d3f39 Merge branch 'fix/GHZS-2643' into 'release'
fix: 神策埋点补充—0606测试 https://jira.shanqu.cc/browse/GHZS-2643

See merge request halo/android/assistant-android!1090
2023-06-07 10:43:55 +08:00
d0fa02b221 fix: 神策埋点补充—0606测试 https://jira.shanqu.cc/browse/GHZS-2643 2023-06-07 10:40:06 +08:00
70e4378ef0 feat: 推广打包接入快手分包sdk https://jira.shanqu.cc/browse/GHZS-2306 2023-06-07 09:28:12 +08:00
f8ee943678 Merge branch 'fix/typo' into 'release'
fix: 调整存档缺省文案

See merge request halo/android/assistant-android!1089
2023-06-06 17:51:43 +08:00
918aa17fbd fix: 调整存档缺省文案 2023-06-06 17:49:56 +08:00
0a7a3d9878 Merge branch 'fix/vgame' into 'release'
fix: 修复畅玩游戏功能没有开启的问题

See merge request halo/android/assistant-android!1088
2023-06-06 17:30:56 +08:00
b6a823e793 fix: 修复畅玩游戏功能没有开启的问题 2023-06-06 17:27:41 +08:00
9a95a4623d Merge branch 'release' into 'dev'
Release

See merge request halo/android/assistant-android!1086
2023-06-06 16:52:39 +08:00
206bc1bfcc Merge branch 'fix/CWZS-71' into 'release'
fix:【光环助手】5.27验收问题汇总(2) https://jira.shanqu.cc/browse/CWZS-71

See merge request halo/android/assistant-android!1085
2023-06-06 16:52:02 +08:00
4045e42a7b fix:【光环助手】5.27验收问题汇总(2) https://jira.shanqu.cc/browse/CWZS-71 2023-06-06 16:39:38 +08:00
15f283d330 Merge branch 'release' into 'dev'
合并5.26.0相关变更

See merge request halo/android/assistant-android!1083
2023-06-06 14:34:00 +08:00
97bafee09e Merge branch 'fix/gapps_install_issue' into 'release'
fix: 修复畅玩安装谷歌框架时应用切换到后台返回时安装卡住的问题

See merge request halo/android/assistant-android!1081
2023-06-06 14:29:45 +08:00
e32745d9d7 Merge branch 'fix/vspace_delete_issue' into 'release'
fix: 修复畅玩组件安装包被外部删除时再安装会一直触发安装空文件的问题

See merge request halo/android/assistant-android!1082
2023-06-06 14:29:41 +08:00
c37a54ac89 fix: 修复畅玩安装谷歌框架时应用切换到后台返回时安装卡住的问题 2023-06-06 14:27:11 +08:00
607d514ae9 fix: 修复畅玩组件安装包被外部删除时再安装会一直触发安装空文件的问题 2023-06-06 14:18:33 +08:00
62ee0b4a6e Merge branch 'feature/GHZS-2632' into 'release'
feat: 神策埋点补充—客户端 https://jira.shanqu.cc/browse/GHZS-2632

See merge request halo/android/assistant-android!1080
2023-06-06 11:31:00 +08:00
11ff054520 feat: 神策埋点补充—客户端 https://jira.shanqu.cc/browse/GHZS-2632 2023-06-06 10:37:43 +08:00
e96fc0f3b4 Merge branch 'fix/vgame_install_issue' into 'release'
fix: 修复畅玩游戏在安装时,手动再触发安装会出现安装失败的问题

See merge request halo/android/assistant-android!1079
2023-06-05 15:58:56 +08:00
a7875b21c9 fix: 修复畅玩游戏在安装时,手动再触发安装会出现安装失败的问题 2023-06-05 15:50:10 +08:00
e53154c810 Merge branch 'feat/GHZS-2596' into 'release'
feat: 游戏平台上报优化 https://jira.shanqu.cc/browse/GHZS-2596

See merge request halo/android/assistant-android!1078
2023-06-05 13:35:18 +08:00
e1d89725bc Merge branch 'fix/download_button_crash' into 'release'
fix: 修复DownloadButton偶发的闪退问题...

See merge request halo/android/assistant-android!1077
2023-06-05 11:31:59 +08:00
1d3ec9eff6 fix: 修复DownloadButton偶发的闪退问题... 2023-06-05 11:31:58 +08:00
0403743533 feat: 游戏平台上报优化 https://jira.shanqu.cc/browse/GHZS-2596 2023-06-05 11:17:06 +08:00
1f25b932d5 Merge branch 'fix/vspace_installed_error' into 'release'
fix: 增强检查应用是否已安装的方法

See merge request halo/android/assistant-android!1075
2023-06-02 15:42:02 +08:00
d3545bc766 fix: 增强检查应用是否已安装的方法 2023-06-02 15:27:36 +08:00
b2b858c403 Merge branch 'fix/vgame_uninstall_error' into 'release'
fix: 修复畅玩游戏安装情况判断异常的问题

See merge request halo/android/assistant-android!1074
2023-06-01 16:03:27 +08:00
debbe08262 fix: 修复畅玩游戏安装情况判断异常的问题 2023-06-01 15:58:35 +08:00
4e746a2b16 Merge branch 'fix/vgame_switch' into 'release'
fix: 修复覆盖安装时因为没有成功获取设置配置而导致功能被关闭的问题

See merge request halo/android/assistant-android!1073
2023-06-01 11:25:37 +08:00
b86e372e5b fix: 修复覆盖安装时因为没有成功获取设置配置而导致功能被关闭的问题 2023-06-01 11:16:42 +08:00
f65e76a94a Merge branch 'fix/CWZS-70' into 'release'
fix: 32位畅玩游戏下载流程埋点—0601测试 https://jira.shanqu.cc/browse/CWZS-70

See merge request halo/android/assistant-android!1072
2023-06-01 10:44:07 +08:00
05c8de827e fix: 32位畅玩游戏下载流程埋点—0601测试 https://jira.shanqu.cc/browse/CWZS-70 2023-06-01 10:39:18 +08:00
78eb10fa3c Merge branch 'feature/CWZS-66' into 'release'
feat: 32位组件-埋点补充-05/29 https://jira.shanqu.cc/browse/CWZS-66

See merge request halo/android/assistant-android!1071
2023-05-31 14:58:17 +08:00
23dfb7378b feat: 32位组件-埋点补充-05/29 https://jira.shanqu.cc/browse/CWZS-66 2023-05-31 14:39:13 +08:00
0e2e175194 Merge remote-tracking branch 'origin/dev' into dev-5.27.0 2023-05-31 11:37:32 +08:00
9050a04c3a Merge remote-tracking branch 'origin/release' into dev
# Conflicts:
#	dependencies.gradle
2023-05-31 11:29:19 +08:00
4ff21914c1 Merge branch 'fix/CWZS-69' into 'dev'
fix: 32位畅玩游戏下载流程—0530测试 https://jira.shanqu.cc/browse/CWZS-68

See merge request halo/android/assistant-android!1070
2023-05-31 09:45:50 +08:00
0dec30ff56 fix: 32位畅玩游戏下载流程—0530测试 https://jira.shanqu.cc/browse/CWZS-68 2023-05-31 09:09:23 +08:00
8668b2b4ec Merge branch 'feature-GHZS-2552' into 'dev-5.27.0'
feat:版主新增帖子置顶权限—0530测试 https://jira.shanqu.cc/browse/GHZS-2552

See merge request halo/android/assistant-android!1068
2023-05-30 16:20:12 +08:00
b758658ea6 feat:版主新增帖子置顶权限—0530测试 https://jira.shanqu.cc/browse/GHZS-2552 2023-05-30 16:20:12 +08:00
4ca9c32762 Merge branch 'feat/support_pad' into 'dev'
feat: 简单适配平板显示

See merge request halo/android/assistant-android!1067
2023-05-30 15:17:00 +08:00
af8fdd5d65 feat: 简单适配平板 2023-05-30 14:54:53 +08:00
36dd4f5a82 Merge branch 'fix/CWZS-65' into 'dev'
fix: 32位畅玩游戏下载流程—0529测试 https://jira.shanqu.cc/browse/CWZS-65

See merge request halo/android/assistant-android!1066
2023-05-30 14:42:00 +08:00
c7161e3483 fix: 32位畅玩游戏下载流程—0529测试 https://jira.shanqu.cc/browse/CWZS-65 2023-05-30 14:30:14 +08:00
bffaaffc56 Merge branch 'fix/remove_useless_request' into 'dev-5.27.0'
fix: 移除无用下载完成统计接口

See merge request halo/android/assistant-android!1065
2023-05-30 11:09:14 +08:00
3257dd4c53 Merge branch 'feat/GHZS-2406' into 'dev-5.27.0'
feat: 下载事件埋点补充 https://jira.shanqu.cc/browse/GHZS-2406

See merge request halo/android/assistant-android!1064
2023-05-30 11:09:02 +08:00
6793945606 feat: 下载事件埋点补充 https://jira.shanqu.cc/browse/GHZS-2406 2023-05-30 11:03:37 +08:00
52be14a4ef fix: 移除无用下载完成统计接口 2023-05-30 09:54:38 +08:00
e2cb5a31cf Merge branch 'fix/CWZS-64' into 'dev'
fix: 32位畅玩游戏下载流程—0529UI测试 https://jira.shanqu.cc/browse/CWZS-64

See merge request halo/android/assistant-android!1063
2023-05-29 18:04:04 +08:00
d807b7973b fix: 32位畅玩游戏下载流程—0529UI测试 https://jira.shanqu.cc/browse/CWZS-64 2023-05-29 18:01:57 +08:00
1fb0d13646 Merge branch 'feat/GHZS-2388' into 'dev'
feat: 广告位前置工作(接入新版 OAID SDK) https://jira.shanqu.cc/browse/GHZS-2389

See merge request halo/android/assistant-android!1062
2023-05-29 17:25:24 +08:00
68a3eadfeb feat: 广告位前置工作(接入新版 OAID SDK) https://jira.shanqu.cc/browse/GHZS-2389 2023-05-29 16:49:25 +08:00
a2d904c634 Merge branch 'feat/CWZS-58' into 'dev-5.27.0'
feat: 畅玩助手及游戏下载流程梳理 https://jira.shanqu.cc/browse/CWZS-58

See merge request halo/android/assistant-android!1061
2023-05-26 17:53:23 +08:00
56cd1bdad7 feat: 畅玩助手及游戏下载流程梳理 https://jira.shanqu.cc/browse/CWZS-58 2023-05-26 17:36:13 +08:00
8b4f71bc75 Merge remote-tracking branch 'origin/dev' into dev-5.27.0 2023-05-26 17:13:26 +08:00
aa069b7a21 Merge remote-tracking branch 'origin/release' into dev
# Conflicts:
#	dependencies.gradle
2023-05-26 16:59:59 +08:00
fb0db2feee Merge branch 'feature-GHZS-2428' into 'dev-5.27.0'
feat:版主新增帖子置顶权限—客户端 https://jira.shanqu.cc/browse/GHZS-2428

See merge request halo/android/assistant-android!1059
2023-05-26 16:36:10 +08:00
4c531b40b0 feat:版主新增帖子置顶权限—客户端 https://jira.shanqu.cc/browse/GHZS-2428 2023-05-26 16:25:08 +08:00
bd11481c2d Merge branch 'feat/GHZS-2450' into 'dev'
feat: H5链接游戏适配优化 https://jira.shanqu.cc/browse/GHZS-2450

See merge request halo/android/assistant-android!1055
2023-05-26 14:36:34 +08:00
a3f1b09da1 feat: H5链接游戏适配优化 https://jira.shanqu.cc/browse/GHZS-2450 2023-05-26 14:27:10 +08:00
1798dfb64d Merge branch 'fix/CWZS-62' into 'dev'
fix: 32位畅玩游戏下载流程

See merge request halo/android/assistant-android!1054
2023-05-26 14:11:35 +08:00
8e5814d389 fix: 32位畅玩游戏下载流程—0526UI测试 https://jira.shanqu.cc/browse/CWZS-63 2023-05-26 14:06:01 +08:00
14c01cf171 fix: 32位畅玩游戏下载流程—0525测试 https://jira.shanqu.cc/browse/CWZS-62 2023-05-26 14:06:01 +08:00
2ee4ad6752 ci: 邮件编译添加 dev-5.27.0 2023-05-25 11:41:55 +08:00
73d7a38886 Merge branch 'feature/GHZS-2448' into 'dev-5.27.0'
feat: 下载流量提示弹窗埋点补充—客户端 https://jira.shanqu.cc/browse/GHZS-2448

See merge request halo/android/assistant-android!1045
2023-05-23 15:28:09 +08:00
e1f70ce788 feat: 下载流量提示弹窗埋点补充—客户端 https://jira.shanqu.cc/browse/GHZS-2448 2023-05-22 17:52:19 +08:00
fe5b93c35a chore: 版本更新至 5.27.0 2023-05-22 11:46:06 +08:00
74c3e7a10d Merge branch 'dev-5.26.0' into dev-5.27.0 2023-05-22 11:41:49 +08:00
deb2b07644 Merge remote-tracking branch 'origin/dev' into dev-5.26.0 2023-05-22 11:27:59 +08:00
c6b433d09c Merge branch 'feature-GHZS-2444' into 'dev-5.27.0'
feat:游戏礼包相关优化—客户端 https://jira.shanqu.cc/browse/GHZS-2444

See merge request halo/android/assistant-android!1039
2023-05-22 11:04:15 +08:00
c556a127f5 feat:游戏礼包相关优化—客户端 https://jira.shanqu.cc/browse/GHZS-2444 2023-05-22 11:00:43 +08:00
bdfd88a4c3 Merge branch 'fix/GHZS-2426' into 'dev-5.26.0'
fix: 已安装列表游戏显示问题 https://jira.shanqu.cc/browse/GHZS-2426

See merge request halo/android/assistant-android!1038
2023-05-19 15:42:25 +08:00
2697fd57b4 fix: 已安装列表游戏显示问题 https://jira.shanqu.cc/browse/GHZS-2426 2023-05-19 15:35:19 +08:00
06e238b2d5 Merge branch 'feature/CWZS-36' into 'dev-5.26.0'
feat: 32位畅玩游戏下载流程—客户端 https://jira.shanqu.cc/browse/CWZS-36

See merge request halo/android/assistant-android!1024
2023-05-18 17:59:29 +08:00
a4c323b5e0 feat: 32位畅玩游戏下载流程—客户端 https://jira.shanqu.cc/browse/CWZS-36 2023-05-18 17:59:28 +08:00
fdfb03348e Merge branch 'feature/GHZS-2408' into 'dev-5.26.0'
feat: 神策SDK升级—客户端 https://jira.shanqu.cc/browse/GHZS-2408

See merge request halo/android/assistant-android!1034
2023-05-18 16:46:10 +08:00
ba65c5e23a feat: 神策SDK升级—客户端 https://jira.shanqu.cc/browse/GHZS-2408 2023-05-18 15:06:21 +08:00
0f2fcdba68 Merge branch 'feature-delete-comment-dialog-title-center-gravity' into 'dev-5.26.0'
feat:评论详情删除评论弹窗标题居中

See merge request halo/android/assistant-android!1025
2023-05-11 17:04:15 +08:00
548156c159 feat:评论详情删除评论弹窗标题居中 2023-05-11 17:01:59 +08:00
ea08a52750 Merge branch 'feature-comment-delete-hint-center-gravity' into 'dev-5.26.0'
feat:评论详情删除评论弹窗内容居中

See merge request halo/android/assistant-android!1023
2023-05-11 15:49:05 +08:00
4c51ca7670 feat:评论详情删除评论弹窗内容居中 2023-05-11 15:43:52 +08:00
d1277aa1aa Merge branch 'feature-CWZS-50' into 'dev-5.26.0'
feat:云存档最新Tab排序逻辑调整—0510测试-客户端 https://jira.shanqu.cc/browse/CWZS-50

See merge request halo/android/assistant-android!1020
2023-05-11 10:12:11 +08:00
2381550e08 feat:云存档最新Tab排序逻辑调整—0510测试-客户端 https://jira.shanqu.cc/browse/CWZS-50 2023-05-11 10:04:54 +08:00
e57017f5f2 Merge branch 'feature-GHZS-2308' into 'dev-5.26.0'
feat: 文章增加评论删除功能—客户端 https://jira.shanqu.cc/browse/GHZS-2308

See merge request halo/android/assistant-android!1016
2023-05-09 17:26:29 +08:00
6506a18d6f feat: 文章增加评论删除功能—客户端 https://jira.shanqu.cc/browse/GHZS-2308 2023-05-09 17:26:29 +08:00
ed79b230aa Merge branch 'feature-CW-35' into 'dev-5.26.0'
feat: 畅玩助手问题反馈入口—客户端 https://jira.shanqu.cc/browse/CWZS-35

See merge request halo/android/assistant-android!1015
2023-05-09 16:41:51 +08:00
ac56650ce5 feat: 畅玩助手问题反馈入口—客户端 https://jira.shanqu.cc/browse/CWZS-35 2023-05-09 16:41:51 +08:00
0e988b2b49 Merge branch 'dev' into 'dev-5.26.0'
合并 5.24 & 5.25 变更

See merge request halo/android/assistant-android!1010
2023-05-08 10:40:28 +08:00
1b3c2da6d3 Merge branch 'feature/GHZS-2306' into 'dev-5.26.0'
feat: 推广打包接入快手分包sdk https://jira.shanqu.cc/browse/GHZS-2306

See merge request halo/android/assistant-android!1008
2023-05-06 16:56:06 +08:00
48209b05e7 feat: 推广打包接入快手分包sdk https://jira.shanqu.cc/browse/GHZS-2306 2023-05-06 16:52:02 +08:00
2da42d48e6 Merge branch 'feature/GHZS-2083' into 'dev-5.26.0'
feat: 推广打包增加配置信息及快速打包功能 https://jira.shanqu.cc/browse/GHZS-2082

See merge request halo/android/assistant-android!1007
2023-05-06 16:28:19 +08:00
f6b1813e88 feat: 推广打包增加配置信息及快速打包功能 https://jira.shanqu.cc/browse/GHZS-2082 2023-05-06 16:19:57 +08:00
9abd0de3fe Merge branch 'feature/CWZS-33' into 'dev-5.26.0'
feat: 下载完成提示条展示时机优化——客户端 https://jira.shanqu.cc/browse/CWZS-33

See merge request halo/android/assistant-android!1004
2023-05-05 16:45:33 +08:00
25cb0de6c0 feat: 下载完成提示条展示时机优化——客户端 https://jira.shanqu.cc/browse/CWZS-33 2023-05-05 16:33:57 +08:00
4db1147c4f Merge branch 'feature/GHZS-2285' into 'dev-5.26.0'
feat: 论坛详情页帖子列表展示优化—客户端 https://jira.shanqu.cc/browse/GHZS-2285

See merge request halo/android/assistant-android!1002
2023-05-05 11:40:10 +08:00
7802082797 feat: 论坛详情页帖子列表展示优化—客户端 https://jira.shanqu.cc/browse/GHZS-2285 2023-05-05 11:34:35 +08:00
81400a37f1 Merge branch 'feature/GHZS-2041' into 'dev-5.26.0'
feat: 光环助手支持神策测试环境与正式环境切换 https://jira.shanqu.cc/browse/GHZS-2041

See merge request halo/android/assistant-android!997
2023-04-28 10:37:07 +08:00
228a786f6c feat: 光环助手支持神策测试环境与正式环境切换 https://jira.shanqu.cc/browse/GHZS-2041 2023-04-28 10:32:36 +08:00
690c2e53d9 ci: CI 自动编译添加 5.26.0 2023-04-27 10:15:12 +08:00
5a72c12233 chore: 版本更改为 5.26.0 2023-04-27 10:15:12 +08:00
74f41404ef Merge branch 'dev-5.25.0' into 'dev-5.26.0'
合并 5.24.0 & 5.25.0 相关变更

See merge request halo/android/assistant-android!992
2023-04-27 09:57:27 +08:00
166 changed files with 3521 additions and 705 deletions

View File

@ -71,7 +71,8 @@ android_build:
exit_codes: 137
only:
- dev
- dev-5.25.0
- dev-5.26.0
- dev-5.27.0
# 代码检查
sonarqube_analysis:
@ -102,7 +103,8 @@ sonarqube_analysis:
exit_codes: 137
only:
- dev
- dev-5.25.0
- dev-5.26.0
- dev-5.27.0
## 发送简易检测结果报告
send_sonar_report:
@ -120,7 +122,8 @@ send_sonar_report:
exit_codes: 137
only:
- dev
- dev-5.25.0
- dev-5.26.0
- dev-5.27.0
oss-upload&send-email:
tags:
@ -152,4 +155,5 @@ oss-upload&send-email:
- /usr/local/bin/python /ci-android-mail.py
only:
- dev
- dev-5.25.0
- dev-5.26.0
- dev-5.27.0

View File

@ -163,7 +163,7 @@ android {
}
productFlavors {
// internal test dev host
// internal 内部测试包使用的 flavor接口包含包括测试和正式环境
internal {
dimension "env"
versionNameSuffix "-debug"
@ -175,7 +175,7 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${DEV_QUICK_LOGIN_APPKEY}\""
}
// publish release host
// publish 发布时候使用的 flavor接口仅包含正式环境
publish {
dimension "env"
@ -201,11 +201,17 @@ android {
kuaishou {
dimension "env"
String KUAI_SHOU_APP_ID = ""
String KUAI_SHOU_APP_NAME = ""
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}\""
buildConfigField "String", "KUAI_SHOU_APP_ID", "\"${KUAI_SHOU_APP_ID}\""
buildConfigField "String", "KUAI_SHOU_APP_NAME", "\"${KUAI_SHOU_APP_NAME}\""
}
gdt {
@ -325,6 +331,7 @@ dependencies {
}
// implementation(project(':feature:vpn'))
implementation(project(':feature:pkg'))
implementation(project(':feature:oaid'))
implementation(project(':feature:floating-window'))
implementation(project(':feature:beizi_startup_ad'))
}

Binary file not shown.

View File

@ -5,15 +5,15 @@ import com.kwai.monitor.log.TurboAgent
import com.kwai.monitor.log.TurboConfig
object KuaishouHelper {
private const val APP_ID = "80655"
private const val APP_NAME = "guanghuanzhushou_1"
private val mAppId by lazy { BuildConfig.KUAI_SHOU_APP_ID.ifEmpty { "81537" } }
private val mAppName by lazy { BuildConfig.KUAI_SHOU_APP_NAME.ifEmpty { "guanghuanzhushou_1" } }
@JvmStatic
fun init(context: Context, channel: String) {
TurboAgent.init(
TurboConfig.TurboConfigBuilder.create(context)
.setAppId(APP_ID)
.setAppName(APP_NAME)
.setAppId(mAppId)
.setAppName(mAppName)
.setAppChannel(channel)
.build()
)

View File

@ -8,7 +8,7 @@ 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
import com.kwai.monitor.payload.TurboHelper
class FlavorProviderImp : IFlavorProvider {
@ -31,7 +31,7 @@ class FlavorProviderImp : IFlavorProvider {
}
override fun getChannelStr(application: Application): String {
var channel = ChannelReaderUtil.getChannel(application)
var channel = TurboHelper.getChannel(application)
if (channel == null || TextUtils.isEmpty(channel.trim())) {
channel = KUAISHOU_CHANNEL
}

Binary file not shown.

Binary file not shown.

View File

@ -112,6 +112,10 @@
tools:replace="android:name,android:allowBackup"
tools:targetApi="n">
<meta-data
android:name="EasyGoClient"
android:value="true" />
<meta-data
android:name="io.sentry.auto-init"
android:value="false" />

View File

@ -0,0 +1,27 @@
{
"easyGoVersion": "1.0",
"client": "com.gh.gamecenter",
"logicEntities": [
{
"head": {
"function": "magicwindow",
"required": "true"
},
"body": {
"mode":"1",
"activityPairs":[
{"from":"com.gh.gamecenter.MainActivity","to":"*"}
],
"defaultDualActivities": {
"mainPages": "com.gh.gamecenter.MainActivity"
},
"UX": {
"supportRotationUxCompat": "false",
"isDraggable": "true"
},
"transActivities":[
]
}
}
]
}

View File

@ -209,7 +209,7 @@ class DefaultJsApi(
runOnUiThread {
// 若畅玩列表中安装了,优先启动畅玩游戏
if (VHelper.isInstalled(packageName)) {
if (!VHelper.showDialogIfVSpaceIsNeeded(context, "", "")) {
if (!VHelper.showDialogIfVSpaceIsNeeded(context, "", "", "", "")) {
VHelper.launch(context, packageName)
}
} else {

View File

@ -9,7 +9,7 @@ class CheckDownloadHandler : ChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity) {
val apk = gameEntity.getApk().safelyGetInRelease(0) ?: return
DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean ->
DialogUtils.checkDownload(context, apk.size, gameEntity.id, gameEntity.name) { isSubscribe: Boolean ->
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity)
} else {

View File

@ -122,17 +122,6 @@ public class Config {
return false;
}
/**
* 是否启用畅玩游戏
*/
public static boolean isVGameEnabled() {
if (getSettings() == null) {
return false;
}
return !"off".equals(getSettings().getGameSmooth());
}
/**
* VPN 开关选项是否开启
*/
@ -229,6 +218,8 @@ public class Config {
if (!TextUtils.isEmpty(json)) {
mSettingsEntity = GsonUtils.fromJson(json, SettingsEntity.class);
}
mSettingsEntity.setGameSmooth("off");
} catch (Exception e) {
e.printStackTrace();
}

View File

@ -24,6 +24,7 @@ import com.gh.common.chain.CertificationHandler;
import com.gh.common.chain.ChainBuilder;
import com.gh.common.chain.ChainHandler;
import com.gh.common.chain.CheckDownloadHandler;
import com.gh.common.chain.CheckStoragePermissionHandler;
import com.gh.common.chain.DownloadDialogHelperHandler;
import com.gh.common.chain.GamePermissionHandler;
import com.gh.common.chain.OverseaDownloadHandler;
@ -397,8 +398,9 @@ public class BindingAdapters {
}
ChainBuilder builder = new ChainBuilder();
builder.addHandler(new ValidateVSpaceHandler());
builder.addHandler(new GamePermissionHandler());
builder.addHandler(new CheckStoragePermissionHandler());
builder.addHandler(new ValidateVSpaceHandler());
builder.addHandler(new BrowserInstallHandler());
builder.addHandler(new PackageCheckHandler());
builder.addHandler(new DownloadDialogHelperHandler());

View File

@ -3,6 +3,7 @@ package com.gh.common.exposure
import android.text.TextUtils
import com.g00fy2.versioncompare.Version
import com.gh.common.util.PackageUtils
import com.gh.common.util.RealNameHelper
import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet
import com.gh.gamecenter.common.utils.toObject
import com.gh.gamecenter.feature.entity.ApkEntity
@ -42,7 +43,9 @@ object ExposureUtils {
source = traceEvent?.source ?: ArrayList(),
eTrace = ExposureTraceUtils.appendTrace(traceEvent),
event = ExposureType.DOWNLOAD
)
).apply {
this.payload.certification = RealNameHelper.getCertificationStatus()
}
if (!TextUtils.isEmpty(entity.id)) {
ExposureManager.log(exposureEvent)
}
@ -78,7 +81,9 @@ object ExposureUtils {
source = traceEvent?.source ?: ArrayList(),
eTrace = ExposureTraceUtils.appendTrace(traceEvent),
event = ExposureType.DOWNLOAD_COMPLETE
)
).apply {
this.payload.certification = RealNameHelper.getCertificationStatus()
}
exposureEvent.payload.host = host
exposureEvent.payload.path = path
exposureEvent.payload.speed = speed

View File

@ -49,7 +49,7 @@ object ArchiveDownloadButtonHelper {
downloadBtn.setOnClickListener {
when {
// 检查是否已安装畅玩助手
!VHelper.isVSpaceInstalled(context) -> showVspaceTipDialog(context, gameEntity)
!VHelper.isVSpaceInstalled(context) -> showVSpaceTipDialog(context, gameEntity)
// 检查是否已安装游戏
!VHelper.isInstalled(packageName) -> {
// 检查游戏是否在安装中
@ -82,7 +82,7 @@ object ArchiveDownloadButtonHelper {
}
}
private fun showVspaceTipDialog(context: Context, gameEntity: GameEntity?) {
private fun showVSpaceTipDialog(context: Context, gameEntity: GameEntity?) {
NewFlatLogUtils.logCloudArchiveVSpaceDownloadDialogShow()
DialogHelper.showDialog(
context,
@ -92,7 +92,7 @@ object ArchiveDownloadButtonHelper {
R.string.cancel.toResString(),
{
NewFlatLogUtils.logCloudArchiveVSpaceDownloadDialogClick(R.string.archive_vspace_dialog_confirm.toResString())
VHelper.showVspaceDialog(context, gameEntity)
VHelper.showVSpaceDialog(context, gameEntity)
},
{
NewFlatLogUtils.logCloudArchiveVSpaceDownloadDialogClick(R.string.cancel.toResString())

View File

@ -3,11 +3,15 @@ package com.gh.common.util;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import androidx.annotation.Nullable;
@ -19,6 +23,7 @@ import com.gh.gamecenter.adapter.viewholder.CommentViewHolder;
import com.gh.gamecenter.common.callback.SimpleCallback;
import com.gh.gamecenter.common.retrofit.BiResponse;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.common.utils.DialogHelper;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.ImageUtils;
import com.gh.gamecenter.core.utils.DisplayUtils;
@ -45,6 +50,7 @@ import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
@ -102,17 +108,27 @@ public class CommentUtils {
}
}
public static void showReportDialog(final CommentEntity commentEntity,
final Context context,
final boolean showConversation,
final String patch) {
final Dialog dialog = new Dialog(context);
LinearLayout container = new LinearLayout(context);
container.setOrientation(LinearLayout.VERTICAL);
container.setBackgroundColor(ContextCompat.getColor(context, R.color.background_white));
container.setPadding(0, DisplayUtils.dip2px(context, 12), 0, DisplayUtils.dip2px(context, 12));
public static void showMorePopupWindow(
final View anchor,
final CommentEntity commentEntity,
final boolean showConversation,
final String patch,
final OnCommentDeleteListener onCommentDeleteListener
) {
Context context = anchor.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View contentView = inflater.inflate(R.layout.layout_popup_container, null);
PopupWindow popupWindow = new PopupWindow(
contentView,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
popupWindow.setBackgroundDrawable(new ColorDrawable(0));
popupWindow.setTouchable(true);
popupWindow.setFocusable(true);
popupWindow.setOutsideTouchable(true);
LinearLayout container = contentView.findViewById(R.id.container);
List<String> dialogType = new ArrayList<>();
dialogType.add("复制");
dialogType.add("投诉");
@ -121,21 +137,23 @@ public class CommentUtils {
dialogType.add("查看对话");
}
String commentUserId = commentEntity.getUser().getId();
String userId = UserManager.getInstance().getUserId();
MeEntity me = commentEntity.getMe();
boolean isCommentedByUser = Objects.equals(commentUserId, userId);
boolean isContentAuthorOrModerator = me != null && (me.isModerator() || me.isContentAuthor());
if (isCommentedByUser || isContentAuthorOrModerator) {
dialogType.add("删除");
}
for (String s : dialogType) {
final TextView reportTv = new TextView(context);
View itemView = inflater.inflate(R.layout.layout_popup_option_item, container, false);
TextView reportTv = itemView.findViewById(R.id.hint_text);
reportTv.setText(s);
reportTv.setTextSize(17);
reportTv.setTextColor(ContextCompat.getColor(context, R.color.title));
reportTv.setBackgroundResource(R.drawable.textview_white_style);
int widthPixels = context.getResources().getDisplayMetrics().widthPixels;
reportTv.setLayoutParams(new LinearLayout.LayoutParams((widthPixels * 9) / 10,
LinearLayout.LayoutParams.WRAP_CONTENT));
reportTv.setPadding(DisplayUtils.dip2px(context, 20), DisplayUtils.dip2px(context, 12),
0, DisplayUtils.dip2px(context, 12));
container.addView(reportTv);
container.addView(itemView);
reportTv.setOnClickListener(v -> {
dialog.cancel();
popupWindow.dismiss();
switch (reportTv.getText().toString()) {
case "复制":
copyText(commentEntity.getContent(), context);
@ -143,19 +161,30 @@ public class CommentUtils {
case "投诉":
CheckLoginUtils.checkLogin(context, patch + "-投诉",
() -> showReportTypeDialog(commentEntity, context));
break;
case "查看对话":
context.startActivity(CommentDetailActivity.getIntent(context, commentEntity.getId(), null));
break;
case "删除":
DialogHelper.INSTANCE.showDialog(
context,
"提示",
"删除评论后,评论下所有的回复都将被删除",
"删除",
"取消",
() -> {
deleteComment(context, commentEntity, onCommentDeleteListener);
return null;
},
null,
new DialogHelper.Config("", false, true, true, false, -1)
);
break;
}
});
}
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(container);
dialog.show();
ExtensionsKt.showAutoOrientation(popupWindow, anchor, 0, 0);
}
private static void showReportTypeDialog(final CommentEntity commentEntity, final Context context) {
@ -430,6 +459,28 @@ public class CommentUtils {
});
}
public static void deleteComment(
final Context context,
final CommentEntity commentEntity,
final OnCommentDeleteListener listener
) {
Dialog dialog = DialogUtils.showWaitDialog(
context,
context.getString(R.string.post_dialog_hint)
);
RetrofitManager.getInstance().getApi()
.deleteComment(commentEntity.getId())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doFinally(dialog::cancel)
.subscribe(new Response<ResponseBody>() {
@Override
public void onResponse(@Nullable ResponseBody response) {
listener.onCommentDelete();
}
});
}
// 设置评论item 用户相关的view(点赞/头像/用户名)
public static void setCommentUserView(Context mContext, CommentViewHolder holder, CommentEntity entity) {
@ -540,4 +591,8 @@ public class CommentUtils {
public interface OnVoteListener {
void onVote();
}
public interface OnCommentDeleteListener {
void onCommentDelete();
}
}

View File

@ -171,14 +171,25 @@ public class DataUtils {
IdCardEntity idCardEntity = data.getIdCard();
if (idCardEntity != null) {
values.put(GhContentProvider.KEY_IS_CERTIFICATED, !TextUtils.isEmpty(data.getIdCard().getId())); // 是否认证
values.put(GhContentProvider.KEY_IS_ADULT,
data.getIdCard().getMinor() == null
|| !data.getIdCard().getMinor()
);
boolean isCertificated = !TextUtils.isEmpty(data.getIdCard().getId());
boolean isAdult = data.getIdCard().getMinor() == null || !data.getIdCard().getMinor();
values.put(GhContentProvider.KEY_IS_CERTIFICATED, isCertificated); // 是否认证
values.put(GhContentProvider.KEY_IS_ADULT, isAdult); // 是否成年
if (!isCertificated) {
RealNameHelper.updateCertificationStatus(0);
} else {
if (isAdult) {
RealNameHelper.updateCertificationStatus(2);
} else {
RealNameHelper.updateCertificationStatus(1);
}
}
} else {
values.put(GhContentProvider.KEY_IS_CERTIFICATED, false);
values.put(GhContentProvider.KEY_IS_ADULT, false);
RealNameHelper.updateCertificationStatus(0);
}
EventBus.getDefault().post(new EBReuse(Constants.EB_REALNAME_RESULT));

View File

@ -121,7 +121,7 @@ public class DialogUtils {
return dialog;
}
public static void checkDownload(Context context, String size, CheckDownloadCallBack callBack) {
public static void checkDownload(Context context, String size, String gameId, String gameName, CheckDownloadCallBack callBack) {
if (!NetworkUtils.isNetworkConnected(context)) {
showNoConnectionDownloadDialog(context, () -> {
},
@ -136,7 +136,9 @@ public class DialogUtils {
// MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(context), "出现弹窗提示");
showDownloadDialog(context,
() -> callBack.onResponse(false),
() -> callBack.onResponse(true));
() -> callBack.onResponse(true),
gameId,
gameName);
}
}
@ -165,9 +167,11 @@ public class DialogUtils {
DialogHelper.showDialog(context, "下载提示", "网络异常,请检查手机网络状态", "知道了", "WiFi自动下载", listener::onConfirm, cancelListener::onCancel, false, "", "");
}
public static void showDownloadDialog(Context context, ConfirmListener listener, CancelListener cancelListener) {
public static void showDownloadDialog(Context context, ConfirmListener listener, CancelListener cancelListener, String gameId, String gameName) {
context = checkDialogContext(context);
NewFlatLogUtils.logDownloadMobileDataDialogShow(gameId, gameName);
final Dialog dialog = new Dialog(context, R.style.GhAlertDialog);
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_download_traffic, null);
@ -177,6 +181,7 @@ public class DialogUtils {
Context finalContext = context;
allowOnce.setOnClickListener(v -> {
NewFlatLogUtils.logDownloadMobileDataDialogClick(gameId, gameName, "允许一次");
AppExecutor.getUiExecutor().executeWithDelay(() -> {
Utils.toast(finalContext, "已使用移动网络下载,请注意流量消耗");
}, 500);
@ -185,11 +190,13 @@ public class DialogUtils {
// MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(finalContext), "本次允许");
});
wifiAuto.setOnClickListener(v -> {
NewFlatLogUtils.logDownloadMobileDataDialogClick(gameId, gameName, "WiFi自动下载");
cancelListener.onCancel();
dialog.dismiss();
// MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(finalContext), "连上WiFi后自动下载");
});
allowAlways.setOnClickListener(v -> {
NewFlatLogUtils.logDownloadMobileDataDialogClick(gameId, gameName, "总是允许");
SPUtils.setBoolean(getTrafficDownloadHintKey(), false);
AppExecutor.getUiExecutor().executeWithDelay(() -> {
// 显示了弹窗以后,即便下面这个 toast 放在 listener.onConfirm 后调用也是显示 listener.onConfirm 里的 toast
@ -201,6 +208,8 @@ public class DialogUtils {
// MtaHelper.onEvent("移动网络下载", NetworkUtils.getMobileNetworkType(finalContext), "总是允许");
});
dialog.setOnCancelListener(downloadDialog -> NewFlatLogUtils.logDownloadMobileDataDialogClick(gameId, gameName, "关闭弹窗"));
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(contentView);
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

View File

@ -800,8 +800,11 @@ object DirectUtils {
jumpActivity(context, bundle)
}
/**
* 畅玩助手反馈跳转
*/
@JvmStatic
fun directToFeedbackCompat(
fun directToVGameFeedback(
context: Context,
content: String? = null,
hintType: String? = null,
@ -816,14 +819,14 @@ object DirectUtils {
if (isQaFeedback) {
bundle.putBoolean(KEY_IS_QA_FEEDBACK, true)
bundle.putString(KEY_QA_CONTENT_ID, qaContentId)
bundle.putSerializable(KEY_SUGGESTTYPE, SuggestType.normal)
bundle.putSerializable(KEY_SUGGESTTYPE, SuggestType.cwzsQuestion)
} else {
bundle.putString(KEY_CONTENT, content)
if (TextUtils.isEmpty(hintType)) {
bundle.putSerializable(KEY_SUGGESTTYPE, SuggestType.gameQuestion)
bundle.putString(KEY_SUGGEST_HINT_TYPE, KEY_PLUGIN)
} else {
bundle.putSerializable(KEY_SUGGESTTYPE, SuggestType.normal)
bundle.putSerializable(KEY_SUGGESTTYPE, SuggestType.cwzsQuestion)
bundle.putString(KEY_SUGGEST_HINT_TYPE, hintType)
}
}

View File

@ -7,7 +7,6 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.collection.ArrayMap
import androidx.recyclerview.widget.RecyclerView
import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.common.chain.*
import com.gh.common.constant.Config
import com.gh.common.dialog.DeviceRemindDialog
@ -25,6 +24,7 @@ import com.gh.download.dialog.DownloadDialog
import com.gh.gamecenter.R
import com.gh.gamecenter.WebActivity
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.gamecenter.common.callback.CancelListener
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
@ -836,6 +836,7 @@ object DownloadItemUtils {
addHandler(CertificationHandler())
addHandler(VersionNumberHandler())
addHandler(OverseaDownloadHandler())
addHandler(CheckStoragePermissionHandler())
addHandler(ValidateVSpaceHandler())
addHandler(CheckDownloadHandler())
}
@ -927,7 +928,12 @@ object DownloadItemUtils {
}
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, object : EmptyCallback {
override fun onCallback() {
DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean ->
DialogUtils.checkDownload(
context,
apk.size,
gameEntity.id,
gameEntity.name
) { isSubscribe: Boolean ->
update(context, gameEntity, entrance, location, isSubscribe, traceEvent)
}
}
@ -1070,6 +1076,7 @@ object DownloadItemUtils {
"game_type", gameEntity.categoryChinese,
"download_status", gameEntity.downloadStatusChinese,
"button_name", buttonName,
"game_schema_type", gameEntity.gameBitChinese,
"page_name", GlobalActivityManager.getCurrentPageEntity().pageName,
"page_id", GlobalActivityManager.getCurrentPageEntity().pageId,
"page_business_id", GlobalActivityManager.getCurrentPageEntity().pageBusinessId,

View File

@ -1,7 +1,5 @@
package com.gh.common.util
import com.gh.gamecenter.common.base.GlobalActivityManager.getCurrentPageEntity
import com.gh.gamecenter.common.base.GlobalActivityManager.getLastPageEntity
import com.gh.common.constant.Config
import com.gh.common.exposure.ExposureUtils
import com.gh.common.simulator.SimulatorDownloadManager
@ -10,6 +8,8 @@ import com.gh.common.xapk.XapkInstaller
import com.gh.download.DownloadDataHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.GlobalActivityManager.getCurrentPageEntity
import com.gh.gamecenter.common.base.GlobalActivityManager.getLastPageEntity
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.callback.ConfirmListener
import com.gh.gamecenter.common.constant.Constants
@ -18,7 +18,6 @@ import com.gh.gamecenter.common.entity.SuggestType
import com.gh.gamecenter.common.eventbus.EBShowDialog
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.feature.entity.GameEntity
@ -235,17 +234,13 @@ object DownloadObserver {
downloadEntity.name + " - " + platform + " - 下载完成"
)
else -> {
if (downloadEntity.isVGame()) {
VHelper.showFloatingWindow(downloadEntity.packageName)
} else {
if (!downloadEntity.isVGame()) {
Utils.toast(mApplication, downloadEntity.name + " - 下载完成")
}
}
}
} else {
if (downloadEntity.isVGame()) {
VHelper.showFloatingWindow(downloadEntity.packageName)
} else {
if (!downloadEntity.isVGame()) {
Utils.toast(mApplication, downloadEntity.name + " - 下载完成")
}
}
@ -281,7 +276,7 @@ object DownloadObserver {
val downloadType = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)
// 是否是自动安装
val isAutoInstall = SPUtils.getBoolean(Constants.SP_AUTO_INSTALL, true)
if (downloadType == Constants.SIMULATOR_DOWNLOAD || isAutoInstall) {
if (downloadType != Constants.VSPACE_32_DOWNLOAD_ONLY && (downloadType == Constants.SIMULATOR_DOWNLOAD || isAutoInstall)) {
if (FileUtils.isEmptyFile(downloadEntity.path)) {
Utils.toast(mApplication, R.string.install_failure_hint)
downloadManager.cancel(downloadEntity.url)
@ -317,9 +312,6 @@ object DownloadObserver {
}
}
}
// 统计下载完成
uploadData(gameId, downloadEntity.platform)
}
// 下载过程分析统计
@ -386,16 +378,22 @@ object DownloadObserver {
SensorsBridge.trackEvent(
"HaloFunGameDownloadDone",
"game_name", downloadEntity.name,
"game_id", downloadEntity.gameId
"game_id", downloadEntity.gameId,
"game_schema_type", if (downloadEntity.getMetaExtra(VHelper.KEY_BIT) == "32") "32位" else "64位"
)
} else if (downloadEntity.gameId == Constants.HALO_FUN_GAME_ID) {
SensorsBridge.trackEvent("HaloFunDownloadDone")
SensorsBridge.trackEvent(
"HaloFunDownloadDone",
"space_schema_type",
if (downloadEntity.packageName == VHelper.VSPACE_32BIT_PACKAGENAME) "32位" else "64位"
)
} else if (downloadEntity.gameId != Constants.GHZS_GAME_ID && downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) != Constants.SIMULATOR_DOWNLOAD) {
SensorsBridge.trackEvent(
"DownloadProcessFinish",
"game_id", downloadEntity.gameId,
"game_name", downloadEntity.meta[Constants.GAME_NAME] ?: "",
"game_type", downloadEntity.meta[Constants.GAME_TYPE] ?: "",
"game_schema_type", if (downloadEntity.getMetaExtra(VHelper.KEY_BIT) == "32") "32位" else "64位",
"page_name", getCurrentPageEntity().pageName,
"page_id", getCurrentPageEntity().pageId,
"page_business_id", getCurrentPageEntity().pageBusinessId,
@ -454,19 +452,4 @@ object DownloadObserver {
DataLogUtils.uploadHijack(mApplication, downloadEntity)
}
// 统计下载
private fun uploadData(id: String, platform: String?) {
val params = HashMap<String, String>()
params["game"] = id
params["platform"] = platform ?: ""
val body = RequestBody.create(
MediaType.parse("application/json"),
JSONObject(params as Map<*, *>).toString()
)
RetrofitManager.getInstance().api.postDownload(body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Response())
}
}

View File

@ -221,7 +221,7 @@ object GameActivityDownloadHelper {
) {
GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info) {
CertificationDialog.showCertificationDialog(context, gameEntity) {
DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean ->
DialogUtils.checkDownload(context, apk.size, gameEntity.id, gameEntity.name) { isSubscribe: Boolean ->
download(context, gameEntity, apk, isSubscribe, entrance, location, traceEvent)
}
}
@ -241,7 +241,12 @@ object GameActivityDownloadHelper {
VHelper.validateVSpaceBeforeAction(context, gameEntity) {
GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info) {
CertificationDialog.showCertificationDialog(context, gameEntity) {
DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean ->
DialogUtils.checkDownload(
context,
apk.size,
gameEntity.id,
gameEntity.name
) { isSubscribe: Boolean ->
download(context, gameEntity, apk, isSubscribe, entrance, location, traceEvent)
}
}
@ -262,7 +267,7 @@ object GameActivityDownloadHelper {
DownloadDialog.showDownloadDialog(context, gameEntity, traceEvent, entrance, location)
} else {
CertificationDialog.showCertificationDialog(context, gameEntity) {
DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean ->
DialogUtils.checkDownload(context, apk.size, gameEntity.id, gameEntity.name) { isSubscribe: Boolean ->
plugin(context, gameEntity, apk, entrance, location, isSubscribe, traceEvent)
}
}
@ -355,7 +360,7 @@ object GameActivityDownloadHelper {
location: String,
traceEvent: ExposureEvent
) {
DialogUtils.checkDownload(context, apk.size) { isSubscribe: Boolean ->
DialogUtils.checkDownload(context, apk.size, gameEntity.id, gameEntity.name) { isSubscribe: Boolean ->
update(context, gameEntity, apk, entrance, location, isSubscribe, traceEvent)
}
}

View File

@ -5,7 +5,6 @@ import com.gh.gamecenter.common.json.JsonObjectBuilder
import com.gh.gamecenter.common.json.json
import com.gh.gamecenter.common.loghub.LoghubUtils
import com.gh.gamecenter.common.utils.LogUtils
import com.gh.gamecenter.common.utils.NewFlatLogUtils
import com.gh.gamecenter.entity.HomeSubSlide
import com.lightgame.utils.Utils
import org.json.JSONArray
@ -54,6 +53,66 @@ object NewFlatLogUtils {
log(json)
}
// 畅玩助手下载弹窗展示事件
@JvmStatic
fun logHaloFunDownloadDialogShow(gameId: String, gameName: String, gameArchitecture: String) {
val json = json {
KEY_EVENT to "halo_fun_download_dialog_show"
"game_id" to gameId
"game_name" to gameName
"game_architecture" to gameArchitecture
parseAndPutMeta().invoke(this)
}
log(json)
}
// 畅玩助手下载点击事件
@JvmStatic
fun logHaloFunDownloadDialogDownloadClick(architecture: String) {
val json = json {
KEY_EVENT to "halo_fun_download_dialog_download_click"
"architecture" to architecture
parseAndPutMeta().invoke(this)
}
log(json)
}
// 畅玩助手安装提示弹窗展示事件
@JvmStatic
fun logHaloFunInstallTipDialogShow(architecture: String) {
val json = json {
KEY_EVENT to "halo_fun_install_tip_dialog_show"
"architecture" to architecture
parseAndPutMeta().invoke(this)
}
log(json)
}
// 畅玩助手更新弹窗展示事件
@JvmStatic
fun logHaloFunUpdateDialogShow(gameId: String, gameName: String, gameArchitecture: String) {
val json = json {
KEY_EVENT to "halo_fun_update_dialog_show"
"game_id" to gameId
"game_name" to gameName
"game_architecture" to gameArchitecture
parseAndPutMeta().invoke(this)
}
log(json)
}
// 畅玩助手32位弹窗事件
@JvmStatic
fun logHaloFun32DialogEvent(event: String, gameId: String, gameName: String) {
val json = json {
KEY_EVENT to event
"game_id" to gameId
"game_name" to gameName
parseAndPutMeta().invoke(this)
}
log(json)
}
// 关联游戏跳转icon点击事件
@JvmStatic
fun logHaloFunGameDetailJumpClick(downloadStatus: String, gameId: String) {
@ -80,11 +139,12 @@ object NewFlatLogUtils {
// 畅玩助手更新弹窗点击事件
@JvmStatic
fun logHaloFunUpdateDialogClick(dialogType: String, buttonType: String) {
fun logHaloFunUpdateDialogClick(dialogType: String, buttonType: String, architecture: String) {
val json = json {
KEY_EVENT to "halo_fun_update_dialog_click"
"dialog_type" to dialogType
KEY_BUTTON_TYPE to buttonType
"architecture" to architecture
parseAndPutMeta().invoke(this)
}
log(json)
@ -2100,4 +2160,29 @@ object NewFlatLogUtils {
}
log(json)
}
// 下载流量提示弹窗展示事件
@JvmStatic
fun logDownloadMobileDataDialogShow(gameId: String, gameName: String) {
val json = json {
KEY_EVENT to "download_mobile_data_dialog_show"
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
// 下载流量提示弹窗点击事件
@JvmStatic
fun logDownloadMobileDataDialogClick(gameId: String, gameName: String, button: String) {
val json = json {
KEY_EVENT to "download_mobile_data_dialog_click"
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
"button" to button
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
}

View File

@ -38,6 +38,7 @@ import com.gh.gamecenter.manager.PackagesManager;
import com.gh.vspace.VHelper;
import com.gh.vspace.db.VGameEntity;
import com.halo.assistant.HaloApp;
import com.lg.vspace.VirtualAppManager;
import com.lightgame.utils.Utils;
import net.dongliu.apk.parser.ApkFile;
@ -554,9 +555,32 @@ public class PackageUtils {
}
}
/**
* 根据包名,判断是否已安装该游戏
* 不区分是否有桌面图标
*/
public static boolean isInstalledFromAllPackage(Context context, String packageName) {
ArrayList<String> allPackageName = getAllPackageName(context);
return allPackageName.contains(packageName);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.getPackageManager().getPackageInfo(
packageName,
PackageManager.PackageInfoFlags.of(0)
);
} else {
context.getPackageManager().getPackageInfo(packageName, 0);
}
return true;
} catch (Exception e) {
ArrayList<String> allPackageName = getAllPackageName(context);
boolean isInstalled = allPackageName.contains(packageName);
if (isInstalled) {
// 能进这里说明上面的 try {} 结果有问题,记录一下
SentryHelper.INSTANCE.onEvent("CHECK_INSTALLED_CONFLICT", "packageName", packageName);
}
return isInstalled;
}
}
/*

View File

@ -16,6 +16,9 @@ object RealNameHelper {
var pendingInstallPkgPath = ""
// 0表示未实名1表示未成年2表示成年
private var mCertificationStatus: Int = 0
/**
* 弹未成年人不能下载游戏弹窗
*/
@ -124,7 +127,21 @@ object RealNameHelper {
DownloadManager.getInstance()
.addInvisiblePendingTask(downloadEntity)
}
}
/**
* 获取实名状态 0表示未实名1表示未成年2表示成年
*/
fun getCertificationStatus(): Int {
return mCertificationStatus
}
/**
* 更新实名状态 0表示未实名1表示未成年2表示成年
*/
@JvmStatic
fun updateCertificationStatus(newCertificationStatus: Int) {
mCertificationStatus = newCertificationStatus
}
}

View File

@ -3,6 +3,7 @@ package com.gh.download
import android.content.pm.PackageInfo
import android.text.TextUtils
import com.gh.common.util.PackageUtils
import com.gh.common.util.RealNameHelper
import com.gh.common.xapk.XapkInstaller
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.exposure.meta.MetaUtil
@ -125,6 +126,7 @@ object DownloadDataHelper {
payloadObject.put("gameName", downloadEntity.name)
payloadObject.put("platform", downloadEntity.platform)
payloadObject.put("package", downloadEntity.packageName)
payloadObject.put("certification", RealNameHelper.getCertificationStatus())
payloadObject.put("filename", getFileName(downloadEntity))
payloadObject.put("task_num", NDataChanger.downloadingTasks.size)
jsonObject.put("payload", payloadObject)
@ -209,6 +211,7 @@ object DownloadDataHelper {
payloadObject.put("gameName", downloadEntity.name)
payloadObject.put("platform", downloadEntity.platform)
payloadObject.put("package", downloadEntity.packageName)
payloadObject.put("certification", RealNameHelper.getCertificationStatus())
payloadObject.put("filename", getFileName(downloadEntity))
payloadObject.put("launch_ms", startupTime)
payloadObject.put("task_num", NDataChanger.downloadingTasks.size)
@ -247,6 +250,7 @@ object DownloadDataHelper {
payloadObject.put("gameName", downloadEntity.name)
payloadObject.put("platform", downloadEntity.platform)
payloadObject.put("package", downloadEntity.packageName)
payloadObject.put("certification", RealNameHelper.getCertificationStatus())
payloadObject.put("filename", getFileName(downloadEntity))
payloadObject.put("total_size", sizeInMB)
if (parallel != null) {
@ -317,6 +321,7 @@ object DownloadDataHelper {
payloadObject.put("gameName", downloadEntity.name)
payloadObject.put("platform", downloadEntity.platform)
payloadObject.put("package", downloadEntity.packageName)
payloadObject.put("certification", RealNameHelper.getCertificationStatus())
payloadObject.put("filename", getFileName(downloadEntity))
payloadObject.put("speed_progress", JSONArray(averageSpeedList))
payloadObject.put("is_finished", downloadEntity.status == DownloadStatus.done)
@ -356,6 +361,7 @@ object DownloadDataHelper {
sheet.put("game_id", downloadEntity.gameId)
sheet.put("platform", downloadEntity.platform)
sheet.put("package", downloadEntity.packageName)
sheet.put("certification", RealNameHelper.getCertificationStatus())
sheet.put("filename", getFileName(downloadEntity))
sheet.put("total_size", downloadEntity.size / 1024 / 1024)
sheet.put("current_progress_size", downloadEntity.progress / 1024)

View File

@ -341,15 +341,18 @@ public class DownloadManager implements DownloadStatusListener {
ExtensionsKt.addMetaExtra(downloadEntity, Constants.EXTRA_DOWNLOAD_TYPE, Constants.SMOOTH_GAME);
ExtensionsKt.addMetaExtra(downloadEntity, DownloadConfig.KEY_PROGRESS_CALLBACK_INTERVAL, "200");
ExtensionsKt.addMetaExtra(downloadEntity, VHelper.KEY_REQUIRED_G_APPS, gameEntity.getGAppsSwitch());
ExtensionsKt.addMetaExtra(downloadEntity, VHelper.KEY_BIT, apkEntity.getBit());
SensorsBridge.trackEvent("HaloFunGameDownloadClick",
"game_name", gameEntity.getName(),
"game_id", gameEntity.getId());
"game_id", gameEntity.getId(),
"game_schema_type", gameEntity.getGameBitChinese());
} else {
SensorsBridge.trackEvent("DownloadProcessBegin",
"game_id", gameEntity.getId(),
"game_name", gameEntity.getName(),
"game_type", gameEntity.getCategoryChinese(),
"game_schema_type", gameEntity.getGameBitChinese(),
"page_name", GlobalActivityManager.getCurrentPageEntity().getPageName(),
"page_id", GlobalActivityManager.getCurrentPageEntity().getPageId(),
"page_business_id", GlobalActivityManager.getCurrentPageEntity().getPageBusinessId(),

View File

@ -373,7 +373,9 @@ class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : Bas
override fun onConfirm() {
DialogUtils.checkDownload(
context,
apkEntity.size
apkEntity.size,
gameEntity.id,
gameEntity.name
) { isSubscribe ->
DownloadManager.createDownload(
context,

View File

@ -30,7 +30,6 @@ import com.ethanhua.skeleton.ViewSkeletonScreen;
import com.gh.base.DownloadToolbarActivity;
import com.gh.common.filter.RegionSettingHelper;
import com.gh.common.history.HistoryHelper;
import com.gh.gamecenter.feature.utils.ApkActiveUtils;
import com.gh.common.util.CheckLoginUtils;
import com.gh.common.util.CollectionUtils;
import com.gh.common.util.DataCollectionUtils;
@ -41,6 +40,8 @@ import com.gh.gamecenter.adapter.viewholder.DetailViewHolder;
import com.gh.gamecenter.common.callback.OnRequestCallBackListener;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.constant.EntranceConsts;
import com.gh.gamecenter.common.eventbus.EBNetworkState;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.ShareUtils;
@ -49,15 +50,16 @@ import com.gh.gamecenter.common.view.VerticalItemDecoration;
import com.gh.gamecenter.core.utils.ClickUtils;
import com.gh.gamecenter.core.utils.DisplayUtils;
import com.gh.gamecenter.core.utils.MtaHelper;
import com.gh.gamecenter.entity.NewsDetailEntity;
import com.gh.gamecenter.eventbus.EBConcernChanged;
import com.gh.gamecenter.eventbus.EBAddComment;
import com.gh.gamecenter.eventbus.EBDeleteComment;
import com.gh.gamecenter.eventbus.EBDownloadStatus;
import com.gh.gamecenter.eventbus.EBPackage;
import com.gh.gamecenter.feature.entity.GameEntity;
import com.gh.gamecenter.feature.entity.MeEntity;
import com.gh.gamecenter.entity.NewsDetailEntity;
import com.gh.gamecenter.feature.entity.NewsEntity;
import com.gh.gamecenter.eventbus.EBConcernChanged;
import com.gh.gamecenter.eventbus.EBDownloadStatus;
import com.gh.gamecenter.common.eventbus.EBNetworkState;
import com.gh.gamecenter.eventbus.EBPackage;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.feature.utils.ApkActiveUtils;
import com.gh.gamecenter.newsdetail.NewsDetailAdapter;
import com.gh.gamecenter.retrofit.RetrofitManager;
import com.lightgame.download.DataWatcher;
@ -557,6 +559,17 @@ public class NewsDetailActivity extends DownloadToolbarActivity implements OnCli
});
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onDeleteComment(EBDeleteComment event) {
adapter.getNewsCommentNum();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onAddComment(EBAddComment event) {
adapter.getNewsCommentNum();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction() & MotionEventCompat.ACTION_MASK) {

View File

@ -149,7 +149,7 @@ public class SkipActivity extends BaseActivity {
if (!TextUtils.isEmpty(qaId)) {
DirectUtils.directToQa(this, qaTitle, qaId);
} else if ("vgame".equals(suggestionType)) {
DirectUtils.directToFeedbackCompat(this, content, "game", isQaFeedback, qaContentId, EntranceConsts.ENTRANCE_BROWSER);
DirectUtils.directToVGameFeedback(this, content, "game", isQaFeedback, qaContentId, EntranceConsts.ENTRANCE_BROWSER);
} else {
DirectUtils.directToFeedback(this, content, null, isQaFeedback, qaContentId, EntranceConsts.ENTRANCE_BROWSER);
}

View File

@ -164,9 +164,11 @@ class SplashScreenActivity : BaseActivity() {
val signatureHash = packageUtilsConfig?.getApkSignatureByPackageName(this, packageName)?.get(0)
val sideLoadInfo = packageUtilsConfig?.getSideLoadedInfo()
val trackEvent = JSONObject()
// 是否首次使用神策
val isFirstTime = SPUtils.getBoolean(Constants.SP_SENSORS_IS_FIRST_TIME, true)
tryCatchInRelease {
trackEvent.run {
put("\$is_first_time", SPUtils.getBoolean(Constants.SP_SENSORS_IS_FIRST_TIME, true))
put("\$is_first_time", isFirstTime)
put("is_side_loaded", sideLoadInfo?.get("is_side_loaded").toBoolean())
put("installer_store", sideLoadInfo?.get("installer_store") ?: "")
put("package_name", packageName)
@ -176,6 +178,10 @@ class SplashScreenActivity : BaseActivity() {
}
}
SensorsBridge.trackEvent("AppLaunch", trackEvent)
if (!isFirstTime && HaloApp.getInstance().isBrandNewInstall) {
// 神策不是第一次使用,但是全局标志为全新安装,有问题,上报数据供后续确认
SentryHelper.onEvent("WRONG_LAUNCH_LOG", "a_id", MetaUtil.getBase64EncodedAndroidId())
}
SPUtils.setBoolean(Constants.SP_SENSORS_IS_FIRST_TIME, false)
}
@ -306,7 +312,7 @@ class SplashScreenActivity : BaseActivity() {
private fun doFlavorInit() {
HaloApp.getInstance().flavorProvider.init(HaloApp.getInstance(), this, PkgHelper.getActivateRatio())
val whiteListChannel = arrayListOf<String>(
val whiteListChannel = arrayListOf(
"GH_206",
"KS-GHZS-KY1",
"KS-GHZS-MC1",
@ -326,12 +332,6 @@ class SplashScreenActivity : BaseActivity() {
private fun initStartUpAdSDK() {
mStartUpAdProvider?.run {
if (shouldEnableSDK(HaloApp.getInstance().channel)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
initOAID(applicationContext) {
HaloApp.getInstance().oaid = it
MetaUtil.refreshMeta()
}
}
initSDK(applicationContext)
}
}

View File

@ -8,27 +8,31 @@ import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.gh.gamecenter.common.constant.ItemViewType;
import com.gh.common.util.CheckLoginUtils;
import com.gh.common.util.CommentUtils;
import com.gh.common.util.DialogUtils;
import com.gh.common.util.DirectUtils;
import com.gh.gamecenter.common.utils.ImageUtils;
import com.gh.gamecenter.core.utils.MtaHelper;
import com.gh.gamecenter.common.utils.TextHelper;
import com.gh.gamecenter.R;
import com.gh.gamecenter.adapter.viewholder.CommentViewHolder;
import com.gh.gamecenter.common.constant.ItemViewType;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.common.utils.ImageUtils;
import com.gh.gamecenter.common.utils.TextHelper;
import com.gh.gamecenter.common.viewholder.FooterViewHolder;
import com.gh.gamecenter.core.utils.MtaHelper;
import com.gh.gamecenter.entity.ArticleCommentParent;
import com.gh.gamecenter.entity.CommentEntity;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.eventbus.EBDeleteComment;
import com.gh.gamecenter.retrofit.RetrofitManager;
import com.lightgame.adapter.BaseRecyclerAdapter;
import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
@ -68,22 +72,34 @@ public class CommentDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
mDataExceptionView = dataExceptionView;
mRecyclerView = recyclerView;
loadData();
mOnCommentCallBackListener.onCommentCallback(null);
refresh();
}
public void loadData() {
public void refresh() {
loadData(1);
}
public void loadMore() {
loadData(mPage + 1);
}
public void loadData(int page) {
if (mIsLoading) return;
mIsLoading = true;
RetrofitManager.getInstance().getApi()
.getCommentTrace(mCommentId, mPage)
.getCommentTrace(mCommentId, page)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Response<List<CommentEntity>>() {
@Override
public void onResponse(List<CommentEntity> response) {
super.onResponse(response);
if (page == 1) {
mCommentList.clear();
}
mCommentList.addAll(response);
if (response.size() < 20) {
mIsOver = true;
}
@ -91,7 +107,7 @@ public class CommentDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
mDataExceptionView.setVisibility(View.GONE);
mRecyclerView.setVisibility(View.VISIBLE);
notifyItemRangeChanged(0, getItemCount() - 1);
mPage++;
mPage = page;
mIsLoading = false;
}
@ -201,8 +217,14 @@ public class CommentDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
});
holder.commentMore.setOnClickListener(v ->
CommentUtils.showReportDialog(commentEntity,
mContext, false, "资讯文章-评论"));
CommentUtils.showMorePopupWindow(
holder.commentMore,
commentEntity,
false,
"资讯文章-评论",
() -> EventBus.getDefault().post(new EBDeleteComment(commentEntity))
)
);
holder.commentUserIconDv.setOnClickListener(v -> DirectUtils.directToHomeActivity(mContext, commentEntity.getUser().getId(), "", "文章-评论详情"));
holder.commentUserNameTv.setOnClickListener(v -> DirectUtils.directToHomeActivity(mContext, commentEntity.getUser().getId(), "", "文章-评论详情"));
@ -254,4 +276,24 @@ public class CommentDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
public boolean isLoading() {
return mIsLoading;
}
public void notifyCommentRemoved(final CommentEntity entity) {
int positionInComments = getCommentIndexByEntity(mCommentList, entity);
if (positionInComments != -1) {
mCommentList.remove(positionInComments);
notifyItemRemoved(positionInComments);
}
}
private static int getCommentIndexByEntity(
final List<CommentEntity> commentList,
final CommentEntity comment
) {
for (int i = 0; i < commentList.size();i++) {
if (Objects.equals(comment.getId(), commentList.get(i).getId())) {
return i;
}
}
return -1;
}
}

View File

@ -42,6 +42,7 @@ import com.gh.gamecenter.databinding.NewsDigestItemBinding;
import com.gh.gamecenter.entity.ArticleCommentParent;
import com.gh.gamecenter.entity.CommentEntity;
import com.gh.gamecenter.entity.ConcernEntity;
import com.gh.gamecenter.eventbus.EBDeleteComment;
import com.gh.gamecenter.manager.VisitManager;
import com.gh.gamecenter.common.retrofit.JSONObjectResponse;
import com.gh.gamecenter.common.retrofit.OkHttpCache;
@ -50,12 +51,14 @@ import com.gh.gamecenter.retrofit.RetrofitManager;
import com.lightgame.adapter.BaseRecyclerAdapter;
import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
@ -151,6 +154,7 @@ public class MessageDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
return;
}
if (isRefresh) {
isOver = false;
mNormalCommentList.clear();
mPage = 1;
}
@ -467,8 +471,14 @@ public class MessageDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
});
holder.commentMore.setOnClickListener(v ->
CommentUtils.showReportDialog(finalCommentEntity,
mContext, true, "资讯文章详情-评论详情"));
CommentUtils.showMorePopupWindow(
holder.commentMore,
finalCommentEntity,
true,
"资讯文章详情-评论详情",
() -> EventBus.getDefault().post(new EBDeleteComment(finalCommentEntity))
)
);
holder.commentUserNameTv.setOnClickListener(v -> DirectUtils.directToHomeActivity(mContext, finalCommentEntity.getUser().getId(), mEntrance, "文章-评论详情"));
holder.commentUserIconDv.setOnClickListener(v -> DirectUtils.directToHomeActivity(mContext, finalCommentEntity.getUser().getId(), mEntrance, "文章-评论详情"));
@ -524,6 +534,42 @@ public class MessageDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
});
}
public void notifyCommentRemoved(final CommentEntity entity) {
if (mHotCommentList.size() > 0) {
int positionInHotComments = getCommentIndexByEntity(mHotCommentList, entity);
if (positionInHotComments != -1) {// 如果在热门评论中存在要被删除的评论,则将此评论移除
mHotCommentList.remove(positionInHotComments);
}
}
if (mNormalCommentList.size() > 0) {
int positionInNormalComments = getCommentIndexByEntity(mNormalCommentList, entity);
if (positionInNormalComments != -1) {// 如果在热门评论中存在要被删除的评论,则将此评论移除
mNormalCommentList.remove(positionInNormalComments);
}
}
if (mConcernEntity != null) {
int commentNum = mConcernEntity.getCommentnum();
mConcernEntity.setCommentnum(commentNum - 1);
}
notifyDataSetChanged();
}
private static int getCommentIndexByEntity(
final List<CommentEntity> commentList,
final CommentEntity comment
) {
for (int i = 0; i < commentList.size();i++) {
if (Objects.equals(comment.getId(), commentList.get(i).getId())) {
return i;
}
}
return -1;
}
private void statNewsViews(final String news_id) {
RetrofitManager.getInstance().getApi().postArticleVisit(news_id)
.subscribeOn(Schedulers.io())
@ -587,7 +633,7 @@ public class MessageDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
}
public void addCommentCount() {
mConcernEntity.setCommentnum(mConcernEntity.getCommentnum());
mConcernEntity.setCommentnum(mConcernEntity.getCommentnum() + 1);
notifyDataSetChanged();
}

View File

@ -166,6 +166,7 @@ public class DetailViewHolder {
"game_type", mGameEntity.getCategoryChinese(),
"download_status", mGameEntity.getDownloadStatusChinese(),
"button_name", mViewHolder.mDownloadPb.getText(),
"game_schema_type", mGameEntity.getGameBitChinese(),
"page_name", GlobalActivityManager.getCurrentPageEntity().getPageName(),
"page_id", GlobalActivityManager.getCurrentPageEntity().getPageId(),
"page_business_id", GlobalActivityManager.getCurrentPageEntity().getPageBusinessId(),
@ -285,9 +286,9 @@ public class DetailViewHolder {
ChainBuilder builder = new ChainBuilder();
builder.addHandler(new UpdateNewSimulatorHandler());
builder.addHandler(new ValidateVSpaceHandler());
builder.addHandler(new GamePermissionHandler());
builder.addHandler(new CheckStoragePermissionHandler());
builder.addHandler(new ValidateVSpaceHandler());
if (mGameEntity.getApk().size() == 1) {
builder.addHandler(new BrowserInstallHandler());

View File

@ -32,6 +32,11 @@ class CloudArchiveManagerViewModel(
) :
ListViewModel<ArchiveEntity, ArchiveEntity>(application) {
companion object {
private const val SORT_TYPE_CREATE = "time.create:-1"
private const val SORT_TYPE_SHARE_AND_CREATE = "time.share:-1,time.create:-1"
}
var archiveConfigStr = ""
private val mNewApi = RetrofitManager.getInstance().newApi
private val mApi = RetrofitManager.getInstance().api
@ -54,10 +59,10 @@ class CloudArchiveManagerViewModel(
override fun provideDataObservable(page: Int): Observable<List<ArchiveEntity>>? = when (mType) {
MyArchiveFragment.Type.MY_ARCHIVE -> {
mNewApi.getMyArchives(gameId, page)
mNewApi.getMyArchives(gameId, page, SORT_TYPE_CREATE)
}
MyArchiveFragment.Type.MY_DOWNLOAD_ARCHIVE -> null
MyArchiveFragment.Type.MY_SHARE_ARCHIVE -> mNewApi.getMyShareArchives(gameId, page)
MyArchiveFragment.Type.MY_SHARE_ARCHIVE -> mNewApi.getMyShareArchives(gameId, page, SORT_TYPE_SHARE_AND_CREATE)
}
override fun provideDataSingle(page: Int): Single<MutableList<ArchiveEntity>>? {
@ -77,7 +82,7 @@ class CloudArchiveManagerViewModel(
url = vArchiveEntity.url,
configUrl = vArchiveEntity.configUrl,
md5 = vArchiveEntity.md5,
time = ArchiveEntity.Time(update = vArchiveEntity.time),
time = ArchiveEntity.Time(create = vArchiveEntity.time),
gameVersion = vArchiveEntity.gameVersion
)
)

View File

@ -48,7 +48,7 @@ class MyArchiveAdapter(
val entity = mEntityList[position]
holder.binding.run {
titleTv.text = entity.name
timeTv.text = entity.time.update.formatTime("yyyy-MM-dd HH:mm")
timeTv.text = entity.time.create.formatTime("yyyy-MM-dd HH:mm")
versionTv.text = "版本:${entity.gameVersion}"
optionsIv.setOnClickListener {
MyArchiveOptionDialogFragment.show(

View File

@ -17,7 +17,7 @@ class MyShareArchiveFragment : MyArchiveFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mBinding.tipsTv.visibility = View.GONE
mBinding.reuseNoneData.reuseNoneDataTv.text = "还没有分享存档噢~"
mBinding.reuseNoneData.reuseNoneDataTv.text = "还没有分享存档噢~"
mBinding.reuseNoneData.reuseNoneDataDescTv.text = "快快把您的有趣存档分享给大家吧!"
}

View File

@ -100,9 +100,6 @@ class CommunityArticleAdapter(
}
}
if (entity.bbs == CommunityEntity()) {
entity.bbs = entity.community
}
holder.binding.selectIv.goneIf(mCurrentOption == ManageOption.OPTION_MANAGER)
holder.binding.selectIv.isChecked = selectItems.contains(entity.id)
holder.binding.imageContainer.setOffset(if (mCurrentOption == ManageOption.OPTION_MANAGER) 40f else 76f)

View File

@ -294,7 +294,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter<ViewHolder> {
case subscribe:
PermissionHelper.checkStoragePermissionBeforeAction(mContext, () -> {
// 下载管理不用判断是否大于50M
DialogUtils.checkDownload(mContext, Float.toString(100F), isSubscribe -> {
DialogUtils.checkDownload(mContext, Float.toString(100F), downloadEntity.getGameId(), downloadEntity.getName(), isSubscribe -> {
if (isSubscribe) {
DownloadManager.getInstance().subscribe(downloadEntity);
statusMap.put(url, DownloadStatus.subscribe.getStatus());

View File

@ -127,11 +127,12 @@ class InstalledGameViewModel(application: Application) : AndroidViewModel(applic
}
if (newEntity.getApk().size > 1) {
for (apkEntity in newEntity.getApk()) {
val packageName: String = sortedList.get(i).packageName
val packageName: String = sortedList[i].packageName
if (packageName == apkEntity.packageName) {
val list = ArrayList<ApkEntity>()
list.add(apkEntity)
newEntity.setApk(list)
newEntity.dropOtherApk()
if (PackageUtils.isCanPluggable(apkEntity)) {
val pluggableCollection =
GameUtils.getPluggableCollectionFromGameEntity(

View File

@ -343,7 +343,9 @@ class UpdatableGameAdapter(private var mViewModel: UpdatableGameViewModel) :
(CurrentActivityHolder.getCurrentActivity() as? FragmentActivity)?.checkStoragePermissionBeforeAction {
DialogUtils.checkDownload(
updateBtn.context,
update.size
update.size,
downloadEntity?.gameId ?: "",
downloadEntity?.name ?: ""
) { isSubscribe: Boolean ->
if (str.contains("")) {
if (update.pluggableCollection != null) {

View File

@ -11,6 +11,8 @@ class AppEntity(
@SerializedName("version_code")
var versionCode: Int = 0,
var category: String? = "",
var url: String? = null,
var size: String? = null,
@ -31,6 +33,8 @@ class AppEntity(
* EVERY_TIME_OPEN每次打开
*/
var alert: String? = null,
// 关联64位更新
var relation: AppEntity? = null
) : Parcelable {
fun isAlertEveryTime() = alert == "EVERY_TIME_OPEN"

View File

@ -0,0 +1,3 @@
package com.gh.gamecenter.eventbus;
public class EBAddComment {}

View File

@ -0,0 +1,3 @@
package com.gh.gamecenter.eventbus
class EBTopCommunityChanged(val communityId: String)

View File

@ -40,7 +40,7 @@ class ForumArticleAskListAdapter(
private var mDefOrderList = listOf("回复", "发布")
private var mVideoOrderList = listOf("推荐", "发布")
private var mFilterPosition = 0
private var mFilterPosition = if (path == "视频") 1 else 0
override fun areItemsTheSame(oldItem: AnswerEntity?, newItem: AnswerEntity?): Boolean {
return oldItem?.id == newItem?.id

View File

@ -21,7 +21,7 @@ class ForumArticleAskListViewModel(application: Application, val bbsId: String =
var sort: String = "time.reply"
var filter: String = "section_id"
var selectedSection = ForumDetailEntity.Section("", "全部")
var videoSort: String = "recommend"
var videoSort: String = "time.upload"
var videoList = arrayListOf<ForumVideoEntity>()
override fun provideDataObservable(page: Int): Observable<MutableList<AnswerEntity>> {

View File

@ -59,6 +59,7 @@ import com.gh.gamecenter.databinding.ItemForumSectionBinding
import com.gh.gamecenter.databinding.PopupForumDetailSectionsBinding
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.eventbus.EBForumFollowChange
import com.gh.gamecenter.eventbus.EBTopCommunityChanged
import com.gh.gamecenter.eventbus.EBTypeChange
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
@ -1226,6 +1227,13 @@ class ForumDetailFragment : BaseLazyTabFragment(), IScrollable {
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onTopCommunityChanged(event: EBTopCommunityChanged) {
if (event.communityId == mBbsId) {
mViewModel?.getForumDetail(true)
}
}
override fun scrollToTop() {
val fragment = mFragmentsList.safelyGetInRelease(mViewPager.currentItem)
if (fragment is IScrollable && fragment.isAdded) {

View File

@ -50,8 +50,12 @@ class ForumDetailViewModel(application: Application, val bbsId: String) : Androi
.subscribe(EmptyResponse())
}
fun getForumDetail() {
mApi.getForumDetail(bbsId)
/**
* 获取论坛详情
* @param refresh 是否刷新后台缓存
*/
fun getForumDetail(refresh: Boolean = false) {
mApi.getForumDetail(bbsId, refresh)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ForumDetailEntity>() {

View File

@ -67,7 +67,7 @@ class CommunityHomeViewModel(application: Application) : AndroidViewModel(applic
articleEntity.count = articleDetailEntity.count
articleDetailEntity.community.id = articleDetailEntity.communityId
articleEntity.community = articleDetailEntity.community
articleEntity.bbs = articleDetailEntity.community
articleEntity.community = articleDetailEntity.community
articleEntity.time = articleDetailEntity.time
articleEntity.title = articleDetailEntity.title
articleEntity.user = articleDetailEntity.user
@ -89,7 +89,7 @@ class CommunityHomeViewModel(application: Application) : AndroidViewModel(applic
?: ""
articleEntity.count = questionDetailEntity.count
articleEntity.community = questionDetailEntity.community
articleEntity.bbs = questionDetailEntity.community
articleEntity.community = questionDetailEntity.community
articleEntity.time = questionDetailEntity.time
articleEntity.title = questionDetailEntity.title ?: ""
articleEntity.user = questionDetailEntity.user
@ -114,7 +114,7 @@ class CommunityHomeViewModel(application: Application) : AndroidViewModel(applic
articleEntity.length = forumVideoEntity.length
articleEntity.videoInfo = forumVideoEntity.videoInfo
articleEntity.count = forumVideoEntity.count
articleEntity.bbs = forumVideoEntity.bbs ?: CommunityEntity()
articleEntity.community = forumVideoEntity.bbs ?: CommunityEntity()
articleEntity.time = TimeEntity(upload = forumVideoEntity.time.upload)
articleEntity.title = forumVideoEntity.title
forumVideoEntity.user.run {

View File

@ -62,7 +62,6 @@ class ForumArticleListAdapter(
ItemViewType.ITEM_BODY -> {
val viewHolder = holder as ForumArticleAskItemViewHolder
val articleEntity = mEntityList[position]
articleEntity.community = CommunityEntity(articleEntity.bbs.id, articleEntity.bbs.name)
if (articleEntity.type == "bbs_article") articleEntity.type = "community_article"
if (articleEntity.type == "bbs_question") articleEntity.type = "question"
@ -102,13 +101,13 @@ class ForumArticleListAdapter(
"question" -> "提问帖"
else -> "提问帖评论"
}
val bbsType = if (bbs.type == "official_bbs") "综合论坛" else "游戏论坛"
val bbsType = if (community.type == "official_bbs") "综合论坛" else "游戏论坛"
NewLogUtils.logRecommendFeedContentClick(
"click_for_you_content",
contentType,
id,
position + 1,
bbs.id,
community.id,
bbsType,
user.id ?: ""
)

View File

@ -1264,6 +1264,8 @@ class HomeSearchToolWrapperFragment : SearchToolWrapperFragment() {
tabViewBinding.titleTv.visibility = View.GONE
tabViewBinding.invisibleTitleTv.visibility = View.GONE
tabViewBinding.titleIv.setFixedHeight(16)
// 部分设备加载图片时会获取到错误的最小宽度,这里为它做个 64DP 的保底尺寸
tabViewBinding.titleIv.setTag(ImageUtils.TAG_TARGET_WIDTH, 64F.dip2px())
tabViewBinding.titleIv.display(tabEntity.img)
tabViewBinding.titleIv.registerLoadingCallback(object : WrapContentDraweeView.LoadingCallback {
override fun loaded() {

View File

@ -59,14 +59,34 @@ class GameGallerySlideViewHolder(val binding: GameGallerySlideItemBinding) : Bas
binding.cardView.setCardBackgroundColor(R.color.text_FAFAFA.toColor(binding.root.context))
}
/**
* 获取分割好的游戏列表数据
*/
private fun getDividedGameList(): ArrayList<ArrayList<GameEntity>> {
val containerList = arrayListOf<ArrayList<GameEntity>>()
val gameDataList = mCachedSubject!!.data!!
val gameListSize = gameDataList.size
val bestNumber = mCachedSubject!!.data!!.size / 3
// 分割大小
val splitSize = gameDataList.size / 3
containerList.add(ArrayList(mCachedSubject!!.data!!.subList(0, bestNumber - 1)))
containerList.add(ArrayList(mCachedSubject!!.data!!.subList(bestNumber, bestNumber * 2 - 1)))
containerList.add(ArrayList(mCachedSubject!!.data!!.subList(bestNumber * 2, mCachedSubject!!.data!!.size - 1)))
if (splitSize > 1) {
containerList.add(ArrayList(gameDataList.subList(0, splitSize)))
containerList.add(ArrayList(gameDataList.subList(splitSize, splitSize * 2)))
containerList.add(ArrayList(gameDataList.subList(splitSize * 2, gameListSize)))
} else if (splitSize == 1) {
containerList.add(ArrayList(gameDataList.subList(0, 1)))
containerList.add(ArrayList(gameDataList.subList(1, 2)))
containerList.add(ArrayList(gameDataList.subList(2, 3)))
} else {
val tempGameList = arrayListOf<GameEntity>()
if (gameListSize != 0) {
tempGameList.add(gameDataList.first())
}
containerList.add(tempGameList)
containerList.add(tempGameList)
containerList.add(tempGameList)
}
return containerList
}
@ -88,6 +108,8 @@ class GameGallerySlideViewHolder(val binding: GameGallerySlideItemBinding) : Bas
}
override fun onBindViewHolder(holder: GameGallerySlideItemViewHolder, position: Int) {
if (gameList.isEmpty()) return
val gameEntity = gameList[position % gameList.size]
runOnIoThread(true) {

View File

@ -159,18 +159,19 @@ class GameVerticalAdapter(
fun checkResetData(updateData: SubjectEntity) {
var dataIds = ""
mSubjectEntity = updateData
mSubjectEntity.data?.forEach {
dataIds += it.id
}
mSubjectEntity = updateData
if ((countAndKey?.first == updateData.data?.size && countAndKey?.second != dataIds) || mDarkMode != DarkModeUtils.isDarkModeOn(
mContext
)
) { // 数量不变,内容发生改变
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) { // 数量发生改变
} else if (countAndKey?.first != updateData.data?.size) {
// 数量发生改变
notifyDataSetChanged()
}

View File

@ -56,8 +56,8 @@ class CloudArchiveListViewModel(
private fun getSortTypeFilter(): String {
return when (mSortType) {
SortType.NEWEST -> "time.create:-1"
else -> "hot:-1"
SortType.NEWEST -> "time.share:-1"
else -> "count.usage:-1,time.share:-1"
}
}

View File

@ -60,11 +60,11 @@ 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()
val isTypeCopy = libaoEntity.receiveMethod == "copy"
if (isTypeCopy) {
// 类型为复制,不需要登录也能直接领取
holder.binding.libaoSchedulePb.visibility = View.GONE
@ -93,10 +93,7 @@ class GameLibaoAdapter(
holder.binding.libaoCodeTv.text = "礼包码:-"
} else {
when (libaoEntity.status) {
"ling" -> {
holder.binding.libaoCodeTv.text = "礼包码:-"
}
"linged", "repeatLing", "repeatLinged" -> {
"linged", "repeatLing", "repeatLinged", "taoed", "repeatTao", "repeatTaoed" -> {
val size = libaoEntity.me?.userDataLibaoList?.size ?: 0
val code = libaoEntity.me?.userDataLibaoList?.get(size - 1)?.code ?: ""
val text = "礼包码:$code"
@ -125,14 +122,7 @@ class GameLibaoAdapter(
initProgressUI(libaoEntity, holder)
} else {
when (libaoEntity.status) {
"ling" -> {
holder.binding.libaoSchedulePb.visibility = View.VISIBLE
holder.binding.remainingTv.visibility = View.VISIBLE
holder.binding.libaoCodeTv.visibility = View.GONE
initProgressUI(libaoEntity, holder)
}
"linged", "repeatLing", "repeatLinged" -> {
"linged", "repeatLing", "repeatLinged", "taoed", "repeatTao", "repeatTaoed" -> {
holder.binding.libaoSchedulePb.visibility = View.GONE
holder.binding.remainingTv.visibility = View.GONE
holder.binding.libaoCodeTv.visibility = View.VISIBLE
@ -152,10 +142,11 @@ class GameLibaoAdapter(
}
}
else -> {
holder.binding.libaoSchedulePb.visibility = View.GONE
holder.binding.remainingTv.visibility = View.GONE
holder.binding.libaoCodeTv.visibility = View.VISIBLE
holder.binding.libaoCodeTv.text = "礼包码:-"
holder.binding.libaoSchedulePb.visibility = View.VISIBLE
holder.binding.remainingTv.visibility = View.VISIBLE
holder.binding.libaoCodeTv.visibility = View.GONE
initProgressUI(libaoEntity, holder)
}
}
}

View File

@ -67,6 +67,7 @@ class HomeGameCollectionAdapter(
val gamesCollectionEntity = itemData.gameCollectionItem
root.layoutParams.width = DisplayUtils.getScreenWidth() - 50F.dip2px()
if (gamesCollectionEntity != null) {
poster.setTag(ImageUtils.TAG_TARGET_WIDTH, DisplayUtils.getScreenWidth() - 50F.dip2px())
ImageUtils.display(poster, gamesCollectionEntity.cover)
ImageUtils.display(userIv, gamesCollectionEntity.user?.icon)
titleTv.text = gamesCollectionEntity.title

View File

@ -127,7 +127,12 @@ class HomeItemGameTestV2ViewHolder(
super.onScrollStateChanged(recyclerView, newState)
Utils.log("onScrollStateChanged: newState = $newState")
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
recyclerView.handler.removeCallbacksAndMessages(null)
// 高速滑动时可能触发空指针闪退
try {
recyclerView.handler.removeCallbacksAndMessages(null)
} catch (e: NullPointerException) {
e.printStackTrace()
}
recyclerView.postDelayed(300) { // 解决动画没结束时候条目和时间轴不对应问题
onRecyclerViewChanged(true)
}

View File

@ -263,7 +263,7 @@ class ConcernAdapter extends BaseRecyclerAdapter<ViewHolder> {
}
// 获取新闻评论数
private void getNewsCommentnum(final List<ConcernEntity> list) {
public void getNewsCommentnum(final List<ConcernEntity> list) {
if (list == null || list.isEmpty()) {
return;
}
@ -508,6 +508,10 @@ class ConcernAdapter extends BaseRecyclerAdapter<ViewHolder> {
return null;
}
public List<ConcernEntity> getConcernList() {
return concernList;
}
public List<LibaoStatusEntity> getLibaoStatusList() {
return libaoStatusList;
}

View File

@ -20,6 +20,8 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.gh.gamecenter.common.callback.OnRequestCallBackListener;
import com.gh.gamecenter.common.base.activity.ToolBarActivity;
import com.gh.common.filter.RegionSettingHelper;
import com.gh.gamecenter.eventbus.EBAddComment;
import com.gh.gamecenter.eventbus.EBDeleteComment;
import com.gh.gamecenter.feature.utils.ApkActiveUtils;
import com.gh.common.util.CheckLoginUtils;
import com.gh.common.util.DataCollectionUtils;
@ -386,6 +388,16 @@ public class ConcernFragment extends ToolbarFragment implements SwipeRefreshLayo
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onAddComment(EBAddComment event) {
mAdapter.getNewsCommentnum(mAdapter.getConcernList());
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onDeleteComment(EBDeleteComment event) {
mAdapter.getNewsCommentnum(mAdapter.getConcernList());
}
@Override
public void onRefresh() {
postDelayedRunnable(mRunnable, 1000);

View File

@ -44,24 +44,32 @@ import com.gh.gamecenter.entity.CommentEntity;
import com.gh.gamecenter.entity.CommentnumEntity;
import com.gh.gamecenter.entity.ConcernEntity;
import com.gh.gamecenter.entity.ViewsEntity;
import com.gh.gamecenter.eventbus.EBAddComment;
import com.gh.gamecenter.eventbus.EBDeleteComment;
import com.gh.gamecenter.login.entity.UserInfoEntity;
import com.gh.gamecenter.login.user.UserManager;
import com.gh.gamecenter.manager.CommentManager;
import com.gh.gamecenter.retrofit.RetrofitManager;
import com.gh.gamecenter.retrofit.service.ApiService;
import com.halo.assistant.HaloApp;
import com.lightgame.listeners.OnBackPressedListener;
import com.lightgame.utils.Util_System_Keyboard;
import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function3;
import io.reactivex.schedulers.Schedulers;
import retrofit2.HttpException;
@ -199,46 +207,34 @@ public class MessageDetailFragment extends ToolbarFragment implements OnCommentC
mBinding.shadowView.setOnClickListener(this);
}
public void getCommentNum() {
RetrofitManager.getInstance().getApi()
.getNewsCommentnum(newsId, Utils.getTime(getContext()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Response<List<CommentnumEntity>>() {
@Override
public void onResponse(List<CommentnumEntity> response) {
super.onResponse(response);
if (response.size() > 0) {
if (!TextUtils.isEmpty(mConcernEntity.getId())) {
commentNum = response.get(0).getNum();
mConcernEntity.setCommentnum(commentNum);
adapter.notifyItemChanged(0);
}
private void getConcernDigest() {
ApiService apiService = RetrofitManager.getInstance().getApi();
Observable.zip(
apiService.getNewsRichDigest(newsId),
apiService.getArticlesVisits(UrlFilterUtils.getFilterQuery("article_ids", newsId)),
apiService.getNewsCommentnum(newsId, Utils.getTime(getContext())),
(concernEntity, viewsEntities, commentNumEntities) -> {
if (commentNumEntities.size() > 0) {
if (!TextUtils.isEmpty(concernEntity.getId())) {
concernEntity.setCommentnum(commentNumEntities.get(0).getNum());
}
}
});
}
private void getConcernDigest() {
RetrofitManager.getInstance().getApi().getNewsRichDigest(newsId)
if (viewsEntities.size() > 0) {
concernEntity.setViews(viewsEntities.get(0).getViews());
}
return concernEntity;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Response<ConcernEntity>() {
@Override
public void onResponse(ConcernEntity response) {
mConcernEntity = response;
if (commentNum == -1) {
getCommentNum();
} else {
mConcernEntity.setCommentnum(commentNum);
}
commentNum = mConcernEntity.getCommentnum();
adapter.addConcernEntity(mConcernEntity);
adapter.notifyDataSetChanged();
adapter.addHotComment();
getNewsViews();
if (commentNum == 0) {
setSoftInput(true);
}
@ -251,22 +247,6 @@ public class MessageDetailFragment extends ToolbarFragment implements OnCommentC
});
}
private void getNewsViews() {
RetrofitManager.getInstance().getApi()
.getArticlesVisits(UrlFilterUtils.getFilterQuery("article_ids", newsId))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Response<List<ViewsEntity>>() {
@Override
public void onResponse(List<ViewsEntity> viewsEntities) {
if (viewsEntities.size() > 0) {
mConcernEntity.setViews(viewsEntities.get(0).getViews());
adapter.notifyItemChanged(0);
}
}
});
}
//软键盘控制
private void setSoftInput(boolean isShow) {
if (isShow) {
@ -297,6 +277,17 @@ public class MessageDetailFragment extends ToolbarFragment implements OnCommentC
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onDeleteComment(EBDeleteComment event) {
adapter.notifyCommentRemoved(event.commentEntity);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onAddComment(EBAddComment event) {
commentNum = -1;
getConcernDigest();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
@ -375,6 +366,8 @@ public class MessageDetailFragment extends ToolbarFragment implements OnCommentC
//修改评论缓存
CommentManager.updateOkhttpCacheForId(getContext(), newsId);
CommentManager.updateOkhttpCache(getContext(), newsId);
// 发送评论更新通知
EventBus.getDefault().post(new EBAddComment());
adapter.notifyItemInserted(adapter.getHotCommentListSize() + 2);
adapter.notifyItemChanged(adapter.getItemCount() - 1); //刷新脚布局高度
} else {

View File

@ -64,6 +64,7 @@ import com.gh.gamecenter.databinding.GamedetailItemNewsBinding;
import com.gh.gamecenter.databinding.NewsDetailCommentBinding;
import com.gh.gamecenter.databinding.NewsdetailItemContentBinding;
import com.gh.gamecenter.databinding.NewsdetailItemGameBinding;
import com.gh.gamecenter.eventbus.EBDeleteComment;
import com.gh.gamecenter.feature.entity.ApkEntity;
import com.gh.gamecenter.entity.ArticleCommentParent;
import com.gh.gamecenter.feature.entity.ColorEntity;
@ -77,9 +78,12 @@ import com.gh.gamecenter.retrofit.RetrofitManager;
import com.lightgame.adapter.BaseRecyclerAdapter;
import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import io.reactivex.android.schedulers.AndroidSchedulers;
@ -579,8 +583,16 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
return true;
});
viewHolder.commentMore.setOnClickListener(v ->
CommentUtils.showReportDialog(commentEntity,
mContext, true, "资讯文章详情-评论"));
CommentUtils.showMorePopupWindow(
viewHolder.commentMore,
commentEntity,
true,
"资讯文章详情-评论",
() -> {
EventBus.getDefault().post(new EBDeleteComment(commentEntity));
}
)
);
viewHolder.commentUserNameTv.setOnClickListener(v -> DirectUtils.directToHomeActivity(mContext, commentEntity.getUser().getId(), mEntrance, "文章-评论列表"));
viewHolder.commentUserIconDv.setOnClickListener(v -> DirectUtils.directToHomeActivity(mContext, commentEntity.getUser().getId(), mEntrance, "文章-评论列表"));
@ -679,10 +691,8 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
@Override
public void onResponse(List<CommentnumEntity> response) {
if (response.size() > 0 && response.get(0).getNum() > 0) {
mCommentNum = response.get(0).getNum();
getNewsHotComment();
}
mCommentNum = response.get(0).getNum();
getNewsHotComment();
}
@Override
@ -726,9 +736,9 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
public void onResponse(List<CommentEntity> response) {
if (response.size() >= 1) {
mIsHotComment = false;
mCommentEntityList = response;
notifyDataSetChanged();
}
mCommentEntityList = response;
notifyDataSetChanged();
}
});
}

View File

@ -0,0 +1,22 @@
package com.gh.gamecenter.oaid
import android.content.Context
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IOAIDProvider
object OAIDHelper {
fun doSystemLoad() {
val oaidProvider = ARouter.getInstance().build(RouteConsts.provider.oaid).navigation() as? IOAIDProvider
oaidProvider?.doSystemLoad()
}
fun getOAID(context: Context, callback: (String) -> Unit) {
val oaidProvider = ARouter.getInstance().build(RouteConsts.provider.oaid).navigation() as? IOAIDProvider
oaidProvider?.getOAID(context, callback)
}
}

View File

@ -132,13 +132,13 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
open fun bindCommendAndVote(entity: ArticleEntity, entrance: String, position: Int? = null) {
binNormalView(entity.transformAnswerEntity())
if (entity.bbs.type == "official_bbs") {
forumIcon?.displayGameIcon(entity.bbs.icon, null)
if (entity.community.type == "official_bbs") {
forumIcon?.displayGameIcon(entity.community.icon, null)
} else {
forumIcon?.displayGameIcon(
entity.bbs.game?.getIcon(),
entity.bbs.game?.iconSubscript,
entity.bbs.game?.iconFloat
entity.community.game?.getIcon(),
entity.community.game?.iconSubscript,
entity.community.game?.iconFloat
)
}
@ -148,17 +148,17 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
"question" -> "提问帖"
else -> "提问帖评论"
}
val bbsType = if (entity.bbs.type == "official_bbs") "综合论坛" else "游戏论坛"
val bbsType = if (entity.community.type == "official_bbs") "综合论坛" else "游戏论坛"
forumNameTv.setOnClickListener {
if (entrance == "社区+(推荐)") {
NewLogUtils.logRecommendFeedForumClick(contentType, entity.id, entity.bbs.id, bbsType)
NewLogUtils.logRecommendFeedForumClick(contentType, entity.id, entity.community.id, bbsType)
}
MtaHelper.onEvent(
getEventId(entrance),
getKey(entrance),
if (entity.bbs.name.isEmpty()) entity.community.name else entity.bbs.name
if (entity.community.name.isEmpty()) entity.community.name else entity.community.name
)
itemView.context.startActivity(
ForumDetailActivity.getIntent(
@ -207,7 +207,7 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
contentType,
id,
(position + 1),
bbs.id,
community.id,
bbsType,
user.id ?: "",
commentType
@ -266,7 +266,7 @@ open class BaseAnswerOrArticleItemViewHolder(itemView: View) : BaseRecyclerViewH
contentType,
id,
position + 1,
bbs.id,
community.id,
bbsType,
user.id ?: ""
)

View File

@ -70,7 +70,7 @@ class MyArticleAdapter(
mContext.startActivity(
ArticleDetailActivity.getIntent(
mContext,
entity.bbs,
entity.community,
entity.id,
mEntrance,
mPath

View File

@ -38,6 +38,7 @@ import com.gh.gamecenter.databinding.FragmentArticleDetailBinding
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.eventbus.EBDeleteCommentDetail
import com.gh.gamecenter.eventbus.EBDeleteDetail
import com.gh.gamecenter.eventbus.EBTopCommunityChanged
import com.gh.gamecenter.feature.entity.ArticleDraftEntity
import com.gh.gamecenter.feature.entity.Permissions
import com.gh.gamecenter.login.user.UserManager
@ -453,6 +454,22 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
}
}
mViewModel.top.observeNonNull(this) { top ->
val topSuccessToast = if(top) {
R.string.article_detail_top_success_toast
} else {
R.string.article_detail_cancel_top_success_toast
}
toast(getString(topSuccessToast))
mViewModel.detailEntity?.let {
EventBus.getDefault().post(
EBTopCommunityChanged(
it.communityId
)
)
}
}
mViewModel.dislike.observeNonNull(this) { disliked ->
if (disliked) {
toast("已反对")
@ -508,58 +525,82 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
private fun showMoreItemDialog() {
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) || mViewModel.detailEntity != null) {
val entities = ArrayList<MenuItemEntity>()
// 申请加精
if (mViewModel.detailEntity?.user?.id == UserManager.getInstance().userId && !mViewModel.detailEntity?.me!!.isModerator && mViewModel.detailEntity?.status == "pass") {
val isEnable =
mViewModel.detailEntity?.getSimplifyChoicenessStatus() != "pass"
mViewModel.detailEntity?.getSimplifyChoicenessStatus() != ArticleDetailEntity.STATUS_PASS
entities.add(
MenuItemEntity(
"申请加精",
getString(R.string.article_detail_more_apply_select_title),
if (isEnable)
R.drawable.icon_more_panel_essence else R.drawable.icon_more_panel_essence_unenable,
isEnable = isEnable
)
)
}
if (mViewModel.detailEntity?.user?.id == UserManager.getInstance().userId && mViewModel.detailEntity?.status == "pass") {
entities.add(MenuItemEntity("修改", R.drawable.icon_more_panel_edit))
// 修改
if (mViewModel.detailEntity?.user?.id == UserManager.getInstance().userId
&& mViewModel.detailEntity?.status == ArticleDetailEntity.STATUS_PASS) {
entities.add(MenuItemEntity(getString(R.string.article_detail_more_edit_title), R.drawable.icon_more_panel_edit))
}
// 举报
if (mViewModel.detailEntity?.user?.id != UserManager.getInstance().userId) {
entities.add(MenuItemEntity("投诉", R.drawable.icon_gamedetail_copyright))
entities.add(MenuItemEntity(getString(R.string.article_detail_more_complaint_title), R.drawable.icon_gamedetail_copyright))
}
val moderatorPermissions = mViewModel.detailEntity?.me?.moderatorPermissions
if (mViewModel.detailEntity?.me?.isModerator == true && mViewModel.detailEntity?.status == "pass") {
if (mViewModel.detailEntity?.getSimplifyChoicenessStatus() == "pass") {
// 取消精选
if (mViewModel.detailEntity?.me?.isModerator == true
&& mViewModel.detailEntity?.status == ArticleDetailEntity.STATUS_PASS) {
if (mViewModel.detailEntity?.getSimplifyChoicenessStatus() == ArticleDetailEntity.STATUS_PASS) {
if ((moderatorPermissions?.cancelHighlightCommunityArticle
?: Permissions.GUEST) > Permissions.GUEST
) {
entities.add(MenuItemEntity("取消精选", R.drawable.icon_more_panel_essence_cancel))
entities.add(MenuItemEntity(getString(R.string.article_detail_more_unselect_title), R.drawable.icon_more_panel_essence_cancel))
}
} else {
if ((moderatorPermissions?.highlightCommunityArticle
?: Permissions.GUEST) > Permissions.GUEST
) {
entities.add(MenuItemEntity("加精选", R.drawable.icon_more_panel_essence))
entities.add(MenuItemEntity(getString(R.string.article_detail_more_select_title), R.drawable.icon_more_panel_essence))
}
}
}
// 修改活动标签
if (mViewModel.detailEntity?.me?.isModerator == true &&
(moderatorPermissions?.updateArticleActivityTag
?: Permissions.GUEST) > Permissions.GUEST &&
mViewModel.detailEntity?.status == "pass"
mViewModel.detailEntity?.status == ArticleDetailEntity.STATUS_PASS
) {
entities.add(MenuItemEntity("修改活动标签", R.drawable.icon_more_panel_modify_label))
entities.add(MenuItemEntity(getString(R.string.article_detail_more_edit_activity_tag_title), R.drawable.icon_more_panel_modify_label))
}
// 隐藏/删除
if (mViewModel.detailEntity?.me?.isModerator == true &&
(moderatorPermissions?.hideCommunityArticle
?: Permissions.GUEST) > Permissions.GUEST
) {
entities.add(MenuItemEntity("隐藏", R.drawable.icon_more_panel_delete))
entities.add(MenuItemEntity(getString(R.string.article_detail_more_hide_title), R.drawable.icon_more_panel_delete))
} else {
if (mViewModel.detailEntity?.user?.id == UserManager.getInstance().userId) {
entities.add(MenuItemEntity("删除", R.drawable.icon_more_panel_delete))
entities.add(MenuItemEntity(getString(R.string.article_detail_more_delete_title), R.drawable.icon_more_panel_delete))
}
}
// 置顶/取消置顶
if (mViewModel.detailEntity?.me?.isModerator == true
&& mViewModel.detailEntity?.me?.isCommunityTop == false
&& (moderatorPermissions?.topCommunityArticle ?: Permissions.GUEST) > Permissions.GUEST) {
entities.add(MenuItemEntity(getString(R.string.article_detail_more_top_title), R.drawable.icon_more_panel_top))
} else if (mViewModel.detailEntity?.me?.isModerator == true
&& mViewModel.detailEntity?.me?.isCommunityTop == true
&& (moderatorPermissions?.cancelTopCommunityArticle ?: Permissions.GUEST) > Permissions.GUEST) {
entities.add(MenuItemEntity(getString(R.string.article_detail_more_cancel_top_title), R.drawable.icon_more_panel_top_cancel))
}
MoreFunctionPanelDialog.showMoreDialog(
requireActivity() as AppCompatActivity, entities, mViewModel.detailEntity?.title
?: "", getShareEntity(), mViewModel.detailEntity?.status ?: "", tag ?: ""
@ -597,7 +638,7 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
return {
val bbsType = if (mViewModel.detailEntity?.type == "game_bbs") "游戏论坛" else "综合论坛"
when (it?.text) {
"修改" -> {
getString(R.string.article_detail_more_edit_title) -> {
startActivityForResult(
ArticleEditActivity.getPatchIntent(
requireContext(),
@ -610,7 +651,7 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
?: "", mViewModel.detailEntity?.community?.id ?: "", bbsType
)
}
"投诉" -> {
getString(R.string.article_detail_more_complaint_title) -> {
ifLogin("帖子详情") {
BbsReportHelper.showReportDialog(mViewModel.detailEntity?.id ?: "")
}
@ -620,7 +661,7 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
?: "", mViewModel.detailEntity?.community?.id ?: "", bbsType
)
}
"申请加精" -> {
getString(R.string.article_detail_more_apply_select_title) -> {
if (mViewModel.detailEntity?.getSimplifyChoicenessStatus() == "apply") {
ToastUtils.showToast("申请加精审核中")
} else {
@ -632,7 +673,7 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
)
}
}
"加精选" -> {
getString(R.string.article_detail_more_select_title) -> {
if (mViewModel.detailEntity?.getSimplifyChoicenessStatus() == "apply") {
ToastUtils.showToast("加精审核中")
} else {
@ -644,10 +685,10 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
)
}
}
"取消精选" -> {
getString(R.string.article_detail_more_unselect_title) -> {
showHighlightDialog(false)
}
"修改活动标签" -> {
getString(R.string.article_detail_more_edit_activity_tag_title) -> {
ChooseActivityDialogFragment.show(
requireActivity() as AppCompatActivity,
ChooseActivityDialogFragment.ActivityLabelLocation.BBS_ARTICLE,
@ -656,7 +697,8 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
tag ?: ""
)
}
"删除", "隐藏" -> {
getString(R.string.article_detail_more_delete_title),
getString(R.string.article_detail_more_hide_title) -> {
DialogHelper.showDialog(
requireContext(),
"提示",
@ -673,6 +715,26 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
?: "", mViewModel.detailEntity?.community?.id ?: "", bbsType
)
}
getString(R.string.article_detail_more_top_title) -> {
TopCommunityCategoryDialog.show(
childFragmentManager
) { category ->
mViewModel.topCommunityArticle(category.id)
}
}
getString(R.string.article_detail_more_cancel_top_title) -> {
DialogHelper.showDialog(
requireContext(),
getString(R.string.article_detail_cancel_top_dialog_title),
getString(R.string.article_detail_cancel_top_dialog_hint),
getString(R.string.article_detail_cancel_top_dialog_confirm),
getString(R.string.article_detail_cancel_top_dialog_cancel),
confirmClickCallback = {
mViewModel.cancelTopCommunityArticle()
},
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
}
}
}
}

View File

@ -57,6 +57,7 @@ class ArticleDetailViewModel(
val mFollowLiveData = MutableLiveData<Boolean>()
val hide = MutableLiveData<Boolean>()
val top = MutableLiveData<Boolean>()
val like = MutableLiveData<VoteEntity>()
val dislike = MutableLiveData<Boolean>()
val highlight = MutableLiveData<Boolean>()
@ -377,6 +378,52 @@ class ArticleDetailViewModel(
})
}
fun topCommunityArticle(topCategoryId: String) {
detailEntity?.let {articleDetail ->
mApi.topCommunityArticle(
articleDetail.id,
articleDetail.communityId,
mapOf(
"title" to articleDetail.title,
"top_category_id" to topCategoryId
).toRequestBody()
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
getArticleDetail()
top.postValue(true)
}
})
}
}
fun cancelTopCommunityArticle() {
detailEntity?.also {articleDetail ->
articleDetail.top?.let { communityTop ->
mApi.cancelTopCommunityArticle(
articleDetail.id,
communityId,
mapOf(
"community_article_top_id" to communityTop.id
).toRequestBody()
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
getArticleDetail()
top.postValue(false)
}
})
}
}
}
fun modifyArticleActivityTag(
articleId: String,
label: ActivityLabelEntity?

View File

@ -0,0 +1,65 @@
package com.gh.gamecenter.qa.article.detail
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil.ItemCallback
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView.NO_POSITION
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.gh.gamecenter.common.utils.layoutInflater
import com.gh.gamecenter.databinding.ItemTopCommunityCategoryBinding
import com.gh.gamecenter.qa.entity.TopCommunityCategory
class TopCommunityCategoryAdapter(
private val callback: (TopCommunityCategory) -> Unit
) : ListAdapter<TopCommunityCategory, TopCommunityCategoryAdapter.ArticleDetailTopCategoryViewHolder>(COMPARATOR) {
private var selectedPosition = -1
companion object {
private val COMPARATOR = object: ItemCallback<TopCommunityCategory>() {
override fun areItemsTheSame(
oldItem: TopCommunityCategory,
newItem: TopCommunityCategory
) : Boolean = oldItem == newItem
override fun areContentsTheSame(
oldItem: TopCommunityCategory,
newItem: TopCommunityCategory
) : Boolean = oldItem == newItem
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleDetailTopCategoryViewHolder =
ArticleDetailTopCategoryViewHolder(
ItemTopCommunityCategoryBinding.inflate(
parent.layoutInflater,
parent,
false
)
)
override fun onBindViewHolder(holder: ArticleDetailTopCategoryViewHolder, position: Int) {
val titleView = holder.binding.root
titleView.text = getItem(position).title
titleView.isSelected = selectedPosition == position
titleView.setOnClickListener {
val pos = holder.bindingAdapterPosition
if (pos == NO_POSITION) return@setOnClickListener
it.isSelected = true
val oldPos = selectedPosition
selectedPosition = pos
notifyItemChanged(oldPos)
callback.invoke(getItem(pos))
}
}
class ArticleDetailTopCategoryViewHolder(
val binding: ItemTopCommunityCategoryBinding
) : ViewHolder(binding.root)
}

View File

@ -0,0 +1,93 @@
package com.gh.gamecenter.qa.article.detail
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.GridLayoutManager
import com.gh.gamecenter.common.utils.observeNonNull
import com.gh.gamecenter.common.view.GridSpacingItemDecoration
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.databinding.DialogTopCommunityCategoryBinding
import com.gh.gamecenter.qa.entity.TopCommunityCategory
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
/**
* 版主帖子置顶-置顶类型
*/
class TopCommunityCategoryDialog private constructor(): BottomSheetDialogFragment() {
private val viewModel: TopCommunityCategoryViewModel by viewModels()
private lateinit var viewBinding: DialogTopCommunityCategoryBinding
private lateinit var topCommunityCategoryAdapter: TopCommunityCategoryAdapter
private lateinit var callback: (TopCommunityCategory) -> Unit
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.getTopCommunityCategory()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = DialogTopCommunityCategoryBinding
.inflate(
inflater,
container,
false
).also {
viewBinding = it
}.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
with(viewBinding) {
rv.adapter = TopCommunityCategoryAdapter{
viewModel.submitTopCommunityCategory(it)
}.also {
topCommunityCategoryAdapter = it
}
rv.layoutManager = GridLayoutManager(context, 2)
rv.addItemDecoration(GridSpacingItemDecoration(2, DisplayUtils.dip2px(8F), false))
}
viewModel.topCommunityCategoryList.observeNonNull(viewLifecycleOwner) {
topCommunityCategoryAdapter.submitList(it)
}
viewModel.selectTopCommunityCategory.observe(viewLifecycleOwner) {
if (it == null) {
viewBinding.tvSubmit.isEnabled = false
viewBinding.tvSubmit.alpha = 0.4F
} else {
viewBinding.tvSubmit.isEnabled = true
viewBinding.tvSubmit.alpha = 1.0F
}
}
viewBinding.ivClose.setOnClickListener {
dismissAllowingStateLoss()
}
viewBinding.tvSubmit.setOnClickListener {
callback.invoke(viewModel.selectTopCommunityCategory.value!!)
dismissAllowingStateLoss()
}
}
companion object {
fun show(
fragmentManager: FragmentManager,
callback: (TopCommunityCategory) -> Unit
) = TopCommunityCategoryDialog().also {
it.callback = callback
}.show(fragmentManager, null)
}
}

View File

@ -0,0 +1,40 @@
package com.gh.gamecenter.qa.article.detail
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.qa.entity.TopCommunityCategory
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.retrofit.service.ApiService
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
/**
* 论坛置顶类型
*/
class TopCommunityCategoryViewModel: ViewModel() {
private val mApi: ApiService = RetrofitManager.getInstance().api
val topCommunityCategoryList = MutableLiveData<List<TopCommunityCategory>>()
val selectTopCommunityCategory = MutableLiveData<TopCommunityCategory?>(null)
fun getTopCommunityCategory() {
mApi.topCommunityCategory
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<List<TopCommunityCategory>>() {
override fun onResponse(response: List<TopCommunityCategory>?) {
response?.let {
topCommunityCategoryList.value = it
}
}
})
}
fun submitTopCommunityCategory(category: TopCommunityCategory) {
selectTopCommunityCategory.value = category
}
}

View File

@ -5,6 +5,7 @@ import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
@ -13,6 +14,8 @@ import android.view.inputmethod.InputMethodManager
import android.widget.*
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
@ -305,8 +308,22 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
commentContainer?.layoutParams = layoutParams
}
mKeyboardHeightProvider = KeyboardHeightProvider(activity)
view.post { mKeyboardHeightProvider?.start() }
// 判断是否处于平板的平行视界模式
val configString = requireContext().resources.configuration.toString()
val isMagicMode = configString.contains("magic-window") || configString.contains("window-magic")
// 平板上的导航方式改成按键时,使用 KeyboardHeightProvider 的 PopupWindow 会拦截掉所有的触摸事件
// 根据测试 Android 11 以前的系统用 WindowInsetsCompat.Type.ime()).bottom 获取不到正确的值
// 所以仅在系统为大于 10 且处于平行视界模式时才切换实现
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && isMagicMode) {
ViewCompat.setOnApplyWindowInsetsListener(requireActivity().window.decorView) { _: View?, insets: WindowInsetsCompat ->
onKeyboardHeightChanged(insets.getInsets(WindowInsetsCompat.Type.ime()).bottom, 0)
insets
}
} else {
mKeyboardHeightProvider = KeyboardHeightProvider(activity)
view.post { mKeyboardHeightProvider?.start() }
}
val emptyHint = mCachedView?.findViewById<TextView>(R.id.reuseNoneDataTv)
emptyHint?.text = "这里还没有人评论噢~"
emptyHint?.setTextColor(R.color.background_white.toColor(requireContext()))

View File

@ -7,6 +7,7 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
@ -151,8 +152,14 @@ class MoreFunctionPanelDialog : BaseDraggableDialogFragment() {
}
private fun addActionItem() {
mMenuItems.forEachIndexed { _, menuItemEntity ->
val itemView = createItemView(menuItemEntity)
mMenuItems.forEachIndexed { index, menuItemEntity ->
val itemView = createItemView(menuItemEntity).apply {
if (index == mMenuItems.lastIndex) {
(layoutParams as MarginLayoutParams).apply {
rightMargin = 16F.dip2px()
}
}
}
itemView.setOnClickListener {
debounceActionWithInterval(it.id, 2000) {
if (menuItemEntity.isEnable) {

View File

@ -3,7 +3,6 @@ package com.gh.gamecenter.qa.entity
import android.os.Parcelable
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.entity.ForumEntity
import com.gh.gamecenter.feature.entity.UserEntity
import com.gh.gamecenter.feature.entity.*
import com.google.gson.annotations.SerializedName
@ -48,7 +47,9 @@ class ArticleDetailEntity(
val activityTag: ActivityTagEntity? = null,
var sections: List<SectionEntity> = ArrayList(),
@SerializedName("section_id")
var sectionIdList: List<String> = ArrayList()
var sectionIdList: List<String> = ArrayList(),
@SerializedName("community_article_top")
var top: CommunityTopEntity? = null
) : Parcelable {
fun getSimplifyChoicenessStatus(): String {
@ -58,6 +59,10 @@ class ArticleDetailEntity(
else -> choicenessStatus ?: ""
}
}
companion object {
const val STATUS_PASS = "pass"
}
}
@Parcelize

View File

@ -36,7 +36,7 @@ class EditorInsertEntity(
fun transform(article: ArticleEntity): EditorInsertEntity {
val entity = EditorInsertEntity()
entity.id = article.id
entity.communityId = article.bbs.id
entity.communityId = article.community.id
entity.type = "community_article"
entity.title = article.title.eliminateDoubleQuote()
entity.brief = article.brief.eliminateDoubleQuote()

View File

@ -56,7 +56,9 @@ class QuestionsDetailEntity(
val source: SourceEntity? = null,
@SerializedName("activity_tag")
val activityTag: ActivityTagEntity? = null,
var sections: List<SectionEntity> = ArrayList()
var sections: List<SectionEntity> = ArrayList(),
@SerializedName("question_top")
var top: CommunityTopEntity? = null
) : Parcelable {
fun getFollowCount(): Int {

View File

@ -0,0 +1,19 @@
package com.gh.gamecenter.qa.entity
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class TopCommunityCategory(
@SerializedName("_id")
val id: String, // 置顶类型ID
val title: String, // 置顶类型名称
val style: Style
): Parcelable {
@Parcelize
data class Style(
val color: String,
val fontColor: String
): Parcelable
}

View File

@ -32,9 +32,11 @@ import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.databinding.FragmentArticleDetailBinding
import com.gh.gamecenter.entity.MenuItemEntity
import com.gh.gamecenter.eventbus.EBDeleteDetail
import com.gh.gamecenter.eventbus.EBTopCommunityChanged
import com.gh.gamecenter.feature.entity.Permissions
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.qa.article.detail.CommentItemData
import com.gh.gamecenter.qa.article.detail.TopCommunityCategoryDialog
import com.gh.gamecenter.qa.comment.CommentActivity
import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter
import com.gh.gamecenter.qa.comment.base.BaseCommentFragment
@ -367,6 +369,22 @@ class NewQuestionDetailFragment :
mViewModel.updateDetailLiveData.observeNonNull(this) {
mAdapter?.questionDetailVH?.bindView(it)
}
mViewModel.top.observeNonNull(this) { top ->
val topSuccessToast = if(top) {
R.string.article_detail_top_success_toast
} else {
R.string.article_detail_cancel_top_success_toast
}
toast(getString(topSuccessToast))
mListViewModel.questionDetail?.let {
EventBus.getDefault().post(
EBTopCommunityChanged(
it.community.id
)
)
}
}
}
private fun updateStartButton() {
@ -545,6 +563,17 @@ class NewQuestionDetailFragment :
}
}
// 置顶/取消置顶
if (questionEntity.me.isModerator
&& !questionEntity.me.isCommunityTop
&& moderatorPermissions.topQuestion > Permissions.GUEST) {
entities.add(MenuItemEntity(getString(R.string.article_detail_more_top_title), R.drawable.icon_more_panel_top))
} else if (questionEntity.me.isModerator
&& questionEntity.me.isCommunityTop
&& moderatorPermissions.cancelTopQuestion > Permissions.GUEST) {
entities.add(MenuItemEntity(getString(R.string.article_detail_more_cancel_top_title), R.drawable.icon_more_panel_top_cancel))
}
MoreFunctionPanelDialog.showMoreDialog(
requireActivity() as AppCompatActivity, entities, questionEntity.title
?: "", getShareEntity(questionEntity), questionEntity.status, tag ?: ""
@ -655,6 +684,29 @@ class NewQuestionDetailFragment :
bbsType
)
}
getString(R.string.article_detail_more_top_title) -> {
TopCommunityCategoryDialog.show(
childFragmentManager
) { category ->
mViewModel.topCommunityQuestion(category.id)
}
}
getString(R.string.article_detail_more_cancel_top_title) -> {
DialogHelper.showDialog(
requireContext(),
getString(R.string.article_detail_cancel_top_dialog_title),
getString(R.string.article_detail_cancel_top_dialog_hint),
getString(R.string.article_detail_cancel_top_dialog_confirm),
getString(R.string.article_detail_cancel_top_dialog_cancel),
confirmClickCallback = {
mViewModel.questionDetail?.top?.let { top ->
mViewModel.cancelTopCommunityQuestion()
}
},
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
}
}
}
}

View File

@ -57,6 +57,7 @@ class NewQuestionDetailViewModel(
val moderatorsHideLiveData = MutableLiveData<Boolean>()
val favoriteLiveData = MutableLiveData<Boolean>()
val mFollowLiveData = MutableLiveData<Boolean>()
val top = MutableLiveData<Boolean>()
val updateDetailLiveData = MutableLiveData<QuestionsDetailEntity>()
var questionDetail: QuestionsDetailEntity? = null
val followLiveData = mFollowLiveData
@ -226,6 +227,49 @@ class NewQuestionDetailViewModel(
})
}
fun topCommunityQuestion(topCategoryId: String) {
questionDetail?.let { questionDetail ->
mApi.topCommunityQuestion(
questionDetail.id,
mapOf(
"title" to questionDetail.title,
"top_category_id" to topCategoryId
).toRequestBody()
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
getQuestionDetail()
top.postValue(true)
}
})
}
}
fun cancelTopCommunityQuestion() {
questionDetail?.also {questionDetail ->
questionDetail.top?.let { communityTop ->
mApi.cancelTopCommunityQuestion(
questionDetail.id,
mapOf(
"question_top_id" to communityTop.id
).toRequestBody()
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
getQuestionDetail()
top.postValue(false)
}
})
}
}
}
fun getFirstAnswer() = mListLiveData.value?.safelyGetInRelease(0)
class Factory(

View File

@ -41,12 +41,14 @@ import com.gh.gamecenter.entity.*
import com.gh.gamecenter.eventbus.EBDeleteDetail
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.eventbus.EBTopCommunityChanged
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.Permissions
import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper
import com.gh.gamecenter.gamedetail.GameDetailFragment
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.qa.article.detail.TopCommunityCategoryDialog
import com.gh.gamecenter.qa.dialog.ChooseActivityDialogFragment
import com.gh.gamecenter.qa.dialog.MoreFunctionPanelDialog
import com.gh.gamecenter.qa.video.detail.comment.VideoCommentFragment
@ -309,6 +311,21 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() {
toast("提交失败")
}
}
mViewModel.top.observeNonNull(this) { top ->
val topSuccessToast = if(top) {
R.string.article_detail_top_success_toast
} else {
R.string.article_detail_cancel_top_success_toast
}
toast(getString(topSuccessToast))
mForumVideoEntity?.let {
EventBus.getDefault().post(
EBTopCommunityChanged(
it.bbsId
)
)
}
}
}
private fun setGameInfo(entity: ForumVideoEntity) {
@ -535,6 +552,17 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() {
}
}
// 置顶/取消置顶
if (mForumVideoEntity?.me?.isModerator == true
&& mForumVideoEntity?.me?.isCommunityTop == false
&& (moderatorPermissions?.topVideo ?: Permissions.GUEST) > Permissions.GUEST) {
entities.add(MenuItemEntity(getString(R.string.article_detail_more_top_title), R.drawable.icon_more_panel_top))
} else if (mForumVideoEntity?.me?.isModerator == true
&& mForumVideoEntity?.me?.isCommunityTop == true
&& (moderatorPermissions?.cancelTopVideo ?: Permissions.GUEST) > Permissions.GUEST) {
entities.add(MenuItemEntity(getString(R.string.article_detail_more_cancel_top_title), R.drawable.icon_more_panel_top_cancel))
}
MoreFunctionPanelDialog.showMoreDialog(
requireActivity() as AppCompatActivity, entities, mForumVideoEntity?.title
?: "", getShareEntity(), mForumVideoEntity?.status ?: "", tag ?: ""
@ -647,6 +675,27 @@ class ForumVideoDetailFragment : BaseFragment_TabLayout() {
?: "", mForumVideoEntity?.bbs?.id ?: "", bbsType
)
}
getString(R.string.article_detail_more_top_title) -> {
TopCommunityCategoryDialog.show(
childFragmentManager
) { category ->
mViewModel.topCommunityVideo(category.id)
}
}
getString(R.string.article_detail_more_cancel_top_title) -> {
DialogHelper.showDialog(
requireContext(),
getString(R.string.article_detail_cancel_top_dialog_title),
getString(R.string.article_detail_cancel_top_dialog_hint),
getString(R.string.article_detail_cancel_top_dialog_confirm),
getString(R.string.article_detail_cancel_top_dialog_cancel),
confirmClickCallback = {
mViewModel.cancelTopCommunityVideo()
},
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
}
}
}
}

View File

@ -23,6 +23,7 @@ import com.gh.gamecenter.feature.entity.Permissions
import com.gh.gamecenter.retrofit.RetrofitManager
import com.google.gson.JsonObject
import com.halo.assistant.HaloApp
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import retrofit2.HttpException
@ -43,6 +44,7 @@ class ForumVideoDetailViewModel(
val highlight = MutableLiveData<Boolean>()
val cancelHighlight = MutableLiveData<Boolean>()
val applyHighlight = MutableLiveData<Boolean>()
val top = MutableLiveData<Boolean>()
var currentToolbarStatus = true
init {
@ -200,6 +202,49 @@ class ForumVideoDetailViewModel(
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.videoHistoryDao().addVideo(videoHistory) } }
}
fun topCommunityVideo(topCategoryId: String) {
detailLiveData.value?.data?.let { videoDetail ->
mApi.topCommunityVideo(
videoDetail.id,
mapOf(
"title" to videoDetail.title,
"top_category_id" to topCategoryId
).toRequestBody()
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
getVideoDetail()
top.postValue(true)
}
})
}
}
fun cancelTopCommunityVideo() {
detailLiveData.value?.data
?.also {videoDetail ->
videoDetail.top?.let { communityTop ->
mApi.cancelTopCommunityVideo(
videoDetail.id,
mapOf(
"video_top_id" to communityTop.id
).toRequestBody()
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
getVideoDetail()
top.postValue(false)
}
})
}
}
}
fun isTopVideoPartlyCached(topVideoUrl: String): Boolean {
val cache = ExoSourceManager.getCacheSingleInstance(HaloApp.getInstance().application, null)
val key = Uri.parse(topVideoUrl).toString()

View File

@ -40,7 +40,6 @@ import com.gh.gamecenter.entity.GameCollectionTagEntity;
import com.gh.gamecenter.entity.GameColumnCollection;
import com.gh.gamecenter.entity.GameDigestEntity;
import com.gh.gamecenter.entity.GameGuidePopupEntity;
import com.gh.gamecenter.entity.GameNavigationWrapper;
import com.gh.gamecenter.entity.GameServerTestV2Entity;
import com.gh.gamecenter.entity.GameVideoInfo;
import com.gh.gamecenter.entity.GamesCollectionDetailEntity;
@ -114,6 +113,7 @@ import com.gh.gamecenter.gamedetail.entity.BigEvent;
import com.gh.gamecenter.gamedetail.entity.NewGameDetailEntity;
import com.gh.gamecenter.login.entity.UserInfoEntity;
import com.gh.gamecenter.personalhome.rating.MyRating;
import com.gh.gamecenter.qa.entity.TopCommunityCategory;
import com.gh.gamecenter.qa.entity.AnswerDetailEntity;
import com.gh.gamecenter.qa.entity.AnswerDraftEntity;
import com.gh.gamecenter.qa.entity.ArticleDetailEntity;
@ -193,13 +193,6 @@ public interface ApiService {
@GET("region_setting")
Single<RegionSetting> getRegionSetting(@Query("channel") String channel);
/**
* 统计下载量
*/
@Headers({"Content-Type: application/json", "Accept: application/json"})
@POST("stat/download")
Observable<ResponseBody> postDownload(@Body RequestBody body);
/**
* 根据包名获取游戏摘要
*/
@ -497,6 +490,12 @@ public interface ApiService {
@POST("articles/comments/{comment_id}:vote")
Observable<ResponseBody> postCommentVote(@Path("comment_id") String comment_id);
/**
* 评论点赞
*/
@POST("articles/comments/{comment_id}:hide")
Observable<ResponseBody> deleteComment(@Path("comment_id") String comment_id);
/**
* 获取新闻评论
*/
@ -1348,6 +1347,56 @@ public interface ApiService {
@POST("communities/articles/{article_id}:hide")
Observable<ResponseBody> hideCommunityArticle(@Path("article_id") String articleId);
/**
* 获取置顶帖子类型
*/
@GET("communities/top_category")
Observable<List<TopCommunityCategory>> getTopCommunityCategory();
/**
* 普通帖-置顶
*/
@POST("communities/{community_id}/articles/{article_id}:set-top")
Observable<ResponseBody> topCommunityArticle(@Path("article_id") String articleId,
@Path("community_id") String communityId,
@Body RequestBody body);
/**
* 普通帖-取消置顶
*/
@POST("communities/{community_id}/articles/{article_id}:cancel-top")
Observable<ResponseBody> cancelTopCommunityArticle(@Path("article_id") String articleId,
@Path("community_id") String communityId,
@Body RequestBody body);
/**
* 提问帖-置顶
*/
@POST("question/{question_id}:set-top")
Observable<ResponseBody> topCommunityQuestion(@Path("question_id") String questionId,
@Body RequestBody body);
/**
* 提问帖-取消置顶
*/
@POST("question/{question_id}:cancel-top")
Observable<ResponseBody> cancelTopCommunityQuestion(@Path("question_id") String questionId,
@Body RequestBody body);
/**
* 视频帖-置顶
*/
@POST("video/{video_id}:set-top")
Observable<ResponseBody> topCommunityVideo(@Path("video_id") String videoId,
@Body RequestBody body);
/**
* 视频帖-取消置顶
*/
@POST("video/{video_id}:cancel-top")
Observable<ResponseBody> cancelTopCommunityVideo(@Path("video_id") String videoId,
@Body RequestBody body);
/**
* 隐藏社区文章评论
@ -2062,7 +2111,7 @@ public interface ApiService {
* 获取论坛详情
*/
@GET("bbses/{bbs_id}")
Observable<ForumDetailEntity> getForumDetail(@Path("bbs_id") String bbsId);
Observable<ForumDetailEntity> getForumDetail(@Path("bbs_id") String bbsId, @Query("refresh") boolean refresh);
/**
* 获取论坛板块列表
@ -2967,7 +3016,7 @@ public interface ApiService {
* 获取我的存档列表
*/
@GET("users/games/{game_id}/archives")
Observable<List<ArchiveEntity>> getMyArchives(@Path("game_id") String gameId, @Query("page") int page);
Observable<List<ArchiveEntity>> getMyArchives(@Path("game_id") String gameId, @Query("page") int page, @Query("sort") String sort);
/**
* 创建我的存档
@ -2985,7 +3034,7 @@ public interface ApiService {
* 获取我的分享存档列表
*/
@GET("users/games/{game_id}/archives/share")
Observable<List<ArchiveEntity>> getMyShareArchives(@Path("game_id") String gameId, @Query("page") int page);
Observable<List<ArchiveEntity>> getMyShareArchives(@Path("game_id") String gameId, @Query("page") int page, @Query("sort") String sort);
/**
* 编辑我的存档

View File

@ -12,6 +12,7 @@ import com.gh.gamecenter.adapter.viewholder.GameViewHolder
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.DrawableView
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.RandomUtils
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.databinding.ItemGameServerTestBigImageBinding
@ -31,6 +32,7 @@ class GameBigImageViewHolder(val binding: ItemGameServerTestBigImageBinding) :
) {
holder.bindGameInfo(gameEntity, adapter, exposureEvent, entrance)
holder.binding.gameImage.goneIf(gameEntity.topVideo != null || gameEntity.homeSetting.image.isEmpty()) {
binding.gameImage.setTag(ImageUtils.TAG_TARGET_WIDTH, DisplayUtils.getScreenWidth() - 32F.dip2px())
ImageUtils.display(binding.gameImage, gameEntity.homeSetting.image)
val hierarchy = binding.gameImage.hierarchy
try {
@ -40,6 +42,10 @@ class GameBigImageViewHolder(val binding: ItemGameServerTestBigImageBinding) :
}
}
holder.binding.autoVideoView.goneIf(gameEntity.topVideo == null) {
binding.autoVideoView.thumbImage.setTag(
ImageUtils.TAG_TARGET_WIDTH,
DisplayUtils.getScreenWidth() - 32F.dip2px()
)
if (!holder.binding.autoVideoView.isInPlayingState) {
GSYVideoOptionBuilder()
.setIsTouchWiget(false)

View File

@ -83,7 +83,12 @@ class VFeedbackDialogFragment : BaseDialogFragment() {
}
mBinding.submitTv.setOnClickListener {
mGame?.run {
mViewModel.postFeedback(this, mBinding.feedbackEt.text.toString(), getSelectedTagString())
mViewModel.postFeedback(
this,
mBinding.feedbackEt.text.toString(),
VHelper.getVersionName(it.context),
getSelectedTagString()
)
}
NewFlatLogUtils.logHaloFunGameExitDialogSubmitClick(
mGame?.id ?: "",
@ -194,12 +199,17 @@ class VFeedbackDialogFragment : BaseDialogFragment() {
class VFeedbackViewModel : ViewModel() {
fun postFeedback(game: GameEntity, message: String, tags: ArrayList<String>) {
fun postFeedback(game: GameEntity, message: String, vSpaceVersion: String?, tags: ArrayList<String>) {
val processedMessage = StringBuilder().apply {
append(game.name)
game.getApk().safelyGetInRelease(0)?.version?.run {
append("-$this")
}
vSpaceVersion?.apply {
append("-$this")
}
if (message.isNotBlank()) {
append("-$message")
}

View File

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.database.sqlite.SQLiteFullException
import android.net.Uri
import android.os.Build
@ -58,15 +59,14 @@ import org.greenrobot.eventbus.EventBus
import java.io.File
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
object VHelper {
// 畅玩游戏是否已被使用过
private const val KEY_V_IS_USED = "v_is_used"
private const val KEY_LAST_PLAYED_TIME = "last_played_time"
private const val KEY_LAST_ALERT_UPDATE_URL = "last_alert_update_url"
private const val KEY_LAST_ALERT_64_UPDATE_URL = "last_alert_64_update_url"
private const val KEY_LAST_ALERT_32_UPDATE_URL = "last_alert_32_update_url"
private const val KEY_TOTAL_PLAYED_TIME = "total_played_time"
private const val G_GMS_PACKAGE_NAME = "com.google.android.gms"
@ -79,8 +79,12 @@ object VHelper {
"https://dev-and-static.ghzs.com/game/apk/smooth_google/google_play_services.zip"
const val G_APPS_DOWNLOAD_URL = "https://and-static.ghzs.com/game/apk/smooth_google/google_play_services.zip"
// 畅玩游戏位数
const val KEY_BIT = "bit"
const val LOG_TAG = "VSPACE"
const val DEFAULT_VSPACE_PACKAGENAME = "com.lg.vspace"
const val VSPACE_32BIT_PACKAGENAME = "$DEFAULT_VSPACE_PACKAGENAME.addon"
const val PLATFORM_V = "畅玩版"
private val mDelegateManager by lazy { VirtualAppManager.get() }
@ -91,7 +95,8 @@ object VHelper {
private val mVGameDao by lazy { VGameDatabase.instance.vGameDao() }
private var mVGameSnapshotList = arrayListOf<VGameEntity>()
private var mUpdateEntity: AppEntity? = null
private var m64UpdateEntity: AppEntity? = null
private var m32UpdateEntity: AppEntity? = null
private var mIsInitialized = false // 是否已初始化
private var mIsServiceConnected = false // AIDL 服务是否成功连接,不可作为 AIDL 仍然可用的依据
@ -109,11 +114,14 @@ object VHelper {
// 因为显示下载畅玩弹窗而处于挂起状态的游戏回调,供安装完畅玩组件后自动下载用
private var mPendingDownloadCallback: (() -> Unit)? = null
// 供安装完畅玩组件判断畅玩游戏位数使用
private var mBit = ""
// 批量安装的 map k: 包名, v: 文件
private var mBatchInstallMap = hashMapOf<String, File>()
// 批量安装的监听
private var mBatchInstallListener: ((isSuccess: Boolean) -> Unit)? = null
private var mBatchInstallListener: ((isSuccess: Boolean, interrupted: Boolean) -> Unit)? = null
// 批量安装失败的统计
private var mBatchInstallFailedCount = 0
@ -123,6 +131,7 @@ object VHelper {
// 下次应用可见时是否需要尝试重连
private var mShouldReConnectOnVisible = false
// 是否已经尝试过重连
private var mHasAlreadyTriedReConnect = false
@ -140,12 +149,22 @@ object VHelper {
private val mPackageObserver by lazy {
PackageObserver.PackageChangeListener {
val vaConfig = Config.getVSettingEntity()?.va ?: return@PackageChangeListener
val isVSpace =
it.packageName == vaConfig.arch32?.packageName || it.packageName == vaConfig.arch64?.packageName
val isVSpace32 = it.packageName == vaConfig.arch32?.packageName
val isVSpace64 = it.packageName == vaConfig.arch64?.packageName
if (!isVSpace) return@PackageChangeListener
if (!isVSpace64 && !isVSpace32) return@PackageChangeListener
// 需要安装32位畅玩时跳过64位畅玩安装成功的回调
val skip64VSpaceInstalled = mBit == "32" && isVSpace64 && !PackageUtils.isInstalledFromAllPackage(
HaloApp.getInstance(),
vaConfig.arch32?.packageName
)
if (it.type == EBPackage.TYPE_INSTALLED) {
SensorsBridge.trackEvent("HaloFunInstallDone", "space_schema_type", if (isVSpace32) "32位" else "64位")
if (skip64VSpaceInstalled) return@PackageChangeListener
// 帮用户启动因为没有安装畅玩组件而没法进行的下载任务
// 因为有可能回调时还在后台,但还要弹窗什么的,所以可能会闪退?先 try catch 一下看看
try {
@ -155,20 +174,24 @@ object VHelper {
}
mPendingDownloadCallback = null
mBit = ""
}
if (!isVIsUsed()) return@PackageChangeListener
if (it.type == EBPackage.TYPE_INSTALLED) {
if (skip64VSpaceInstalled) return@PackageChangeListener
// 即时调用大概率调不起来,我也不知道为什么,只能延迟大法了
AppExecutor.uiExecutor.executeWithDelay({
connectService()
}, 500)
SensorsBridge.trackEvent("HaloFunInstallDone")
} else if (it.type == EBPackage.TYPE_UNINSTALLED) {
// 执行卸载逻辑
mIsServiceConnected = false
mInstalledInfoList.clear()
if (isVSpace64) {
// 执行卸载逻辑
mIsServiceConnected = false
mInstalledInfoList.clear()
}
} else if (it.type == EBPackage.TYPE_REPLACED) {
connectService()
}
@ -195,8 +218,15 @@ object VHelper {
connectService(shouldCheckUpdate = true)
}
// 检查畅玩助手组件更新
getVSpaceUpdate(config.arch64)
// 检查畅玩助手64位组件更新
getVSpaceUpdate(config.arch64, true)
}
if (config?.arch32 != null
&& PackageUtils.isInstalledFromAllPackage(context, config.arch32.packageName)
) {
// 检查畅玩助手32位组件更新
getVSpaceUpdate(config.arch32, false)
}
PackageObserver.registerPackageChangeChangeListener(mPackageObserver)
}
@ -276,6 +306,9 @@ object VHelper {
// 已断开
mIsServiceConnected = false
// 断开时清空正在安装列表
mInstallingVaPathSet.clear()
if (failCode == BinderPool.CONNECT_STATE_NOT_INSTALLED) {
Utils.log(LOG_TAG, "未安装畅玩助手")
}
@ -354,15 +387,19 @@ object VHelper {
* 畅玩助手是否已安装
*/
fun isVSpaceInstalled(context: Context): Boolean {
val config = Config.getVSettingEntity()?.va
return config?.arch64 != null && PackageUtils.isInstalledFromAllPackage(context, config.arch64.packageName)
return PackageUtils.isInstalledFromAllPackage(context, VirtualAppManager.AIDL_SERVER_PACKAGE_NAME)
}
/**
* 显示下载畅玩空间弹窗
*/
fun showVspaceDialog(context: Context, gameEntity: GameEntity?) {
VSpaceDialogFragment.showDownloadDialog(context, getVSpaceDownloadEntity(false), gameEntity)
fun showVSpaceDialog(context: Context, gameEntity: GameEntity?) {
VSpaceDialogFragment.showDownloadDialog(
context,
appEntity64 = getVSpaceDownloadEntity(true),
appEntity32 = getVSpaceDownloadEntity(false),
gameEntity = gameEntity
)
}
/**
@ -404,9 +441,22 @@ object VHelper {
*/
@JvmStatic
fun isInstalled(packageName: String?): Boolean {
val isInstalled = mInstalledInfoList.any { it.packageName == packageName }
var isInstalled = mInstalledInfoList.any { it.packageName == packageName }
Utils.log(LOG_TAG, "$packageName 已安装列表里 -> $isInstalled")
// 有可能 mInstalledInfoList 获取异常,导致判断为未安装,手动调用 AIDL 再查一次
if (!isInstalled
&& mIsServiceConnected
&& mDelegateManager.isConnectAidlInterface) {
try {
isInstalled = mDelegateManager.checkGameInstalled(packageName)
Utils.log(LOG_TAG, "手动调用 AIDL 获取安装情况 -> $isInstalled")
} catch (e: RuntimeException) {
Utils.log(LOG_TAG, "手动调用 AIDL 获取安装情况异常 ${e.localizedMessage}")
}
}
return isInstalled
}
@ -449,44 +499,105 @@ object VHelper {
fun validateVSpaceBeforeAction(context: Context, gameEntity: GameEntity?, callback: () -> Unit) {
// 仅下载类型为畅玩的类型才执行判断
if (gameEntity == null || gameEntity.isVGame()) {
if (showDialogIfVSpaceIsNeeded(context, gameEntity?.id ?: "", gameEntity?.name ?: "")) {
if (showDialogIfVSpaceIsNeeded(
context,
gameEntity?.id ?: "",
gameEntity?.name ?: "",
gameEntity?.categoryChinese ?: "",
gameEntity?.gameBit ?: ""
)
) {
mBit = gameEntity?.gameBit ?: ""
mPendingDownloadCallback = callback
return
}
val vaConfig64 = Config.getVSettingEntity()?.va?.arch64
val installedSpaceVersionCode = PackageUtils.getVersionCodeByPackageName(vaConfig64?.packageName)
val vaConfig32 = Config.getVSettingEntity()?.va?.arch32
val installed64SpaceVersionCode = PackageUtils.getVersionCodeByPackageName(vaConfig64?.packageName)
val installed32SpaceVersionCode = PackageUtils.getVersionCodeByPackageName(vaConfig32?.packageName)
// 检查更新
val containsUpdate = shouldShowVSpaceUpdate(mUpdateEntity, installedSpaceVersionCode)
val contains64Update = shouldShowVSpaceUpdate(m64UpdateEntity, installed64SpaceVersionCode)
val contains32Update = if (gameEntity?.gameBit == "32") shouldShowVSpaceUpdate(
m32UpdateEntity,
installed32SpaceVersionCode
) else false
if (containsUpdate) {
val dialogType = if (mUpdateEntity!!.isAlertEveryTime()) "强制更新" else "提示更新"
NewFlatLogUtils.logHaloFunEvent("halo_fun_update_dialog_show")
SPUtils.setString(KEY_LAST_ALERT_UPDATE_URL, mUpdateEntity!!.url + mUpdateEntity!!.alert)
var containsRelated64Update = false
if (contains32Update) {
// 检查关联64位是否需要更新
if (m32UpdateEntity?.relation != null) {
containsRelated64Update =
shouldShowVSpaceUpdate(m32UpdateEntity?.relation, installed64SpaceVersionCode, true)
}
}
if (contains32Update || contains64Update) {
val gameBit = gameEntity?.gameBit
val updateEntity = if (gameBit == "32" && contains32Update) m32UpdateEntity else m64UpdateEntity
val dialogType = if (updateEntity!!.isAlertEveryTime()) "强制更新" else "提示更新"
NewFlatLogUtils.logHaloFunUpdateDialogShow(
gameEntity?.id ?: "",
gameEntity?.name ?: "",
if (gameBit == "32") "32位" else "64位"
)
SPUtils.setString(
if (updateEntity.category == "32-bit") KEY_LAST_ALERT_32_UPDATE_URL else KEY_LAST_ALERT_64_UPDATE_URL,
updateEntity.url + updateEntity.alert
)
DialogHelper.showDialog(
context = context,
title = "服务工具更新提示",
content = mUpdateEntity!!.content.toString(),
content = updateEntity.content.toString(),
cancelText = "立即更新",
confirmText = "继续游戏",
cancelClickCallback = {
NewFlatLogUtils.logHaloFunUpdateDialogClick(dialogType, "立即更新")
val appEntity64 =
if (gameBit == "32" && contains32Update && m32UpdateEntity?.relation != null) {
// 32位游戏且包含32位更新和32位关联64位更新
// 存在64位单独更新时对比32位关联的64位更新与64位单独更新推送的版本号
if (contains64Update && (m64UpdateEntity?.versionCode
?: 0) > (m32UpdateEntity?.relation?.versionCode ?: 0)
) {
// 64位单独更新版本号更高时使用64位单独更新
m64UpdateEntity
} else if (containsRelated64Update) {
// 不存在64位单独更新或32位关联64位更新版本号更高
// 对比已安装版本号和32位关联的64位更新版本号32位关联的64位更新版本号更高时使用32位关联的64位更新
m32UpdateEntity?.relation
} else {
null
}
} else if ((gameBit != "32" && contains64Update) || (gameBit == "32" && contains64Update && !contains32Update)) {
// 64位游戏且包含64位单独更新 / 32位游戏无32位更新且包含64位更新
m64UpdateEntity
} else {
null
}
val appEntity32 = if (contains32Update) updateEntity else null
NewFlatLogUtils.logHaloFunUpdateDialogClick(
dialogType,
"立即更新",
if (appEntity64 != null && appEntity32 != null) "32位&64位" else if (appEntity64 != null) "64位" else "32位"
)
VSpaceDialogFragment.showDownloadDialog(
context,
getVSpaceDownloadEntity(true),
appEntity64,
appEntity32,
gameEntity,
autoDownload = true,
isUpdate = true
)
},
confirmClickCallback = {
NewFlatLogUtils.logHaloFunUpdateDialogClick(dialogType, "继续游戏")
NewFlatLogUtils.logHaloFunUpdateDialogClick(dialogType, "继续游戏", "")
callback.invoke()
},
extraConfig = DialogHelper.Config(centerTitle = true),
uiModificationCallback = {
if (mUpdateEntity!!.isAlertEveryTime()) {
if (updateEntity.isAlertEveryTime()) {
it.confirmTv.visibility = View.GONE
it.cancelTv.setTextColor(R.color.theme_font.toColor(context))
}
@ -553,7 +664,14 @@ object VHelper {
) {
Utils.log(LOG_TAG, "尝试安装新游戏 ${downloadEntity.path}")
if (showDialogIfVSpaceIsNeeded(context, downloadEntity.gameId, downloadEntity.name)) return
if (showDialogIfVSpaceIsNeeded(
context,
downloadEntity.gameId,
downloadEntity.name,
downloadEntity.getMetaExtra(Constants.GAME_TYPE),
downloadEntity.getMetaExtra(KEY_BIT)
)
) return
// 更新此包名对应的 gameId Map
mTempPackageNameAndGameIdMap[downloadEntity.packageName] = downloadEntity.gameId
@ -586,7 +704,7 @@ object VHelper {
val path = downloadEntity.path
// 正在安装中,忽略重复调用 (手动安装时强行再安装,避免安装结果没有回调时永远卡住,无法安装)
if (!mInstallingVaPathSet.contains(path) || isManualInstall) {
if (!mInstallingVaPathSet.contains(path)) {
try {
mInstallingVaPathSet.add(path)
@ -599,6 +717,8 @@ object VHelper {
} catch (e: Exception) {
ToastUtils.toast(e.localizedMessage ?: "")
}
} else {
Utils.log(LOG_TAG, "$path 正在安装中,此次安装调用无效")
}
}
@ -629,13 +749,13 @@ object VHelper {
/**
* 注册批量安装结果回调,值为空时为取消注册
*/
fun registerBatchInstallListener(listener: ((isSuccess: Boolean) -> Unit)? = null) {
fun registerBatchInstallListener(listener: ((isSuccess: Boolean, isInterrupted: Boolean) -> Unit)? = null) {
mBatchInstallListener = listener
}
/**
* 安装新应用
* 不会作为游戏存储,目前应用于谷歌框架
* 不会作为游戏存储,目前应用于谷歌框架
* 因为是静默安装,所以不检查其它限制条件
*/
private fun install(context: Context, file: File) {
@ -645,12 +765,19 @@ object VHelper {
val installClosure: () -> Unit = {
val path = file.path
// 正在安装中,忽略重复调用 (手动安装时强行再安装,避免安装结果没有回调时永远卡住,无法安装)
if (!mInstallingVaPathSet.contains(path)) {
try {
mInstallingVaPathSet.add(path)
runOnUiThread {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
&& !PackageUtils.isAppOnForeground(context)
) {
mBatchInstallListener?.invoke(false, true)
Utils.log(LOG_TAG, "应用切换至后台,批量安装被打断")
return@runOnUiThread
}
mInstallingVaPathSet.add(path)
val intent = VirtualAppManager.getInstallIntent(context, path, packageName)
Utils.log(LOG_TAG, "正在安装 $packageName")
@ -704,6 +831,9 @@ object VHelper {
}
)
insertInstalledGameToProvider(downloadEntity)
runOnUiThread {
showFloatingWindow(downloadEntity)
}
} else {
ToastUtils.toast("安装出现异常, ${result.status}")
}
@ -718,7 +848,7 @@ object VHelper {
if (result.status == 0) {
updateInstalledList()
} else {
Utils.log(LOG_TAG, "安装出现异常, ${result.status}")
ToastUtils.toast("安装出现异常, ${result.status}")
}
}
@ -750,7 +880,7 @@ object VHelper {
if (mBatchInstallMap.size == 0) {
runOnUiThread {
// 批量安装完成后回调成功/失败事件
mBatchInstallListener?.invoke(mBatchInstallFailedCount == 0)
mBatchInstallListener?.invoke(mBatchInstallFailedCount == 0, false)
}
}
}
@ -808,23 +938,50 @@ object VHelper {
fun installOrLaunch(context: Context, downloadEntity: DownloadEntity) {
Utils.log(LOG_TAG, "检测是需要安装还是启动 ${downloadEntity.gameId}")
installOrLaunch(context, downloadEntity.packageName, downloadEntity.gameId)
installOrLaunch(
context,
downloadEntity.packageName,
downloadEntity.gameId,
downloadEntity.name,
downloadEntity.getMetaExtra(Constants.GAME_TYPE),
downloadEntity.getMetaExtra(KEY_BIT)
)
}
@JvmStatic
fun installOrLaunch(context: Context, gameEntity: GameEntity) {
Utils.log(LOG_TAG, "检测是需要安装还是启动 ${gameEntity.id}")
installOrLaunch(context, gameEntity.getUniquePackageName() ?: "", gameEntity.id)
installOrLaunch(
context,
gameEntity.getUniquePackageName() ?: "",
gameEntity.id,
gameEntity.name ?: "",
gameEntity.category ?: "",
gameEntity.gameBit
)
}
/**
* 安装或启动应用
*/
private fun installOrLaunch(context: Context, packageName: String, gameId: String) {
private fun installOrLaunch(
context: Context,
packageName: String,
gameId: String,
gameName: String,
gameType: String,
bit: String
) {
Utils.log(LOG_TAG, "检测是需要安装还是启动 $packageName")
validateVSpaceBeforeAction(context, null) {
validateVSpaceBeforeAction(context, GameEntity().apply {
id = gameId
name = gameName
downloadStatus = "smooth"
category = gameType
setApk(arrayListOf(ApkEntity(packageName = packageName, bit = bit)))
}) {
if (isInstalled(packageName)) {
launch(context, packageName)
} else {
@ -953,6 +1110,26 @@ object VHelper {
}
}
/**
* 获取畅玩助手版本号
*/
fun getVersionName(context: Context): String? = try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager.getPackageInfo(
VirtualAppManager.AIDL_SERVER_PACKAGE_NAME,
PackageManager.PackageInfoFlags.of(0)
)
} else {
context.packageManager.getPackageInfo(
VirtualAppManager.AIDL_SERVER_PACKAGE_NAME,
0
)
}.versionName
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
null
}
/**
* 清除游戏数据
*/
@ -1029,16 +1206,11 @@ object VHelper {
*/
fun getPackageInstalledLiveData(): LiveData<String> = mPackageInstalledLiveData
fun showFloatingWindow(packageName: String) {
val vDownloadList = DownloadManager.getInstance().allVDownloadTaskSnapshots
vDownloadList.firstOrNull {
it.packageName == packageName
}?.let {
val topActivity = AppManager.getInstance().currentActivity() ?: return
if (topActivity.isFinishing || topActivity is VSpaceLoadingActivity) return
fun showFloatingWindow(downloadEntity: DownloadEntity) {
val topActivity = AppManager.getInstance().currentActivity() ?: return
if (topActivity.isFinishing || topActivity is VSpaceLoadingActivity) return
VLoadCompleteWindowHelper.showFloatingWindow(topActivity, toGameEntity(it))
}
VLoadCompleteWindowHelper.showFloatingWindow(topActivity, toGameEntity(downloadEntity))
}
/**
@ -1046,7 +1218,13 @@ object VHelper {
* 如果已安装或配置为空返回 true
* 未安装的情况下会弹弹窗
*/
fun showDialogIfVSpaceIsNeeded(context: Context, gameId: String, gameName: String): Boolean {
fun showDialogIfVSpaceIsNeeded(
context: Context,
gameId: String,
gameName: String,
gameType: String,
bit: String
): Boolean {
Utils.log(LOG_TAG, "检测是否已安装畅玩空间")
val vaConfig = Config.getVSettingEntity()?.va
@ -1057,34 +1235,50 @@ object VHelper {
return true
}
// TODO 检测 32 位
if (!PackageUtils.isInstalledFromAllPackage(context, vaConfig.arch64?.packageName)) {
VSpaceDialogFragment.showDownloadDialog(
context,
getVSpaceDownloadEntity(false),
gameId = gameId,
gameName = gameName
)
Utils.log(LOG_TAG, "显示下载畅玩空间弹窗")
return true
val is64VSpaceInstalled = PackageUtils.isInstalledFromAllPackage(context, vaConfig.arch64?.packageName)
val is32VSpaceInstalled = PackageUtils.isInstalledFromAllPackage(context, vaConfig.arch32?.packageName)
when {
!is64VSpaceInstalled -> {
VSpaceDialogFragment.showDownloadDialog(
context,
appEntity64 = getVSpaceDownloadEntity(true),
appEntity32 = getVSpaceDownloadEntity(false),
autoDownload = true,
gameId = gameId,
gameName = gameName,
gameType = gameType,
bit = bit
)
Utils.log(LOG_TAG, "显示下载64位畅玩空间弹窗")
return true
}
bit == "32" && !is32VSpaceInstalled -> {
VSpace32DialogFragment.showDownloadDialog(
context,
getVSpaceDownloadEntity(false),
autoDownload = true,
gameId = gameId,
gameName = gameName
)
Utils.log(LOG_TAG, "显示下载32位畅玩空间弹窗")
return true
}
}
Utils.log(LOG_TAG, "畅玩空间已安装")
return false
}
private fun getVSpaceDownloadEntity(forUpdate: Boolean): AppEntity {
return if (!forUpdate) {
val appEntity = AppEntity()
val vaConfig64 = Config.getVSettingEntity()?.va?.arch64
appEntity.versionCode = vaConfig64?.versionCode ?: 0
appEntity.version = vaConfig64?.versionName
appEntity.url = vaConfig64?.url
private fun getVSpaceDownloadEntity(is64Bit: Boolean): AppEntity {
val appEntity = AppEntity()
val vaConfig =
if (is64Bit) Config.getVSettingEntity()?.va?.arch64 else Config.getVSettingEntity()?.va?.arch32
appEntity.versionCode = vaConfig?.versionCode ?: 0
appEntity.version = vaConfig?.versionName
appEntity.url = vaConfig?.url
appEntity
} else {
mUpdateEntity!!
}
return appEntity
}
/**
@ -1173,7 +1367,8 @@ object VHelper {
SensorsBridge.trackEvent(
"HaloFunGameDownloadClick",
"game_name", originDownloadEntity.name,
"game_id", originDownloadEntity.gameId
"game_id", originDownloadEntity.gameId,
"game_schema_type", if (originDownloadEntity.getMetaExtra(KEY_BIT) == "32") "32位" else "64位"
)
}
@ -1222,7 +1417,7 @@ object VHelper {
* 获取畅玩空间更新
*/
@SuppressLint("CheckResult")
private fun getVSpaceUpdate(config: VSetting.VaArch) {
private fun getVSpaceUpdate(config: VSetting.VaArch, is64Bit: Boolean) {
val installedVersionName = PackageUtils.getVersionNameByPackageName(config.packageName)
val installedVersionCode = PackageUtils.getVersionCodeByPackageName(config.packageName)
@ -1232,7 +1427,7 @@ object VHelper {
.subscribeOn(Schedulers.io())
.subscribe(object : BiResponse<AppEntity>() {
override fun onSuccess(data: AppEntity) {
mUpdateEntity = data
if (is64Bit) m64UpdateEntity = data else m32UpdateEntity = data
}
})
}
@ -1256,7 +1451,8 @@ object VHelper {
*/
private fun shouldShowVSpaceUpdate(
updateEntity: AppEntity?,
installedSpaceVersionCode: Int
installedSpaceVersionCode: Int,
isRelatedUpdate: Boolean = false
): Boolean {
if (updateEntity == null) return false
@ -1264,10 +1460,14 @@ object VHelper {
if (!hasNewerVersion) return false
// 关联更新忽略检查上次提示更新
if (isRelatedUpdate) return true
if (updateEntity.isAlertEveryTime()) return true
val lastAlertUpdateUrl = updateEntity.url + updateEntity.alert
if (updateEntity.isAlertOnceADay()
&& SPUtils.getString(KEY_LAST_ALERT_UPDATE_URL) != (mUpdateEntity!!.url + mUpdateEntity!!.alert)
&& SPUtils.getString(if (updateEntity.category == "32-bit") KEY_LAST_ALERT_32_UPDATE_URL else KEY_LAST_ALERT_64_UPDATE_URL) != lastAlertUpdateUrl
) {
return true
}
@ -1323,10 +1523,25 @@ object VHelper {
/**
* 畅玩功能是否启用
* 设备系统版本大于 7.1 且后台接口开启时才启用畅玩功能
*/
@JvmStatic
fun isVGameOn() = BuildConfig.DEBUG
|| (BuildConfig.IS_VGAME_ON && Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Config.isVGameEnabled())
|| (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && isVGameOnByConfigSettings())
/**
* 后台接口是否启用畅玩功能(若获取不到开关情况时,判断用户是否曾用过畅玩功能)
*/
private fun isVGameOnByConfigSettings(): Boolean {
val configSettings = Config.getSettings()
// 当 settings 为空时,若曾经用过畅玩,也允许用户继续使用畅玩
return if (configSettings == null) {
isVIsUsed()
} else {
"on" == configSettings.gameSmooth
}
}
/**
* 检查畅玩游戏是否满足生成存档要求

View File

@ -0,0 +1,330 @@
package com.gh.vspace
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.gh.common.exposure.ExposureUtils
import com.gh.common.exposure.ExposureUtils.DownloadType.DOWNLOAD
import com.gh.common.exposure.ExposureUtils.DownloadType.UPDATE
import com.gh.common.util.DataCollectionUtils
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.PackageInstaller
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseDraggableDialogFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.DialogVspace32Binding
import com.gh.gamecenter.entity.AppEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.halo.assistant.HaloApp
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadConfig
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus.*
import com.lightgame.utils.AppManager
import java.io.File
class VSpace32DialogFragment : BaseDraggableDialogFragment() {
private var mAppEntity: AppEntity? = null
private var mGameId: String = ""
private var mGameName: String = ""
private val mDownloadUrl by lazy { mAppEntity?.url ?: "" }
private val mBinding by lazy { DialogVspace32Binding.inflate(layoutInflater) }
private var mIsLogInstallShow = false
private var mIsLogAutoInstallClick = false
private val mDataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
if (downloadEntity.url == mDownloadUrl && isAdded) {
updateDownloadButton(downloadEntity)
}
}
override fun onDataInit(downloadEntity: DownloadEntity) {
onDataChanged(downloadEntity)
}
}
override fun getRootView(): View = mBinding.root
override fun getDragCloseView(): View = mBinding.dragClose
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mAppEntity = arguments?.get(KEY_APP_ENTITY_32) as AppEntity
mGameId = arguments?.getString(EntranceConsts.KEY_GAME_ID) ?: ""
mGameName = arguments?.getString(EntranceConsts.KEY_GAME_NAME) ?: ""
}
@SuppressLint("ClickableViewAccessibility")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return mBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mViewModel = viewModelProvider<VSpaceDialogFragment.VSpaceDialogViewModel>()
mViewModel.packageLiveData.observe(this) {
if (it.packageName == VHelper.VSPACE_32BIT_PACKAGENAME) {
dismissAllowingStateLoss()
}
}
val downloadSnapshot = DownloadManager.getInstance()
.getDownloadEntitySnapshotByPackageName(VHelper.VSPACE_32BIT_PACKAGENAME)
mBinding.downloadBtn.text = if (downloadSnapshot?.status == done) "安装" else "下载"
mBinding.downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
mBinding.descTv.text = "$mGameName》需安装完整的畅玩服务组件,安装后即可给您带来急速的畅玩体验~"
mBinding.privacyPolicyTv.setOnClickListener {
NewFlatLogUtils.logHaloFunEvent("halo_fun_download_dialog_privacy_click")
DirectUtils.directToWebView(requireContext(), Constants.SMOOTH_GAME_PRIVACY_POLICY_ADDRESS)
}
if (downloadSnapshot?.status == done) {
NewFlatLogUtils.logHaloFunEvent("halo_fun_32_install_tip_dialog_show")
SensorsBridge.trackEvent("HaloFunExpandInstallDialogShow")
mIsLogInstallShow = true
} else {
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_download_tip_dialog_show", mGameId, mGameName)
SensorsBridge.trackEvent("HaloFunExpandDownloadDialogShow", "game_id", mGameId, "game_name", mGameName)
}
mBinding.downloadBtn.setOnClickListener {
if (downloadSnapshot?.status == done) {
if (!File(downloadSnapshot.path).exists()) {
ToastUtils.toast("畅玩组件已损坏,即将重新下载")
DownloadManager.getInstance().cancel(downloadSnapshot.url)
DownloadManager.getInstance().add(downloadSnapshot)
} else {
PackageInstaller.install(requireContext(), downloadSnapshot)
NewFlatLogUtils.logHaloFunEvent("halo_fun_32_install_tip_dialog_click")
SensorsBridge.trackEvent("HaloFunExpandInstallButtonClick")
}
} else {
val downloadEntity = DownloadEntity()
val name = "畅玩助手V" + mAppEntity?.version + "(32位)"
val downloadId = PackageInstaller.createDownloadId(name)
downloadEntity.url = mDownloadUrl
downloadEntity.name = name
downloadEntity.platform = "官方版"
downloadEntity.gameId = "62bd412bbbf04747cd3de539"
downloadEntity.path = PackageInstaller.getDownloadPathWithId(downloadId, "apk")
downloadEntity.packageName = VHelper.VSPACE_32BIT_PACKAGENAME
downloadEntity.addMetaExtra(DownloadConfig.KEY_PROGRESS_CALLBACK_INTERVAL, "200")
val gameEntity = GameEntity(downloadEntity.gameId, downloadEntity.name)
gameEntity.gameVersion = mAppEntity?.version ?: ""
// 确定下载类型
val downloadType =
if (arguments?.getBoolean(VSpaceDialogFragment.KEY_IS_UPDATE) == true) UPDATE else DOWNLOAD
downloadEntity.exposureTrace = ExposureUtils.logADownloadExposureEvent(
gameEntity,
downloadEntity.platform,
null,
downloadType
).toJson()
AppExecutor.uiExecutor.executeWithDelay({
DownloadManager.getInstance().cancel(mDownloadUrl)
DownloadManager.getInstance().add(downloadEntity)
}, 200)
DataCollectionUtils.uploadDownload(HaloApp.getInstance(), downloadEntity, "开始")
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_download_tip_dialog_click", mGameId, mGameName)
SensorsBridge.trackEvent("HaloFunExpandDownloadDialogDownloadClick")
}
}
}
override fun onStart() {
super.onStart()
DownloadManager.getInstance().addObserver(mDataWatcher)
// 上面监听安装包名变化的 LiveData 监听有可能被冲掉了
// 手动再检查一下安装状态,避免出现已安装但是没有 dismiss 弹窗的问题
if (PackageUtils.isInstalled(requireContext(), VHelper.VSPACE_32BIT_PACKAGENAME)) {
dismissAllowingStateLoss()
}
}
override fun onStop() {
super.onStop()
DownloadManager.getInstance().removeObserver(mDataWatcher)
}
private fun updateDownloadButton(downloadEntity: DownloadEntity) {
val downloadBtn = mBinding.downloadBtn
downloadBtn.progress = (downloadEntity.progress * 10).toInt()
when (downloadEntity.status) {
downloading -> {
downloadBtn.setText(R.string.pause)
downloadBtn.progress = (downloadEntity.percent * 10).toInt()
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().pause(mDownloadUrl)
}
}
pause -> {
downloadBtn.setText(R.string.resume)
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, true)
}
}
overflow,
timeout,
neterror,
diskisfull,
waiting,
subscribe -> {
downloadBtn.setText(R.string.waiting)
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, false)
}
}
done -> {
downloadBtn.setText(R.string.install)
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.INSTALL_NORMAL
if (!mIsLogInstallShow) {
NewFlatLogUtils.logHaloFunEvent("halo_fun_32_install_tip_dialog_show")
SensorsBridge.trackEvent("HaloFunExpandInstallDialogShow")
mIsLogInstallShow = true
}
val isVSpace32DownloadOnly =
downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) == Constants.VSPACE_32_DOWNLOAD_ONLY
val isAutoInstall = SPUtils.getBoolean(Constants.SP_AUTO_INSTALL, true)
if (!isVSpace32DownloadOnly && isAutoInstall && !mIsLogAutoInstallClick) {
NewFlatLogUtils.logHaloFunEvent("halo_fun_32_install_tip_dialog_click")
SensorsBridge.trackEvent("HaloFunExpandInstallButtonClick")
mIsLogAutoInstallClick = true
}
downloadBtn.setOnClickListener {
if (!File(downloadEntity.path).exists()) {
ToastUtils.toast("畅玩组件已损坏,即将重新下载")
DownloadManager.getInstance().cancel(downloadEntity.url)
DownloadManager.getInstance().add(downloadEntity)
} else {
PackageInstaller.install(requireContext(), downloadEntity)
NewFlatLogUtils.logHaloFunEvent("halo_fun_32_install_tip_dialog_click")
SensorsBridge.trackEvent("HaloFunExpandInstallButtonClick")
}
}
}
cancel,
hijack,
notfound,
uncertificated,
unqualified -> {
downloadBtn.text = "下载"
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, true)
}
}
else -> {
// do nothing
}
}
}
companion object {
const val KEY_APP_ENTITY_32 = "app_entity_32"
const val KEY_AUTO_DOWNLOAD = "auto_download"
const val KEY_IS_UPDATE = "is_update"
@JvmStatic
fun showDownloadDialog(
context: Context?,
appEntity32: AppEntity,
autoDownload: Boolean = false,
isUpdate: Boolean = false,
gameId: String = "",
gameName: String = ""
) {
val fragmentActivity: FragmentActivity = if (context is FragmentActivity) {
context
} else {
val currentActivity = AppManager.getInstance().currentActivity()
if (currentActivity is FragmentActivity) {
currentActivity
} else {
throw IllegalStateException("current activity context must be FragmentActivity")
}
}
// 防止重复弹出
if (hasDialogDisplayedInCurrentActivity(fragmentActivity)) return
val downloadDialog = VSpace32DialogFragment().apply {
arguments = Bundle().apply {
putParcelable(KEY_APP_ENTITY_32, appEntity32)
putString(EntranceConsts.KEY_GAME_ID, gameId)
putString(EntranceConsts.KEY_GAME_NAME, gameName)
putBoolean(KEY_AUTO_DOWNLOAD, autoDownload)
putBoolean(KEY_IS_UPDATE, isUpdate)
}
}
downloadDialog.show(
fragmentActivity.supportFragmentManager,
VSpace32DialogFragment::class.java.name
)
}
@JvmStatic
fun showDownloadDialog(
context: Context?,
appEntity32: AppEntity,
gameEntity: GameEntity?,
autoDownload: Boolean = false,
isUpdate: Boolean = false
) {
showDownloadDialog(
context,
appEntity32,
autoDownload,
isUpdate,
gameEntity?.id ?: "",
gameEntity?.name ?: ""
)
}
private fun hasDialogDisplayedInCurrentActivity(fragmentActivity: FragmentActivity): Boolean {
val fragments: List<Fragment> = fragmentActivity.supportFragmentManager.fragments
fragments.forEach { fragment ->
if (fragment is VSpace32DialogFragment) return true
}
return false
}
}
}

View File

@ -13,12 +13,8 @@ import androidx.lifecycle.ViewModel
import com.gh.common.exposure.ExposureUtils
import com.gh.common.exposure.ExposureUtils.DownloadType.DOWNLOAD
import com.gh.common.exposure.ExposureUtils.DownloadType.UPDATE
import com.gh.common.util.DataCollectionUtils
import com.gh.common.util.DirectUtils
import com.gh.common.util.*
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.PackageInstaller
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.download.DownloadManager
import com.gh.download.PackageObserver
import com.gh.gamecenter.R
@ -28,27 +24,37 @@ import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.DialogVspaceBinding
import com.gh.gamecenter.entity.AppEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.view.DownloadButton
import com.halo.assistant.HaloApp
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadConfig
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus.*
import com.lightgame.utils.AppManager
import java.io.File
class VSpaceDialogFragment : BaseDraggableDialogFragment() {
private var mAppEntity: AppEntity? = null
private var mAppEntity64: AppEntity? = null
private var mAppEntity32: AppEntity? = null
private var mGameId: String = ""
private var mGameName: String = ""
private val mDownloadUrl by lazy { mAppEntity?.url ?: "" }
private var mBit: String = "" // 畅玩游戏位数
private var mIsUpdate = false
private val mDownloadUrl64 by lazy { mAppEntity64?.url ?: "" }
private val mDownloadUrl32 by lazy { mAppEntity32?.url ?: "" }
private val mBinding by lazy { DialogVspaceBinding.inflate(layoutInflater) }
private var mIsLogInstallShow = false
private var mIsLogAutoInstallClick = false
private val mDataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
if (downloadEntity.url == mDownloadUrl && isAdded) {
if (((mAppEntity64 != null && downloadEntity.url == mDownloadUrl64) || (mAppEntity64 == null && downloadEntity.url == mDownloadUrl32)) && isAdded) {
updateDownloadButton(downloadEntity)
}
}
@ -64,9 +70,12 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mAppEntity = arguments?.get(KEY_ENTITY) as AppEntity
mAppEntity64 = arguments?.get(KEY_APP_ENTITY_64) as? AppEntity
mAppEntity32 = arguments?.get(KEY_APP_ENTITY_32) as? AppEntity
mGameId = arguments?.getString(EntranceConsts.KEY_GAME_ID) ?: ""
mGameName = arguments?.getString(EntranceConsts.KEY_GAME_NAME) ?: ""
mBit = arguments?.getString(VHelper.KEY_BIT) ?: ""
mIsUpdate = arguments?.getBoolean(KEY_IS_UPDATE) == true
}
@SuppressLint("ClickableViewAccessibility")
@ -84,59 +93,95 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() {
val mViewModel = viewModelProvider<VSpaceDialogViewModel>()
mViewModel.packageLiveData.observe(this) {
if (it.packageName == VHelper.DEFAULT_VSPACE_PACKAGENAME) {
if (mIsUpdate) showVSpace32UpdateDialogIfNeeded() else showVSpace32DownloadDialogIfNeeded()
dismissAllowingStateLoss()
}
if (it.packageName == VHelper.VSPACE_32BIT_PACKAGENAME) {
dismissAllowingStateLoss()
}
}
mBinding.downloadBtn.text = "下载畅玩助手服务组件"
mBinding.downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
mBinding.descTv.text = "检测到您未安装畅玩64位组件\n点击下载即可体验急速畅玩~"
mBinding.descTv.text = "$mGameName》需先安装畅玩服务组件,安装后即可进入游戏体验新鲜功能~"
mBinding.privacyPolicyTv.setOnClickListener {
NewFlatLogUtils.logHaloFunEvent("halo_fun_download_dialog_privacy_click")
DirectUtils.directToWebView(requireContext(), Constants.SMOOTH_GAME_PRIVACY_POLICY_ADDRESS)
}
val downloadSnapshot = DownloadManager.getInstance()
.getDownloadEntitySnapshotByPackageName(VHelper.DEFAULT_VSPACE_PACKAGENAME)
.getDownloadEntitySnapshotByPackageName(if (mAppEntity64 != null) VHelper.DEFAULT_VSPACE_PACKAGENAME else VHelper.VSPACE_32BIT_PACKAGENAME)
mBinding.downloadBtn.setOnClickListener {
NewFlatLogUtils.logHaloFunEvent("halo_fun_download_dialog_download_click")
SensorsBridge.trackEvent("HaloFunDownloadDialogDownloadClick", "game_id", mGameId, "game_name", mGameName)
val name = "畅玩助手V" + mAppEntity?.version
val downloadId = PackageInstaller.createDownloadId(name)
val downloadEntity = DownloadEntity()
downloadEntity.url = mDownloadUrl
downloadEntity.name = name
downloadEntity.platform = "官方版"
downloadEntity.gameId = "62bd412bbbf04747cd3de539"
downloadEntity.path = PackageInstaller.getDownloadPathWithId(downloadId, "apk")
downloadEntity.packageName = VHelper.DEFAULT_VSPACE_PACKAGENAME
downloadEntity.addMetaExtra(DownloadConfig.KEY_PROGRESS_CALLBACK_INTERVAL, "200")
// 确定下载类型
val downloadType =
if (arguments?.getBoolean(KEY_IS_UPDATE) == true) UPDATE else DOWNLOAD
val gameEntity = GameEntity(downloadEntity.gameId, downloadEntity.name)
gameEntity.gameVersion = mAppEntity?.version ?: ""
val downloadType = if (mIsUpdate) UPDATE else DOWNLOAD
var downloadEntity64: DownloadEntity? = null
var downloadEntity32: DownloadEntity? = null
val event = ExposureUtils.logADownloadExposureEvent(
gameEntity,
downloadEntity.platform,
null,
downloadType
)
if (mAppEntity64 != null) {
downloadEntity64 = getDownloadEntity(true)
downloadEntity.exposureTrace = event.toJson()
val gameEntity64 = GameEntity(downloadEntity64.gameId, downloadEntity64.name)
gameEntity64.gameVersion = mAppEntity64?.version ?: ""
downloadEntity64.exposureTrace = ExposureUtils.logADownloadExposureEvent(
gameEntity64,
downloadEntity64.platform,
null,
downloadType
).toJson()
// 收集下载数据
DataCollectionUtils.uploadDownload(HaloApp.getInstance(), downloadEntity64, "开始")
}
val is32VSpaceInstalled = PackageUtils.isInstalledFromAllPackage(context, VHelper.VSPACE_32BIT_PACKAGENAME)
if (mBit == "32" && mAppEntity32 != null && !(is32VSpaceInstalled && !mIsUpdate)) {
downloadEntity32 = getDownloadEntity(false)
// 非32位单独下载时下载完成不弹出安装界面
if (mAppEntity64 != null) {
downloadEntity32.addMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE, Constants.VSPACE_32_DOWNLOAD_ONLY)
}
val gameEntity32 = GameEntity(downloadEntity32.gameId, downloadEntity32.name)
gameEntity32.gameVersion = mAppEntity32?.version ?: ""
downloadEntity32.exposureTrace = ExposureUtils.logADownloadExposureEvent(
gameEntity32,
downloadEntity32.platform,
null,
downloadType
).toJson()
DataCollectionUtils.uploadDownload(HaloApp.getInstance(), downloadEntity32, "开始")
}
AppExecutor.uiExecutor.executeWithDelay({
DownloadManager.getInstance().cancel(mDownloadUrl)
DownloadManager.getInstance().add(downloadEntity)
if (downloadEntity64 != null) {
DownloadManager.getInstance().cancel(mDownloadUrl64)
DownloadManager.getInstance().add(downloadEntity64)
}
if (downloadEntity32 != null) {
DownloadManager.getInstance().cancel(mDownloadUrl32)
DownloadManager.getInstance().add(downloadEntity32)
}
}, 200)
// 收集下载数据
DataCollectionUtils.uploadDownload(HaloApp.getInstance(), downloadEntity, "开始")
val architecture =
if (downloadEntity64 != null && downloadEntity32 != null) "32位&64位" else if (downloadEntity64 != null) "64位" else "32位"
NewFlatLogUtils.logHaloFunDownloadDialogDownloadClick(architecture)
SensorsBridge.trackEvent(
"HaloFunDownloadDialogDownloadClick",
"game_id",
mGameId,
"game_name",
mGameName,
"space_schema_type",
architecture,
"game_schema_type",
if (mBit == "32") "32位" else "64位"
)
}
if (arguments?.getBoolean(KEY_AUTO_DOWNLOAD) == true && downloadSnapshot == null) {
@ -144,6 +189,17 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() {
}
}
private fun getDownloadEntity(is64Bit: Boolean) = DownloadEntity().apply {
url = if (is64Bit) mDownloadUrl64 else mDownloadUrl32
name =
"畅玩助手V" + (if (is64Bit) mAppEntity64?.version else mAppEntity32?.version) + "(${if (is64Bit) 64 else 32}位)"
platform = "官方版"
gameId = "62bd412bbbf04747cd3de539"
path = PackageInstaller.getDownloadPathWithId(PackageInstaller.createDownloadId(name), "apk")
packageName = if (is64Bit) VHelper.DEFAULT_VSPACE_PACKAGENAME else VHelper.VSPACE_32BIT_PACKAGENAME
addMetaExtra(DownloadConfig.KEY_PROGRESS_CALLBACK_INTERVAL, "200")
}
override fun onStart() {
super.onStart()
DownloadManager.getInstance().addObserver(mDataWatcher)
@ -151,8 +207,36 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() {
// 上面监听安装包名变化的 LiveData 监听有可能被冲掉了
// 手动再检查一下安装状态,避免出现已安装但是没有 dismiss 弹窗的问题
if (PackageUtils.isInstalled(requireContext(), VHelper.DEFAULT_VSPACE_PACKAGENAME)) {
if (mIsUpdate) showVSpace32UpdateDialogIfNeeded() else showVSpace32DownloadDialogIfNeeded()
dismissAllowingStateLoss()
}
if (PackageUtils.isInstalled(requireContext(), VHelper.VSPACE_32BIT_PACKAGENAME)) {
dismissAllowingStateLoss()
}
}
private fun showVSpace32DownloadDialogIfNeeded() {
val is32VSpaceInstalled =
PackageUtils.isInstalledFromAllPackage(requireContext(), VHelper.VSPACE_32BIT_PACKAGENAME)
if (!is32VSpaceInstalled && mBit == "32" && mAppEntity32 != null && !mAppEntity32?.url.isNullOrEmpty()) {
VSpace32DialogFragment.showDownloadDialog(
requireContext(),
mAppEntity32!!,
gameId = mGameId,
gameName = mGameName
)
}
}
private fun showVSpace32UpdateDialogIfNeeded() {
if (mBit == "32" && mAppEntity32 != null && !mAppEntity32?.url.isNullOrEmpty()) {
VSpaceUpdate32DialogFragment.showDownloadDialog(
requireContext(),
mAppEntity32!!,
gameId = mGameId,
gameName = mGameName
)
}
}
override fun onStop() {
@ -169,7 +253,7 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() {
downloadBtn.progress = (downloadEntity.percent * 10).toInt()
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().pause(mDownloadUrl)
DownloadManager.getInstance().pause(downloadEntity.url)
}
}
@ -197,9 +281,27 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() {
downloadBtn.setText(R.string.install)
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.INSTALL_NORMAL
val vSpaceType = if (downloadEntity.url == mDownloadUrl64) "64位" else "32位"
if (!mIsLogInstallShow) {
NewFlatLogUtils.logHaloFunInstallTipDialogShow(vSpaceType)
SensorsBridge.trackEvent("HaloFunInstallDialogShow", "space_schema_type", vSpaceType)
mIsLogInstallShow = true
}
if (SPUtils.getBoolean(Constants.SP_AUTO_INSTALL, true) && !mIsLogAutoInstallClick) {
SensorsBridge.trackEvent("HaloFunInstallButtonClick", "space_schema_type", vSpaceType)
mIsLogAutoInstallClick = true
}
downloadBtn.setOnClickListener {
PackageInstaller.install(requireContext(), downloadEntity)
SensorsBridge.trackEvent("HaloFunInstallButtonClick")
if (!File(downloadEntity.path).exists()) {
ToastUtils.toast("畅玩组件已损坏,即将重新下载")
DownloadManager.getInstance().cancel(downloadEntity.url)
DownloadManager.getInstance().add(downloadEntity)
} else {
PackageInstaller.install(requireContext(), downloadEntity)
SensorsBridge.trackEvent("HaloFunInstallButtonClick", "space_schema_type", vSpaceType)
}
}
}
@ -221,18 +323,22 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() {
}
companion object {
const val KEY_ENTITY = "entity"
const val KEY_APP_ENTITY_64 = "app_entity_64"
const val KEY_APP_ENTITY_32 = "app_entity_32"
const val KEY_AUTO_DOWNLOAD = "auto_download"
const val KEY_IS_UPDATE = "is_update"
@JvmStatic
fun showDownloadDialog(
context: Context?,
appEntity: AppEntity,
appEntity64: AppEntity?,
appEntity32: AppEntity?,
autoDownload: Boolean = false,
isUpdate: Boolean = false,
gameId: String = "",
gameName: String = ""
gameName: String = "",
gameType: String = "",
bit: String = ""
) {
val fragmentActivity: FragmentActivity = if (context is FragmentActivity) {
context
@ -248,11 +354,16 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() {
// 防止重复弹出
if (hasDialogDisplayedInCurrentActivity(fragmentActivity)) return
NewFlatLogUtils.logHaloFunEvent("halo_fun_download_dialog_show")
NewFlatLogUtils.logHaloFunDownloadDialogShow(gameId, gameName, if (bit == "32") "32位" else "64位")
if (context is BaseActivity) {
SensorsBridge.trackEvent(
"HaloFunDownloadDialogShow",
"game_id", gameId,
"game_name", gameName,
"game_type", gameType,
"game_schema_type", if (bit == "32") "32位" else "64位",
"last_page_name", context::class.java.simpleName,
"last_page_name", context::class.java.simpleName,
"last_page_id", context.getUniqueId(),
"last_page_business_id", context.getBusinessId().first,
@ -262,9 +373,11 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() {
val downloadDialog = VSpaceDialogFragment().apply {
arguments = Bundle().apply {
putParcelable(KEY_ENTITY, appEntity)
if (appEntity64 != null) putParcelable(KEY_APP_ENTITY_64, appEntity64)
if (appEntity32 != null) putParcelable(KEY_APP_ENTITY_32, appEntity32)
putString(EntranceConsts.KEY_GAME_ID, gameId)
putString(EntranceConsts.KEY_GAME_NAME, gameName)
putString(VHelper.KEY_BIT, bit)
putBoolean(KEY_AUTO_DOWNLOAD, autoDownload)
putBoolean(KEY_IS_UPDATE, isUpdate)
}
@ -278,12 +391,23 @@ class VSpaceDialogFragment : BaseDraggableDialogFragment() {
@JvmStatic
fun showDownloadDialog(
context: Context?,
appEntity: AppEntity,
appEntity64: AppEntity?,
appEntity32: AppEntity?,
gameEntity: GameEntity?,
autoDownload: Boolean = false,
isUpdate: Boolean = false
) {
showDownloadDialog(context, appEntity, autoDownload, isUpdate, gameEntity?.id ?: "", gameEntity?.name ?: "")
showDownloadDialog(
context,
appEntity64,
appEntity32,
autoDownload,
isUpdate,
gameEntity?.id ?: "",
gameEntity?.name ?: "",
gameEntity?.categoryChinese ?: "",
gameEntity?.gameBit ?: ""
)
}
private fun hasDialogDisplayedInCurrentActivity(fragmentActivity: FragmentActivity): Boolean {

View File

@ -0,0 +1,300 @@
package com.gh.vspace
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.gh.common.exposure.ExposureUtils
import com.gh.common.exposure.ExposureUtils.DownloadType.DOWNLOAD
import com.gh.common.exposure.ExposureUtils.DownloadType.UPDATE
import com.gh.common.util.DataCollectionUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.PackageInstaller
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.download.DownloadManager
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.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.DialogVspaceUpdate32Binding
import com.gh.gamecenter.entity.AppEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.halo.assistant.HaloApp
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadConfig
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus.*
import com.lightgame.utils.AppManager
import java.io.File
class VSpaceUpdate32DialogFragment : BaseDialogFragment() {
private var mAppEntity: AppEntity? = null
private var mGameId: String = ""
private var mGameName: String = ""
private val mDownloadUrl by lazy { mAppEntity?.url ?: "" }
private val mBinding by lazy { DialogVspaceUpdate32Binding.inflate(layoutInflater) }
private val mDataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
if (downloadEntity.url == mDownloadUrl && isAdded) {
updateDownloadButton(downloadEntity)
}
}
override fun onDataInit(downloadEntity: DownloadEntity) {
onDataChanged(downloadEntity)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mAppEntity = arguments?.get(KEY_APP_ENTITY_32) as AppEntity
mGameId = arguments?.getString(EntranceConsts.KEY_GAME_ID) ?: ""
mGameName = arguments?.getString(EntranceConsts.KEY_GAME_NAME) ?: ""
}
@SuppressLint("ClickableViewAccessibility")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return mBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mViewModel = viewModelProvider<VSpaceDialogFragment.VSpaceDialogViewModel>()
mViewModel.packageLiveData.observe(this) {
if (it.packageName == VHelper.VSPACE_32BIT_PACKAGENAME) {
dismissAllowingStateLoss()
}
}
val downloadSnapshot = DownloadManager.getInstance()
.getDownloadEntitySnapshotByPackageName(VHelper.VSPACE_32BIT_PACKAGENAME)
mBinding.downloadBtn.text = if (downloadSnapshot?.status == done) "安装" else "下载"
mBinding.downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
mBinding.contentTv.text = "$mGameName》需安装完整的畅玩服务组件,安装后即可给您带来急速的畅玩体验~"
mBinding.downloadBtn.setOnClickListener {
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName)
if (downloadSnapshot?.status == done) {
if (!File(downloadSnapshot.path).exists()) {
ToastUtils.toast("畅玩组件已损坏,即将重新下载")
DownloadManager.getInstance().cancel(downloadSnapshot.url)
DownloadManager.getInstance().add(downloadSnapshot)
} else {
PackageInstaller.install(requireContext(), downloadSnapshot)
}
} else {
val downloadEntity = DownloadEntity()
val name = "畅玩助手V" + mAppEntity?.version + "(32位)"
val downloadId = PackageInstaller.createDownloadId(name)
downloadEntity.url = mDownloadUrl
downloadEntity.name = name
downloadEntity.platform = "官方版"
downloadEntity.gameId = "62bd412bbbf04747cd3de539"
downloadEntity.path = PackageInstaller.getDownloadPathWithId(downloadId, "apk")
downloadEntity.packageName = VHelper.VSPACE_32BIT_PACKAGENAME
downloadEntity.addMetaExtra(DownloadConfig.KEY_PROGRESS_CALLBACK_INTERVAL, "200")
val gameEntity = GameEntity(downloadEntity.gameId, downloadEntity.name)
gameEntity.gameVersion = mAppEntity?.version ?: ""
// 确定下载类型
val downloadType =
if (arguments?.getBoolean(VSpaceDialogFragment.KEY_IS_UPDATE) == true) UPDATE else DOWNLOAD
downloadEntity.exposureTrace = ExposureUtils.logADownloadExposureEvent(
gameEntity,
downloadEntity.platform,
null,
downloadType
).toJson()
AppExecutor.uiExecutor.executeWithDelay({
DownloadManager.getInstance().cancel(mDownloadUrl)
DownloadManager.getInstance().add(downloadEntity)
}, 200)
DataCollectionUtils.uploadDownload(HaloApp.getInstance(), downloadEntity, "开始")
}
}
}
override fun onStart() {
super.onStart()
DownloadManager.getInstance().addObserver(mDataWatcher)
// 上面监听安装包名变化的 LiveData 监听有可能被冲掉了
// 手动再检查一下安装状态,避免出现已安装但是没有 dismiss 弹窗的问题
if (PackageUtils.isInstalled(requireContext(), VHelper.VSPACE_32BIT_PACKAGENAME)) {
dismissAllowingStateLoss()
}
}
override fun onStop() {
super.onStop()
DownloadManager.getInstance().removeObserver(mDataWatcher)
}
private fun updateDownloadButton(downloadEntity: DownloadEntity) {
val downloadBtn = mBinding.downloadBtn
downloadBtn.progress = (downloadEntity.progress * 10).toInt()
when (downloadEntity.status) {
downloading -> {
downloadBtn.setText(R.string.pause)
downloadBtn.progress = (downloadEntity.percent * 10).toInt()
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().pause(mDownloadUrl)
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName)
}
}
pause -> {
downloadBtn.setText(R.string.resume)
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, true)
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName)
}
}
overflow,
timeout,
neterror,
diskisfull,
waiting,
subscribe -> {
downloadBtn.setText(R.string.waiting)
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, false)
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName)
}
}
done -> {
downloadBtn.setText(R.string.install)
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.INSTALL_NORMAL
downloadBtn.setOnClickListener {
if (!File(downloadEntity.path).exists()) {
ToastUtils.toast("畅玩组件已损坏,即将重新下载")
DownloadManager.getInstance().cancel(downloadEntity.url)
DownloadManager.getInstance().add(downloadEntity)
} else {
PackageInstaller.install(requireContext(), downloadEntity)
NewFlatLogUtils.logHaloFun32DialogEvent(
"halo_fun_32_update_tip_dialog_click",
mGameId,
mGameName
)
}
}
}
cancel,
hijack,
notfound,
uncertificated,
unqualified -> {
downloadBtn.text = "下载"
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, true)
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName)
}
}
else -> {
// do nothing
}
}
}
companion object {
const val KEY_APP_ENTITY_32 = "app_entity_32"
const val KEY_AUTO_DOWNLOAD = "auto_download"
const val KEY_IS_UPDATE = "is_update"
@JvmStatic
fun showDownloadDialog(
context: Context?,
appEntity32: AppEntity,
autoDownload: Boolean = false,
isUpdate: Boolean = false,
gameId: String = "",
gameName: String = ""
) {
val fragmentActivity: FragmentActivity = if (context is FragmentActivity) {
context
} else {
val currentActivity = AppManager.getInstance().currentActivity()
if (currentActivity is FragmentActivity) {
currentActivity
} else {
throw IllegalStateException("current activity context must be FragmentActivity")
}
}
// 防止重复弹出
if (hasDialogDisplayedInCurrentActivity(fragmentActivity)) return
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_show", gameId, gameName)
val downloadDialog = VSpaceUpdate32DialogFragment().apply {
arguments = Bundle().apply {
putParcelable(KEY_APP_ENTITY_32, appEntity32)
putString(EntranceConsts.KEY_GAME_ID, gameId)
putString(EntranceConsts.KEY_GAME_NAME, gameName)
putBoolean(KEY_AUTO_DOWNLOAD, autoDownload)
putBoolean(KEY_IS_UPDATE, isUpdate)
}
}
downloadDialog.show(
fragmentActivity.supportFragmentManager,
VSpaceUpdate32DialogFragment::class.java.name
)
}
@JvmStatic
fun showDownloadDialog(
context: Context?,
appEntity32: AppEntity,
gameEntity: GameEntity?,
autoDownload: Boolean = false,
isUpdate: Boolean = false
) {
showDownloadDialog(
context,
appEntity32,
autoDownload,
isUpdate,
gameEntity?.id ?: "",
gameEntity?.name ?: ""
)
}
private fun hasDialogDisplayedInCurrentActivity(fragmentActivity: FragmentActivity): Boolean {
val fragments: List<Fragment> = fragmentActivity.supportFragmentManager.fragments
fragments.forEach { fragment ->
if (fragment is VSpaceUpdate32DialogFragment) return true
}
return false
}
}
}

View File

@ -18,7 +18,7 @@ interface VArchiveDao {
@Query("DELETE FROM VArchiveEntity")
fun clearAllArchives()
@Query("SELECT * FROM VArchiveEntity")
@Query("SELECT * FROM VArchiveEntity ORDER BY time desc")
fun getAllLiveData(): LiveData<List<VArchiveEntity>>
}

View File

@ -34,6 +34,7 @@ class GAppsDownloadDialogFragment : BaseBottomDialogFragment<DialogGappsDownload
private var mIsInstalling = false // 是否正在安装,避免重复触发
private var mRequiredAutoInstall = false // 是否需要自动安装(下载完成后自动安装)
private var mRequiredReTriggerInstall = false // 是否需要重新触发安装(因为切换到后台被中断)
private var mInstallSuccess = false // 安装是否已经成功
private var mInstallProgressAnimator: ValueAnimator? = null
@ -52,22 +53,28 @@ class GAppsDownloadDialogFragment : BaseBottomDialogFragment<DialogGappsDownload
File(mGAppsUnZipDestPath).also { if (!it.exists()) it.mkdir() }
}
private val mBatchInstallListener: (isSuccess: Boolean) -> Unit = { isSuccess ->
val elapsedTime =
(System.currentTimeMillis() - mGAppsDownloadAndInstallStartTimestampMills + mGAppsDownloadAndInstallElapsedTimeMills) / 1000
mGAppsDownloadAndInstallStartTimestampMills = 0L
mGAppsDownloadAndInstallElapsedTimeMills = 0L
if (isSuccess) {
showSuccessStyle()
mInstallSuccess = true
NewFlatLogUtils.logGAppsInstallSuccess(mGameId, mGameName, elapsedTime.toInt())
clearCaches()
private val mBatchInstallListener: (isSuccess: Boolean, isInterrupted: Boolean) -> Unit = { isSuccess, isInterrupted ->
if (isInterrupted) {
// 安装过程被打断
mIsInstalling = false
mRequiredReTriggerInstall = true
} else {
mInstallSuccess = false
NewFlatLogUtils.logGAppsInstallFailed(mGameId, mGameName, elapsedTime.toInt())
resetDownloadStatus(null)
val elapsedTime =
(System.currentTimeMillis() - mGAppsDownloadAndInstallStartTimestampMills + mGAppsDownloadAndInstallElapsedTimeMills) / 1000
mGAppsDownloadAndInstallStartTimestampMills = 0L
mGAppsDownloadAndInstallElapsedTimeMills = 0L
if (isSuccess) {
showSuccessStyle()
mInstallSuccess = true
NewFlatLogUtils.logGAppsInstallSuccess(mGameId, mGameName, elapsedTime.toInt())
clearCaches()
} else {
mInstallSuccess = false
NewFlatLogUtils.logGAppsInstallFailed(mGameId, mGameName, elapsedTime.toInt())
resetDownloadStatus(null)
}
}
}
@ -113,6 +120,16 @@ class GAppsDownloadDialogFragment : BaseBottomDialogFragment<DialogGappsDownload
NewFlatLogUtils.logGAppsDialogShowed(mGameId, mGameName)
}
override fun onResume() {
super.onResume()
// 执行安装重试
if (mRequiredReTriggerInstall) {
batchInstall(mGAppsUnZipDestFile)
mRequiredReTriggerInstall = false
}
}
private fun updateViewByDownload(downloadEntity: SimpleDownloadEntity?) {
if (downloadEntity != null) {
updateViewByDownloadStatus(downloadEntity.status)
@ -291,7 +308,7 @@ class GAppsDownloadDialogFragment : BaseBottomDialogFragment<DialogGappsDownload
runOnUiThread {
mInstallProgressAnimator = ValueAnimator.ofInt(500, 1000)
mInstallProgressAnimator?.duration = 10 * 1000L
mInstallProgressAnimator?.duration = INSTALL_ANIMATION_DURATION
mInstallProgressAnimator?.interpolator = DecelerateInterpolator()
mInstallProgressAnimator?.addUpdateListener {
mBinding.downloadButton.progress = it.animatedValue as Int
@ -333,6 +350,7 @@ class GAppsDownloadDialogFragment : BaseBottomDialogFragment<DialogGappsDownload
private const val TRIGGER_PACKAGE_NAME = "trigger_package_name" // 触发弹窗的原始游戏包名
private const val GAME_ID = "game_id"
private const val GAME_NAME = "game_name"
private const val INSTALL_ANIMATION_DURATION = 30 * 1000L // 安装动画时长
fun getInstance(packageName: String, gameId: String, gameName: String): GAppsDownloadDialogFragment {
return GAppsDownloadDialogFragment().also {

View File

@ -11,6 +11,7 @@ import android.os.Build;
import android.os.StrictMode;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import androidx.collection.ArrayMap;
@ -43,6 +44,7 @@ import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.Injection;
import com.gh.gamecenter.common.constant.Config;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
import com.gh.gamecenter.common.image.EmptyDecoder;
import com.gh.gamecenter.common.tracker.Tracker;
import com.gh.gamecenter.common.utils.DarkModeUtils;
@ -58,6 +60,7 @@ import com.gh.gamecenter.core.utils.SPUtils;
import com.gh.gamecenter.entity.SubjectRecommendEntity;
import com.gh.gamecenter.fragment.MainWrapperRepository;
import com.gh.gamecenter.login.user.UserRepository;
import com.gh.gamecenter.oaid.OAIDHelper;
import com.gh.gamecenter.packagehelper.PackageRepository;
import com.gh.gamecenter.core.provider.IFlavorProvider;
import com.gh.gamecenter.provider.FlavorProviderImp;
@ -200,10 +203,26 @@ public class HaloApp extends MultiDexApplication {
mInstance = this;
// 每个进程都用自己的进程名作为后缀的文件夹来存 WebView cache
// https://sentry.shanqu.cc/organizations/lightgame/issues/285063/?project=22&query=is%3Aunresolved
// https://stackoverflow.com/a/61748345/4812571 这个无效
// WebView.disableWebView() 也无效
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
String process = getProcessName();
WebView.setDataDirectorySuffix(process);
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
for (IApplication application : mApplicationList) {
application.onCreate(mInstance);
}
// 似乎只是 load SO 不涉及方法调用,所以可以在隐私政策前调用吧?
OAIDHelper.INSTANCE.doSystemLoad();
// 70ms
PlayerFactory.setPlayManager(Exo2PlayerManager.class);
CacheFactory.setCacheManager(ExoPlayerCacheManager.class);
@ -273,6 +292,12 @@ public class HaloApp extends MultiDexApplication {
// 获取/更新 GID 和 读 SP 的操作不需要 delay
DataUtils.getGid();
OAIDHelper.INSTANCE.getOAID(this, s -> {
setOAID(s);
MetaUtil.INSTANCE.refreshMeta();
return null;
});
// 获取 settings 配置
ExtensionsKt.doOnMainProcessOnly(this, com.gh.common.constant.Config::getGhzsSettings);

View File

@ -173,6 +173,10 @@ class WebFragment : LazyFragment(), IScrollable {
closeBtn.visibility = View.VISIBLE
closeBtn.setOnClickListener { requireActivity().finish() }
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mBinding?.fullscreenContainer?.elevation = 10F.dip2px().toFloat()
mBinding?.closeBtn?.elevation = 11F.dip2px().toFloat()
}
} else {
initMenu()
}
@ -461,6 +465,19 @@ class WebFragment : LazyFragment(), IScrollable {
return true
}
override fun onShowCustomView(view: View, callback: CustomViewCallback) {
super.onShowCustomView(view, callback)
fullscreenContainer.removeAllViews()
fullscreenContainer.visibility = View.VISIBLE
fullscreenContainer.addView(view)
}
override fun onHideCustomView() {
super.onHideCustomView()
fullscreenContainer.visibility = View.GONE
fullscreenContainer.removeAllViews()
}
override fun onProgressChanged(view: WebView, newProgress: Int) {
webProgressbar.progress = newProgress
if (newProgress == 100) {

View File

@ -35,8 +35,13 @@ import com.gh.gamecenter.adapter.OnCommentCallBackListener;
import com.gh.gamecenter.entity.CommentEntity;
import com.gh.gamecenter.entity.MessageEntity;
import com.gh.gamecenter.common.base.fragment.ToolbarFragment;
import com.gh.gamecenter.eventbus.EBAddComment;
import com.gh.gamecenter.eventbus.EBDeleteComment;
import com.lightgame.utils.Util_System_Keyboard;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.json.JSONException;
import org.json.JSONObject;
@ -122,7 +127,7 @@ public class CommentDetailFragment extends ToolbarFragment implements OnCommentC
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE && !mAdapter.isOver() && !mAdapter.isLoading()) {
mAdapter.loadData();
mAdapter.loadMore();
}
}
});
@ -140,6 +145,11 @@ public class CommentDetailFragment extends ToolbarFragment implements OnCommentC
requireView().post(() -> mKeyboardHeightProvider.start());
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onDeleteComment(EBDeleteComment event) {
mAdapter.notifyCommentRemoved(event.commentEntity);
}
@Override
public void onResume() {
super.onResume();
@ -180,7 +190,7 @@ public class CommentDetailFragment extends ToolbarFragment implements OnCommentC
@Override
public void onCommentCallback(CommentEntity entity) {
mCommentEntity = entity;
setSoftInput(true);
setSoftInput(mCommentEntity != null);
}
@Override
@ -215,6 +225,8 @@ public class CommentDetailFragment extends ToolbarFragment implements OnCommentC
mCommentDetailCommentEt.setText("");
setSoftInput(false);
mAdapter.refresh();
EventBus.getDefault().post(new EBAddComment());
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="@color/theme_font" />
<item android:color="@color/text_subtitle" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

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