Compare commits

...

185 Commits

Author SHA1 Message Date
b3ee742faf chore: 版本更新至 5.41.1 2025-05-09 18:38:25 +08:00
ead61c1916 Merge branch 'hotfix/v5.41.0-1070/crash' into 'release'
hotfix: 修复搜索页下载闪退问题

See merge request halo/android/assistant-android!2183
2025-05-09 18:37:42 +08:00
9bb20ca41f hotfix: 修复搜索页下载闪退问题 2025-05-09 18:36:59 +08:00
74942be890 Merge branch 'fix/issues-445867' into 'release'
fix:https://sentry.shanqu.cc/organizations/lightgame/issues/445867/?project=22...

See merge request halo/android/assistant-android!2182
2025-05-09 11:16:32 +08:00
391eb64df1 fix:https://sentry.shanqu.cc/organizations/lightgame/issues/445867/?project=22... 2025-05-09 11:16:32 +08:00
03ba6f5614 Merge branch 'fix/GHZSCY-7961-pr' into 'release'
fix:【光环助手】V5.41.0版本出包-05/08测试-客户端 https://jira.shanqu.cc/browse/GHZSCY-7961

See merge request halo/android/assistant-android!2181
2025-05-09 10:37:10 +08:00
1552a3e95d fix:【光环助手】V5.41.0版本出包-05/08测试-客户端 https://jira.shanqu.cc/browse/GHZSCY-7961 2025-05-09 10:34:57 +08:00
9b7526773c Merge branch 'fix/game_detail_video' into 'release'
fix: 修复游戏详情页视频主动播放时退出页面没有停止播放的问题

See merge request halo/android/assistant-android!2179
2025-05-08 18:20:51 +08:00
764402b701 fix: 修复游戏详情页视频主动播放时退出页面没有停止播放的问题 2025-05-08 18:16:30 +08:00
dc7ebbc308 Merge branch 'fix/GHZSCY-7951-mr' into 'release'
fix: 评论区版本号显示优化-客户端 https://jira.shanqu.cc/browse/GHZSCY-7951

See merge request halo/android/assistant-android!2178
2025-05-08 16:49:09 +08:00
065ebe32ed fix: 评论区版本号显示优化-客户端 https://jira.shanqu.cc/browse/GHZSCY-7951 2025-05-08 16:49:09 +08:00
c284fcd531 Merge branch 'fix/game_detail_video' into 'release'
fix: 修复游戏详情页视频主动播放时退出页面没有停止播放的问题

See merge request halo/android/assistant-android!2177
2025-05-08 16:37:29 +08:00
101aa11855 fix: 修复游戏详情页视频主动播放时退出页面没有停止播放的问题 2025-05-08 16:26:55 +08:00
21a4ff5560 Merge branch 'revert-GHZSCY-7496' into 'dev'
Revert "feat: 专题合集新增自定义设置—客户端 https://jira.shanqu.cc/browse/GHZSCY-7496"

See merge request halo/android/assistant-android!2174
2025-05-08 10:40:50 +08:00
034b7f07ee Revert "feat: 专题合集新增自定义设置—客户端 https://jira.shanqu.cc/browse/GHZSCY-7496"
This reverts commit d874b91b
2025-05-08 10:32:37 +08:00
d3c9cb7776 Merge branch 'revert-26e272ed' into 'dev'
Revert "Merge branch 'feat/GHZSCY-7512-pr' into 'dev'"

See merge request halo/android/assistant-android!2173
2025-05-08 09:21:42 +08:00
8593f5cd0d Revert "Merge branch 'feat/GHZSCY-7512-pr' into 'dev'"
This reverts merge request !2172
2025-05-08 09:21:23 +08:00
26e272edd0 Merge branch 'feat/GHZSCY-7512-pr' into 'dev'
feat:分类新增搜索功能—客户端 https://jira.shanqu.cc/browse/GHZSCY-7512

See merge request halo/android/assistant-android!2172
2025-05-07 15:16:09 +08:00
cc0683c3c7 feat:分类新增搜索功能—客户端 https://jira.shanqu.cc/browse/GHZSCY-7512 2025-05-07 15:09:35 +08:00
7d9f36d587 Merge branch 'feat/GHZSCY-7848-pr' into 'dev'
feat:奇游加速器第三期迭代—客户端 https://jira.shanqu.cc/browse/GHZSCY-7864

See merge request halo/android/assistant-android!2168
2025-05-07 10:33:50 +08:00
eac6bc2c7f feat:奇游加速器第三期迭代—客户端 https://jira.shanqu.cc/browse/GHZSCY-7864 2025-05-07 10:33:50 +08:00
1fd0d7f215 Merge branch 'feat/GHZSCY-7889' into 'dev'
feat:神策埋点优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-7889

See merge request halo/android/assistant-android!2169
2025-05-07 09:30:12 +08:00
cd5748fa1c feat:神策埋点优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-7889 2025-05-07 09:30:12 +08:00
3cc4a2b9bc Merge branch 'fix/GHZSCY-7907' into 'dev'
feat:【光环助手】自定义页面-游戏组件显示问题 https://jira.shanqu.cc/browse/GHZSCY-7907

See merge request halo/android/assistant-android!2170
2025-05-07 09:29:46 +08:00
55056b772e feat:【光环助手】自定义页面-游戏组件显示问题 https://jira.shanqu.cc/browse/GHZSCY-7907 2025-05-07 09:29:46 +08:00
a11acbb8c4 Merge branch 'fix/dsp_install_error' into 'dev'
feat: 广告系统-APP启动埋点事件优化 https://jira.shanqu.cc/browse/GHZSCY-7937

See merge request halo/android/assistant-android!2166
2025-04-30 15:24:51 +08:00
24d59aaa85 fix: 广告系统-APP启动埋点事件优化 https://jira.shanqu.cc/browse/GHZSCY-7937 2025-04-30 14:46:03 +08:00
f57a95b82c feat: 广告系统-APP启动埋点事件优化 https://jira.shanqu.cc/browse/GHZSCY-7937 2025-04-30 13:55:09 +08:00
3a78b307d3 Merge branch 'feat/GHZSCY-7820-continued' into 'dev'
feat:新增dsp广告合作模式—客户端 https://jira.shanqu.cc/browse/GHZSCY-7820

See merge request halo/android/assistant-android!2157
2025-04-30 09:59:24 +08:00
c4dccb7aa7 feat:新增dsp广告合作模式—客户端 https://jira.shanqu.cc/browse/GHZSCY-7820 2025-04-30 09:59:24 +08:00
7612c6afd7 Merge branch 'feat/GHZSCY-7927' into 'dev'
feat: GameDetailModuleClick埋点上报优化-客户端 https://jira.shanqu.cc/browse/GHZSCY-7927

See merge request halo/android/assistant-android!2165
2025-04-28 15:27:29 +08:00
2c781bcbf1 feat: GameDetailModuleClick埋点上报优化-客户端 https://jira.shanqu.cc/browse/GHZSCY-7927 2025-04-28 15:27:29 +08:00
7ab09e42ca Merge branch 'feat/GHZSCY-7869' into 'dev'
feat: 游戏详情组件UI调整—客户端 https://jira.shanqu.cc/browse/GHZSCY-7869

See merge request halo/android/assistant-android!2164
2025-04-27 17:24:27 +08:00
292c96586a feat: 游戏详情组件UI调整—客户端 https://jira.shanqu.cc/browse/GHZSCY-7869 2025-04-27 17:24:27 +08:00
7681b63e27 Merge branch 'fix/GHZSCY-7922' into 'dev'
fix:【光环助手】视频播放问题 https://jira.shanqu.cc/browse/GHZSCY-7922

See merge request halo/android/assistant-android!2163
2025-04-27 15:18:28 +08:00
95966be1a1 fix:【光环助手】视频播放问题 https://jira.shanqu.cc/browse/GHZSCY-7922 2025-04-27 15:18:28 +08:00
9eab3361a0 Merge branch 'fix/iothread-livedata' into 'dev'
fix: 在 io 线程设置 livedata value 导致的问题

See merge request halo/android/assistant-android!2162
2025-04-27 14:30:14 +08:00
d0d5ccdc45 fix: 在 io 线程设置 livedata value 导致的问题 2025-04-27 14:29:09 +08:00
c61d0bc9cc Revert "feat: 重新启用在华为 Android 10 上的开屏广告,测试闪退情况"
This reverts commit aed8e50db3.
2025-04-27 11:05:24 +08:00
2da07354e7 Merge branch 'feat/GHZSCY-7496' into 'dev'
feat: 专题合集新增自定义设置—客户端 https://jira.shanqu.cc/browse/GHZSCY-7496

See merge request halo/android/assistant-android!2161
2025-04-27 11:03:31 +08:00
d874b91b7c feat: 专题合集新增自定义设置—客户端 https://jira.shanqu.cc/browse/GHZSCY-7496 2025-04-27 11:03:31 +08:00
62e62d3a93 chore: 版本更新至 5.41.0 2025-04-27 10:03:20 +08:00
2e806d77e1 Merge branch 'fix/GHZSCY-7916' into 'dev'
Resolve GHZSCY-7916 "Fix/"

Closes GHZSCY-7916

See merge request halo/android/assistant-android!2160
2025-04-25 16:33:50 +08:00
30da988ebc fix: 助手更新显示问题 https://jira.shanqu.cc/browse/GHZSCY-7916 2025-04-25 16:27:12 +08:00
fd61e83f8e Revert "Revert "fix: "点"符号设置颜色""
This reverts commit e5161ae350.
2025-04-24 15:44:34 +08:00
0712960a30 Revert "Revert "feat: 【光环助手】实名认证特殊字符输入优化 https://jira.shanqu.cc/browse/GHZSCY-7702""
This reverts commit b1b231a309.
2025-04-24 15:44:34 +08:00
5dfdaa0353 Merge remote-tracking branch 'origin/release' into dev 2025-04-24 15:16:28 +08:00
fa4dce66a6 Merge branch 'feat/GHZSCY-7704' into 'dev'
【畅玩】实名认证特殊符号以及字符限制优化

See merge request halo/android/assistant-android!2159
2025-04-22 10:09:24 +08:00
8a017df220 Revert .gitlab-ci.yml 2025-04-22 10:08:42 +08:00
3a0be0a6ee 【单机&畅玩】实名认证特殊符号以及字符限制优化 https://jira.shanqu.cc/browse/GHZSCY-7704 2025-04-21 17:47:07 +08:00
d50eeddde2 Merge branch 'fix/GHZSCY-7860' into 'release'
fix:【光环助手】游戏专题显示问题 https://jira.shanqu.cc/browse/GHZSCY-7860

See merge request halo/android/assistant-android!2158
2025-04-15 11:23:14 +08:00
89aa6e7abc fix:【光环助手】游戏专题显示问题 https://jira.shanqu.cc/browse/GHZSCY-7860 2025-04-15 11:23:14 +08:00
ab795ab538 Merge branch 'feat/more-xapk-error-log' into 'release'
feat: xapk 解压失败增加异常摘要字段

See merge request halo/android/assistant-android!2156
2025-04-10 10:08:07 +08:00
ba0854ad9a feat: xapk 解压失败增加异常摘要字段 2025-04-10 10:00:57 +08:00
fc2051387a Merge branch 'cherry-pick' into 'release'
合并部分需求到 release

See merge request halo/android/assistant-android!2155
2025-04-10 09:43:58 +08:00
ab2973c7be feat: 游戏预约相关优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-7828 2025-04-10 09:35:01 +08:00
2a08ea681d fix: 修复游戏详情视频/图集Tab闪烁的问题 2025-04-10 09:35:00 +08:00
e23d510fb0 fix: 游戏详情-视频/图集tab 神策埋点补充—客户端 https://jira.shanqu.cc/browse/GHZSCY-7781 2025-04-10 09:35:00 +08:00
3c6ee0c782 chore: 版本更新至 5.40.2 2025-04-09 15:43:49 +08:00
07840822a8 Merge branch 'fix/GHZSCY-7806-pick' into 'release'
fix:【光环助手】通知栏目 滚动问题 https://jira.shanqu.cc/browse/GHZSCY-7806

See merge request halo/android/assistant-android!2154
2025-04-09 13:45:37 +08:00
12e547d333 fix:【光环助手】通知栏目 滚动问题 https://jira.shanqu.cc/browse/GHZSCY-7806 2025-04-09 13:42:14 +08:00
8bb3736ad1 Merge branch 'fix/redirect-crash' into 'release'
fix: 捕抓重定向后的下载地址 url 异常造成的闪退

See merge request halo/android/assistant-android!2153
2025-04-09 11:28:39 +08:00
aa9ba5163f fix: 捕抓重定向后的下载地址 url 异常造成的闪退 2025-04-09 11:27:54 +08:00
23033edc42 Merge branch 'fix/sentry-user-id' into 'release'
feat: sentry 日志使用 base64 转码后的 androidId 替换原始随机 id

See merge request halo/android/assistant-android!2152
2025-04-09 11:18:05 +08:00
308e134aff feat: sentry 日志使用 base64 转码后的 androidId 替换原始随机 id 2025-04-09 11:17:30 +08:00
ac78ea0498 Merge branch 'feat/upload-accelerator-set-token-log' into 'release'
feat:将奇游加速器 setToken 错误日志从sentry转移到火山云

See merge request halo/android/assistant-android!2151
2025-04-09 11:13:31 +08:00
38bab9cf4f feat:将奇游加速器 setToken 错误日志从sentry转移到火山云 2025-04-09 11:13:30 +08:00
e3f883b784 Merge branch 'feat/GHZSCY-7828' into 'dev'
feat: 游戏预约相关优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-7828

See merge request halo/android/assistant-android!2150
2025-04-08 15:31:12 +08:00
fbb81d7e45 feat: 游戏预约相关优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-7828 2025-04-08 15:28:21 +08:00
750ec04c9d Merge branch 'fix/game_detail_cover_tab' into 'dev'
fix: 修复游戏详情视频/图集Tab闪烁的问题

See merge request halo/android/assistant-android!2149
2025-04-07 18:00:37 +08:00
e23a3d3938 fix: 修复游戏详情视频/图集Tab闪烁的问题 2025-04-07 17:57:14 +08:00
d75020cdb5 Merge branch 'feat/GHZSCY-7781' into 'dev'
fix: 游戏详情-视频/图集tab 神策埋点补充—客户端 https://jira.shanqu.cc/browse/GHZSCY-7781

See merge request halo/android/assistant-android!2148
2025-04-07 09:54:40 +08:00
c56c71d1f1 fix: 游戏详情-视频/图集tab 神策埋点补充—客户端 https://jira.shanqu.cc/browse/GHZSCY-7781 2025-04-07 09:54:40 +08:00
ccfa50d748 Merge branch 'feat/GHZSCY-7515' into 'dev'
feat:开服订阅通知频率优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-7515

See merge request halo/android/assistant-android!2145
2025-04-03 11:33:41 +08:00
7176a5a4c4 feat:开服订阅通知频率优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-7515 2025-04-03 11:33:41 +08:00
d007092193 Merge branch 'feat/GHZSCY-7702' into 'dev'
feat: 实名认证字符优化 (光环助手和畅玩)

See merge request halo/android/assistant-android!2147
2025-04-03 10:45:54 +08:00
2552743ff2 Merge branch 'feat/GHZSCY-7702' into 'dev'
feat: 实名认证字符优化 (光环助手和畅玩)

See merge request halo/android/assistant-android!2146
2025-04-03 10:42:01 +08:00
4faf15ffe7 feat: 实名认证字符优化 (光环助手和畅玩) 2025-04-03 10:42:01 +08:00
33d6ed75ad ci: revert 2025-04-03 10:39:55 +08:00
2eb4878b12 Merge branch 'fix/GHZSCY-7806' into 'dev'
fix:【光环助手】通知栏目 滚动问题 https://jira.shanqu.cc/browse/GHZSCY-7806

See merge request halo/android/assistant-android!2144
2025-04-03 10:07:41 +08:00
2bea3c72d7 fix:【光环助手】通知栏目 滚动问题 https://jira.shanqu.cc/browse/GHZSCY-7806 2025-04-03 09:59:57 +08:00
fa3db9d521 fix: 实名认证页面首页,右上角多显示了“·”的按钮 https://jira.shanqu.cc/browse/GHZSCY-7702,https://jira.shanqu.cc/browse/GHZSCY-7791 2025-04-02 16:36:30 +08:00
8084cda37d Merge branch 'hotfix/v5.40.1-1151/update_upload_sdk' into 'release'
更新火山云上传 SDK

See merge request halo/android/assistant-android!2143
2025-04-02 11:53:34 +08:00
e624f34de1 fix: 禁用火山云上传分片功能 2025-04-02 10:29:56 +08:00
b832c0c14f Merge branch 'hotfix/v5.40.1-1151/GHZSCY-7796' into 'release'
云存档引导游戏下载问题 https://jira.shanqu.cc/browse/GHZSCY-7796

See merge request halo/android/assistant-android!2142
2025-04-01 17:38:48 +08:00
6610c43937 chore: 更新火山云上传 SDK 2025-04-01 17:38:19 +08:00
ec29d94fb7 fix: 云存档引导游戏下载问题 https://jira.shanqu.cc/browse/GHZSCY-7796 2025-04-01 14:44:46 +08:00
efee9409bf Merge branch 'feat/add-room-ktx' into 'dev'
为了Room能和Flow一起使用,需要引入room-ktx

See merge request halo/android/assistant-android!2141
2025-04-01 09:31:26 +08:00
38a7eaf780 为了Room能和Flow一起使用,需要引入room-ktx 2025-04-01 09:28:05 +08:00
a1b4233fdb Merge branch 'feat/GHZSCY-7784' into 'dev'
feat:处理以 LiveData 形式直接监听数据库变更的代码 https://jira.shanqu.cc/browse/GHZSCY-7784

See merge request halo/android/assistant-android!2140
2025-03-31 17:20:33 +08:00
60c24e7457 feat:处理以 LiveData 形式直接监听数据库变更的代码 https://jira.shanqu.cc/browse/GHZSCY-7784 2025-03-31 17:20:33 +08:00
b6ec74d789 chore: 版本更新只 5.40.1 2025-03-31 14:31:26 +08:00
2d7224cf16 Merge branch 'fix/crashes' into 'release'
修复Sentry闪退

See merge request halo/android/assistant-android!2139
2025-03-31 11:43:45 +08:00
3f45344b54 Merge branch 'feat/update-qy-sdk' into 'release'
feat:更新奇游加速器sdk

See merge request halo/android/assistant-android!2138
2025-03-31 11:39:28 +08:00
1c3dbce08d feat:更新奇游加速器sdk 2025-03-31 11:39:28 +08:00
7844800b0e fix: 禁用RecyclerView动画效果避免异常闪退 https://sentry.shanqu.cc/organizations/lightgame/issues/441824/?project=22 2025-03-31 11:38:05 +08:00
ebcfd9c85d fix: 修复游戏详情页偶发空指针闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/443716/?project=22 2025-03-31 11:37:07 +08:00
3825315f65 Merge branch 'hotfix/v5.40.0-1150/router-crash' into 'release'
fix: 尝试修复进程启动时路由异常导致的闪退问题

See merge request halo/android/assistant-android!2137
2025-03-28 17:53:50 +08:00
3ffe4f9bc6 fix: 尝试修复路由异步初始化导致的闪退问题 2025-03-28 17:05:59 +08:00
729685e764 feat: 实名认证字符优化 (光环助手和畅玩)
https://jira.shanqu.cc/browse/GHZSCY-7702https://jira.shanqu.cc/browse/GHZSCY-7704
2025-03-25 17:54:35 +08:00
b2fc01ae48 Merge branch 'feat/va-relative' into 'release'
Revert 畅玩实名认证特殊字符

See merge request halo/android/assistant-android!2135
2025-03-25 17:29:42 +08:00
06f932af14 Revert 畅玩实名认证特殊字符 2025-03-25 17:29:13 +08:00
c2fe3bb64a Merge branch 'hotfix/GHZSCY-7702' into 'release'
Revert "fix: "点"符号设置颜色"

See merge request halo/android/assistant-android!2134
2025-03-25 17:24:30 +08:00
b1b231a309 Revert "feat: 【光环助手】实名认证特殊字符输入优化 https://jira.shanqu.cc/browse/GHZSCY-7702"
This reverts commit cc05dabb93.
2025-03-25 17:23:29 +08:00
e5161ae350 Revert "fix: "点"符号设置颜色"
This reverts commit 2ba39188f7.
2025-03-25 17:22:58 +08:00
4dfa7733ad Merge branch 'feat/va-relative' into 'dev'
feat: 5.40.0 va相关的更新

See merge request halo/android/assistant-android!2133
2025-03-25 14:32:58 +08:00
3906d9b84b feat: 5.40.0 va相关的更新 2025-03-25 14:31:39 +08:00
775e6cc689 Merge branch 'feat/remove-duplicate-log' into 'dev'
删除多余的日志上报

See merge request halo/android/assistant-android!2132
2025-03-25 14:25:39 +08:00
019b9f3f82 删除多余的日志上报 2025-03-25 14:24:05 +08:00
49f752dab5 Merge branch 'fix/jg_push_source_entrance' into 'dev'
fix: 修复极光推送跳转没有携带来源信息的问题

See merge request halo/android/assistant-android!2131
2025-03-25 10:45:05 +08:00
0cbda119d4 fix: 修复极光推送跳转没有携带来源信息的问题 2025-03-25 10:43:59 +08:00
aed8e50db3 feat: 重新启用在华为 Android 10 上的开屏广告,测试闪退情况 2025-03-25 10:17:57 +08:00
da5bb9eaf3 Merge branch 'feat/GHZSCY-7525' into 'dev'
feat: 头条推广包转化归因优化及SDK更新 https://jira.shanqu.cc/browse/GHZSCY-7525.cc/browse/GHZSCY-7525

See merge request halo/android/assistant-android!2130
2025-03-25 09:57:44 +08:00
d7593de98b feat: 头条推广包转化归因优化及SDK更新 https://jira.shanqu.cc/browse/GHZSCY-7525.cc/browse/GHZSCY-7525 2025-03-25 09:57:08 +08:00
69d2d699fe Merge branch 'feat/GHZSCY-7712' into 'dev'
feat: 后台功能面板组件交互优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-7712

See merge request halo/android/assistant-android!2129
2025-03-24 14:31:32 +08:00
a438ef16c0 feat: 后台功能面板组件交互优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-7712 2025-03-24 14:31:32 +08:00
d5a2cecd29 Merge branch 'fix/GHZSCY-7737' into 'dev'
fix:【光环助手】游戏详情下载触发跳转异常 https://jira.shanqu.cc/browse/GHZSCY-7737

See merge request halo/android/assistant-android!2128
2025-03-24 11:30:23 +08:00
65a4e675fa fix:【光环助手】游戏详情下载触发跳转异常 https://jira.shanqu.cc/browse/GHZSCY-7737 2025-03-24 11:30:23 +08:00
ec3e6d5f7b Merge branch 'fix/GHZSCY-7691' into 'dev'
fix: 2025/3/14 - 埋点补充 https://jira.shanqu.cc/browse/GHZSCY-7691

See merge request halo/android/assistant-android!2116
2025-03-24 10:03:53 +08:00
07b0afce03 fix: 2025/3/14 - 埋点补充 https://jira.shanqu.cc/browse/GHZSCY-7691 2025-03-24 10:03:53 +08:00
852a6d32d6 Merge branch 'fix/GHZSCY-7736' into 'dev'
fix: 间距显示问题-客户端 https://jira.shanqu.cc/browse/GHZSCY-7736

See merge request halo/android/assistant-android!2127
2025-03-24 09:44:19 +08:00
55022d8b1c fix: 间距显示问题-客户端 https://jira.shanqu.cc/browse/GHZSCY-7736 2025-03-24 09:42:26 +08:00
eb7d407e92 Merge branch 'fix/GHZSCY-7735' into 'dev'
fix: 版本求更新显示优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-7735

See merge request halo/android/assistant-android!2126
2025-03-21 18:02:02 +08:00
733cd1e198 fix: 版本求更新显示优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-7735 2025-03-21 17:57:02 +08:00
1d767018f6 Merge branch 'fix/va/unity-crash' into 'dev'
fix: 部分unity游戏启动闪退

See merge request halo/android/assistant-android!2125
2025-03-21 17:36:55 +08:00
0a3616a1c0 fix: 部分unity游戏启动闪退 2025-03-21 17:36:09 +08:00
05108ab478 Merge branch 'fix/crash-in-splash' into 'dev'
fix:java.lang.RuntimeException: Unable to start activity...

See merge request halo/android/assistant-android!2124
2025-03-21 11:23:33 +08:00
508c90cc63 fix:java.lang.RuntimeException: Unable to start activity... 2025-03-21 11:23:33 +08:00
6242f79f80 Merge branch 'fix/GHZSCY-7728' into 'dev'
fix: 部分位置跳转到游戏开服表无【新增】功能 https://jira.shanqu.cc/browse/GHZSCY-7728

See merge request halo/android/assistant-android!2123
2025-03-20 16:29:36 +08:00
71dd964440 fix: 部分位置跳转到游戏开服表无【新增】功能 https://jira.shanqu.cc/browse/GHZSCY-7728 2025-03-20 16:25:22 +08:00
63fc720c40 Merge remote-tracking branch 'origin/release' into dev 2025-03-20 15:42:57 +08:00
5da02c4b64 Merge branch 'hotfix/v5.39.5-1195/rank_text_width_issue' into 'release'
fix: 修复搜索页推荐榜单文字宽度问题

See merge request halo/android/assistant-android!2122
2025-03-20 15:16:40 +08:00
f94b28a085 fix: 修复搜索页推荐榜单文字宽度问题 2025-03-20 15:15:24 +08:00
e8a199b5c0 Merge branch 'feat/GHZSCY-7702' into 'dev'
fix: "点"符号设置颜色

See merge request halo/android/assistant-android!2121
2025-03-20 11:06:51 +08:00
2ba39188f7 fix: "点"符号设置颜色 2025-03-20 11:06:37 +08:00
963074d5d8 Merge branch 'feat/GHZSCY-7704' into 'dev'
fix: “点”符号大小和颜色设置

See merge request halo/android/assistant-android!2120
2025-03-20 10:51:39 +08:00
cc629819d9 fix: “点”符号大小和颜色设置 2025-03-20 10:51:13 +08:00
d82505ab5d Merge branch 'feat/GHZSCY-7704' into 'dev'
feat: [畅玩]实名认证特殊符号以及字符限制优化 https://jira.shanqu.cc/browse/GHZSCY-7704

See merge request halo/android/assistant-android!2119
2025-03-20 09:46:54 +08:00
a917214b36 feat: [畅玩]实名认证特殊符号以及字符限制优化 https://jira.shanqu.cc/browse/GHZSCY-7704 2025-03-20 09:46:34 +08:00
bdf9094799 Merge branch 'feat/common-chips' into 'dev'
feat:添加通用组件:选项标签Chips

See merge request halo/android/assistant-android!2118
2025-03-20 09:45:39 +08:00
df0d0604b9 feat:添加通用组件:选项标签Chips 2025-03-20 09:39:51 +08:00
f100d906ab Merge branch 'feat/GHZSCY-7702' into 'dev'
feat: 【光环助手】实名认证特殊字符输入优化 https://jira.shanqu.cc/browse/GHZSCY-7702

See merge request halo/android/assistant-android!2117
2025-03-19 17:51:51 +08:00
cc05dabb93 feat: 【光环助手】实名认证特殊字符输入优化 https://jira.shanqu.cc/browse/GHZSCY-7702 2025-03-19 17:45:52 +08:00
bd39ac2eaf Merge branch 'fix/drop_font_color_in_dark_mode' into 'dev'
fix: 修复部分富文本中的颜色在深色模式下没有去除的问题

See merge request halo/android/assistant-android!2115
2025-03-19 17:10:52 +08:00
31903d21c0 fix: 修复部分富文本中的颜色在深色模式下没有去除的问题 2025-03-19 17:03:45 +08:00
266fcc4c38 Merge branch 'feat/GHZSCY-7642' into 'dev'
feat: 优化汉化UI

See merge request halo/android/assistant-android!2114
2025-03-19 11:31:34 +08:00
075ed04191 feat: 优化汉化UI 2025-03-19 11:30:55 +08:00
c804c2f7fd Merge branch 'feat/GHZSCY-7642' into 'dev'
feat: 畅玩汉化翻译功能

See merge request halo/android/assistant-android!2113
2025-03-18 11:39:09 +08:00
882f0a7b3e feat: 畅玩汉化翻译功能 2025-03-18 11:37:18 +08:00
3cf86a500b Merge branch 'feat/GHZSCY-7568' into 'dev'
feat: 游戏详情改版补充优化1—客户端(1) https://jira.shanqu.cc/browse/GHZSCY-7568

See merge request halo/android/assistant-android!2112
2025-03-18 11:14:49 +08:00
3cd70b5b8f feat: 游戏详情改版补充优化1—客户端(1) https://jira.shanqu.cc/browse/GHZSCY-7568 2025-03-18 11:14:49 +08:00
60f35a89b4 Merge branch 'feat/GHZSCY-6982-2' into 'dev'
feat:【光环助手】搜索业务:神策埋点相关搜索结果点击事件新增序号属性position https://jira.shanqu.cc/browse/GHZSCY-6982

See merge request halo/android/assistant-android!2111
2025-03-17 15:43:10 +08:00
1503c23246 feat:【光环助手】搜索业务:神策埋点相关搜索结果点击事件新增序号属性position https://jira.shanqu.cc/browse/GHZSCY-6982 2025-03-17 15:38:01 +08:00
ea9e91618f Merge branch 'fix/GHZSCY-7685' into 'dev'
fix:游戏预约功能(第六期)—0313运营测试-客户端...

See merge request halo/android/assistant-android!2110
2025-03-17 11:12:07 +08:00
d52fcc2185 fix:游戏预约功能(第六期)—0313运营测试-客户端... 2025-03-17 11:12:07 +08:00
db047c36d7 Merge remote-tracking branch 'origin/release' into dev
# Conflicts:
#	dependencies.gradle
2025-03-14 15:22:00 +08:00
1e58bec4a0 Merge branch 'feat/GHZSCY-7668' into 'dev'
feat: 光环助手测试包增加相关快捷功能 https://jira.shanqu.cc/browse/GHZSCY-7668

See merge request halo/android/assistant-android!2109
2025-03-14 15:19:22 +08:00
87ff1a64b3 Merge branch 'fix/improper-router' into 'release'
fix: 修复光环未正常启动也能使用路由跳转至指定页面的问题

See merge request halo/android/assistant-android!2108
2025-03-14 15:16:21 +08:00
b3678d7a54 fix: 修复光环未正常启动也能使用路由跳转至指定页面的问题 2025-03-14 14:28:53 +08:00
c7fa04792b chore: 版本更新至 5.39.5 2025-03-14 10:14:08 +08:00
76ea531419 Merge branch 'fix/GHZSCY-7687' into 'release'
fix:【光环助手】搜索内容标签点击跳转异常 https://jira.shanqu.cc/browse/GHZSCY-7687

See merge request halo/android/assistant-android!2107
2025-03-13 18:20:31 +08:00
d821da0b2c fix:【光环助手】搜索内容标签点击跳转异常 https://jira.shanqu.cc/browse/GHZSCY-7687 2025-03-13 18:20:31 +08:00
d9b137504a Merge branch 'fix/GHZSCY-7661' into 'release'
fix:【光环助手】游戏详情自定义tab关联自定义页面的视频播放问题 https://jira.shanqu.cc/browse/GHZSCY-7661

See merge request halo/android/assistant-android!2105
2025-03-13 16:36:39 +08:00
93369f5676 fix:【光环助手】游戏详情自定义tab关联自定义页面的视频播放问题 https://jira.shanqu.cc/browse/GHZSCY-7661 2025-03-13 16:36:39 +08:00
b109c3bf6f Merge branch 'fix/GHZSCY-7681' into 'dev'
fix: 视频/图片上传问题 https://jira.shanqu.cc/browse/GHZSCY-7681

See merge request halo/android/assistant-android!2106
2025-03-13 16:03:36 +08:00
bb80560a49 fix: 视频/图片上传问题 https://jira.shanqu.cc/browse/GHZSCY-7681 2025-03-13 16:02:48 +08:00
c43a9e3b6e feat: 光环助手测试包增加相关快捷功能 https://jira.shanqu.cc/browse/GHZSCY-7668 2025-03-13 14:53:03 +08:00
34d255d258 Merge branch 'feat/GHZSCY-7676' into 'release'
fix: 新游开测相关功能优化(第四期)—0313运营测试-客户端 https://jira.shanqu.cc/browse/GHZSCY-7676

See merge request halo/android/assistant-android!2104
2025-03-13 13:43:15 +08:00
3ca17cea07 fix: 新游开测相关功能优化(第四期)—0313运营测试-客户端 https://jira.shanqu.cc/browse/GHZSCY-7676 2025-03-13 13:40:10 +08:00
d3e23bfbb5 Merge branch 'feat/GHZSCY-6834' into 'dev'
feat:游戏预约功能(第六期)—客户端 https://jira.shanqu.cc/browse/GHZSCY-6834

See merge request halo/android/assistant-android!2102
2025-03-11 16:53:11 +08:00
273a9f010d feat:游戏预约功能(第六期)—客户端 https://jira.shanqu.cc/browse/GHZSCY-6834 2025-03-11 16:53:11 +08:00
e8cbaf89ff Merge branch 'feat/GHZSCY-6817' into 'dev'
feat:游戏专题新增“竖式图文列表”样式—客户端 https://jira.shanqu.cc/browse/GHZSCY-6817

See merge request halo/android/assistant-android!2101
2025-03-11 14:45:23 +08:00
0e3586a556 Merge branch 'feat/GHZSCY-7048' into 'dev'
feat: 推广包快手API新增次留上报 https://jira.shanqu.cc/browse/GHZSCY-7048

See merge request halo/android/assistant-android!2099
2025-03-11 14:38:02 +08:00
d546651c06 feat:游戏专题新增“竖式图文列表”样式—客户端 https://jira.shanqu.cc/browse/GHZSCY-6817 2025-03-11 14:24:46 +08:00
815f567f44 Merge branch 'feat/GHZSCY-6812' into 'dev'
feat:https://jira.shanqu.cc/browse/GHZSCY-6812 发表内容输入规则优化—客户端

See merge request halo/android/assistant-android!2100
2025-03-11 14:20:41 +08:00
ba3e9357a1 feat:https://jira.shanqu.cc/browse/GHZSCY-6812 发表内容输入规则优化—客户端 2025-03-11 14:20:41 +08:00
312448daae Merge remote-tracking branch 'origin/release' into dev
# Conflicts:
#	dependencies.gradle
2025-03-11 10:53:15 +08:00
a00a1ced7e Merge branch 'fix/message_link_skip' into 'release'
fix: 修复消息中心跳转问题

See merge request halo/android/assistant-android!2098
2025-03-11 10:42:18 +08:00
1587ca8e6a fix: 修复消息中心跳转问题 2025-03-11 10:35:39 +08:00
86b4761a1c Merge branch 'fix/game_detail_dark_mode' into 'release'
fix: 修复游戏详情页闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/379887/events/0f296b9965884a4f944867479ad7d6c7/?project=22

See merge request halo/android/assistant-android!2097
2025-03-10 10:04:15 +08:00
0446d87b24 fix: 修复游戏详情切换深色模式出现的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/440456/events/3cfa315cbb734e9688984dddf75fefbd/?project=22 2025-03-10 09:48:04 +08:00
8a6d476636 Merge branch 'feat/GHZSCY-7541' into 'dev'
feat:客户端穿山甲广告SDK升级新版本—客户端 https://jira.shanqu.cc/browse/GHZSCY-7541

See merge request halo/android/assistant-android!2093
2025-03-06 14:33:05 +08:00
17b34c9e6f feat:客户端穿山甲广告SDK升级新版本—客户端 https://jira.shanqu.cc/browse/GHZSCY-7541 2025-03-06 14:31:16 +08:00
69241c489b chore: 版本更新至 5.40.0 2025-03-04 16:23:00 +08:00
5d4648b3d2 feat: 推广包快手API新增次留上报 https://jira.shanqu.cc/browse/GHZSCY-7048 2024-11-27 15:59:53 +08:00
289 changed files with 6927 additions and 1862 deletions

View File

@ -113,6 +113,7 @@ android {
buildConfigField "String", "WECHAT_SECRET", "\"${WECHAT_SECRET}\""
buildConfigField "String", "TENCENT_APPID", "\"${TENCENT_APPID}\""
buildConfigField "String", "WEIBO_APPKEY", "\"${WEIBO_APPKEY}\""
buildConfigField "String", "DSP_API_HOST","\"${DSP_API_HOST}\""
// 一体包的32位畅玩游戏助手包名
buildConfigField "String", "EXT_PACKAGE_NAME", "\"${rootProject.ext.EXT_PACKAGE_NAME}\""
}
@ -346,7 +347,7 @@ repositories {
android.applicationVariants.configureEach { variant ->
variant.mergeAssets.doLast {
def assetDir = variant.mergeAssetsProvider.get().outputDir.get()
def unwantedAssets = ['2011394667', 'gdt_plugin/gdtadv2.jar']
def unwantedAssets = ['1832823466', 'gdt_plugin/gdtadv2.jar']
unwantedAssets.each { assetPath ->
def file = new File([assetDir, assetPath].join(File.separator))

View File

@ -77,6 +77,7 @@
### TEA
-keep class com.gh.gamecenter.TeaHelper { *; }
-keep class com.bytedance.ads.convert.broadcast.common.EncryptionTools {*;}
### EasyFloat
-keep class com.lzf.easyfloat.* {*;}

Binary file not shown.

View File

@ -71,6 +71,10 @@
<!-- 适配 双开/分身 游戏授权登录 -->
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<!-- 日历 -->
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-sdk tools:overrideLibrary="
com.shuyu.gsyvideoplayer,
com.shuyu.gsyvideoplayer.lib,

File diff suppressed because one or more lines are too long

View File

@ -19,10 +19,10 @@ import java.net.URLConnection
object AdPluginDownloadHelper : InnerDownloadListener {
private const val CSJ_FILE_NAME = "2011394667"
private const val CSJ_FILE_NAME = "1832823466"
private const val GDT_FILE_NAME = "gdt_plugin/gdtadv2.jar"
private const val CSJ_PLUGIN_URL = "https://and-static.ghzs66.com/android/static/2011394667"
private const val CSJ_PLUGIN_URL = "https://and-static.ghzs66.com/android/static/1832823466"
private const val GDT_PLUGIN_URL = "https://and-static.ghzs66.com/android/static/gdtadv2.jar"
private var csjDownloadedCallback: (() -> Unit)? = null

View File

@ -3,6 +3,8 @@ package com.gh.common
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Base64
import android.view.View
import android.webkit.JavascriptInterface
@ -11,6 +13,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import com.gh.common.exposure.ExposureManager
import com.gh.common.util.*
import com.gh.common.util.LogUtils
@ -18,6 +21,7 @@ import com.gh.download.DownloadManager
import com.gh.download.PackageObserver
import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.ImageViewerActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.WebActivity
import com.gh.gamecenter.common.callback.BiCallback
import com.gh.gamecenter.common.constant.Constants
@ -35,6 +39,7 @@ import com.gh.gamecenter.common.utils.SensorsBridge.EVENT_NAME
import com.gh.gamecenter.common.utils.SensorsBridge.KEY_IS_FIRST_TIME
import com.gh.gamecenter.common.view.dsbridge.CompletionHandler
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.provider.IAcceleratorProvider
import com.gh.gamecenter.core.provider.IPushProvider
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.runOnUiThread
@ -77,7 +82,7 @@ import java.util.*
class DefaultJsApi(
var context: Context,
val entrance: String = "",
private var mFragment: Fragment? = null,
private val mFragment: Fragment? = null,
private var mBbsId: String? = "",
private var mOriginUrl: String? = "",
private val mForumName: String? = "",
@ -94,6 +99,8 @@ class DefaultJsApi(
private var mDownloadHandler: CompletionHandler<Any>? = null // 下载信息回调
private var mExposureEvent: ExposureEvent? = null // 活动曝光实体
private val handler = Handler(Looper.getMainLooper())
init {
if (mFragment != null) {
EventBus.getDefault().register(this)
@ -240,6 +247,12 @@ class DefaultJsApi(
VHelper.launch(context, packageName)
}
} else {
val wechatPkgName = "com.tencent.mm"
if (packageName == wechatPkgName && !PackageUtils.isInstalled(context, wechatPkgName)) {
// 如果是微信客户端,需要检查是否安装微信
ToastUtils.showToast(R.string.wechat_app_not_install_tips.toResString())
return@runOnUiThread
}
PackageLauncher.launchApp(context, packageName = packageName)
}
}
@ -502,6 +515,44 @@ class DefaultJsApi(
}
}
@JavascriptInterface
fun saveWechatQRCode(msg: Any) {
val base64StringData = msg.toString()
runOnUiThread {
(context as? FragmentActivity)?.checkStoragePermissionBeforeAction {
runOnIoThread {
val base64String = base64StringData.replace("data:image/png;base64", "")
tryWithDefaultCatch {
val imageFile =
File(HaloApp.getInstance().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".png")
val decodedString = Base64.decode(base64String, Base64.DEFAULT)
val bos = BufferedOutputStream(FileOutputStream(imageFile))
bos.write(decodedString)
bos.flush()
bos.close()
ImageUtils.saveImageToFile(imageFile, "", true) {
// 这里是 ui 线程
// 保存微信二维码成功1s 以后跳转微信
if (mFragment != null && mFragment.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
handler.removeCallbacksAndMessages(null)
handler.postDelayed({
val wechatPkgName = "com.tencent.mm"
if (!PackageUtils.isInstalled(context, wechatPkgName)) {
ToastUtils.showToast(R.string.wechat_app_not_install_tips.toResString())
return@postDelayed
}
PackageLauncher.launchApp(context, packageName = wechatPkgName)
}, 1000)
}
}
}
}
}
}
}
@JavascriptInterface
fun loginWithCallback(msg: Any, handler: CompletionHandler<Any>) {
mLoginHandler = handler
@ -750,6 +801,14 @@ class DefaultJsApi(
listener?.onStopGameAccelerate()
}
@JavascriptInterface
fun getDurationRemainingTime(msg: Any, handler: CompletionHandler<Any>) {
TheRouter.get(IAcceleratorProvider::class.java)?.loadQyUserPermissionData {
val durationExpiredMinute = AcceleratorDataHolder.instance.vipEntity?.durationExpiredTime ?: 0L
handler.complete(durationExpiredMinute)
}
}
@JavascriptInterface
fun refreshToken(token: Any, handler: CompletionHandler<Any>) {
val accessToken = token.toString()
@ -798,6 +857,8 @@ class DefaultJsApi(
}
EventBus.getDefault().unregister(this@DefaultJsApi)
handler.removeCallbacksAndMessages(null)
}
}

View File

@ -66,6 +66,8 @@ public class Config {
public static final String WGAME_CPM_BUSIAPPID = BuildConfig.WGAME_CPM_BUSIAPPID;
public static final String WGAME_CPM_API_HOST = EnvHelper.getWGameCPMHost();
public static final String DSP_API_HOST = EnvHelper.getDspHost();
// Third-Party confs
public static final String WECHAT_APPID = BuildConfig.WECHAT_APPID;
public static final String WECHAT_SECRET = BuildConfig.WECHAT_SECRET;

View File

@ -1,5 +1,10 @@
package com.gh.common.databind;
import static com.gh.gamecenter.entity.SubjectEntity.SUBJECT_TAG_SELLING_POINT;
import static com.gh.gamecenter.entity.SubjectEntity.SUBJECT_TAG_TEST;
import static com.gh.gamecenter.entity.SubjectEntity.SUBJECT_TAG_TYPE;
import static com.gh.gamecenter.entity.SubjectEntity.SUBJECT_TAG_UPDATE;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
@ -8,11 +13,14 @@ import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import android.view.ViewParent;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import com.gh.common.chain.BrowserInstallHandler;
import com.gh.common.chain.CheckDownloadHandler;
@ -48,6 +56,7 @@ import com.gh.download.server.BrowserInstallHelper;
import com.gh.gamecenter.DownloadManagerActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.WebActivity;
import com.gh.gamecenter.common.databinding.LayoutGameItemSellingPointBinding;
import com.gh.gamecenter.common.entity.LinkEntity;
import com.gh.gamecenter.common.utils.DarkModeUtils;
import com.gh.gamecenter.common.utils.ExtensionsKt;
@ -55,6 +64,7 @@ import com.gh.gamecenter.common.utils.FileUtils;
import com.gh.gamecenter.common.utils.NewFlatLogUtils;
import com.gh.gamecenter.common.utils.SensorsBridge;
import com.gh.gamecenter.core.utils.MtaHelper;
import com.gh.gamecenter.core.utils.TimeUtils;
import com.gh.gamecenter.core.utils.ToastUtils;
import com.gh.gamecenter.feature.entity.ApkEntity;
import com.gh.gamecenter.feature.entity.GameEntity;
@ -264,7 +274,9 @@ public class BindingAdapters {
SensorsBridge.trackInstallGameClick(
gameEntity.getId(),
gameEntity.getName() != null ? gameEntity.getName() : "",
"主动安装"
"主动安装",
gameEntity.isDspGame(),
gameEntity.getDspAdId()
);
DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity);
@ -289,7 +301,7 @@ public class BindingAdapters {
});
break;
case RESERVED:
ReservationHelper.showCancelReservationDialog(progressBar.getContext(),gameEntity, () -> {
ReservationHelper.showCancelReservationDialog(progressBar.getContext(), gameEntity, () -> {
ReservationHelper.cancelReservation(gameEntity, () -> {
updateReservation(progressBar, gameEntity);
});
@ -472,39 +484,37 @@ public class BindingAdapters {
}
}
// 包含测试开服标签
public static void setGameTags(LinearLayout layout, GameEntity gameEntity) {
/**
* 包含测试开服标签
*
* @param layout
* @param gameEntity
* @param subjectTag 默认为 “”只有游戏专题可以配置subjectTag
*/
public static void setGameTags(LinearLayout layout, GameEntity gameEntity, String subjectTag) {
try {
if (layout.getVisibility() == View.GONE) return;
ArrayList<TagStyleEntity> tagStyle = new ArrayList<>();
TestEntity test = gameEntity.getTest();
if (test != null
// 这个判断用于开测表列表
&& !"type_tag".equals(test.getGameTag())) {
if ("custom".equals(test.getGameTag())) {
TagStyleEntity typeTag = new TagStyleEntity();
if (!TextUtils.isEmpty(test.getText())) {
typeTag.setName(test.getText() != null ? test.getText() : "");
} else {
typeTag.setName(test.getType() != null ? test.getType() : "");
}
typeTag.setBackground("E8F3FF");
typeTag.setColor("1383EB");
tagStyle.add(typeTag);
} else {
TagStyleEntity typeTag = new TagStyleEntity();
boolean isDarkModeOn = DarkModeUtils.INSTANCE.isDarkModeOn(layout.getContext());
typeTag.setName(test.getType() != null ? test.getType() : "");
typeTag.setBackground("1AFFA142");
typeTag.setColor(isDarkModeOn ? "EB9238" : "FFA142");
tagStyle.add(typeTag);
if (test != null && subjectTag.equals(SUBJECT_TAG_TEST)) {
// 显示开测表标签
TagStyleEntity typeTag = new TagStyleEntity();
boolean isDarkModeOn = DarkModeUtils.INSTANCE.isDarkModeOn(layout.getContext());
typeTag.setName(test.getType() != null ? test.getType() : "");
typeTag.setBackground("1AFFA142");
typeTag.setColor(isDarkModeOn ? "EB9238" : "FFA142");
tagStyle.add(typeTag);
TagStyleEntity timeTag = new TagStyleEntity();
TagStyleEntity timeTag = new TagStyleEntity();
if (test.getStartPending()) {
timeTag.setName(test.getStartText());
} else {
timeTag.setName(GameViewUtils.getGameTestDate(test.getStart()));
timeTag.setBackground("1A06CEA8");
timeTag.setColor(isDarkModeOn ? "07A385" : "06CEA8");
tagStyle.add(timeTag);
}
timeTag.setBackground("1A06CEA8");
timeTag.setColor(isDarkModeOn ? "07A385" : "06CEA8");
tagStyle.add(timeTag);
} else {
tagStyle = gameEntity.getTagStyle();
}
@ -514,6 +524,68 @@ public class BindingAdapters {
}
}
public static void setGameTagsWithSellingPoint(LinearLayout layout, LayoutGameItemSellingPointBinding binding, GameEntity gameEntity, String subjectTag) {
if (subjectTag.equals(SUBJECT_TAG_SELLING_POINT)) {
layout.setVisibility(View.GONE);
binding.getRoot().setVisibility(View.VISIBLE);
GameEntity.SellingPoints sellingPoints = gameEntity.getSellingPoints();
if (sellingPoints != null) {
binding.tvSellingPoints.setVisibility(View.VISIBLE);
binding.tvSellingPoints.setText(sellingPoints.getText());
} else {
binding.tvSellingPoints.setVisibility(View.GONE);
}
ArrayList<TagStyleEntity> tagStyle = gameEntity.getTagStyle();
StringBuilder tagText = new StringBuilder();
for (int i = 0; i < tagStyle.size(); i++) {
if (i < 3) {
tagText.append(i == 0 ? "" : "·").append(tagStyle.get(i).getName());
}
}
binding.gtcvTags.setText(tagText);
} else {
layout.setVisibility(View.VISIBLE);
binding.getRoot().setVisibility(View.GONE);
switch (subjectTag) {
case SUBJECT_TAG_UPDATE:
List<TagStyleEntity> updateTags = new ArrayList<>();
TagStyleEntity updateTag = new TagStyleEntity(
"local_generated",
TimeUtils.getFormatTime(gameEntity.getUpdateTime(), "MM-dd") + " 更新",
"",
"1383EB",
"E8F3FF",
"1383EB",
false
);
updateTags.add(updateTag);
GameViewUtils.setLabelList(layout.getContext(), layout, updateTags);
break;
case SUBJECT_TAG_TYPE:
GameViewUtils.setLabelList(layout.getContext(), layout, gameEntity.getTagStyle());
break;
default:
setGameTags(layout, gameEntity, subjectTag);
break;
}
}
ViewParent parent = binding.getRoot().getParent();
if (parent instanceof ConstraintLayout) {
ConstraintLayout constraintLayout = (ConstraintLayout) parent;
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);
constraintSet.clear(R.id.gameDesSpace, ConstraintSet.BOTTOM);
if (subjectTag.equals(SUBJECT_TAG_SELLING_POINT)) {
constraintSet.connect(R.id.gameDesSpace, ConstraintSet.BOTTOM, R.id.layout_selling_points, ConstraintSet.TOP);
} else {
constraintSet.connect(R.id.gameDesSpace, ConstraintSet.BOTTOM, R.id.label_list, ConstraintSet.TOP);
}
constraintSet.applyTo(constraintLayout);
}
}
public static void setVideoDetailGameTags(LinearLayout layout, GameEntity gameEntity) {
try {
ArrayList<TagStyleEntity> tagStyle = new ArrayList<>();

View File

@ -0,0 +1,294 @@
package com.gh.common.dialog
import android.content.Context
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import com.gh.common.util.DirectUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.HaloApp
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.core.utils.CurrentActivityHolder
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.databinding.DialogFragmentAccelerateExporationBinding
import kotlinx.parcelize.Parcelize
public class AccelerateExpirationDialogFragment : BaseDialogFragment() {
private lateinit var binding: DialogFragmentAccelerateExporationBinding
private var _reminder: ExpirationReminder? = null
override fun onBack(): Boolean {
return true
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_reminder = arguments?.getParcelable(KEY_EXPIRATION_REMINDER)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return DialogFragmentAccelerateExporationBinding.inflate(inflater, container, false)
.also {
binding = it
}.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val reminder = _reminder ?: return
val (title, content) = when (reminder) {
is ExpirationReminder.UserExpired -> {
SensorsBridge.trackMonthlyPackageExpiresDialogShow(
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance
)
getString(
R.string.membership_expiration_reminder_title,
"${reminder.days}"
) to getString(R.string.membership_expiration_reminder_content)
}
is ExpirationReminder.InsufficientDuration -> {
SensorsBridge.trackInsufficientDurationDialogShow(
reminder.gameId,
reminder.gameName,
reminder.pkgName,
reminder.sourceEntrance
)
getString(
R.string.duration_time_out_reminder_title,
"${reminder.hours}"
) to getString(R.string.duration_time_out_reminder_content)
}
is ExpirationReminder.DurationExpired -> {
SensorsBridge.trackTimedPackageExpiresDialogShow(
reminder.gameId,
reminder.gameName,
reminder.pkgName,
reminder.sourceEntrance
)
getString(
R.string.duration_expiration_reminder_title,
"${reminder.days}"
) to getString(R.string.duration_expiration_reminder_content)
}
is ExpirationReminder.TimeRunsOut -> {
SensorsBridge.trackDurationExhaustedDialogShow()
getString(R.string.accelerator_member_expired_title) to getString(R.string.accelerator_member_expired_content)
}
}
binding.tvTitle.text = title
binding.tvContent.text = content
binding.tvCancel.setOnClickListener {
when (reminder) {
is ExpirationReminder.UserExpired -> {
SensorsBridge.trackMonthlyPackageExpiresDialogClick(
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_KNOW
)
}
is ExpirationReminder.InsufficientDuration -> {
SensorsBridge.trackInsufficientDurationDialogClick(
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_KNOW
)
}
is ExpirationReminder.DurationExpired -> {
SensorsBridge.trackTimedPackageExpiresDialogClick(
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_KNOW
)
}
is ExpirationReminder.TimeRunsOut -> {
SensorsBridge.trackDurationExhaustedDialogClick(BUTTON_NAME_KNOW)
}
}
dismissAllowingStateLoss()
}
binding.tvSubmit.setOnClickListener {
when (reminder) {
is ExpirationReminder.UserExpired -> {
SensorsBridge.trackMonthlyPackageExpiresDialogClick(
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_BUY
)
}
is ExpirationReminder.InsufficientDuration -> {
SensorsBridge.trackInsufficientDurationDialogClick(
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_BUY
)
}
is ExpirationReminder.DurationExpired -> {
SensorsBridge.trackTimedPackageExpiresDialogClick(
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_BUY
)
}
is ExpirationReminder.TimeRunsOut -> {
SensorsBridge.trackDurationExhaustedDialogClick(BUTTON_NAME_BUY)
}
}
SensorsBridge.trackMyAssetsPageShow(
reminder.pkgName,
reminder.gameId,
reminder.gameName,
reminder.dialogName
)
dismissAllowingStateLoss()
DirectUtils.navigateToMyAssetsPage(requireContext(), reminder.sourceEntrance)
}
}
override fun onStart() {
super.onStart()
val width = HaloApp.getInstance().resources.displayMetrics.widthPixels - 60F.dip2px()
val height = dialog?.window?.attributes?.height ?: ViewGroup.LayoutParams.WRAP_CONTENT
dialog?.window?.setLayout(width, height)
}
companion object {
const val KEY_EXPIRATION_REMINDER = "key_expiration_reminder"
private const val BUTTON_NAME_KNOW = "知道了"
private const val BUTTON_NAME_BUY = "前往购买"
fun checkDialogShown(context: Context, reminder: ExpirationReminder, callback: ((Boolean) -> Unit)? = null) {
when (reminder) {
is ExpirationReminder.UserExpired -> {
val days = SPUtils.getLong(Constants.SP_ACCELERATOR_USER_REMAINING_REMIND)
if (reminder.days != days) {
SPUtils.setLong(Constants.SP_ACCELERATOR_USER_REMAINING_REMIND, reminder.days)
show(context, reminder)
callback?.invoke(true)
} else {
callback?.invoke(false)
}
}
is ExpirationReminder.InsufficientDuration -> {
val hours = SPUtils.getLong(Constants.SP_ACCELERATOR_DURATION_REMAINING_HOURS)
if (reminder.hours != hours) {
SPUtils.setLong(Constants.SP_ACCELERATOR_DURATION_REMAINING_HOURS, reminder.hours)
show(context, reminder)
callback?.invoke(true)
} else {
callback?.invoke(false)
}
}
is ExpirationReminder.DurationExpired -> {
val days = SPUtils.getLong(Constants.SP_ACCELERATOR_DURATION_REMAINING_DAYS)
if (reminder.days != days) {
SPUtils.setLong(Constants.SP_ACCELERATOR_DURATION_REMAINING_DAYS, reminder.days)
show(context, reminder)
callback?.invoke(true)
} else {
callback?.invoke(false)
}
}
is ExpirationReminder.TimeRunsOut -> {
val hasShow = SPUtils.getBoolean(Constants.SP_ACCELERATOR_MEMBERSHIP_EXPIRED)
if (!hasShow) {
SPUtils.setBoolean(Constants.SP_ACCELERATOR_MEMBERSHIP_EXPIRED, true)
show(context, reminder)
callback?.invoke(true)
} else {
callback?.invoke(false)
}
}
}
}
fun show(context: Context, reminder: ExpirationReminder) {
if (context is AppCompatActivity) {
context.supportFragmentManager
} else {
(CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
}?.let {
val fragment = AccelerateExpirationDialogFragment().apply {
arguments = Bundle().apply {
putParcelable(KEY_EXPIRATION_REMINDER, reminder)
}
}
fragment.show(it, AcceleratorPermissionGuideDialogFragment::class.java.name)
}
}
}
sealed class ExpirationReminder(
val gameId: String,
val gameName: String,
val pkgName: String,
val sourceEntrance: String
) :
Parcelable {
abstract val dialogName: String
@Parcelize
data class InsufficientDuration(
val hours: Long,
private val _gameId: String,
private val _gameName: String,
private val _pkgName: String,
private val _sourceEntrance: String,
) :
ExpirationReminder(_gameId, _gameName, _pkgName, _sourceEntrance) {
override val dialogName: String
get() = "时长不足提示弹窗"
}
@Parcelize
data class DurationExpired(
val days: Long,
private val _gameId: String,
private val _gameName: String,
private val _pkgName: String,
private val _sourceEntrance: String,
) :
ExpirationReminder(_gameId, _gameName, _pkgName, _sourceEntrance) {
override val dialogName: String
get() = "计时套餐临期提示弹窗"
}
@Parcelize
data class UserExpired(
val days: Long,
private val _gameId: String,
private val _gameName: String,
private val _pkgName: String,
private val _sourceEntrance: String,
) :
ExpirationReminder(_gameId, _gameName, _pkgName, _sourceEntrance) {
override val dialogName: String
get() = "包月套餐临期提示弹窗"
}
@Parcelize
data class TimeRunsOut(
private val _sourceEntrance: String,
) : ExpirationReminder("", "", "", _sourceEntrance) {
override val dialogName: String
get() = "时长耗尽提示弹窗"
}
}
}

View File

@ -0,0 +1,68 @@
package com.gh.common.dialog
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import com.gh.gamecenter.common.HaloApp
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.core.utils.CurrentActivityHolder
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.databinding.DialogFragmentAcceleratorPermissionGuideBinding
class AcceleratorPermissionGuideDialogFragment : BaseDialogFragment() {
private lateinit var binding: DialogFragmentAcceleratorPermissionGuideBinding
private var callback: (() -> Unit)? = null
override fun onBack(): Boolean {
return true
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return DialogFragmentAcceleratorPermissionGuideBinding.inflate(inflater, container, false)
.also {
binding = it
}.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
SPUtils.setBoolean(Constants.SP_ACCELERATOR_PERMISSION_GUIDE_SHOW, true)
binding.tvSubmit.setOnClickListener {
dismiss()
callback?.invoke()
}
}
fun setOnCallback(block: () -> Unit) {
this.callback = block
}
override fun onStart() {
super.onStart()
val width = HaloApp.getInstance().resources.displayMetrics.widthPixels - 60F.dip2px()
val height = dialog?.window?.attributes?.height ?: ViewGroup.LayoutParams.WRAP_CONTENT
dialog?.window?.setLayout(width, height)
}
companion object {
fun show(context: Context, callback: () -> Unit) {
if (context is AppCompatActivity) {
context.supportFragmentManager
} else {
(CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
}?.let {
val fragment = AcceleratorPermissionGuideDialogFragment()
fragment.setOnCallback(callback)
fragment.show(it, AcceleratorPermissionGuideDialogFragment::class.java.name)
}
}
}
}

View File

@ -16,6 +16,7 @@ import com.gh.common.pop.EditBindWechatPop
import com.gh.common.pop.RealNameTipsPop
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.entity.WechatConfigEntity
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.toObject
@ -83,79 +84,90 @@ class ReserveSuccessReminderDialog(
handlers.clear()
binding.flContentContainer.removeAllViews()
val smsConfig = reserveReminder.smsConfig
val wechatConfig = reserveReminder.wechatConfig
when {
reserveReminder.onlyShowWechatReminder && !wechatConfig.isReminderEnable -> { // 只显示微信:未开启
binding.tvContent.setText(R.string.reverse_success_without_reminder_tips)
arrayListOf(OnlyWechatReminderUnableHandler(16.dp, binding.flContentContainer, this))
val configTypes = mutableListOf<Int>()
// 短信
if (reserveReminder.hasSmsConfig) {
if (reserveReminder.smsConfig.notice) {
configTypes.add(SMS_REMINDER_ENABLE_TYPE)
} else {
configTypes.add(SMS_REMINDER_UNABLE_TYPE)
}
reserveReminder.onlyShowWechatReminder && wechatConfig.isReminderEnable -> { // 只显示微信:已开
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
arrayListOf(
WechatReminderEnableHandler(
reserveReminder.wechatConfig.nickName,
8.dp,
binding.flContentContainer,
this
)
)
}
!smsConfig.notice && !wechatConfig.isReminderEnable -> { // 短信,微信未开启
binding.tvContent.setText(R.string.reverse_success_without_reminder_tips)
arrayListOf(
SmsReminderUnableHandler(16.dp, binding.flContentContainer, this),
WechatReminderUnableHandler(8.dp, binding.flContentContainer, this)
)
}
smsConfig.notice && wechatConfig.isReminderEnable -> {// 短信,微信已开启
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
arrayListOf(
SmsReminderEnableHandler(reserveReminder.smsConfig, 8.dp, binding.flContentContainer, this),
WechatReminderEnableHandler(
reserveReminder.wechatConfig.nickName,
8.dp,
binding.flContentContainer,
this
)
)
}
smsConfig.notice && !wechatConfig.isReminderEnable -> { // 短信开启,微信未开启
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
arrayListOf(
SmsReminderEnableHandler(smsConfig, 8.dp, binding.flContentContainer, this),
WechatReminderUnableHandler(16.dp, binding.flContentContainer, this)
)
}
!smsConfig.notice && wechatConfig.isReminderEnable -> { // 微信开启,短信未开启
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
arrayListOf(
WechatReminderEnableHandler(
wechatConfig.nickName,
8.dp,
binding.flContentContainer,
this
),
SmsReminderUnableHandler(16.dp, binding.flContentContainer, this)
)
}
else -> {
binding.tvContent.setText(R.string.reverse_success_without_reminder_tips)
arrayListOf()
}
}.let {
handlers.clear()
handlers.addAll(it)
}
// 微信
if (reserveReminder.wechatConfig.isReminderEnable) {
configTypes.add(WECHAT_REMINDER_ENABLE_TYPE)
} else {
configTypes.add(WECHAT_REMINDER_UNABLE_TYPE)
}
// 日历
if (reserveReminder.hasCalendarConfig) {
if (reserveReminder.calendarConfig.notice) {
configTypes.add(CALENDAR_REMINDER_ENABLE_TYPE)
} else {
configTypes.add(CALENDAR_REMINDER_UNABLE_TYPE)
}
}
if (configTypes.size == 1) {
// 只有微信提醒
if (configTypes.first() == WECHAT_REMINDER_ENABLE_TYPE) {
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
handlers.add(
WechatReminderEnableHandler(reserveReminder.wechatConfig, 8.dp, binding.flContentContainer, this)
)
} else {
binding.tvContent.setText(R.string.reverse_success_without_reminder_tips)
handlers.add(OnlyWechatReminderUnableHandler(16.dp, binding.flContentContainer, this))
}
} else {
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
var isLargerSpacing = true
fun getPaddingTop(): Int {
val paddingTop = if (isLargerSpacing) 16.dp else 8.dp
isLargerSpacing = false
return paddingTop
}
configTypes.sorted().forEach {
when (it) {
SMS_REMINDER_ENABLE_TYPE -> {
isLargerSpacing = true
SmsReminderEnableHandler(reserveReminder.smsConfig, 8.dp, binding.flContentContainer, this)
}
SMS_REMINDER_UNABLE_TYPE -> {
SmsReminderUnableHandler(getPaddingTop(), binding.flContentContainer, this)
}
WECHAT_REMINDER_ENABLE_TYPE -> {
isLargerSpacing = true
WechatReminderEnableHandler(
reserveReminder.wechatConfig,
8.dp,
binding.flContentContainer,
this
)
}
WECHAT_REMINDER_UNABLE_TYPE ->
WechatReminderUnableHandler(getPaddingTop(), binding.flContentContainer, this)
CALENDAR_REMINDER_ENABLE_TYPE -> {
isLargerSpacing = true
CalendarReminderEnableHandler(8.dp, binding.flContentContainer, this)
}
CALENDAR_REMINDER_UNABLE_TYPE ->
CalendarReminderUnableHandler(getPaddingTop(), binding.flContentContainer, this)
else -> null
}?.let(handlers::add)
}
}
handlers.forEach {
binding.flContentContainer.addView(it.init())
}
@ -220,9 +232,14 @@ class ReserveSuccessReminderDialog(
listener.changeWechatBinding()
}
override fun updateCalendarReminder() {
listener.updateCalendarReminder()
}
override fun onStart() {
super.onStart()
window?.let {
it.setDimAmount(0.4F)
it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
val params = it.attributes
params.width = DisplayUtils.dip2px(300F)
@ -244,6 +261,13 @@ class ReserveSuccessReminderDialog(
private val Int.dp: Int
get() = DisplayUtils.dip2px(this.toFloat())
private const val SMS_REMINDER_ENABLE_TYPE = 1
private const val WECHAT_REMINDER_ENABLE_TYPE = 2
private const val CALENDAR_REMINDER_ENABLE_TYPE = 3
private const val SMS_REMINDER_UNABLE_TYPE = 4
private const val WECHAT_REMINDER_UNABLE_TYPE = 5
private const val CALENDAR_REMINDER_UNABLE_TYPE = 6
fun create(context: Context, listener: OnReserveReminderListener) =
ReserveSuccessReminderDialog(
context,
@ -360,7 +384,7 @@ class ReserveSuccessReminderDialog(
}
class WechatReminderEnableHandler(
private val nickName: String,
private val wechatConfig: WechatConfigEntity,
topMargin: Int,
parent: ViewGroup,
listener: OnReserveSuccessListener
@ -380,7 +404,7 @@ class ReserveSuccessReminderDialog(
}.root
override fun initView() {
binding.tvWechat.text = nickName
binding.tvWechat.text = wechatConfig.nickName
binding.vModifyWechat.setOnClickListener {
editWechatPop.showAsDropDown(
binding.ivModifyWechat,
@ -411,6 +435,51 @@ class ReserveSuccessReminderDialog(
}
class CalendarReminderUnableHandler(
topMargin: Int,
parent: ViewGroup,
listener: OnReserveSuccessListener
) : ReminderContentHandler(topMargin, parent, listener) {
private lateinit var binding: LayoutReserveWechatReminderUnableBinding
// 复用微信提醒未开启状态布局
override fun createView(inflater: LayoutInflater) =
LayoutReserveWechatReminderUnableBinding.inflate(inflater, parent, false)
.also {
binding = it
}.root
override fun initView() {
binding.tvWechatReminderTitle.setText(R.string.calendar_reminders)
binding.tvWechatReminderDescription.setText(R.string.calendar_reminders_description)
binding.vWechatAdd.setOnClickListener {
listener.updateCalendarReminder()
}
}
}
class CalendarReminderEnableHandler(
topMargin: Int,
parent: ViewGroup,
listener: OnReserveSuccessListener
) : ReminderContentHandler(topMargin, parent, listener) {
private lateinit var binding: LayoutReserveCalendarReminderUnableBinding
override fun createView(inflater: LayoutInflater) =
LayoutReserveCalendarReminderUnableBinding.inflate(inflater, parent, false)
.also {
binding = it
}.root
override fun initView() {
}
}
}
@ -425,4 +494,6 @@ interface OnReserveSuccessListener {
fun verifyPhoneNumber()
fun changeWechatBinding()
fun updateCalendarReminder()
}

View File

@ -19,7 +19,7 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
ExposureThrottleBus(
{ commitExposure(it) },
Consumer(Throwable::printStackTrace),
{ commitWXCPMExposure(it) }
{ commitExternalExposure(it) }
)
}
var layoutManager: LayoutManager? = null
@ -31,7 +31,7 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
if (fragment == f) {
visibleState?.let { commitExposure(it) }
visibleState?.let { commitWXCPMExposure(it) }
visibleState?.let { commitExternalExposure(it) }
throttleBus.clear()
}
}
@ -97,23 +97,33 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
}
/**
* 微信小游戏CPM曝光事件接口上报由于普通曝光事件的上报通道存在节流特性不符合CPM接口对于数据上报的实时性和准确性的要求所以使用额外的通道进行上报
* 提交第三方的曝光事件 微信小游戏CPM 和 DSP 游戏)
* 由于普通曝光事件的上报通道存在节流特性不符合CPM接口对于数据上报的实时性和准确性的要求所以使用额外的通道进行上报
*/
private fun commitWXCPMExposure(visibleState: ExposureThrottleBus.VisibleState) {
private fun commitExternalExposure(visibleState: ExposureThrottleBus.VisibleState) {
val eventList = arrayListOf<ExposureEvent>()
val cpmEventList = arrayListOf<ExposureEvent>()
val dspEventList = arrayListOf<ExposureEvent>()
for (pos in visibleState.firstVisiblePosition..visibleState.lastVisiblePosition) {
try {
exposable.getEventByPosition(pos)?.let {
if (it.payload.miniGameType == Constants.WECHAT_MINI_GAME_CPM) {
eventList.add(it)
when (it.payload.miniGameType) {
Constants.WECHAT_MINI_GAME_CPM -> cpmEventList.add(it)
Constants.DSP_GAME -> dspEventList.add(it)
else -> {
// do nothing
}
}
}
exposable.getEventListByPosition(pos)?.let { list ->
list.forEach {
if (it.payload.miniGameType == Constants.WECHAT_MINI_GAME_CPM) {
eventList.add(it)
when (it.payload.miniGameType) {
Constants.WECHAT_MINI_GAME_CPM -> cpmEventList.add(it)
Constants.DSP_GAME -> dspEventList.add(it)
else -> {
// do nothing
}
}
}
}
@ -121,7 +131,8 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
// Just ignore the error.
}
}
ExposureManager.logCPM(eventList)
ExposureManager.logExternal(cpmEventList, dspEventList)
}
}

View File

@ -6,7 +6,9 @@ import com.gh.gamecenter.common.loghub.TLogHubHelper
import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet
import com.gh.gamecenter.common.utils.toJson
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.dsp.DspReportHelper
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureType
import com.gh.gamecenter.feature.minigame.wechat.WGameSubjectCPMListReportHelper
import com.lightgame.utils.Utils
import com.volcengine.model.tls.LogItem
@ -36,6 +38,10 @@ object ExposureManager {
AppExecutor.logExecutor.execute {
if (event.payload.miniGameType == Constants.WECHAT_MINI_GAME_CPM) {
WGameSubjectCPMListReportHelper.reportExposure(event)
} else if (event.payload.miniGameType == Constants.DSP_GAME) {
if (event.event == ExposureType.DOWNLOAD_COMPLETE) {
DspReportHelper.report(event.payload.downloadUrl)
}
}
if (!exposureCache.contains(event.id)) {
exposureSet.add(event)
@ -64,12 +70,18 @@ object ExposureManager {
}
/**
* Log a wechat mini game cpm collection of exposure event.
* Log a collection of exposure event for external use.
*/
fun logCPM(eventList: List<ExposureEvent>) {
fun logExternal(cpmEventList: List<ExposureEvent>, dspEventList: ArrayList<ExposureEvent>) {
AppExecutor.logExecutor.execute {
if (eventList.isNotEmpty()) {
WGameSubjectCPMListReportHelper.reportExposure(eventList.toSet())
if (cpmEventList.isNotEmpty()) {
WGameSubjectCPMListReportHelper.reportExposure(cpmEventList.toSet())
}
if (dspEventList.isNotEmpty()) {
for (event in dspEventList) {
DspReportHelper.report(event.payload.showUrl)
}
}
}
}

View File

@ -89,6 +89,7 @@ object ExposureUtils {
exposureEvent.payload.path = path
exposureEvent.payload.speed = speed
exposureEvent.payload.redirectedUrlList = redirectedUrlList
exposureEvent.payload.downloadUrl = gameEntity.downloadUrl
ExposureManager.log(exposureEvent)
ExposureManager.commitSavedExposureEvents(forcedUpload = true)

View File

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.PackageFlavorHelper
import com.gh.gamecenter.common.utils.debounceActionWithInterval
import com.gh.gamecenter.common.utils.toJson
import com.gh.gamecenter.common.utils.toObject
@ -123,10 +124,16 @@ object RegionSettingHelper {
mIsInit = false
}
val fakeIp = if (PackageFlavorHelper.IS_TEST_FLAVOR) {
SPUtils.getString(Constants.SP_TEST_FLAVOR_IP)
} else {
""
}
// 使用默认的 Schdulers.io() 可能会触发 OOM
RetrofitManager.getInstance()
.api
.getRegionSetting(HaloApp.getInstance().channel)
.getRegionSetting(HaloApp.getInstance().channel, fakeIp)
.subscribeOn(Schedulers.io())
.subscribe(object : BiResponse<RegionSetting>() {
override fun onSuccess(data: RegionSetting) {

View File

@ -0,0 +1,29 @@
package com.gh.common.interceptor
import com.gh.gamecenter.common.constant.RouteConsts
import com.halo.assistant.HaloApp
import com.therouter.router.RouteItem
import com.therouter.router.interceptor.RouterReplaceInterceptor
import com.therouter.router.matchRouteMap
/**
* 主拦截器
*/
class MainInterceptor: RouterReplaceInterceptor() {
override fun replace(routeItem: RouteItem?): RouteItem? {
if (routeItem == null) return null
// 用户是否已经同意隐私政策
val isUserAcceptPrivacyPolicy = HaloApp.isUserAcceptPrivacyPolicy(HaloApp.getInstance())
// 如果用户已经同意隐私政策并且应用已经启动,直接返回 routeItem ,运行跳转
if (isUserAcceptPrivacyPolicy && HaloApp.getInstance().isAlreadyUpAndRunning) {
return routeItem
}
// 指向调整为 SplashScreenActivity
return matchRouteMap(RouteConsts.activity.splashActivity)
}
}

View File

@ -59,7 +59,7 @@ class CustomFloatingWindowHandler(priority: Int) : PriorityChainHandler(priority
override fun onProcess(): Boolean {
when (getStatus()) {
STATUS_VALID -> {
_showFloatingAction.value = Event(data)
_showFloatingAction.postValue(Event(data))
return true
// floatingWindowProvider.showFloatingWindowOnly(
// mFragment!!,

View File

@ -64,7 +64,8 @@ object GlobalPriorityChainHelper : ISuperiorChain {
* 预启动所有的优先级弹窗管理链
*/
fun preStart(withSpecialDelay: Boolean) {
val launchRedirectHandler = LaunchRedirectHandler(-101)
val launchRedirectHandler = LaunchRedirectHandler(-102)
val membershipExpiredHandler = MembershipExpiredHandler(-101)
val updateDialogHandler = UpdateDialogHandler(-100)
val privacyPolicyDialogHandler = PrivacyPolicyDialogHandler(-99)
val notificationPermissionDialogHandler = NotificationPermissionDialogHandler(0)
@ -74,6 +75,7 @@ object GlobalPriorityChainHelper : ISuperiorChain {
val resumeDownloadHandler = ResumeDownloadHudHandler(4)
mainChain.addHandler(launchRedirectHandler)
mainChain.addHandler(membershipExpiredHandler)
mainChain.addHandler(updateDialogHandler)
mainChain.addHandler(privacyPolicyDialogHandler)
mainChain.addHandler(welcomeDialogHandler)
@ -83,6 +85,7 @@ object GlobalPriorityChainHelper : ISuperiorChain {
mainChain.addHandler(resumeDownloadHandler)
launchRedirectHandler.doPreProcess()
membershipExpiredHandler.doPreProcess()
updateDialogHandler.doPreProcess()
// 首次启动延迟 300ms保证请求首次启动时已经获取到了 GID 、 OAID 等标记

View File

@ -0,0 +1,80 @@
package com.gh.common.prioritychain
import android.os.Bundle
import androidx.fragment.app.FragmentActivity
import com.gh.common.dialog.AccelerateExpirationDialogFragment
import com.gh.gamecenter.common.constant.Constants.SP_ACCELERATOR_MEMBERSHIP_EXPIRED
import com.gh.gamecenter.core.utils.CurrentActivityHolder
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.login.user.UserRepository
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
class MembershipExpiredHandler(priority: Int) : PriorityChainHandler(priority) {
fun doPreProcess() {
UserRepository.getInstance().setAutoLoginListener {
val hasShow = SPUtils.getBoolean(SP_ACCELERATOR_MEMBERSHIP_EXPIRED)
val vipEntity = AcceleratorDataHolder.instance.vipEntity
if (getStatus() == STATUS_PENDING) {
if (vipEntity == null ||
vipEntity.vipStatus ||
vipEntity.isNewUser ||
vipEntity.isVipRefundUser ||
vipEntity.isDurationRefundUser ||
hasShow
) {
processNext()
} else {
updateStatus(STATUS_VALID)
process()
}
} else {
if (vipEntity == null ||
vipEntity.vipStatus ||
vipEntity.isNewUser ||
vipEntity.isVipRefundUser ||
vipEntity.isDurationRefundUser ||
hasShow
) {
updateStatus(STATUS_INVALID)
} else {
updateStatus(STATUS_VALID)
}
}
}
}
override fun onProcess(): Boolean {
val currentActivity = CurrentActivityHolder.getCurrentActivity()
if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) {
when (getStatus()) {
STATUS_VALID -> {
val reminder = AccelerateExpirationDialogFragment.ExpirationReminder.TimeRunsOut("")
SPUtils.setBoolean(SP_ACCELERATOR_MEMBERSHIP_EXPIRED, true)
val dialogFragment = AccelerateExpirationDialogFragment().apply {
arguments = Bundle().apply {
putParcelable(
AccelerateExpirationDialogFragment.KEY_EXPIRATION_REMINDER, reminder
)
}
}
dialogFragment.dialog?.setOnDismissListener {
processNext()
}
dialogFragment.show(
(currentActivity as FragmentActivity).supportFragmentManager,
AccelerateExpirationDialogFragment::class.java.name
)
return true
}
STATUS_INVALID -> {
processNext()
}
}
}
return false
}
}

View File

@ -4,8 +4,10 @@ import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import com.gh.common.databind.BindingAdapters
import com.gh.gamecenter.common.databinding.LayoutGameItemSellingPointBinding
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.provider.IBindingAdaptersProvider
import com.gh.gamecenter.home.custom.adapter.CustomViewExt
@com.therouter.inject.ServiceProvider
class BindingAdaptersProviderImpl : IBindingAdaptersProvider {
@ -18,7 +20,7 @@ class BindingAdaptersProviderImpl : IBindingAdaptersProvider {
}
override fun setGameTags(layout: LinearLayout, gameEntity: GameEntity) {
BindingAdapters.setGameTags(layout, gameEntity)
BindingAdapters.setGameTags(layout, gameEntity, "")
}
override fun setMessageUnread(view: TextView, unreadCount: Int) {
@ -28,4 +30,17 @@ class BindingAdaptersProviderImpl : IBindingAdaptersProvider {
override fun setGame(view: View, gameEntity: GameEntity) {
BindingAdapters.setGame(view, gameEntity)
}
override fun setGameTagsWithSellingPoints(
layout: LinearLayout,
sellingPointsBinding: LayoutGameItemSellingPointBinding,
gameEntity: GameEntity,
subjectTag: String
) {
BindingAdapters.setGameTagsWithSellingPoint(layout, sellingPointsBinding, gameEntity, subjectTag)
}
override fun setGameDescription(tvDesc: TextView, briefStyle: String, game: GameEntity) {
CustomViewExt.setDescription(tvDesc, briefStyle, game)
}
}

View File

@ -31,4 +31,6 @@ class BuildConfigImpl : IBuildConfigProvider {
override fun getWGameCPMBusiAppId(): String = BuildConfig.WGAME_CPM_BUSIAPPID
override fun getLogProducerProject(): String = BuildConfig.LOG_HUB_PROJECT
override fun getDspApiHost(): String = BuildConfig.DSP_API_HOST
}

View File

@ -2,6 +2,8 @@ package com.gh.common.provider
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.entity.GameUpdateEntity
@ -11,6 +13,7 @@ import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.provider.IDownloadButtonClickedProvider
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.packagehelper.PackageRepository
import com.halo.assistant.HaloApp
import com.lightgame.download.DownloadEntity
import com.lightgame.utils.Utils
@ -27,6 +30,9 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider {
var packageName = ""
var exposureSourceList: List<ExposureSource>? = null
var customPageTrackData: CustomPageTrackData? = null
val pushMessageId = (HaloApp.get(Constants.PUSH_MESSAGE_ID, false) as? String) ?: ""
val pushLinkId = (HaloApp.get(Constants.PUSH_LINK_ENTITY, false) as? LinkEntity)?.link ?: ""
val isFromPush = pushMessageId.isNotEmpty()
val boundedObject = downloadButton.getObject()
@ -113,8 +119,10 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider {
}
// 小游戏的启动不需要上报下载点击事件
// @see https://jira.shanqu.cc/browse/GHZSCY-7013
if (boundedObject is GameEntity && boundedObject.isMiniGame()) {
// @see https://jira.shanqu.cc/browse/GHZSCY-7013 & https://jira.shanqu.cc/browse/GHZSCY-7918
if (boundedObject is GameEntity
&& (boundedObject.isMiniGame() || boundedObject.isDspGame)
) {
return
}
@ -136,6 +144,9 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider {
"last_page_name", GlobalActivityManager.getLastPageEntity().pageName,
"last_page_id", GlobalActivityManager.getLastPageEntity().pageId,
"last_page_business_id", GlobalActivityManager.getLastPageEntity().pageBusinessId,
"is_from_push_notifications", isFromPush,
"message_id", pushMessageId,
"link_id", pushLinkId,
*customPageKV
)
}

View File

@ -1,24 +1,57 @@
package com.gh.common.provider
import com.gh.common.util.PackageUtils
import android.annotation.SuppressLint
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.core.provider.IAcceleratorDataHolderProvider
import com.gh.gamecenter.feature.entity.BaseEntity
import com.gh.gamecenter.feature.entity.VipEntity
import com.gh.gamecenter.login.retrofit.RetrofitManager
import com.gh.gamecenter.login.user.UserManager
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
@com.therouter.inject.ServiceProvider
class IAcceleratorDataHolderProviderImpl : IAcceleratorDataHolderProvider {
override fun setVipEntity(vip: Any) {
if (vip is VipEntity) {
AcceleratorDataHolder.instance.setVipEntity(vip)
override fun isPaidVip(): Boolean =
AcceleratorDataHolder.instance.isPaidVip
override fun getGhVersionName(): String {
return PackageUtils.getGhVersionName()
}
/**
* 请注意,由于光环后端无法获取 奇游时长套餐 加速时长,这里的 vipStatus不准
* 这里只做参考具体的状态需要奇游sdk提供如果奇游sdk返回失败则以这条接口结果为准
*/
@SuppressLint("CheckResult")
override fun loadVipEntityFromGh(userId: String, callBack: (Any?) -> Unit) {
RetrofitManager.getInstance().newApi.getVipStatus(
userId.ifBlank { UserManager.getInstance().userId },
"gjonline_vip",
true
)
.compose(singleToMain())
.subscribe(object : BiResponse<BaseEntity<VipEntity>>() {
override fun onSuccess(data: BaseEntity<VipEntity>) {
callBack(data.data)
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
callBack(null)
}
})
}
override fun updateVipEntity(data: Any) {
if (data is VipEntity) {
AcceleratorDataHolder.instance.setVipEntity(data)
}
}
override fun addInitFunResult(result: String) {
AcceleratorDataHolder.instance.addInitFunResult(result)
}
override fun getInitFunResults(): List<String> {
return AcceleratorDataHolder.instance.initResults
}
override fun clear() {
AcceleratorDataHolder.instance.clear()

View File

@ -5,7 +5,6 @@ import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.core.provider.IWechatPayResultProvider
import com.gh.gamecenter.feature.entity.OrderEntity
import com.gh.gamecenter.feature.entity.VipEntity
import com.gh.gamecenter.feature.eventbus.EBPayState
import com.halo.assistant.accelerator.repository.AccelerationRepository
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
@ -25,14 +24,8 @@ class WechatPayResultProviderImpl : IWechatPayResultProvider {
// 支付成功
EventBus.getDefault().post(EBPayState.PaySuccess)
// 先刷新本地状态,支付成功,肯定是付费会员
AcceleratorDataHolder.instance.setVipEntity(
VipEntity(
_vipStatus = true,
_isNewUser = false,
_isTryVip = false
)
)
// 先刷新本地状态,支付成功
AcceleratorDataHolder.instance.handleUserRechargeSuccess(orderEntity)
SensorsBridge.trackMemberRechargeResult(
AccelerationRepository.PAYMENT_TYPE_WECHAT,

View File

@ -2,6 +2,7 @@ package com.gh.common.util
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
@ -12,15 +13,18 @@ import okhttp3.ResponseBody
object ActivationHelper {
private const val HAS_SENT_ACTIVATED_INFO = "has_sent_activated_info"
private const val SENT_ACTIVATED_INFO_TIME = "sent_activated_info_time"
private const val HAS_SENT_RETENTION_INFO = "has_sent_retention_info"
var mHasSentActivatedInfo = SPUtils.getBoolean(HAS_SENT_ACTIVATED_INFO, false)
private var hasSentActivatedInfo = SPUtils.getBoolean(HAS_SENT_ACTIVATED_INFO, false)
private var hasSentRetentionInfo = SPUtils.getBoolean(HAS_SENT_RETENTION_INFO, false)
/**
* 发送激活信息 (用于推广)
*/
@JvmStatic
fun sendActivationInfo() {
if (!mHasSentActivatedInfo) {
if (!hasSentActivatedInfo) {
RetrofitManager.getInstance()
.api.postActivationInfo()
.subscribeOn(Schedulers.io())
@ -28,8 +32,36 @@ object ActivationHelper {
override fun onResponse(response: ResponseBody?) {
super.onResponse(response)
mHasSentActivatedInfo = true
hasSentActivatedInfo = true
SPUtils.setBoolean(HAS_SENT_ACTIVATED_INFO, true)
SPUtils.setLong(SENT_ACTIVATED_INFO_TIME, System.currentTimeMillis())
}
})
}
}
/**
* 发送次日留存信息 (用于推广)
*/
@JvmStatic
fun sendRetentionInfo() {
if (hasSentActivatedInfo && !hasSentRetentionInfo) {
val activateTimeMillis = SPUtils.getLong(SENT_ACTIVATED_INFO_TIME, 0)
val currentTimeMillis = System.currentTimeMillis()
if (!TimeUtils.isNextDay(activateTimeMillis, currentTimeMillis)) {
return
}
RetrofitManager.getInstance()
.api.postRetentionInfo()
.subscribeOn(Schedulers.io())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
super.onResponse(response)
hasSentRetentionInfo = true
SPUtils.setBoolean(HAS_SENT_RETENTION_INFO, true)
}
})
}

View File

@ -55,6 +55,8 @@ public class DetailDownloadUtils {
}
viewHolder.setSpeedViewsVisible(false);
// 默认为显示状态
viewHolder.getDownloadPb().setVisibility(View.VISIBLE);
// 根据预置的配置更新 ViewHolder 的状态 (譬如青少年模式、下载内容为空等)
if (updateViewHolderWithPredefinedConfig(viewHolder, gameEntity)) {
@ -100,9 +102,9 @@ public class DetailDownloadUtils {
viewHolder.getLocalDownloadContainer().setVisibility(View.VISIBLE);
}
// 畅玩未安装,且当前下载的默认类型为"普通下载",且用户未安装时,禁用双按钮,禁用畅玩
if (!VHelper.isInstalled(gameEntity.getUniquePackageName())
&& !PackagesManager.isInstalled(gameEntity.getUniquePackageName())
// 1.畅玩未安装/当前游戏配置了加速,且当前下载的默认类型为"普通下载",且用户未安装时,禁用双按钮,禁用畅玩
if ((!VHelper.isInstalled(gameEntity.getUniquePackageName())
&& !PackagesManager.isInstalled(gameEntity.getUniquePackageName()) || gameEntity.getCanSpeed())
&& downloadEntity != null
&& ExtensionsKt.isLocalDownloadInDualDownloadMode(downloadEntity)
) {
@ -110,8 +112,22 @@ public class DetailDownloadUtils {
if (viewHolder.getOverlayTv() != null) {
viewHolder.getOverlayTv().setVisibility(View.GONE);
}
// 此时需要将 本地占位按钮样式改成单条样式
if (viewHolder.getLocalDownloadContainer() != null) {
viewHolder.getLocalDownloadContainer().setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_fill_gradient_blue);
}
if (viewHolder.getLocalDownloadTitleTv() != null) {
viewHolder.getLocalDownloadTitleTv().setTextColor(ExtensionsKt.toColor(com.gh.gamecenter.common.R.color.text_aw_primary, viewHolder.getContext()));
}
} else {
viewHolder.getDownloadPb().setVisibility(View.VISIBLE);
// 将占位按钮样式恢复成左半边样式
if (viewHolder.getLocalDownloadContainer() != null) {
viewHolder.getLocalDownloadContainer().setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_light_fill_blue);
}
if (viewHolder.getLocalDownloadTitleTv() != null) {
viewHolder.getLocalDownloadTitleTv().setTextColor(ExtensionsKt.toColor(com.gh.gamecenter.common.R.color.text_theme, viewHolder.getContext()));
}
}
}
@ -138,12 +154,11 @@ public class DetailDownloadUtils {
showDualDownloadButton,
downloadEntity
);
if(!showVGame){
String rawBtnText = GameUtils.getDownloadBtnText(viewHolder.getContext(), gameEntity, false, showVGame, PluginLocation.only_game);
viewHolder.checkIfShowSpeedUi(rawBtnText);
if (downloadEntity != null && downloadEntity.getStatus() != DownloadStatus.done) {
// 如果存在未完成的任务,则不显示加速按钮
} else {
viewHolder.checkIfShowSpeedUi(showVGame, showDualDownloadButton);
}
} else {
// 游戏包含多 APK 的情况
viewHolder.getMultiVersionDownloadTv().setText("选择下载你的版本" + (TextUtils.isEmpty(downloadAddWord) ? "" : "-" + downloadAddWord));
@ -610,7 +625,7 @@ public class DetailDownloadUtils {
return (int) Math.ceil(downloadEntity.getPercent());
}
private static String convertSizeString(String sizeString) {
public static String convertSizeString(String sizeString) {
String numberPart;
String indicator;

View File

@ -854,7 +854,8 @@ object DirectUtils {
entrance: String? = null,
autoDownload: Boolean? = null,
tab: String? = "",
traceEvent: ExposureEvent? = null
traceEvent: ExposureEvent? = null,
from: String? = null
) {
if (id.isEmpty()) return
@ -862,6 +863,7 @@ object DirectUtils {
.path(RouteConsts.activity.gameDetailActivity)
.appendQueryParameter(KEY_ENTRANCE, entrance)
.appendQueryParameter(KEY_GAME_ID, id)
.appendQueryParameter(KEY_FROM, from)
.build()
TheRouter
@ -1128,9 +1130,15 @@ object DirectUtils {
@JvmStatic
fun directToWebView(context: Context, url: String, entrance: String? = null) {
directToWebView(context, url, entrance, null)
}
@JvmStatic
fun directToWebView(context: Context, url: String, entrance: String? = null, title: String? = null) {
if (url.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(EntranceConsts.KEY_GAMENAME, title)
if (url.contains("android_page_type=singleton")) {
bundle.putString(KEY_TO, SingletonWebActivity::class.java.simpleName)
} else {

View File

@ -61,7 +61,7 @@ object DownloadItemUtils {
gameEntity: GameEntity,
downloadEntity: DownloadEntity,
adapter: RecyclerView.Adapter<out RecyclerView.ViewHolder?>?,
index: Int
index: Int,
) {
if (gameEntity.id != downloadEntity.gameId) {
adapter?.notifyItemChanged(index)
@ -139,7 +139,7 @@ object DownloadItemUtils {
context: Context,
gameEntity: GameEntity,
holder: GameViewHolder,
hideDownloadBtnIfNoAvailableContent: Boolean
hideDownloadBtnIfNoAvailableContent: Boolean,
) {
updateItem(
context = context,
@ -155,7 +155,7 @@ object DownloadItemUtils {
context: Context,
gameEntity: GameEntity,
holder: GameViewHolder,
briefStyle: String?
briefStyle: String?,
) {
updateItem(
context = context,
@ -177,7 +177,7 @@ object DownloadItemUtils {
hideDownloadBtnIfNoAvailableContent: Boolean = false,
briefStyle: String? = null,
isShowRecommendStar: Boolean = false,
listener: DownloadButton.OnUpdateListener? = null
listener: DownloadButton.OnUpdateListener? = null,
) {
holder.gameDownloadBtn.putObject(gameEntity)
@ -223,7 +223,7 @@ object DownloadItemUtils {
gameEntity: GameEntity,
hideDownloadBtnIfNoAvailableContent: Boolean = false,
pluginLocation: PluginLocation? = PluginLocation.only_game,
listener: DownloadButton.OnUpdateListener?
listener: DownloadButton.OnUpdateListener?,
) {
// 控制是否显示下载按钮
downloadBtn.goneIf(context.getString(R.string.app_name) == gameEntity.name)
@ -452,7 +452,8 @@ object DownloadItemUtils {
DownloadStatus.subscribe,
DownloadStatus.diskisfull,
DownloadStatus.diskioerror,
DownloadStatus.overflow -> {
DownloadStatus.overflow,
-> {
buttonStyle = DownloadButton.ButtonStyle.NORMAL
setText(com.gh.gamecenter.feature.R.string.resume)
listener?.completion(downloadBtn.text)
@ -482,7 +483,7 @@ object DownloadItemUtils {
holder: GameViewHolder,
gameEntity: GameEntity,
briefStyle: String?,
isShowRecommendStar: Boolean = false
isShowRecommendStar: Boolean = false,
) {
updateItemViewStatus(holder, briefStyle, gameEntity.columnRecommend, isShowRecommendStar)
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity)
@ -507,7 +508,7 @@ object DownloadItemUtils {
holder: GameViewHolder,
gameEntity: GameEntity,
briefStyle: String?,
isShowRecommendStar: Boolean = false
isShowRecommendStar: Boolean = false,
) {
val entryMap = gameEntity.getEntryMap()
var downloadEntity: DownloadEntity? = null
@ -535,11 +536,12 @@ object DownloadItemUtils {
context: Context,
holder: GameViewHolder,
downloadEntity: DownloadEntity,
isMultiVersion: Boolean = false
isMultiVersion: Boolean = false,
) {
when (downloadEntity.status) {
DownloadStatus.redirected,
DownloadStatus.downloading -> {
DownloadStatus.downloading,
-> {
if (isMultiVersion) {
holder.gameDownloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
val darkMode =
@ -556,7 +558,8 @@ object DownloadItemUtils {
)
} else {
holder.gameDownloadTips?.visibility = View.GONE
holder.gameDownloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
holder.gameDownloadBtn.buttonStyle =
DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
holder.gameDownloadBtn.progress = (downloadEntity.percent * 10).toInt()
holder.gameDownloadBtn.text = downloadEntity.percent.toString() + "%"
}
@ -577,13 +580,15 @@ object DownloadItemUtils {
DownloadStatus.diskioerror,
DownloadStatus.diskisfull,
DownloadStatus.subscribe,
DownloadStatus.overflow -> {
DownloadStatus.overflow,
-> {
if (isMultiVersion) {
holder.gameDownloadTips?.visibility = View.VISIBLE
holder.gameDownloadTips?.setDownloadTipsAnimation(false)
}
holder.gameDownloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
holder.gameDownloadBtn.text = context.getString(com.gh.gamecenter.feature.R.string.resume)
holder.gameDownloadBtn.text =
context.getString(com.gh.gamecenter.feature.R.string.resume)
}
DownloadStatus.done -> {
@ -616,7 +621,7 @@ object DownloadItemUtils {
holder: GameViewHolder,
briefStyle: String?,
recommendStyle: LinkEntity?,
isShowRecommendStar: Boolean = false
isShowRecommendStar: Boolean = false,
) {
holder.gameDownloadTips?.visibility = View.GONE
// 推荐指数优先,现暂时为游戏单详情列表游戏使用
@ -655,7 +660,11 @@ object DownloadItemUtils {
}
// 缺省情况下回落到游戏简介
if (TextUtils.isEmpty(briefStyle) || briefStyle!!.contains("brief") || briefStyle.contains("recommend")) {
if (TextUtils.isEmpty(briefStyle)
|| briefStyle!!.contains("brief")
|| briefStyle.contains("recommend")
|| briefStyle.contains("test&appointment")
) {
holder.gameDes?.visibility = View.VISIBLE
} else {
holder.gameDes?.visibility = View.GONE
@ -671,7 +680,7 @@ object DownloadItemUtils {
adapter: RecyclerView.Adapter<out RecyclerView.ViewHolder?>?,
entrance: String,
location: String,
sourceEntrance: String = "其他"
sourceEntrance: String = "其他",
) {
setOnClickListener(
context,
@ -760,7 +769,7 @@ object DownloadItemUtils {
traceEvent: ExposureEvent?,
clickCallback: EmptyCallback?,
refreshCallback: EmptyCallback?,
allStateClickCallback: EmptyCallback?
allStateClickCallback: EmptyCallback?,
) {
// 为 downloadButton 添加游戏实体,供点击的时候上报用
downloadBtn.putObject(gameEntity)
@ -996,7 +1005,7 @@ object DownloadItemUtils {
entrance: String,
location: String,
traceEvent: ExposureEvent? = null,
refreshCallback: EmptyCallback? = null
refreshCallback: EmptyCallback? = null,
) {
val str =
if (downloadBtn is DownloadButton) downloadBtn.text else context.getString(com.gh.gamecenter.feature.R.string.download)
@ -1105,15 +1114,17 @@ object DownloadItemUtils {
val downloadEntity = SimulatorGameManager.findDownloadEntityByUrl(apk.url)
com.gh.gamecenter.common.utils.NewFlatLogUtils.logGameInstall(
gameId = downloadEntity?.gameId ?: "",
gameName = downloadEntity?.name ?: "",
gameId = gameEntity.id,
gameName = gameEntity.name ?: "",
trigger = "主动安装"
)
SensorsBridge.trackInstallGameClick(
gameId = downloadEntity?.gameId ?: "",
gameName = downloadEntity?.name ?: "",
action = "主动安装"
gameId = gameEntity.id,
gameName = gameEntity.name ?: "",
action = "主动安装",
isDspGame = gameEntity.isDspGame,
dspAdId = gameEntity.dspAdId
)
if (gameEntity.simulator != null) {
@ -1239,7 +1250,7 @@ object DownloadItemUtils {
location: String,
asVGame: Boolean,
isSubscribe: Boolean,
traceEvent: ExposureEvent?
traceEvent: ExposureEvent?,
) {
if (gameEntity.getApk().isEmpty()) return
@ -1283,7 +1294,7 @@ object DownloadItemUtils {
entrance: String,
location: String,
isSubscribe: Boolean,
traceEvent: ExposureEvent?
traceEvent: ExposureEvent?,
) {
val msg = FileUtils.isCanDownload(context, gameEntity.getApk().firstOrNull()?.size ?: "")
if (TextUtils.isEmpty(msg)) {
@ -1310,7 +1321,7 @@ object DownloadItemUtils {
gameEntity: GameEntity,
position: Int,
adapter: RecyclerView.Adapter<out RecyclerView.ViewHolder?>?,
refreshCallback: EmptyCallback?
refreshCallback: EmptyCallback?,
) {
val apkEntity = gameEntity.getApk().firstOrNull()
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity)
@ -1353,7 +1364,7 @@ object DownloadItemUtils {
location: String,
asVGame: Boolean,
isSubscribe: Boolean,
traceEvent: ExposureEvent?
traceEvent: ExposureEvent?,
) {
// 执行更新操作前,先清理历史下载任务,避免冲突

View File

@ -14,15 +14,16 @@ 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
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.common.entity.SimpleGameEntity
import com.gh.gamecenter.common.entity.SuggestType
import com.gh.gamecenter.common.eventbus.EBShowDialog
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.NewFlatLogUtils
import com.gh.gamecenter.core.utils.GsonUtils
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.dsp.DspReportHelper
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.feature.entity.CustomPageTrackData
import com.gh.gamecenter.feature.entity.GameEntity
@ -281,11 +282,10 @@ object DownloadObserver {
private fun performDownloadCompleteAction(
downloadEntity: DownloadEntity,
downloadManager: DownloadManager
downloadManager: DownloadManager,
) {
if (downloadEntity.name.contains(mApplication.getString(R.string.app_name))) {
statDoneEvent(downloadEntity)
MtaHelper.onEvent("软件更新", "下载完成")
// 会有 ActivityNotFoundException 异常catch 掉不管了
tryWithDefaultCatch {
if (Constants.SILENT_UPDATE != downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)) {
@ -298,7 +298,9 @@ object DownloadObserver {
SensorsBridge.trackInstallGameClick(
gameName = downloadEntity.name,
gameId = downloadEntity.gameId,
action = "自动安装"
action = "自动安装",
isDspGame = downloadEntity.getMetaExtra(Constants.DSP_GAME) == "true",
dspAdId = downloadEntity.getMetaExtra(Constants.DSP_AD_ID)
)
// TODO 在 Android 11 上没有授权安装未知应用的权限前第一次调用这个方法系统会杀掉我们的进程...
@ -395,7 +397,9 @@ object DownloadObserver {
SensorsBridge.trackInstallGameClick(
gameId = downloadEntity.gameId,
gameName = downloadEntity.name,
action = "自动安装"
action = "自动安装",
isDspGame = downloadEntity.getMetaExtra(Constants.DSP_GAME) == "true",
dspAdId = downloadEntity.getMetaExtra(Constants.DSP_AD_ID)
)
PackageInstaller.install(mApplication, downloadEntity, false, ignoreAsVGame = false)
}
@ -504,7 +508,8 @@ object DownloadObserver {
gameVersion = downloadEntity.versionName ?: "",
isPlatformRecommend = isPlatformRecommend,
adIconActive = downloadEntity.meta[Constants.AD_ICON_ACTIVE].toBoolean(),
isAdData = downloadEntity.meta[Constants.IS_AD_DATA].toBoolean()
isAdData = downloadEntity.meta[Constants.IS_AD_DATA].toBoolean(),
downloadUrl = downloadEntity.meta[Constants.DOWNLOAD_URL]
),
downloadEntity.platform,
downloadEntity.exposureTrace,
@ -556,24 +561,81 @@ object DownloadObserver {
} else {
arrayOf()
}
SensorsBridge.trackEventWithExposureSource(
"DownloadProcessFinish",
exposureEvent?.source,
"game_id", downloadEntity.gameId,
"game_name", downloadEntity.meta[Constants.GAME_NAME] ?: "",
"game_type", downloadEntity.meta[Constants.GAME_CATEGORY_IN_CHINESE] ?: "",
"game_label", downloadEntity.tags.joinToString(","),
"game_schema_type", if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
"page_name", getCurrentPageEntity().pageName,
"page_id", getCurrentPageEntity().pageId,
"page_business_id", getCurrentPageEntity().pageBusinessId,
"last_page_name", getLastPageEntity().pageName,
"last_page_id", getLastPageEntity().pageId,
"last_page_business_id", getLastPageEntity().pageBusinessId,
"download_status", downloadEntity.meta[Constants.DOWNLOAD_STATUS_IN_CHINESE] ?: "",
"download_type", if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
*kvs
)
val pushMessageId = (HaloApp.get(Constants.PUSH_MESSAGE_ID, false) as? String) ?: ""
val pushLinkId = (HaloApp.get(Constants.PUSH_LINK_ENTITY, false) as? LinkEntity)?.link ?: ""
val isFromPush = pushMessageId.isNotEmpty()
if (downloadEntity.getMetaExtra(Constants.DSP_GAME) != "true") {
SensorsBridge.trackEventWithExposureSource(
"DownloadProcessFinish",
exposureEvent?.source,
"game_id",
downloadEntity.gameId,
"game_name",
downloadEntity.meta[Constants.GAME_NAME] ?: "",
"game_type",
downloadEntity.meta[Constants.GAME_CATEGORY_IN_CHINESE] ?: "",
"game_label",
downloadEntity.tags.joinToString(","),
"game_schema_type",
if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
"page_name",
getCurrentPageEntity().pageName,
"page_id",
getCurrentPageEntity().pageId,
"page_business_id",
getCurrentPageEntity().pageBusinessId,
"last_page_name",
getLastPageEntity().pageName,
"last_page_id",
getLastPageEntity().pageId,
"last_page_business_id",
getLastPageEntity().pageBusinessId,
"download_status",
downloadEntity.meta[Constants.DOWNLOAD_STATUS_IN_CHINESE] ?: "",
"download_type",
if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
"is_from_push_notifications",
isFromPush,
"message_id",
pushMessageId,
"link_id",
pushLinkId,
*kvs
)
} else {
val searchContent = downloadEntity.getMetaExtra(Constants.SEARCH_KEY)
SensorsBridge.trackEventWithExposureSource(
"DspAdDownloadFinish",
exposureEvent?.source,
"ad_id", downloadEntity.getMetaExtra(Constants.DSP_AD_ID),
"search_content", searchContent,
"game_column_name", downloadEntity.getMetaExtra(Constants.SUBJECT_NAME),
"game_column_id", downloadEntity.getMetaExtra(Constants.SUBJECT_ID),
"game_id", downloadEntity.gameId,
"game_name", downloadEntity.meta[Constants.GAME_NAME] ?: "",
"game_type", downloadEntity.meta[Constants.GAME_CATEGORY_IN_CHINESE] ?: "",
"game_label", downloadEntity.tags.joinToString(","),
"game_schema_type", if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
"page_name", getCurrentPageEntity().pageName,
"page_id", getCurrentPageEntity().pageId,
"page_business_id", getCurrentPageEntity().pageBusinessId,
"last_page_name", getLastPageEntity().pageName,
"last_page_id", getLastPageEntity().pageId,
"last_page_business_id", getLastPageEntity().pageBusinessId,
"download_status", downloadEntity.meta[Constants.DOWNLOAD_STATUS_IN_CHINESE] ?: "",
"download_type", if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
"is_from_push_notifications", isFromPush,
"message_id", pushMessageId,
"link_id", pushLinkId,
"location", if (searchContent.isEmpty()) "自定义页面" else "游戏搜索结果列表",
*kvs
)
DspReportHelper.report(downloadEntity.getMetaExtra(Constants.DOWNLOAD_URL))
}
}
DataCollectionUtils.uploadDownload(mApplication, downloadEntity, "完成")

View File

@ -111,7 +111,12 @@ object GameUtils {
var gh_id: Any?
apkFor@ for (apkEntity in gameEntity.getApk()) {
val isInstalledLocally = PackagesManager.isInstalled(apkEntity.packageName)
val isInstalledLocally =
if (gameEntity.isDspGame) {
PackageHelper.validLocalPackageNameSet.contains(apkEntity.packageName)
} else {
PackagesManager.isInstalled(apkEntity.packageName)
}
// filter by packageName
val settings = Config.getSettings()

View File

@ -2,21 +2,29 @@ package com.gh.common.util
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.gh.common.dialog.AccelerateExpirationDialogFragment
import com.gh.download.DownloadManager
import com.gh.download.PackageObserver
import com.gh.gamecenter.DownloadManagerActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.NewFlatLogUtils
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.core.provider.IAcceleratorProvider
import com.gh.gamecenter.core.utils.CurrentActivityHolder
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.hud.HeadUpDisplayHelper
import com.gh.gamecenter.hud.HeadUpDisplayLogHelper
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository
import com.halo.assistant.HaloApp
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
import com.gh.gamecenter.R
import com.gh.gamecenter.hud.HeadUpDisplayLogHelper
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.getMetaExtra
import com.gh.gamecenter.dsp.DspReportHelper
import com.lightgame.utils.Utils
import com.therouter.TheRouter
import org.greenrobot.eventbus.EventBus
object PackageChangeHelper : DefaultLifecycleObserver {
@ -27,12 +35,13 @@ object PackageChangeHelper : DefaultLifecycleObserver {
private const val UNINSTALL_PENDING = 2
private const val UPDATE_PENDING = 3
// <包名pending 类型,应用版本> Triple
private var pendingPackageTriple: Triple<String, Int, String>? = null
private var pendingGhId: String? = null
// <包名pending 类型,应用版本光环ID> 的 pending 集合
private var pendingPackageMap: HashMap<String, PackageEntity> = hashMapOf()
private var pendingHUDShowed: Boolean = false
private var isLaunching = true
/**
* 添加一个等待中,待确定是否已成功安装的应用
*/
@ -43,24 +52,21 @@ object PackageChangeHelper : DefaultLifecycleObserver {
Utils.log(TAG, "添加了: $packageName 包名等待安装成功")
pendingHUDShowed = false
pendingPackageTriple = Triple(packageName, INSTALL_PENDING, "")
pendingPackageMap[packageName] = PackageEntity(packageName, INSTALL_PENDING, "")
} else {
Utils.log(TAG, "添加了: $packageName 包名等待安装更新成功")
val ghId = PackageUtils.getGhId(packageName)
// 记录光环插件相关信息,用于安装成功后的处理
if (ghId != null) {
pendingGhId = ghId.toString()
}
val ghId = PackageUtils.getGhId(packageName)?.toString() ?: ""
pendingHUDShowed = false
pendingPackageTriple = Triple(packageName, UPDATE_PENDING, installData.version)
pendingPackageMap[packageName] =
PackageEntity(packageName, UPDATE_PENDING, installData.version, ghId)
}
}
fun isInstallPendingPackage(packageName: String): Boolean {
return pendingPackageTriple?.first == packageName
return pendingPackageMap[packageName]?.packageName == packageName
}
/**
@ -68,87 +74,159 @@ object PackageChangeHelper : DefaultLifecycleObserver {
*/
fun addUninstallPendingPackage(packageName: String) {
Utils.log(TAG, "添加了: $packageName 包名等待卸载成功")
pendingPackageTriple = Triple(packageName, UNINSTALL_PENDING, "")
pendingPackageMap[packageName] = PackageEntity(packageName, UNINSTALL_PENDING, "")
}
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
isLaunching = true
}
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
if (pendingPackageTriple != null) {
val packageName = pendingPackageTriple?.first ?: return
val isInstallPending = pendingPackageTriple?.second == INSTALL_PENDING
val isUninstallPending = pendingPackageTriple?.second == UNINSTALL_PENDING
val isUpdatePending = pendingPackageTriple?.second == UPDATE_PENDING
if (pendingPackageMap.size > 0) {
calculateAndDispatchPackageChanges()
}
val pendingVersion = pendingPackageTriple?.third ?: ""
refreshVipStatus()
}
/**
* 应用从后台切到前台如果是登录状态需要刷新用户的vip状态
*/
private fun refreshVipStatus() {
val iAcceleratorProvider = TheRouter.get(IAcceleratorProvider::class.java)
if (iAcceleratorProvider?.hasToken() == true &&
AcceleratorDataHolder.instance.enableRefresh &&
AcceleratorDataHolder.instance.vipEntity != null &&
!isLaunching
) {
iAcceleratorProvider.loadQyUserPermissionData {
val vip = AcceleratorDataHolder.instance.vipEntity
if (vip != null && !vip.vipStatus && !vip.isNewUser && !vip.isVipRefundUser && !vip.isDurationRefundUser) {
// 获取当前
val currentActivity = CurrentActivityHolder.getCurrentActivity()
val reminder = AccelerateExpirationDialogFragment.ExpirationReminder.TimeRunsOut("")
if (currentActivity != null && HaloApp.getInstance().isRunningForeground) {
AccelerateExpirationDialogFragment.checkDialogShown(currentActivity, reminder)
}
}
}
}
}
/**
* 计算并分发包名的安装状态
*/
private fun calculateAndDispatchPackageChanges() {
val completeList = arrayListOf<PackageEntity>()
val pendingHudPackageList = arrayListOf<String>()
for (packageEntity in pendingPackageMap.values) {
val packageName = packageEntity.packageName
val pendingVersion = packageEntity.version
val pendingGhId = packageEntity.ghId
val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName)
val isInstalled = installedVersionName != null
if (isInstallPending) {
if (packageEntity.isInstallPending()) {
if (isInstalled) {
pendingPackageTriple = null
pendingGhId = null
completeList += packageEntity
PackageRepository.addInstalledGame(PackageRepository.packageFilterManager, packageName)
PackageRepository.addInstalledGame(
PackageRepository.packageFilterManager,
packageName
)
// 添加到额外的包名白名单,下次启动同时查询此包的安装情况
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = true)
PackageHelper.updateAdditionalWhiteListPackageName(
packageName = packageName,
isAdd = true
)
performInstallSuccessAction(packageName)
} else {
// 任务不存在了,将等待更新安装结果的 triple 置为 null
if (DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName) == null) {
pendingPackageTriple = null
// 任务不存在了,将等待更新安装结果的 packageEntity 置为 null
if (DownloadManager.getInstance()
.getDownloadEntitySnapshotByPackageName(packageName) == null
) {
completeList += packageEntity
} else {
showHUDIfNeeded(packageName)
pendingHudPackageList.add(packageName)
}
}
} else if (isUninstallPending && !isInstalled) {
pendingPackageTriple = null
pendingGhId = null
} else if (packageEntity.isUninstallPending() && !isInstalled) {
completeList += packageEntity
// 从额外的包名白名单移除,下次启动时不再查询此包的安装情况
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = false)
PackageHelper.updateAdditionalWhiteListPackageName(
packageName = packageName,
isAdd = false
)
performUninstallSuccessAction(packageName)
} else if (isUpdatePending) {
} else if (packageEntity.isUpdatePending()) {
val isUpdateValid = if (installedVersionName != pendingVersion) {
true
} else {
!pendingGhId.isNullOrEmpty() && pendingGhId != PackageUtils.getGhId(packageName).toString()
pendingGhId.isNotEmpty()
&& pendingGhId != PackageUtils.getGhId(packageName).toString()
}
pendingPackageTriple = null
pendingGhId = null
if (isUpdateValid) {
performUninstallSuccessAction(packageName)
performInstallSuccessAction(packageName)
completeList += packageEntity
} else {
// 任务不存在了,将等待更新安装结果的 triple 置为 null
if (DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName) == null) {
pendingPackageTriple = null
// 任务不存在了,将等待更新安装结果的 packageEntity 置为 null
if (DownloadManager.getInstance()
.getDownloadEntitySnapshotByPackageName(packageName) == null
) {
completeList += packageEntity
} else {
showHUDIfNeeded(packageName)
pendingHudPackageList.add(packageName)
}
}
// 添加到额外的包名白名单,下次启动同时查询此包的安装情况
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = true)
PackageHelper.updateAdditionalWhiteListPackageName(
packageName = packageName,
isAdd = true
)
}
}
for (invalidPackage in completeList) {
pendingPackageMap.remove(invalidPackage.packageName)
}
if (pendingHudPackageList.isNotEmpty()) {
showHUDIfNeeded(pendingHudPackageList.last())
}
}
override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)
isLaunching = false
}
private fun showHUDIfNeeded(packageName: String) {
if (pendingHUDShowed) return
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName) ?: return
val downloadEntity =
DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName)
?: return
if (downloadEntity.gameId == Constants.GHZS_GAME_ID) return
val activity = CurrentActivityHolder.getCurrentActivity() ?: return
if (activity is DownloadManagerActivity) return
pendingHUDShowed = true
HeadUpDisplayHelper.showHUD(activity,
HeadUpDisplayHelper.showHUD(
activity,
downloadEntity.icon ?: "",
activity.getString(R.string.hud_has_pending_install_game),
activity.getString(R.string.hud_head_to_download_manager),
@ -189,16 +267,26 @@ object PackageChangeHelper : DefaultLifecycleObserver {
private fun performInstallSuccessAction(
packageName: String,
cachedGameEntity: GameEntity? = null,
withLog: Boolean = true
withLog: Boolean = true,
) {
Utils.log(TAG, "安装了: $packageName 包名的程序")
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName)
val gameId = if (downloadEntity != null && downloadEntity.gameId != null) downloadEntity.gameId else ""
val gameName = if (downloadEntity != null && downloadEntity.name != null) downloadEntity.name else ""
val downloadEntity =
DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName)
val gameId = downloadEntity?.gameId ?: ""
val gameName = downloadEntity?.name ?: ""
if (withLog) {
if (withLog && gameId.isNotEmpty()) {
NewFlatLogUtils.logGameInstallComplete(gameId, gameName)
SensorsBridge.trackInstallGameFinish(gameId, gameName)
SensorsBridge.trackInstallGameFinish(
gameId,
gameName,
downloadEntity?.getMetaExtra(Constants.DSP_GAME) == "true",
downloadEntity?.getMetaExtra(Constants.DSP_AD_ID) ?: ""
)
if (downloadEntity?.getMetaExtra(Constants.DSP_GAME) == "true") {
DspReportHelper.report(downloadEntity.getMetaExtra(Constants.INSTALL_URL))
}
}
InstallUtils.getInstance().removeInstall(packageName)
@ -237,4 +325,23 @@ object PackageChangeHelper : DefaultLifecycleObserver {
EventBus.getDefault().post(uninstallEb)
}
private class PackageEntity(
val packageName: String,
val type: Int,
val version: String,
val ghId: String = "",
) {
fun isInstallPending(): Boolean {
return type == INSTALL_PENDING
}
fun isUninstallPending(): Boolean {
return type == UNINSTALL_PENDING
}
fun isUpdatePending(): Boolean {
return type == UPDATE_PENDING
}
}
}

View File

@ -95,7 +95,8 @@ object ReservationHelper {
it.categoryChinese,
data.wechatConfig.isReminderEnable,
if (data.hasSmsConfig) data.smsConfig.notice else null,
if (data.isEnableAutoDownload) data.wifiAutoDownload else null
if (data.isEnableAutoDownload) data.wifiAutoDownload else null,
if (data.hasCalendarConfig) false else null
)
}

View File

@ -2,12 +2,10 @@ package com.gh.common.util
import android.os.Bundle
import androidx.fragment.app.Fragment
import com.therouter.TheRouter
import com.gh.common.iinterface.ISuperiorChain
import com.gh.gamecenter.amway.AmwayFragment
import com.gh.gamecenter.category2.CategoryV2Fragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.common.provider.IHelpAndFeedbackProvider
import com.gh.gamecenter.discovery.DiscoveryFragment
@ -38,6 +36,7 @@ import com.gh.gamecenter.toolbox.ToolboxFragment
import com.gh.gamecenter.video.detail.HomeVideoFragment
import com.gh.gamecenter.wrapper.ToolbarWrapperFragment
import com.halo.assistant.fragment.WebFragment
import com.therouter.TheRouter
/**
* 通用跳转Fragment方法

View File

@ -306,21 +306,14 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
NDataChanger.notifyDataChanged(downloadEntity)
DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
// 仅官网渠道上报 XAPK 异常信息
if (HaloApp.getInstance().channel == "GH_206") {
SentryHelper.onEvent(
"XAPK_UNZIP_ERROR",
"gameName", downloadEntity.name,
"errorDigest", exception.localizedMessage
)
}
DownloadDataHelper.uploadDownloadStatusEvent(downloadEntity, "xapk解压失败")
SensorsBridge.trackGameDecompressionFailed(
downloadEntity.gameId,
downloadEntity.name,
downloadEntity.categoryChinese
downloadEntity.categoryChinese,
exception.localizedMessage ?: "unknown error"
)
}

View File

@ -29,6 +29,7 @@ import com.gh.common.util.PackageUtils;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.base.GlobalActivityManager;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.entity.LinkEntity;
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
import com.gh.gamecenter.common.utils.DeviceUtils;
import com.gh.gamecenter.common.utils.ExtensionsKt;
@ -360,6 +361,17 @@ public class DownloadManager implements DownloadStatusListener {
ExtensionsKt.addMetaExtra(downloadEntity, VHelper.KEY_REQUIRED_G_APPS, gameEntity.getGAppsSwitch());
}
if (gameEntity.isDspGame()) {
ExtensionsKt.addMetaExtra(downloadEntity, Constants.DSP_GAME, "true");
ExtensionsKt.addMetaExtra(downloadEntity, Constants.SEARCH_KEY, gameEntity.getSearchKey());
ExtensionsKt.addMetaExtra(downloadEntity, Constants.SUBJECT_NAME, gameEntity.getSubjectName());
ExtensionsKt.addMetaExtra(downloadEntity, Constants.SUBJECT_ID, gameEntity.getSubjectId());
ExtensionsKt.addMetaExtra(downloadEntity, Constants.INSTALL_URL, gameEntity.getInstallUrl());
ExtensionsKt.addMetaExtra(downloadEntity, Constants.DOWNLOAD_URL, gameEntity.getDownloadUrl());
ExtensionsKt.addMetaExtra(downloadEntity, Constants.DSP_AD_ID, gameEntity.getDspAdId());
}
ExtensionsKt.addMetaExtra(downloadEntity, Constants.KEY_BIT, apkEntity.getBit());
// 记录是否为双下载按钮模式
@ -451,8 +463,15 @@ public class DownloadManager implements DownloadStatusListener {
trackDownloadType = "本地下载";
}
String pushMessageId = HaloApp.get(Constants.PUSH_MESSAGE_ID, false) instanceof String
? (String) HaloApp.get(Constants.PUSH_MESSAGE_ID, false)
: "";
String pushLinkId = HaloApp.get(Constants.PUSH_LINK_ENTITY, false) instanceof LinkEntity
? ((LinkEntity) HaloApp.get(Constants.PUSH_LINK_ENTITY, false)).getLink()
: "";
boolean isFromPush = !pushMessageId.isEmpty();
String[] arrayKv = {
Object[] arrayKv = {
"game_id", gameEntity.getId(),
"game_name", gameEntity.getName(),
"game_type", gameEntity.getCategoryChinese(),
@ -465,17 +484,49 @@ public class DownloadManager implements DownloadStatusListener {
"last_page_id", GlobalActivityManager.getLastPageEntity().getPageId(),
"last_page_business_id", GlobalActivityManager.getLastPageEntity().getPageBusinessId(),
"download_status", gameEntity.getDownloadStatusChinese(),
"download_type", trackDownloadType
"download_type", trackDownloadType,
"is_from_push_notifications", isFromPush,
"message_id", pushMessageId,
"link_id", pushLinkId,
};
List<String> kvs = new ArrayList<>(Arrays.asList(arrayKv));
List<Object> kvs = new ArrayList<>(Arrays.asList(arrayKv));
if (customPageTrackData != null) {
kvs.addAll(Arrays.asList(customPageTrackData.toKV()));
}
SensorsBridge.trackEventWithExposureSource("DownloadProcessBegin",
downloadExposureEvent.getSource(), kvs.toArray(new String[0])
);
if (!gameEntity.isDspGame()) {
SensorsBridge.trackEventWithExposureSource(
"DownloadProcessBegin",
downloadExposureEvent.getSource(), kvs.toArray(new Object[0])
);
} else {
String searchKey = gameEntity.getSearchKey();
String loc;
if (TextUtils.isEmpty(searchKey)) {
loc = "自定义页面";
} else {
loc = "游戏搜索结果列表";
}
kvs.add("ad_id");
kvs.add(gameEntity.getDspAdId());
kvs.add("search_content");
kvs.add(searchKey);
kvs.add("game_column_name");
kvs.add(gameEntity.getSubjectName());
kvs.add("game_column_id");
kvs.add(gameEntity.getSubjectId());
kvs.add("location");
kvs.add(loc);
SensorsBridge.trackEventWithExposureSource(
"DspAdDownloadBegin",
downloadExposureEvent.getSource(), kvs.toArray(new Object[0])
);
}
//TODO remove
DownloadManager.getInstance().putStatus(downloadEntity.getUrl(), DownloadStatus.downloading);

View File

@ -175,7 +175,9 @@ object PackageObserver {
SensorsBridge.trackInstallGameClick(
gameId = mDownloadEntity.gameId,
gameName = mDownloadEntity.name,
action = "自动安装"
action = "自动安装",
isDspGame = mDownloadEntity.getMetaExtra(Constants.DSP_GAME) == "true",
dspAdId = mDownloadEntity.getMetaExtra(Constants.DSP_AD_ID)
)
PackageInstaller.install(application, mDownloadEntity)
}

View File

@ -164,7 +164,8 @@ class DownloadDialog : BaseDraggableDialogFragment() {
true,
mTraceEvent,
mEntrance,
mLocation
mLocation,
onDownloadClickAction
)
mBinding.collectionList.layoutManager = createLayoutManager(itemList)
mBinding.collectionList.adapter = mCollectionAdapter

View File

@ -114,7 +114,6 @@ class GameDetailActivity : DownloadToolbarActivity() {
view,
listOf(
R.id.menu_download_iv,
R.id.gameBigEvent,
R.id.cardContainer,
R.id.iv_reserve,
R.id.iv_concern,

View File

@ -119,6 +119,7 @@ import com.sina.weibo.sdk.auth.AuthInfo;
import com.sina.weibo.sdk.openapi.IWBAPI;
import com.sina.weibo.sdk.openapi.WBAPIFactory;
import com.therouter.TheRouter;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.jetbrains.annotations.NotNull;
@ -222,7 +223,7 @@ public class MainActivity extends BaseActivity {
DialogHelper.showCenterWarningDialog(this, "发生闪退", "光环助手发生了闪退,建议安装到最新版本修复异常"
, "马上反馈", "马上安装修复",
() -> {
DirectUtils.directToGameDetail(this, Constants.GHZS_GAME_ID, "", "crash", true, "desc", null);
DirectUtils.directToGameDetail(this, Constants.GHZS_GAME_ID, "", "crash", true, "desc", null, "");
return null;
},
() -> {
@ -255,9 +256,12 @@ public class MainActivity extends BaseActivity {
// 跳转至其它页面
if (getIntent() != null
&& getIntent().getExtras() != null
&& getIntent().getBooleanExtra(EntranceConsts.KEY_REQUIRE_REDIRECT, false)) {
doSkip();
&& getIntent().getExtras() != null) {
if (getIntent().getBooleanExtra(EntranceConsts.KEY_REQUIRE_REDIRECT, false)) {
doSkip();
} else if (!TextUtils.isEmpty(getIntent().getStringExtra(EntranceConsts.KEY_THE_ROUTER_PATH))) {
doRedirect(getIntent().getStringExtra(EntranceConsts.KEY_THE_ROUTER_PATH));
}
}
// debug 模式下的快速跳转页面
@ -456,6 +460,10 @@ public class MainActivity extends BaseActivity {
handler.removeCallbacksAndMessages(null);
releaseExoSourceCache();
// 移除推送触发启动记录
HaloApp.remove(Constants.PUSH_MESSAGE_ID);
HaloApp.remove(Constants.PUSH_LINK_ENTITY);
}
/**
@ -820,6 +828,19 @@ public class MainActivity extends BaseActivity {
}, 500);
}
/**
* 重定向至 TheRouter 配置的页面
*/
private void doRedirect(String path) {
if (getIntent().getExtras() != null) {
// 更新 intent 数据,避免页面重建重新跳转
getIntent().getExtras().putString(EntranceConsts.KEY_THE_ROUTER_PATH, "");
AppExecutor.getUiExecutor().executeWithDelay(() -> {
TheRouter.build(path).navigation(this);
}, 500L);
}
}
@NonNull
private Function0<Unit> launchGame(String gamePackageName) {
return () -> {
@ -923,7 +944,9 @@ public class MainActivity extends BaseActivity {
SensorsBridge.trackInstallGameClick(
finalDownloadEntity.getGameId(),
finalDownloadEntity.getName(),
"主动安装"
"主动安装",
"true".equals(ExtensionsKt.getMetaExtra(finalDownloadEntity, Constants.DSP_GAME)),
ExtensionsKt.getMetaExtra(finalDownloadEntity, Constants.DSP_AD_ID)
);
PackageInstaller.install(MainActivity.this, finalDownloadEntity);
}, 200);
@ -1044,7 +1067,9 @@ public class MainActivity extends BaseActivity {
SensorsBridge.trackInstallGameClick(
downloadEntity.getGameId(),
downloadEntity.getName(),
"自动安装"
"自动安装",
"true".equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.DSP_GAME)),
ExtensionsKt.getMetaExtra(downloadEntity, Constants.DSP_AD_ID)
);
PackageInstaller.install(this, downloadEntity, false, false);
}

View File

@ -1,6 +1,7 @@
package com.gh.gamecenter;
import static com.gh.gamecenter.common.constant.EntranceConsts.ENTRANCE_BROWSER;
import static com.gh.gamecenter.common.constant.EntranceConsts.ENTRANCE_OTHER;
import static com.gh.gamecenter.common.constant.EntranceConsts.ENTRANCE_PUSH;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ANSWER;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ARCHIVE_LOGIN;
@ -35,6 +36,7 @@ import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_VIDEO_STREAM
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_VIDEO_STREAMING_HOME;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_WEB;
import static com.gh.gamecenter.common.constant.EntranceConsts.KEY_DATA;
import static com.gh.gamecenter.common.constant.EntranceConsts.KEY_FROM;
import static com.gh.gamecenter.common.constant.EntranceConsts.KEY_GAME_NAME;
import static com.gh.gamecenter.common.constant.EntranceConsts.KEY_NAME;
import static com.gh.gamecenter.common.constant.EntranceConsts.KEY_PACKAGENAME;
@ -100,7 +102,6 @@ public class SkipActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri uri = getIntent().getData();
Bundle bundle;
if (uri != null) {
@ -160,7 +161,8 @@ public class SkipActivity extends BaseActivity {
DirectUtils.directToArticle(this, path, entrance);
break;
case HOST_GAME:
DirectUtils.directToGameDetail(this, path, "", entrance, "true".equals(uri.getQueryParameter("auto_download")), to, null);
String from = uri.getQueryParameter(KEY_FROM);
DirectUtils.directToGameDetail(this, path, "", entrance, "true".equals(uri.getQueryParameter("auto_download")), to, null, from);
break;
case HOST_COLUMN:
DirectUtils.directToSubject(this, path, uri.getQueryParameter(KEY_NAME), entrance, null, SubjectData.SubjectType.NORMAL);
@ -180,7 +182,7 @@ public class SkipActivity extends BaseActivity {
DirectUtils.directToAnswerDetail(this, path, entrance, pathName);
break;
case HOST_QUESTION:
DirectUtils.directToQuestionDetail(this, path, entrance, pathName, "");
DirectUtils.directToQuestionDetail(this, path, entrance, pathName, isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
break;
case HOST_TOOLBOX:
DirectUtils.directToToolbox(this, uri.getQueryParameter("gameId"), uri.getQueryParameter("toolboxUrl"), entrance);
@ -190,7 +192,7 @@ public class SkipActivity extends BaseActivity {
break;
// 社区文章格式一
case "community.article":
DirectUtils.directToCommunityArticle(this, uri.getQueryParameter("articleId"), uri.getQueryParameter("communityId"), entrance, pathName, "");
DirectUtils.directToCommunityArticle(this, uri.getQueryParameter("articleId"), uri.getQueryParameter("communityId"), entrance, pathName, isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
break;
// 社区文章格式二
case "communities":
@ -211,13 +213,13 @@ public class SkipActivity extends BaseActivity {
}
}
if ("articles".equals(type)) {
DirectUtils.directToCommunityArticle(this, typeId, communityId, entrance, pathName, "");
DirectUtils.directToCommunityArticle(this, typeId, communityId, entrance, pathName, isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
break;
}
break;
case HOST_VIDEO:
DirectUtils.directToVideoDetail(this, path, VideoDetailContainerViewModel.Location.HOTTEST_GAME_VIDEO.getValue(),
false, id, entrance, pathName, TextUtils.isEmpty(referer) ? "" : referer, "");
false, id, entrance, pathName, TextUtils.isEmpty(referer) ? "" : referer, isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
break;
case HOST_UPLOAD_VIDEO://跳转上传视频
String titleParameter = uri.getQueryParameter("title");
@ -237,7 +239,7 @@ public class SkipActivity extends BaseActivity {
break;
case HOST_VIDEO_SINGLE:
DirectUtils.directToVideoDetail(this, path, VideoDetailContainerViewModel.Location.SINGLE_VIDEO.getValue(),
false, "", entrance, pathName, TextUtils.isEmpty(referer) ? "" : referer, "");
false, "", entrance, pathName, TextUtils.isEmpty(referer) ? "" : referer, isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
break;
case HOST_VIDEO_MORE:
gameId = uri.getQueryParameter("gameId");
@ -294,7 +296,7 @@ public class SkipActivity extends BaseActivity {
EntranceUtils.jumpActivityCompat(this, bundle);
break;
case EntranceConsts.HOST_VIDEO_DETAIL:
DirectUtils.directToVideoDetail(this, path, entrance, "", "");
DirectUtils.directToVideoDetail(this, path, entrance, "", isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
break;
case HOST_LIBAO:
DirectUtils.directToGiftDetail(this, path, entrance);
@ -315,7 +317,7 @@ public class SkipActivity extends BaseActivity {
break;
case HOST_COLUMN_COLLECTION:
DirectUtils.directToColumnCollection(this, path, -1, entrance, "", "", "", "", null,false);
DirectUtils.directToColumnCollection(this, path, -1, entrance, "", "", "", "", null, false);
break;
case EntranceConsts.HOST_BLOCK:
name = uri.getQueryParameter("name");
@ -357,7 +359,7 @@ public class SkipActivity extends BaseActivity {
byte[] linkData = Base64.decode(dataString, Base64.DEFAULT);
String linkDataString = new String(linkData, "UTF-8");
LaunchRedirect launchRedirect = GsonUtils.fromJson(linkDataString, LaunchRedirect.class);
DirectUtils.directToLinkPage(this, launchRedirect, entrance, "", "");
DirectUtils.directToLinkPage(this, launchRedirect, entrance, "", isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
}
} catch (Exception e) {
e.printStackTrace();
@ -414,13 +416,13 @@ public class SkipActivity extends BaseActivity {
break;
case HOST_ARCHIVE_LOGIN:
String gamePkg = uri.getQueryParameter(EntranceConsts.KEY_GAME_PKG);
if(CheckLoginUtils.isLogin()) {
if (CheckLoginUtils.isLogin()) {
VHelper.INSTANCE.updateAuthorizeInfo(true);
} else {
Bundle newBundle = new Bundle();
newBundle.putString(EntranceConsts.KEY_TO, LoginActivity.class.getName());
EntranceUtils.jumpActivityCompat(this, newBundle, null, (resultCode, data) -> {
if(CheckLoginUtils.isLogin()) {
if (CheckLoginUtils.isLogin()) {
VHelper.INSTANCE.updateAuthorizeInfo(true);
}
VHelper.launch(this, gamePkg, false, false);

View File

@ -5,6 +5,7 @@ import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.preference.PreferenceManager
@ -26,6 +27,9 @@ import com.gh.common.util.UsageStatsHelper.checkAndPostUsageStats
import com.gh.download.DownloadManager
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.constant.EntranceConsts.KEY_FROM
import com.gh.gamecenter.common.constant.EntranceConsts.KEY_GAMEID
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.tracker.TrackerLogger
import com.gh.gamecenter.common.utils.*
@ -90,8 +94,9 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen {
showPrivacyDialog()
} else {
val spanBuilder = buildSpannedString {
append("这个弹窗只会在右上角有环境标签的测试包出现" +
"\n进入应用以后还可以到关于我们页面长按应用图标重新选择"
append(
"这个弹窗只会在右上角有环境标签的测试包出现" +
"\n进入应用以后还可以到关于我们页面长按应用图标重新选择"
)
bold {
color(com.gh.gamecenter.common.R.color.text_theme.toColor(this@SplashScreenActivity)) {
@ -104,7 +109,7 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen {
executeDex2OatInAdvance()
DialogHelper.showDialog(
context = this,
title ="选择环境",
title = "选择环境",
content = spanBuilder,
confirmText = "正式环境",
cancelText = "测试环境",
@ -145,6 +150,10 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen {
val trackEvent = JSONObject()
// 是否首次使用神策
val isFirstTime = SPUtils.getBoolean(Constants.SP_SENSORS_IS_FIRST_TIME, true)
val therouterPath = intent.extras?.getString(EntranceConsts.KEY_THE_ROUTER_PATH) ?: ""
val uri = Uri.parse(therouterPath)
val isFromWechat = WECHAT_APPOINTMENT == if (!uri.isOpaque) uri.getQueryParameter(KEY_FROM) else false
val gameId = if (!uri.isOpaque) uri.getQueryParameter(KEY_GAMEID) else ""
tryCatchInRelease {
trackEvent.run {
put("\$is_first_time", isFirstTime)
@ -154,6 +163,10 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen {
put("signature", signatureHash)
put("app_name", appProvider?.getAppName())
put("install_first_time", if (HaloApp.getInstance().isBrandNewInstall) "" else "")
if (isFromWechat) {
put("source_entrance", WECHAT_NOTIFICATION)
put("page_business_id", gameId)
}
}
}
SensorsBridge.trackEvent("AppLaunch", trackEvent)
@ -324,6 +337,9 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen {
const val HONOR_CULPRIT_ID = 12324
const val HONOR_CULPRIT_CHANNEL = "荣耀通道"
private const val WECHAT_APPOINTMENT = "wechat_appointment"
private const val WECHAT_NOTIFICATION = "微信通知"
@JvmStatic
fun getSplashScreenIntent(context: Context?, bundle: Bundle?): Intent {
val intent = Intent(context, SplashScreenActivity::class.java)

View File

@ -23,6 +23,7 @@ import com.gh.common.simulator.NewSimulatorGameManager
import com.gh.common.simulator.SimulatorDownloadManager
import com.gh.common.simulator.SimulatorGameManager
import com.gh.common.util.*
import com.gh.common.util.GameUtils.getDownloadBtnText
import com.gh.common.util.LogUtils
import com.gh.common.xapk.XapkInstaller
import com.gh.common.xapk.XapkUnzipStatus
@ -44,6 +45,7 @@ import com.gh.gamecenter.core.utils.StringUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.eventbus.EBScroll
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.PluginLocation
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.feature.view.DownloadButton.ButtonStyle
@ -102,17 +104,23 @@ class DetailViewHolder(
val speedContainer: ConstraintLayout?
private val ivFreeVipTag: ImageView?
private val gMoreZone: Group?
private val gUpdate: Group?
private val ivUpdate: ImageView?
private val tvSize: TextView?
private val vUpdate: View?
private val tvUpdate: TextView?
private var isShowVaUpdate = true
// 注意 View 的命名
init {
downloadBottom = view.findViewById(R.id.detail_ll_bottom)
downloadPb = view.findViewById(R.id.detail_progressbar)
downloadTips = view.findViewById(R.id.downloadTipsLottie)
downloadPb = downloadBottom.findViewById(R.id.detail_progressbar)
downloadTips = downloadBottom.findViewById(R.id.downloadTipsLottie)
overlayTv = view.findViewById(R.id.overlayTv)
overlayContainer = view.findViewById(R.id.overlayContainer)
extraOverlayTv = view.findViewById(R.id.extraOverlayTv)
multiVersionDownloadTv = view.findViewById(R.id.multiVersionDownloadTv)
multiVersionDownloadTv = downloadBottom.findViewById(R.id.multiVersionDownloadTv)
localDownloadContainer = view.findViewById(R.id.localDownloadContainer)
localDownloadSizeTv = view.findViewById(R.id.localDownloadSizeTv)
localDownloadTitleTv = view.findViewById(R.id.localDownloadTitleTv)
@ -121,8 +129,14 @@ class DetailViewHolder(
speedContainer = view.findViewById(R.id.cl_speed_container)
ivFreeVipTag = view.findViewById(R.id.iv_free_vip_tag)
gMoreZone = view.findViewById(R.id.g_more_zone)
gUpdate = view.findViewById(R.id.g_update)
ivUpdate = view.findViewById(R.id.iv_update)
tvSize = view.findViewById(R.id.tv_size)
vUpdate = view.findViewById(R.id.v_update)
tvUpdate = view.findViewById(R.id.tv_update)
context = view.context
com.gh.gamecenter.common.R.color.text_aw_primary.toColor()
var gameDownloadMode = gameEntity.getGameDownloadButtonMode()
@ -181,8 +195,19 @@ class DetailViewHolder(
com.gh.gamecenter.feature.R.string.download_local
)
)
vUpdate?.setOnClickListener {
if (isShowVaUpdate) {
vGameDownloadListener.onClick(vUpdate)
} else {
localDownloadListener.onClick(vUpdate)
}
}
}
}
downloadPb.putWidgetBusinessName("游戏详情页")
// "DownLoadbuttonClick" 埋点需要上报traceEvent信息
@ -199,25 +224,83 @@ class DetailViewHolder(
}
fun checkIfShowSpeedUi(rawBtnText: String) {
fun checkIfShowSpeedUi(showVGame: Boolean, showDualDownloadButton: Boolean) {
acceleratorUiHelper?.let {
when {
rawBtnText == "启动" && gameEntity.canSpeed -> {
downloadPb.goneIf(true)
localDownloadButton?.goneIf(true)
it.checkIfShowSpeedUi(true)
val showSpeedUi = when {
gameEntity.canSpeed && showDualDownloadButton -> { // 双按钮
val localText = getDownloadBtnText(context, gameEntity, false, false, PluginLocation.only_game);
val downloadText =
getDownloadBtnText(context, gameEntity, false, true, PluginLocation.only_game)
when {
localText.contains(com.gh.gamecenter.feature.R.string.update.toResString()) -> { // 本地游戏需要更新
localDownloadContainer?.goneIf(true)
downloadPb.goneIf(true)
overlayTv?.goneIf(true)
gUpdate?.visibility = View.VISIBLE
ivUpdate?.goneIf(true)
tvSize?.goneIf(false)
tvSize?.text =
DetailDownloadUtils.convertSizeString(gameEntity.getApk().firstOrNull()?.size ?: "")
tvUpdate?.setText(R.string.update)
isShowVaUpdate = false
true
}
localText.contains(com.gh.gamecenter.feature.R.string.launch.toResString()) && downloadText == "更新" -> { // 畅玩游戏需要更新:显示 加速/更新
localDownloadContainer?.goneIf(true)
downloadPb.goneIf(true)
overlayTv?.goneIf(true)
gUpdate?.visibility = View.VISIBLE
ivUpdate?.goneIf(false)
tvSize?.goneIf(true)
tvUpdate?.setText(R.string.update)
isShowVaUpdate = true
true
}
localText.contains(com.gh.gamecenter.feature.R.string.launch.toResString()) -> { // 本地游戏为启动状态:显示 加速/畅玩
localDownloadContainer?.goneIf(true)
downloadPb.goneIf(true)
overlayTv?.goneIf(true)
gUpdate?.visibility = View.VISIBLE
tvUpdate?.setText(R.string.v_play)
tvSize?.goneIf(true)
isShowVaUpdate = true
true
}
else -> {
gUpdate?.visibility = View.GONE
tvSize?.goneIf(true)
false
}
}
}
rawBtnText == "更新" && gameEntity.canSpeed -> {
it.checkIfShowSpeedUi(true)
gameEntity.canSpeed && !showVGame -> {
gUpdate?.visibility = View.GONE
val downloadText = getDownloadBtnText(context, gameEntity, false, false, PluginLocation.only_game)
when {
downloadText.contains(com.gh.gamecenter.feature.R.string.launch.toResString()) -> {
localDownloadContainer?.goneIf(true)
downloadPb.goneIf(true)
true
}
downloadText.contains(R.string.update.toResString()) -> true
else -> false
}
}
else -> {
it.checkIfShowSpeedUi(false)
}
else -> false
}
}
it.checkIfShowSpeedUi(showSpeedUi, showDualDownloadButton)
}
}
fun setSpeedViewsVisible(isVisible: Boolean) {
@ -311,7 +394,9 @@ class DetailViewHolder(
SensorsBridge.trackInstallGameClick(
mGameEntity.id,
mGameEntity.name ?: "",
"主动安装"
"主动安装",
mGameEntity.isDspGame,
mGameEntity.dspAdId
)
PackageInstaller.install(mViewHolder.context, mDownloadEntity)
}
@ -436,7 +521,9 @@ class DetailViewHolder(
SensorsBridge.trackInstallGameClick(
gameId = mGameEntity.id,
gameName = mGameEntity.name ?: "",
action = "主动安装"
action = "主动安装",
isDspGame = mGameEntity.isDspGame,
dspAdId = mGameEntity.dspAdId
)
val url = mGameEntity.getApk().firstOrNull()?.url
val downloadEntity = SimulatorGameManager.findDownloadEntityByUrl(url)

View File

@ -242,7 +242,7 @@ class CategoryV2ListAdapter(
gameIconView.displayGameIcon(gameEntity)
gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F
BindingAdapters.setGameName(gameName, gameEntity, false)
BindingAdapters.setGameTags(labelList, gameEntity)
BindingAdapters.setGameTags(labelList, gameEntity, "")
gameRating.setDrawableStart(if (gameEntity.commentCount > 3) com.gh.gamecenter.feature.R.drawable.game_horizontal_rating.toDrawable() else null)
gameRating.text = if (gameEntity.commentCount > 3) {
if (gameEntity.star == 10.0F) "10" else gameEntity.star.toString()

View File

@ -318,7 +318,9 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter<ViewHolder> {
SensorsBridge.trackInstallGameClick(
downloadEntity.getGameId(),
downloadEntity.getName(),
"主动安装"
"主动安装",
false,
""
);
PackageInstaller.install(mContext, downloadEntity);
}
@ -397,7 +399,9 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter<ViewHolder> {
SensorsBridge.trackInstallGameClick(
downloadEntity.getGameId(),
downloadEntity.getName(),
"主动安装"
"主动安装",
"true".equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.DSP_GAME)),
ExtensionsKt.getMetaExtra(downloadEntity, Constants.DSP_AD_ID)
);
PackageInstaller.install(mContext, downloadEntity);
}

View File

@ -15,6 +15,7 @@ import com.gh.gamecenter.common.base.BaseSimpleDao
import com.gh.gamecenter.common.base.GlobalActivityManager.getCurrentPageEntity
import com.gh.gamecenter.common.base.GlobalActivityManager.getLastPageEntity
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.GsonUtils.toJson
@ -142,6 +143,8 @@ class UpdatableGameViewModel(
// 按包名分
for (update in updatableList) {
if (update.id == Constants.GHZS_GAME_ID) continue
var list = packageNameAndUpdateListMap[update.packageName]
if (list == null) {
list = arrayListOf()
@ -686,6 +689,9 @@ class UpdatableGameViewModel(
// 收集下载数据
DataCollectionUtils.uploadDownload(getApplication(), downloadEntity, "开始")
val pushMessageId = (HaloApp.get(Constants.PUSH_MESSAGE_ID, false) as? String) ?: ""
val pushLinkId = (HaloApp.get(Constants.PUSH_LINK_ENTITY, false) as? LinkEntity)?.link ?: ""
val isFromPush = pushMessageId.isNotEmpty()
SensorsBridge.trackEventWithExposureSource(
"DownloadProcessBegin",
event.source,
@ -701,6 +707,9 @@ class UpdatableGameViewModel(
"last_page_business_id", getLastPageEntity().pageBusinessId,
"download_status", update.downloadStatusChinese,
"download_type", "本地下载",
"is_from_push_notifications", isFromPush,
"message_id", pushMessageId,
"link_id", pushLinkId,
)
}

View File

@ -0,0 +1,46 @@
package com.gh.gamecenter.dsp
import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet
import com.gh.gamecenter.core.AppExecutor
import com.lightgame.utils.Utils
import okhttp3.OkHttpClient
import okhttp3.Request
object DspReportHelper {
private const val TAG = "DspReportHelper"
private val reportCache by lazy { FixedSizeLinkedHashSet<String>(100) }
fun report(url: String?) {
url ?: return
AppExecutor.logExecutor.execute {
if (reportCache.contains(url)) {
Utils.log(TAG, "遇到重复上报,自动过滤 $url")
return@execute
}
try {
val client = OkHttpClient()
val request = Request.Builder()
.url(url)
.build()
Utils.log(TAG, "Report is executing: $url")
val response = client.newCall(request).execute()
if (response.isSuccessful) {
// Add the URL to the cache to prevent duplicate reports
reportCache.add(url)
Utils.log(TAG, "Report successful: ${response.body?.string()}")
} else {
Utils.log(TAG, "Request failed with code: ${response.code}")
}
} catch (e: Exception) {
Utils.log(TAG, "Request failed: ${e.message}")
}
}
}
}

View File

@ -0,0 +1,36 @@
package com.gh.gamecenter.dsp.data
import com.gh.gamecenter.common.exposure.meta.MetaUtil
import com.gh.gamecenter.common.utils.toRequestBody
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.retrofit.service.DspApiService
import io.reactivex.Single
class GameSubjectDSPRemoteDataSource(private val api: DspApiService = RetrofitManager.getInstance().dspApiService) {
fun getDspGames(type: String, count: Int): Single<List<GameEntity>> {
val meta = MetaUtil.getMeta()
val request = mapOf(
"device" to mapOf(
"oaid" to (meta.oaid ?: ""),
"brand" to (meta.manufacturer ?: ""),
"model" to (meta.model ?: ""),
"osv" to (MetaUtil.getAndroidVersion()),
),
"count" to count,
"scene" to 3,
"busid" to "guanghuan1205"
)
return api.fetch(request.toRequestBody())
.map {
val gameEntityList = mutableListOf<GameEntity>()
for (bidEntity in it.bidList) {
val gameEntity = bidEntity.toGameEntity()
gameEntityList.add(gameEntity)
}
gameEntityList
}
}
}

View File

@ -11,6 +11,8 @@ class ReserveReminderEntity(
private var _smsConfig: SmsConfig? = null,
@SerializedName("wechat_config")
private var _wechatConfig: WechatConfigEntity? = null,
@SerializedName("calendar_config")
private var _calendarConfig: CalendarConfig? = null,
@SerializedName("mirror_type")
private val _mirrorType: String? = null,
@SerializedName("wifi_auto_download")
@ -34,8 +36,14 @@ class ReserveReminderEntity(
_wechatConfig = value
}
val onlyShowWechatReminder: Boolean
get() = _smsConfig == null
val hasCalendarConfig: Boolean
get() = _calendarConfig != null
var calendarConfig: CalendarConfig
get() = _calendarConfig ?: CalendarConfig()
set(value) {
_calendarConfig = value
}
val mirrorType: String
get() = _mirrorType ?: ""
@ -81,4 +89,39 @@ class ReserveReminderEntity(
}
}
@Parcelize
class CalendarConfig(
@SerializedName("notice")
private val _notice: Boolean? = null,
@SerializedName("title")
private val _title: String? = null,
@SerializedName("time_start")
private val _timeStart: Long? = null,
@SerializedName("time_end")
private val _timeEnd: Long? = null,
@SerializedName("advance_seconds")
private val _advanceSeconds: Long? = null,
@SerializedName("remark")
private val _remark: String? = null
) : Parcelable {
val notice: Boolean
get() = _notice ?: false
val title: String
get() = _title ?: ""
val timeStart: Long
get() = _timeStart ?: 0L
val timeEnd: Long
get() = _timeEnd ?: 0L
val advanceSeconds: Long
get() = _advanceSeconds ?: 0L
val remark: String
get() = _remark ?: ""
}
}

View File

@ -3,6 +3,7 @@ package com.gh.gamecenter.entity
import android.os.Parcelable
import com.gh.common.filter.RegionSettingHelper
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.personalhome.home.UserHistoryViewModel
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@ -19,11 +20,20 @@ data class SearchSubjectEntity(
val adIconActive: Boolean = false,
// 本地字段标记是否为微信小游戏CPM专题
var isWGameSubjectCPM: Boolean = false,
val type: String = ""
// 本地字段标记是否为DSP专题
var isDspSubject: Boolean = false,
val type: String = "",
@SerializedName("column_type")
val columnType: String = "", // DSP 专题类型 (DSP专题类型apk下载应用、mini_game微信小程序/小游戏)
@SerializedName("show_download") // 下载/秒玩按钮true显示、false不显示专题类型为apk则为下载按钮专题类型为mini_game则为秒完按钮
val showDownload: Boolean = false,
val size: Int = -1, // 专题游戏数量
) : Parcelable {
companion object {
const val TYPE_WECHAT_GAME_CPM_COLUMN = "wechat_game_cpm_column"
const val TYPE_DSP_GAME_COLUMN = "dsp_game_column"
}
fun getFilterGame() = RegionSettingHelper.filterGame(games)

View File

@ -117,7 +117,12 @@ data class SubjectEntity(
@SerializedName("show_index_icon_subscript")
val showIndexIconSubscript: Boolean = false,
@SerializedName("welfare_info")
val welfareInfo: WelfareInfo? = null
val welfareInfo: WelfareInfo? = null,
@SerializedName("column_type")
private val _columnType: String? = null,
@SerializedName("size")
private val _size: Size? = null
) : Parcelable {
@IgnoredOnParcel
@ -130,12 +135,13 @@ data class SubjectEntity(
@IgnoredOnParcel
private var filteredData: MutableList<GameEntity>? = null
val subjectType: SubjectData.SubjectType get() = when {
isQQColumn -> SubjectData.SubjectType.QQ_GAME
isWechatColumnCPM -> SubjectData.SubjectType.WECHAT_GAME_CPM
isWechatColumn -> SubjectData.SubjectType.WECHAT_GAME
else -> SubjectData.SubjectType.NORMAL
}
val subjectType: SubjectData.SubjectType
get() = when {
isQQColumn -> SubjectData.SubjectType.QQ_GAME
isWechatColumnCPM -> SubjectData.SubjectType.WECHAT_GAME_CPM
isWechatColumn -> SubjectData.SubjectType.WECHAT_GAME
else -> SubjectData.SubjectType.NORMAL
}
val isMiniGame: Boolean get() = isQQColumn || isWechatColumn
@ -148,4 +154,28 @@ data class SubjectEntity(
val list: Int
get() = max(min(_list ?: 0, data?.size ?: 0), 1)
val columnType: String
get() = _columnType ?: ""
val size: Size
get() = _size ?: Size()
var isDspSubject: Boolean = false
companion object {
const val SUBJECT_TAG_UPDATE = "update" // 更新时间
const val SUBJECT_TAG_TYPE = "type" // 游戏标签
const val SUBJECT_TAG_TEST = "test" // 开测时间
const val SUBJECT_TAG_SELLING_POINT = "selling_points&type" // 卖点文案+游戏标签
}
@Parcelize
data class Size(
@SerializedName("index")
private val _index: Int? = null
) : Parcelable {
val index: Int
get() = _index ?: 0
}
}

View File

@ -23,7 +23,6 @@ import com.gh.gamecenter.eventbus.EBUserFollow
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.forum.home.ForumScrollCalculatorHelper
import com.gh.gamecenter.video.detail.CustomManager
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@ -125,10 +124,6 @@ class ForumArticleAskListFragment : LazyListFragment<AnswerEntity, ForumArticleA
override fun onFragmentPause() {
super.onFragmentPause()
pauseVideo()
}
override fun onDestroy() {
super.onDestroy()
mScrollCalculatorHelper?.currentPlayer?.release()
}
@ -263,9 +258,7 @@ class ForumArticleAskListFragment : LazyListFragment<AnswerEntity, ForumArticleA
mBaseHandler.postDelayed({
tryCatchInRelease {
if (position != 0L) {
if (currentPlayer?.currentState == GSYVideoView.CURRENT_STATE_PAUSE) {
currentPlayer?.startPlayLogic(true)
}
currentPlayer?.startPlayLogic(true)
} else {
currentPlayer?.release()
}

View File

@ -160,12 +160,15 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
}
//监控播放错误
// override fun onError(what: Int, extra: Int) {
// super.onError(what, extra)
override fun onError(what: Int, extra: Int) {
super.onError(what, extra)
// Utils.toast(context, "网络错误,视频播放失败")
// setViewShowState(mStartButton, View.INVISIBLE)
//// errorContainer.visibility = View.VISIBLE
// }
// errorContainer.visibility = View.VISIBLE
// 部分设备由于MediaCodec实例达到上限导致 CodecException: Error 0xfffffff4在此处尝试释放所有视频
CustomManager.clearAllVideo()
}
override fun releaseVideos() {
CustomManager.releaseAllVideos(getKey())

View File

@ -37,9 +37,9 @@ class UserSearchListFragment : LazyListFragment<FollowersOrFansEntity, UserSearc
SensorsBridge.trackUserSearchResultClick(
GlobalActivityManager.getCurrentPageEntity().pageId,
GlobalActivityManager.getCurrentPageEntity().pageName,
mListViewModel.sourceEntrance,
mSearchKey,
SearchActivity.toTrackSearchType(mSearchType),
mListViewModel.sourceEntrance,
userId,
position
)

View File

@ -15,10 +15,10 @@ class GameHorizontalSimpleItemViewHolder(val binding: GameHorizontalSimpleItemBi
view.text = name
view.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
view.viewTreeObserver.removeOnGlobalLayoutListener(this);
val newText = autoSplitText(view);
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
val newText = autoSplitText(view)
if (!TextUtils.isEmpty(newText)) {
view.text = newText;
view.text = newText
}
}
})

View File

@ -137,7 +137,6 @@ class GameHorizontalSlideAdapter(
}
}
// notifyDataSetChanged 会出现页面抖动情况
fun checkResetData(subjectEntity: SubjectEntity) {
var dataIds = ""

View File

@ -3,10 +3,10 @@ package com.gh.gamecenter.game.vertical
import android.content.Context
import android.graphics.Color
import android.graphics.Typeface
import android.os.Build
import android.text.TextUtils
import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import android.widget.Space
@ -17,11 +17,12 @@ import androidx.core.content.ContextCompat
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieDrawable
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.feature.view.GameIconView
import com.gh.gamecenter.R
import com.gh.gamecenter.common.databinding.LayoutGameItemSellingPointBinding
import com.gh.gamecenter.common.utils.setDrawableEnd
import com.gh.gamecenter.common.view.GameTagContainerView
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.feature.view.GameIconView
import splitties.dimensions.dip
import splitties.views.backgroundColor
import splitties.views.dsl.constraintlayout.*
@ -63,6 +64,7 @@ class GameItemUi(override val ctx: Context) : Ui {
var gamePlayCountTv: TextView
var mGameDesSpace: Space
var sellingPointsBinding: LayoutGameItemSellingPointBinding
override val root: ConstraintLayout = constraintLayout {
padding = dip(16)
@ -84,6 +86,7 @@ class GameItemUi(override val ctx: Context) : Ui {
gameTagContainer = GameTagContainerView(ctx).apply { id = R.id.label_list }
mGameDesSpace = space { }.apply { id = R.id.gameDesSpace }
recommendConstraintLayout = initRecommendConstraintLayout()
sellingPointsBinding = LayoutGameItemSellingPointBinding.inflate(LayoutInflater.from(context))
add(iconIv, lParams(dip(64), dip(64)) {
topOfParent()
@ -142,6 +145,12 @@ class GameItemUi(override val ctx: Context) : Ui {
endToEndOf(mGameDesSpace)
orientation = LinearLayout.HORIZONTAL
})
add(sellingPointsBinding.root, lParams(0, wrapContent) {
topToBottomOf(mGameDesSpace)
bottomToBottomOf(iconIv)
startToEndOf(gamePlayCountTv)
endToEndOf(mGameDesSpace)
})
add(gamePlayCountTv, lParams(wrapContent, wrapContent) {
topToBottomOf(mGameDesSpace)
bottomToBottomOf(iconIv)
@ -197,7 +206,8 @@ class GameItemUi(override val ctx: Context) : Ui {
startPadding = dip(2)
endPadding = dip(2)
ellipsize = TextUtils.TruncateAt.END
background = ContextCompat.getDrawable(context, com.gh.gamecenter.feature.R.drawable.bg_advance_download_game_subtitle)
background =
ContextCompat.getDrawable(context, com.gh.gamecenter.feature.R.drawable.bg_advance_download_game_subtitle)
setTextColor(ContextCompat.getColor(context, com.gh.gamecenter.common.R.color.text_secondary))
visibility = View.GONE
}
@ -305,7 +315,12 @@ class GameItemUi(override val ctx: Context) : Ui {
gravity = Gravity.CENTER
text = "展开"
setTextColor(ContextCompat.getColor(context, com.gh.gamecenter.common.R.color.white))
setDrawableEnd(AppCompatResources.getDrawable(context, com.gh.gamecenter.feature.R.drawable.ic_jump_universal))
setDrawableEnd(
AppCompatResources.getDrawable(
context,
com.gh.gamecenter.feature.R.drawable.ic_jump_universal
)
)
compoundDrawablePadding = dip(2)
visibility = View.GONE
}

View File

@ -166,7 +166,7 @@ class GameVerticalAdapter(
false
)
BindingAdapters.setGame(iconIv, gameEntity)
BindingAdapters.setGameTags(gameTagContainer, gameEntity)
BindingAdapters.setGameTags(gameTagContainer, gameEntity, subjectData?.tag ?: "")
GameItemViewHolder.initServerType(gameNameTv, serverTypeTv, gameEntity)
gameDesTv.text = gameEntity.decoratedDes
GameItemViewHolder.initGameSubtitleAndAdLabel(
@ -225,10 +225,10 @@ class GameVerticalAdapter(
subjectData?.briefStyle
)
DownloadItemUtils.setOnClickListener(
context, downloadTv, gameEntity, position,
adapter, entrance, location = location, traceEvent = gameEntity.exposureEvent
)
DownloadItemUtils.setOnClickListener(
context, downloadTv, gameEntity, position,
adapter, entrance, location = location, traceEvent = gameEntity.exposureEvent
)
root.setPadding(paddingStart, 8F.dip2px(), paddingEnd, 8F.dip2px())
}

View File

@ -610,7 +610,7 @@ open class GameCollectionDetailAdapter(
gameIconView.displayGameIcon(gameEntity)
gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F
BindingAdapters.setGameName(gameName, gameEntity, false)
BindingAdapters.setGameTags(labelList, gameEntity)
BindingAdapters.setGameTags(labelList, gameEntity, "")
gameRating.setDrawableStart(
if (gameEntity.commentCount > 3) com.gh.gamecenter.feature.R.drawable.game_horizontal_rating.toDrawable(mContext) else null
)

View File

@ -45,7 +45,6 @@ import com.google.gson.JsonArray
import com.google.gson.reflect.TypeToken
import com.halo.assistant.HaloApp
import com.halo.assistant.accelerator.repository.AccelerationRepository
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
import com.lightgame.utils.Utils
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
@ -128,12 +127,16 @@ class GameDetailViewModel(
var isSkipOnPageSelected = false // 是否触发论坛/专区Tab跳转
var defaultCoverEntity: CoverEntity? = null
var coverTabSequence = 1 // 用于埋点,详情视频/图集tab 当前选中tab的序号从1开始
var coverTabName = "" // 用于埋点,详情视频/图集tab 详情视频/图集tab 当前选中tab的tab名称
var isGameInstalled = false
private var isGameUpdatable = false
private val compositeDisposable = CompositeDisposable()
private var userRelatedInfoReceivedCallback: (() -> Unit)? = null
init {
loadData()
}
@ -190,13 +193,6 @@ class GameDetailViewModel(
game = response
gameLiveData.postValue(Resource.success(game))
loadGameDetailData()
if (game?.canSpeed == true) {
// 如果当前游戏支持加速则刷新vip状态
val userId = UserManager.getInstance().userId
if (userId.isNotBlank()) {
UserRepository.getInstance().refreshVipStatus(userId, true)
}
}
}
override fun onFailure(e: HttpException?) {
@ -769,8 +765,14 @@ class GameDetailViewModel(
.subscribe({
unifiedUserTrendEntity = it
emitter.onSuccess(Result.success(it))
userRelatedInfoReceivedCallback?.invoke()
userRelatedInfoReceivedCallback = null
}, {
emitter.onSuccess(Result.failure(it))
userRelatedInfoReceivedCallback?.invoke()
userRelatedInfoReceivedCallback = null
})
}
@ -800,9 +802,21 @@ class GameDetailViewModel(
tabSelectedLiveData.postValue(Event(LinkEntity(type = type)))
}
fun performContentCardClicked(contentCardType: String): Boolean {
/**
* 发送内容卡片点击事件
* @param contentCardType 内容卡片类型
* @param waitForUserRelatedResult 是否等待用户相关信息请求结果
* @return 是否成功触发点击事件
*/
fun performContentCardClicked(contentCardType: String, waitForUserRelatedResult: Boolean = false): Boolean {
return if (contentCardList?.any { it.link.type == contentCardType } == true) {
contentCardClickedLiveData.postValue(Event(contentCardType))
if (waitForUserRelatedResult && CheckLoginUtils.isLogin()) {
userRelatedInfoReceivedCallback = {
contentCardClickedLiveData.postValue(Event(contentCardType))
}
} else {
contentCardClickedLiveData.postValue(Event(contentCardType))
}
true
} else {
false
@ -812,7 +826,7 @@ class GameDetailViewModel(
fun performScrollToServer(): Boolean {
// 如果该游戏的【内容卡片】在详情中显示类型为【游戏开服】的卡片时,触发点击类型为【游戏开服】的内容卡片进入开服日历表页面
// 如果该游戏的【内容卡片】在详情中没有【游戏开服】的卡片时,定位到游戏详情页-详情tab-【游戏开服】组件
return if (performContentCardClicked(ContentCardEntity.TYPE_SERVER)) {
return if (performContentCardClicked(ContentCardEntity.TYPE_SERVER, true)) {
true
} else if (detailDataListLiveData.value?.any { it.linkServer != null } == true) {
scrollToListPositionLiveData.postValue(Event(LinkEntity(type = GameDetailData.TYPE_SERVER)))
@ -962,7 +976,7 @@ class GameDetailViewModel(
unifiedUserTrendEntity.game?.let {
if (it.isEmpty()) return@let
for (game in it) {
meLiveData.postValue(game.me)
meLiveData.value = game.me
break
}
}

View File

@ -45,6 +45,7 @@ import com.gh.gamecenter.common.base.fragment.BaseLazyFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.common.eventbus.EBReuse
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.json.json
import com.gh.gamecenter.common.mvvm.Status
@ -757,6 +758,7 @@ class GameDetailWrapperFragment : BaseLazyFragment(), IScrollable {
bundle.putString(EntranceConsts.KEY_CUSTOM_PAGE_ID, tabEntity.link?.link)
bundle.putString(EntranceConsts.KEY_CUSTOM_PAGE_NAME, tabEntity.link?.text)
bundle.putBoolean(EntranceConsts.KEY_SHOW_FLOATING_WINDOW, false)
bundle.putBoolean(EntranceConsts.KEY_SHOW_PULL_DOWN_PUSH, false)
}
}
fragment?.let {
@ -765,7 +767,7 @@ class GameDetailWrapperFragment : BaseLazyFragment(), IScrollable {
}
}
viewPager.setRestoredCurItem(destinationTab)
currentVpPosition = destinationTab
onPageSelected(destinationTab)
viewPager.offscreenPageLimit = fragmentsList.size
viewPager.adapter =
FragmentAdapter(childFragmentManager, fragmentsList, tabEntityList.map { it.name })
@ -1110,6 +1112,13 @@ class GameDetailWrapperFragment : BaseLazyFragment(), IScrollable {
return true
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(reuse: EBReuse) {
if ("download" == reuse.type) {
downloadBinding.detailProgressbar.performClick()
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(status: EBDownloadStatus) {
updateDownloadCountHint(packageViewModel.filterSameUpdateLiveData.value)
@ -1440,6 +1449,7 @@ class GameDetailWrapperFragment : BaseLazyFragment(), IScrollable {
* 3. 当前游戏 APK 不为 1 个
* 4. 当前游戏类型不为畅玩
* 5. 当前游戏不是双下载时使用本地下载进行下载
* 6. 当前游戏支持加速
*/
private fun isSpecialDownloadDialogAvailable(downloadEntity: DownloadEntity? = null): Boolean {
if (Config.getNewApiSettingsEntity()?.install?.questionTip?.linkEntity == null) return false
@ -1448,7 +1458,7 @@ class GameDetailWrapperFragment : BaseLazyFragment(), IScrollable {
if (downloadEntity?.asVGame() == true) return false
if (downloadEntity?.isSimulatorGame() == true) return false
if (downloadEntity?.isLocalDownloadInDualDownloadMode() == true) return false
if (gameEntity?.canSpeed == true) return false
return true
}
@ -1549,9 +1559,12 @@ class GameDetailWrapperFragment : BaseLazyFragment(), IScrollable {
downloadMenuIcon?.setImageResource(R.drawable.toolbar_download)
if (::viewModel.isInitialized) {
updateConcernMenuIcon(viewModel.meLiveData.value?.isGameConcerned ?: false)
detailViewHolder.downloadPb.buttonStyle = detailViewHolder.downloadPb.buttonStyle
if (detailViewHolder.downloadTips.visibility == View.VISIBLE) {
detailViewHolder.downloadTips.setDownloadTipsAnimation(detailViewHolder.downloadTips.isAnimating)
if (gameEntity != null) {
val viewHolder = detailViewHolder
viewHolder.downloadPb.buttonStyle = detailViewHolder.downloadPb.buttonStyle
if (viewHolder.downloadTips.isVisible) {
viewHolder.downloadTips.setDownloadTipsAnimation(detailViewHolder.downloadTips.isAnimating)
}
}
}
controlReserveBtn()

View File

@ -3,8 +3,11 @@ package com.gh.gamecenter.gamedetail
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.gh.gamecenter.common.constant.Constants.SP_ACCELERATOR_MEMBERSHIP_EXPIRED
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.core.provider.IAcceleratorProvider
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.feature.entity.BaseEntity
import com.gh.gamecenter.feature.entity.TrialEntity
import com.gh.gamecenter.feature.entity.VipEntity
@ -13,6 +16,7 @@ import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.login.user.UserRepository
import com.halo.assistant.accelerator.AccelerationUseCase
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
import com.therouter.TheRouter
import io.reactivex.disposables.CompositeDisposable
class StartingAcceleratorViewModel : ViewModel() {
@ -21,13 +25,14 @@ class StartingAcceleratorViewModel : ViewModel() {
val useCase = AccelerationUseCase()
private val _restartingAcceleratorAction = MutableLiveData<Event<Boolean>>()
val restartingAcceleratorAction: LiveData<Event<Boolean>> = _restartingAcceleratorAction
private val _restartingAcceleratorAction = MutableLiveData<Event<Unit>>()
val restartingAcceleratorAction: LiveData<Event<Unit>> = _restartingAcceleratorAction
fun loadAcceleratorToken() {
val userId = UserManager.getInstance().userId
if (userId.isNotBlank()) {
UserRepository.getInstance().setAcceleratorToken(userId) {
_restartingAcceleratorAction.value = Event(it)
// 这里就算setToken失败也要调用启动加速失败会走正常的日志上报
_restartingAcceleratorAction.value = Event(Unit)
}
}
}
@ -41,16 +46,15 @@ class StartingAcceleratorViewModel : ViewModel() {
.subscribe(object : BiResponse<BaseEntity<TrialEntity>>() {
override fun onSuccess(data: BaseEntity<TrialEntity>) {
if (data.data?.result == true) {
// 刷新vip状态
// 这里先刷新内存数据
AcceleratorDataHolder.instance.setVipEntity(
VipEntity(
_vipStatus = true,
_isNewUser = false,
_isTryVip = true
)
// 试用三小时下单成功
val permissionEntity = VipEntity(
_vipStatus = true,
_isNewUser = false,
_isTryVip = true,
)
AcceleratorDataHolder.instance.setVipEntity(permissionEntity)
TheRouter.get(IAcceleratorProvider::class.java)?.loadQyUserPermissionData()
SPUtils.setBoolean(SP_ACCELERATOR_MEMBERSHIP_EXPIRED, false)
_rechargeTrailResult.value = Event(true)
} else {
_rechargeTrailResult.value = Event(false)
@ -64,6 +68,33 @@ class StartingAcceleratorViewModel : ViewModel() {
}).let(compositeDisposable::add)
}
private val _vipEntity = MutableLiveData<Event<VipEntity?>>()
val vipEntity: LiveData<Event<VipEntity?>> = _vipEntity
fun loadVipStatus() {
val enableRefreshVipStatus = AcceleratorDataHolder.instance.enableRefresh
if (enableRefreshVipStatus) {
useCase.loadVipStatus()
.compose(singleToMain())
.subscribe(object : BiResponse<BaseEntity<VipEntity>>() {
override fun onSuccess(data: BaseEntity<VipEntity>) {
val vip = data.data
if (vip != null) {
AcceleratorDataHolder.instance.setVipEntity(vip)
}
_vipEntity.value = Event(vip)
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
_vipEntity.value = Event(null)
}
}).let(compositeDisposable::add)
} else {
_vipEntity.value = Event(AcceleratorDataHolder.instance.vipEntity)
}
}
override fun onCleared() {
super.onCleared()
compositeDisposable.clear()

View File

@ -15,7 +15,6 @@ import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.databinding.DetailDownloadItemBinding
import com.gh.gamecenter.feature.entity.AcctGameInfo
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.VipEntity
import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorClient
import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorValidator
import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment
@ -56,6 +55,8 @@ class GameDetailAcceleratorUiHelper(private val binding: DetailDownloadItemBindi
private var hasAnyAcctRecord = false
private var showDualDownloadButton: Boolean = false
private val accelerationListener = object : OnAccelerateListener {
override fun onStateChanged(state: AccelerateState) {
when (state) {
@ -77,7 +78,7 @@ class GameDetailAcceleratorUiHelper(private val binding: DetailDownloadItemBindi
}
private val onDataHolderListener = object : AcceleratorDataHolder.OnDataHolderListener {
override fun onVipStateChanged(vip: VipEntity) {
override fun onVipStateChanged() {
showFreeTag()
}
}
@ -132,13 +133,17 @@ class GameDetailAcceleratorUiHelper(private val binding: DetailDownloadItemBindi
val isCurrentGameAccelerating = AcceleratorDataHolder.instance.isCurrentGameAccelerating(game.id)
when {
isCurrentGameAccelerating -> {// 如果当前游戏正处于加速状态,则需要隐藏当前下载按钮
binding.detailProgressbar.goneIf(true)
}
if (!showDualDownloadButton) {
when {
isCurrentGameAccelerating -> {// 如果当前游戏正处于加速状态,则需要隐藏当前下载按钮
binding.detailProgressbar.goneIf(true)
}
binding.detailProgressbar.text == "更新" -> { // 游戏没有处于加速状态,如果 下载按钮为 “更新” 状态,则需要显示出来
binding.detailProgressbar.goneIf(false)
binding.detailProgressbar.text == "更新" -> { // 游戏没有处于加速状态,如果 下载按钮为 “更新” 状态,则需要显示出来
binding.detailProgressbar.setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_light_fill_blue)
binding.detailProgressbar.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
binding.detailProgressbar.goneIf(false)
}
}
}
@ -153,14 +158,13 @@ class GameDetailAcceleratorUiHelper(private val binding: DetailDownloadItemBindi
} ?: R.string.network_acceleration.toResString()
}
fun checkIfShowSpeedUi(show: Boolean) {
fun checkIfShowSpeedUi(show: Boolean, showDualDownloadButton: Boolean) {
this.showDualDownloadButton = showDualDownloadButton
if (!isInit) {
return
}
showSpeedUi = show
binding.clSpeedContainer.goneIf(!showSpeedUi) {
binding.detailProgressbar.setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_light_fill_blue)
binding.detailProgressbar.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
// 是否需要展示弹窗
showGuideLayerIfNeed()
updateSpeedUi()
@ -224,7 +228,7 @@ class GameDetailAcceleratorUiHelper(private val binding: DetailDownloadItemBindi
game.id,
game.name ?: ""
)
guideView?.show(binding.clSpeedContainer, context, uiListener::onStartAccelerator)
guideView?.show(binding.clSpeed, context, uiListener::onStartAccelerator)
}
}
@ -244,6 +248,7 @@ class GameDetailAcceleratorUiHelper(private val binding: DetailDownloadItemBindi
if (isInvalidClick()) {
return
}
val lastAcctGameInfo = last
val memberType = AcceleratorDataHolder.instance.memberType
@ -278,6 +283,7 @@ class GameDetailAcceleratorUiHelper(private val binding: DetailDownloadItemBindi
doStartAccelerating(context, game, gameInfo)
}
}
}
private fun showZoneList(context: Context, game: GameEntity) {
@ -290,13 +296,14 @@ class GameDetailAcceleratorUiHelper(private val binding: DetailDownloadItemBindi
SOURCE_ENTRANCE_GAME_DETAIL
)
}
}
private var clickTime = 0L
private fun doStartAccelerating(context: Context, game: GameEntity, zoneInfo: AcctGameInfo.ZoneInfo) {
val request = AcceleratorValidator.Request(isVip, isNewUser, game, SOURCE_ENTRANCE_GAME_DETAIL)
val request = AcceleratorValidator.Request(game, SOURCE_ENTRANCE_GAME_DETAIL)
AcceleratorClient.newInstance()
.execute(context, request, object : AcceleratorValidator.ValidateListener {
override fun finished(context: Context) {

View File

@ -22,7 +22,7 @@ class AcceleratorClient(
AcceleratorLoginInterceptor(),
AcceleratorPackageCheckInterceptor(),
AcceleratorRealNameInterceptor(),
AcceleratorVipInterceptor(),
AcceleratorPermissionGuideInterceptor(),
AcceleratorStateInterceptor()
)
}

View File

@ -0,0 +1,26 @@
package com.gh.gamecenter.gamedetail.accelerator.chain
import android.content.Context
import com.gh.common.dialog.AcceleratorPermissionGuideDialogFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.core.utils.SPUtils
class AcceleratorPermissionGuideInterceptor : AcceleratorValidator.Interceptor {
override fun intercept(
context: Context,
chain: AcceleratorValidator.Chain,
listener: AcceleratorValidator.ValidateListener?
) {
val hasShow = SPUtils.getBoolean(Constants.SP_ACCELERATOR_PERMISSION_GUIDE_SHOW)
val request = chain.request
if (hasShow) {
chain.proceed(context, request, listener)
} else {
AcceleratorPermissionGuideDialogFragment.show(context) {
if (chain.isValidContext(context)) {
chain.proceed(context, request, listener)
}
}
}
}
}

View File

@ -31,11 +31,9 @@ class AcceleratorValidator {
@Parcelize
data class Request(
val isVip: Boolean,
val isNewUser: Boolean,
val game: GameEntity,
val sourceEntrance: String
):Parcelable
) : Parcelable
}

View File

@ -1,8 +1,14 @@
package com.gh.gamecenter.gamedetail.accelerator.chain
import android.content.Context
import com.gh.common.dialog.AccelerateExpirationDialogFragment
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.toResString
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment
import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment.Companion.SPEED_ENABLE_VIP
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
class AcceleratorVipInterceptor : AcceleratorValidator.Interceptor {
@ -12,20 +18,24 @@ class AcceleratorVipInterceptor : AcceleratorValidator.Interceptor {
listener: AcceleratorValidator.ValidateListener?
) {
val request = chain.request
val isVip = request.isVip
val isNewUser = request.isNewUser
if (isVip || isNewUser) {
val vip = AcceleratorDataHolder.instance.vipEntity
if (vip?.vipStatus == true || vip?.isNewUser == true) {
chain.proceed(context, request, listener)
} else {
val game = request.game
AcceleratorDialogFragment.show(
SPEED_ENABLE_VIP,
game.getUniquePackageName() ?: "",
game.id,
game.name ?: "",
request.sourceEntrance,
context
)
val reminder = AccelerateExpirationDialogFragment.ExpirationReminder.TimeRunsOut(request.sourceEntrance)
AccelerateExpirationDialogFragment.checkDialogShown(context, reminder) {
if (!it) {
val game = request.game
AcceleratorDialogFragment.show(
SPEED_ENABLE_VIP,
game.getUniquePackageName() ?: "",
game.id,
game.name ?: "",
request.sourceEntrance,
context
)
}
}
}
}
}

View File

@ -86,9 +86,7 @@ class AcceleratorZoneDialogFragment : BaseBottomDialogFragment<DialogFragmentAcc
sourceEntrance
)
val isVip = acceleratorDataHolder.isVip
val isNewUser = acceleratorDataHolder.isNewUser
val request = AcceleratorValidator.Request(isVip, isNewUser, game, sourceEntrance)
val request = AcceleratorValidator.Request(game, sourceEntrance)
AcceleratorClient.newInstance()
.execute(it, request, object : AcceleratorValidator.ValidateListener {
override fun finished(context: Context) {

View File

@ -10,30 +10,35 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.viewModels
import com.gh.common.dialog.AccelerateExpirationDialogFragment
import com.gh.common.util.PackageLauncher
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.core.callback.AccelerateState
import com.gh.gamecenter.core.callback.OnAccelerateListener
import com.gh.gamecenter.core.provider.IAcceleratorProvider
import com.gh.gamecenter.core.utils.CurrentActivityHolder
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.DialogFragmentStartingAcceleratorBinding
import com.gh.gamecenter.feature.entity.AcctGameInfo
import com.gh.gamecenter.feature.entity.AcctRecord
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.VipEntity
import com.gh.gamecenter.gamedetail.StartingAcceleratorViewModel
import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.DISTRICT_SERVER_HAVA
import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.SOURCE_ENTRANCE_GAME_DETAIL
import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment.Companion.SPEED_ENABLE_VIP
import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment.Companion.SPEED_START_FAILURE
import com.gh.gamecenter.livedata.EventObserver
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.login.user.UserRepository
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
import com.therouter.TheRouter
import io.reactivex.disposables.CompositeDisposable
import java.util.*
import kotlin.math.ceil
import kotlin.math.max
class StartingAcceleratorDialogFragment : BaseDialogFragment() {
@ -91,14 +96,30 @@ class StartingAcceleratorDialogFragment : BaseDialogFragment() {
// 加速失败上传奇游加速器log
unloadAcceleratorErrorLog(game)
}
}
} else {
// 由于每次加速前,都会检测加速会员权限,所以一般不会出现这种场景,这里只是为了兼容特殊情况,弹出相应提示
if (state.isPermissionExpired) {
// 加速会员过期 ,如果之前没有弹出过耗尽提醒,则弹出提示
val reminder =
AccelerateExpirationDialogFragment.ExpirationReminder.TimeRunsOut(sourceEntrance)
AccelerateExpirationDialogFragment.checkDialogShown(requireContext(), reminder) {
if (!it) {
// 之前弹出过耗尽提醒弹窗,这里直接展示加速失败
AcceleratorDialogFragment.show(
SPEED_START_FAILURE,
game.getUniquePackageName() ?: "",
game.id,
game.name ?: "",
sourceEntrance,
requireContext()
)
}
}
if (state.isPermissionExpired) {
// 加速会员过期刷新本地vip状态
val userId = UserManager.getInstance().userId
UserRepository.getInstance().refreshVipStatus(userId, true)
// 加速失败上传奇游加速器log
unloadAcceleratorErrorLog(game)
iAcceleratorProvider?.loadQyUserPermissionData()
// 加速失败上传奇游加速器log
unloadAcceleratorErrorLog(game)
}
}
dismissAllowingStateLoss()
@ -178,14 +199,6 @@ class StartingAcceleratorDialogFragment : BaseDialogFragment() {
binding.tvProgress.text = getString(R.string.accelerating_with_progress, "0")
iAcceleratorProvider?.bindAccRelatedListener("", accelerationListener)
val isVip = acceleratorDataHolder.isVip
val isNewUser = acceleratorDataHolder.isNewUser
if (isNewUser && !isVip) {
// 新用户并且还不是vip
viewModel.rechargeTrial()
} else {
startGameAccelerate()
}
viewModel.restartingAcceleratorAction.observe(viewLifecycleOwner, EventObserver {
startGameAccelerate()
@ -208,6 +221,162 @@ class StartingAcceleratorDialogFragment : BaseDialogFragment() {
}
})
viewModel.vipEntity.observe(viewLifecycleOwner, EventObserver {
if (it == null) {
// 获取用户vip数据失败直接弹出加速失败
AcceleratorDialogFragment.show(
SPEED_START_FAILURE,
game.getUniquePackageName() ?: "",
game.id,
game.name ?: "",
sourceEntrance,
requireContext()
)
dismissAllowingStateLoss()
trackNetworkAccelerationStartupResult("失败(获取vip数据失败)")
} else {
// 获取用户vip数据成功
canUserEnableAcceleration(it) {
startGameAccelerate()
}
}
})
viewModel.loadVipStatus()
}
private fun canUserEnableAcceleration(vipEntity: VipEntity, callback: () -> Unit) {
if (vipEntity.isNewUser) {
// 如果是新用户,下单试用套餐
viewModel.rechargeTrial()
return
}
val serviceTime = vipEntity.timestamp
val userRemainingDays = calculateCalendarDaysDiff(vipEntity.vipExpireTime, serviceTime)
//如果是试用会员 或者是 包月会员,并且还没有临期: 直接启动。
if (vipEntity.isTryVip || (vipEntity.vipStatus && userRemainingDays > 3)) {
callback()
return
}
// 包月会员不足三天
if (vipEntity.isMonthVip && userRemainingDays in 1..3) {
val reminder = AccelerateExpirationDialogFragment.ExpirationReminder.UserExpired(
userRemainingDays, game.id, game.name ?: "", game.getUniquePackageName() ?: "", sourceEntrance
)
AccelerateExpirationDialogFragment.checkDialogShown(requireContext(), reminder) {
if (it) {
dismissAllowingStateLoss()
} else {
callback()
}
}
return
}
// 检查时长套餐是否快要耗尽
val durationRemainingMinute = vipEntity.durationExpiredTime
if (vipEntity.isDurationVip && durationRemainingMinute < 0) {
// 用户是时长会员,但是剩余时长为 -1 ,说明后端拉取奇游剩余时长失败,此时无法判断用户的时长会员是否到期,直接开启加速
callback()
return
}
// 用户是时长会员,并且将要消耗完毕
val durationRemainingHours = ceil(durationRemainingMinute / 60.0).toLong()
if (vipEntity.isDurationVip && durationRemainingHours in 1..3) {
val reminder = AccelerateExpirationDialogFragment.ExpirationReminder.InsufficientDuration(
durationRemainingHours, game.id, game.name ?: "", game.getUniquePackageName() ?: "", sourceEntrance
)
AccelerateExpirationDialogFragment.checkDialogShown(requireContext(), reminder) {
if (it) {
dismissAllowingStateLoss()
} else {
callback()
}
}
return
}
// 检查时长套餐是否快要过期
val durationRemainingDays = calculateCalendarDaysDiff(vipEntity.durationVipExpiredTime, serviceTime)
if (vipEntity.isDurationVip && durationRemainingDays in 1..3) {
val reminder = AccelerateExpirationDialogFragment.ExpirationReminder.DurationExpired(
durationRemainingDays, game.id, game.name ?: "", game.getUniquePackageName() ?: "", sourceEntrance
)
AccelerateExpirationDialogFragment.checkDialogShown(requireContext(), reminder) {
if (it) {
dismissAllowingStateLoss()
} else {
callback()
}
}
return
}
// 检查会员是否到期
val hasShow = SPUtils.getBoolean(Constants.SP_ACCELERATOR_MEMBERSHIP_EXPIRED)
val showExpiredDialog = !vipEntity.vipStatus &&
!vipEntity.isNewUser &&
!vipEntity.isVipRefundUser &&
!vipEntity.isDurationRefundUser &&
!hasShow
if (showExpiredDialog) {
val reminder = AccelerateExpirationDialogFragment.ExpirationReminder.TimeRunsOut(sourceEntrance)
AccelerateExpirationDialogFragment.checkDialogShown(requireContext(), reminder) {
if (it) {
dismissAllowingStateLoss()
} else {
callback()
}
}
return
}
// 检查是否是会员
if (vipEntity.vipStatus) {
callback()
} else {
// 弹出去充值弹窗
AcceleratorDialogFragment.show(
SPEED_ENABLE_VIP,
game.getUniquePackageName() ?: "",
game.id,
game.name ?: "",
sourceEntrance,
requireContext()
)
dismissAllowingStateLoss()
}
}
private fun calculateCalendarDaysDiff(timeSeconds1: Long, timeSeconds2: Long): Long {
// 转换秒为毫秒
val millis1 = timeSeconds1 * 1000
val millis2 = timeSeconds2 * 1000
val calendar1 = Calendar.getInstance().apply {
timeInMillis = millis1
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val calendar2 = Calendar.getInstance().apply {
timeInMillis = millis2
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
// 计算两个日期零点之间的差值(毫秒),再转换为天数
val diff = (calendar1.timeInMillis - calendar2.timeInMillis) / (1000 * 60 * 60 * 24)
// 当diff == 0 时,当天过期,此时剩余天数为 1 天
return if (diff >= 0) diff + 1 else -1
}

View File

@ -86,7 +86,8 @@ class GameDetailFragment : LazyFragment(), IScrollable {
GameDetailCoverAdapter(
requireContext(),
this@GameDetailFragment,
viewModel
viewModel,
scrollCalculatorHelper
) {
binding.coverSfv.isVisible = shouldShowCoverFilter && it
}
@ -187,15 +188,20 @@ class GameDetailFragment : LazyFragment(), IScrollable {
coverSfv.goneIf(!shouldShowCoverFilter) {
val defaultTabPosition =
tabNameList.indexOfFirst { tabName -> viewModel.defaultCoverEntity?.tabName == tabName }
viewModel.coverTabSequence = defaultTabPosition + 1
viewModel.coverTabName = viewModel.defaultCoverEntity?.tabName ?: ""
coverSfv.setItemList(tabNameList, if (defaultTabPosition != -1) defaultTabPosition else 0)
coverSfv.setOnCheckedCallback { position ->
val checkedText = tabNameList.getOrNull(position)
val currentCoverEntity = it.getOrNull(coverPosition)
viewModel.coverTabSequence = position + 1
viewModel.coverTabName = checkedText ?: ""
SensorsBridge.trackEvent("GameDetailMediaTabClick", json {
"game_id" to gameEntity?.id
"game_name" to gameEntity?.name
"sequence" to position + 1
"tab_name" to checkedText
"sequence" to viewModel.coverTabSequence
"tab_name" to viewModel.coverTabName
"game_type" to gameEntity?.categoryChinese
})
if (currentCoverEntity?.tabName == checkedText) return@setOnCheckedCallback
@ -307,7 +313,7 @@ class GameDetailFragment : LazyFragment(), IScrollable {
private fun initDetailRv() {
binding.detailRv.run {
(itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
itemAnimator = null
layoutManager = detailLayoutManager
adapter = detailListAdapter
addOnScrollListener(object : OnScrollListener() {

View File

@ -23,6 +23,7 @@ import com.gh.gamecenter.databinding.ItemGameCoverVideoBinding
import com.gh.gamecenter.gamedetail.GameDetailViewModel
import com.gh.gamecenter.gamedetail.detail.GameDetailFragment
import com.gh.gamecenter.gamedetail.entity.CoverEntity
import com.gh.gamecenter.gamedetail.video.GameDetailScrollCalculatorHelper
import com.gh.gamecenter.gamedetail.video.TopVideoView
import com.lightgame.adapter.BaseRecyclerAdapter
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
@ -33,6 +34,7 @@ class GameDetailCoverAdapter(
context: Context,
private val fragment: GameDetailFragment,
private val viewModel: GameDetailViewModel,
private val scrollCalculatorHelper: GameDetailScrollCalculatorHelper,
private val showOrHideCoverFilter: ((Boolean) -> Unit)
) : BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
@ -108,6 +110,14 @@ class GameDetailCoverAdapter(
.setAutoFullWithSize(true)
.setDismissControlTime(5000)
.setVideoAllCallBack(object : GSYSampleCallBack() {
override fun onClickStartIcon(url: String?, vararg objects: Any?) {
scrollCalculatorHelper.currentPlayer = holder.binding.player
}
override fun onClickResume(url: String?, vararg objects: Any?) {
scrollCalculatorHelper.currentPlayer = holder.binding.player
}
override fun onQuitFullscreen(url: String?, vararg objects: Any) {
orientationUtils.backToProtVideo()
holder.binding.player.uploadVideoStreamingPlaying("退出全屏")
@ -115,9 +125,9 @@ class GameDetailCoverAdapter(
})
.build(holder.binding.player)
holder.binding.player.gameName = viewModel.game?.name ?: ""
holder.binding.player.viewModel = viewModel
holder.binding.player.showOrHideCoverFilter = showOrHideCoverFilter
holder.binding.player.scrollCalculatorHelper = scrollCalculatorHelper
holder.binding.player.video = topVideo
holder.binding.player.updateThumb(topVideo.poster)

View File

@ -51,7 +51,7 @@ class GameDetailRelatedGameAdapter(
holder.binding.root.setPadding(paddingStart, 8F.dip2px(), paddingEnd, 8F.dip2px())
holder.binding.gameNameTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
holder.binding.gameNameTv.text = gameEntity.name
BindingAdapters.setGameTags(holder.binding.gameLabelLl, gameEntity)
BindingAdapters.setGameTags(holder.binding.gameLabelLl, gameEntity, "")
holder.binding.gameIconView.displayGameIcon(gameEntity)
holder.binding.gameIconView.setBorderColor(com.gh.gamecenter.common.R.color.resource_border)
holder.itemView.setOnClickListener {

View File

@ -46,7 +46,7 @@ class GameDetailBriefItemViewHolder(
highlightedTextClickListener = TextHelper.CopyToClipboardHighlightedTextClick()
)
briefTv.post {
expandTv.isVisible = briefTv.lineCount == 3 && briefTv.layout.getEllipsisCount(2) > 0
expandTv.isVisible = briefTv.lineCount == 3 && (briefTv.layout?.getEllipsisCount(2) ?: 0) > 0
}
expandTv.setOnClickListener {
SensorsBridge.trackGameDetailModuleClick(
@ -57,6 +57,7 @@ class GameDetailBriefItemViewHolder(
moduleType,
entity.name,
sequence,
"简介文案",
gameStatus = gameStatus
)
GameDetailScrollableTextDialogFragment.show(
@ -86,6 +87,9 @@ class GameDetailBriefItemViewHolder(
moduleType,
itemData?.linkGameBrief?.name,
sequence,
"游戏标签",
index + 1,
linkText = gameTag.name,
gameStatus = gameStatus
)
context.startActivity(

View File

@ -82,6 +82,7 @@ class GameDetailComprehensivePanelItemViewHolder(
root.updateLayoutParams<MarginLayoutParams> {
topMargin = if (position == 0) 0 else 8F.dip2px()
}
titleIv.goneIf(!data.showTitle)
titleTv.goneIf(!data.showTitle) {
titleTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
titleTv.text = data.title
@ -127,6 +128,7 @@ class GameDetailComprehensivePanelItemViewHolder(
topMargin = if (position == 0) 0 else 16F.dip2px()
bottomMargin = if (position == itemCount - 1) 0 else if (data.showTitle) 12F.dip2px() else 8F.dip2px()
}
titleIv.goneIf(!data.showTitle)
titleTv.goneIf(!data.showTitle) {
titleTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
titleTv.text = data.title
@ -150,6 +152,7 @@ class GameDetailComprehensivePanelItemViewHolder(
root.updateLayoutParams<MarginLayoutParams> {
topMargin = if (position == 0) 0 else 12F.dip2px()
}
titleIv.goneIf(!data.showTitle)
titleTv.goneIf(!data.showTitle) {
titleTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
titleTv.text = data.title
@ -179,17 +182,23 @@ class GameDetailComprehensivePanelItemViewHolder(
override fun onBindViewHolder(holder: ComprehensivePanelFunctionItemViewHolder, position: Int) {
val data = dataList.getOrNull(position) ?: return
holder.binding.numberIv.setImageResource(R.drawable.bg_game_detail_comprehensive_panel_function_number)
holder.binding.numberIv.goneIf(dataList.size < 2) {
holder.binding.numberIv.setImageResource(R.drawable.bg_game_detail_comprehensive_panel_function_number)
}
holder.binding.numberTv.run {
setTextColor(com.gh.gamecenter.common.R.color.text_aw_primary.toColor(context))
setTypeface(Typeface.createFromAsset(context.assets, Constants.DIN_FONT_PATH))
text = (position + 1).toString()
goneIf(dataList.size < 2) {
setTextColor(com.gh.gamecenter.common.R.color.text_aw_primary.toColor(context))
setTypeface(Typeface.createFromAsset(context.assets, Constants.DIN_FONT_PATH))
text = (position + 1).toString()
}
}
holder.binding.contentTv.run {
setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(context))
maxLines = if (parentViewHolder.showPart && !parentViewHolder.isExpand) 1 else Int.MAX_VALUE
text = data.text
post {
if (layout == null) return@post
val hasEllipsize = layout.getEllipsisCount(0) > 0
if (parentViewHolder.showPart && hasEllipsize && !parentViewHolder.binding.expandTv.isVisible) {
parentViewHolder.binding.expandTv.isVisible = true

View File

@ -2,10 +2,8 @@ package com.gh.gamecenter.gamedetail.detail.viewholder
import androidx.lifecycle.LifecycleOwner
import com.gh.gamecenter.databinding.ItemGameDetailContentCardDoubleBinding
import com.gh.gamecenter.databinding.LayoutGameDetailContentCardBinding
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.gamedetail.GameDetailViewModel
import com.gh.gamecenter.gamedetail.entity.ContentCardEntity
import com.gh.gamecenter.gamedetail.entity.GameDetailData
class GameDetailContentCardDoubleItemViewHolder(
@ -19,51 +17,24 @@ class GameDetailContentCardDoubleItemViewHolder(
super.bindView(data)
data.linkContentCard?.let {
val getGameStatus = { gameStatus }
bindContentCard(
GameDetailContentCardSingleItemViewHolder.bindContentCard(
lifecycleOwner,
viewModel,
it.first(),
data.cardBackground,
binding.firstCardContainer,
baseTrackData.apply { supSequence = 0 },
getGameStatus
)
bindContentCard(
GameDetailContentCardSingleItemViewHolder.bindContentCard(
lifecycleOwner,
viewModel,
it[1],
null,
binding.secondCardContainer,
baseTrackData.apply { supSequence = 1 },
getGameStatus
)
}
}
companion object {
fun bindContentCard(
lifecycleOwner: LifecycleOwner,
viewModel: GameDetailViewModel,
contentCardEntity: ContentCardEntity,
itemBinding: LayoutGameDetailContentCardBinding,
trackData: GameDetailModuleTrackData,
getGameStatus: () -> String
) {
itemBinding.run {
GameDetailContentCardSingleItemViewHolder.bindContentCard(
root.context,
lifecycleOwner,
viewModel,
contentCardEntity,
root,
titleTv,
iconIv,
contentTv,
contentBannerView,
redDotTv,
newIv,
trackData,
getGameStatus
)
}
}
}
}

View File

@ -1,12 +1,8 @@
package com.gh.gamecenter.gamedetail.detail.viewholder
import android.content.Context
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.common.constant.Config
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
@ -19,10 +15,12 @@ import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.TextBannerView
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.databinding.ItemGameDetailContentCardSingleBinding
import com.gh.gamecenter.databinding.LayoutGameDetailContentCardBinding
import com.gh.gamecenter.feature.entity.MeEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.gamedetail.GameDetailViewModel
import com.gh.gamecenter.gamedetail.entity.CardBackground
import com.gh.gamecenter.gamedetail.entity.ContentCardEntity
import com.gh.gamecenter.gamedetail.entity.ContentCardEntity.Companion.TYPE_ARCHIVE
import com.gh.gamecenter.gamedetail.entity.ContentCardEntity.Companion.TYPE_BBS
@ -51,144 +49,147 @@ class GameDetailContentCardSingleItemViewHolder(
val contentCardEntity = data.linkContentCard?.first() ?: return
binding.run {
bindContentCard(
context,
lifecycleOwner,
viewModel,
contentCardEntity,
container,
titleTv,
iconIv,
contentTv,
contentBannerView,
redDotTv,
newIv,
data.cardBackground,
firstCardContainer,
baseTrackData.apply { supSequence = 0 }
) { gameStatus }
}
}
companion object {
fun bindContentCard(
context: Context,
lifecycleOwner: LifecycleOwner,
viewModel: GameDetailViewModel,
contentCardEntity: ContentCardEntity,
containerView: ConstraintLayout,
titleTv: TextView,
iconIv: SimpleDraweeView,
contentTv: TextView,
contentBannerView: TextBannerView,
redDotTv: TextView,
newIv: ImageView,
cardBackground: CardBackground?,
itemBinding: LayoutGameDetailContentCardBinding,
trackData: GameDetailModuleTrackData,
getGameStatus: () -> String
) {
if (contentCardEntity.id == containerView.tag) {
if (contentBannerView.isVisible) contentBannerView.updateView()
containerView.background = com.gh.gamecenter.common.R.drawable.bg_shape_f8_radius_8.toDrawable(context)
titleTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
contentTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
return
}
containerView.tag = contentCardEntity.id
titleTv.text = contentCardEntity.title
ImageUtils.display(iconIv, contentCardEntity.icon)
val showContentTv =
contentCardEntity.showDes && (contentCardEntity.des.isNotEmpty() || contentCardEntity.link.type == TYPE_GIFT)
contentTv.visibleIf(showContentTv) {
contentTv.text =
if (contentCardEntity.link.type == TYPE_GIFT && contentCardEntity.des.isEmpty()) "${contentCardEntity.libao?.total}个游戏礼包" else contentCardEntity.des
contentTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
}
val showContentBannerView =
contentCardEntity.showDes && contentCardEntity.link.type == TYPE_SERVER && contentCardEntity.server != null && contentCardEntity.des.isEmpty()
contentBannerView.goneIf(!showContentBannerView) {
val server = contentCardEntity.server ?: return@goneIf
val nowTime = System.currentTimeMillis()
var timeDiff = 0L
var closestIndex = 0
val bannerTextDataList = server.calendar.mapIndexed { index, serverCalendarEntity ->
val diff = abs(nowTime - serverCalendarEntity.getTime() * 1000L)
if (diff < timeDiff || index == 0) {
timeDiff = diff
closestIndex = index
with(itemBinding) {
val context = root.context
if (contentCardEntity.id == root.tag) {
if (contentBannerView.isVisible) contentBannerView.updateView()
cardBackground?.colorTransparent?.let {
root.setRoundedColorBackgroundByColorInt(
it.RGBAToARGB().hexStringToIntColor(),
8F
)
} ?: run {
root.background = com.gh.gamecenter.common.R.drawable.bg_shape_f8_radius_8.toDrawable(context)
}
val serverTime =
if (TimeUtils.isToday(serverCalendarEntity.getTime()))
serverCalendarEntity.getFormatTime("今天 HH:mm")
else if (TimeUtils.isTomorrow(serverCalendarEntity.getTime()))
serverCalendarEntity.getFormatTime("明天 HH:mm")
else
serverCalendarEntity.getFormatTime("MM-dd HH:mm")
TextBannerView.BannerTextData("${serverTime ?: ""} ${serverCalendarEntity.type}")
titleTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
contentTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
return@with
}
contentBannerView.setDataList(bannerTextDataList, closestIndex)
contentBannerView.startBannerLoop()
}
redDotTv.goneIf(!((contentCardEntity.link.type == TYPE_SERVER && contentCardEntity.server?.total != 0) || contentCardEntity.link.type == TYPE_GIFT)) {
if ((contentCardEntity.link.type == TYPE_SERVER) && (contentCardEntity.server?.calendar?.isNotEmpty() == true))
redDotTv.text = contentCardEntity.server?.total.toString()
if ((contentCardEntity.link.type == TYPE_GIFT) && (contentCardEntity.libao != null))
redDotTv.text = contentCardEntity.libao?.total.toString()
}
val showNewTag = contentCardEntity.link.type == TYPE_ARCHIVE && contentCardEntity.archive != null && contentCardEntity.showNewTag
newIv.goneIf(!showNewTag)
containerView.setOnClickListener {
NewFlatLogUtils.logGameDetailGameContentCardClick(
contentCardEntity.title ?: "",
viewModel.game?.name ?: "",
viewModel.game?.id ?: "",
contentCardEntity.link.type ?: "",
contentCardEntity.link.link ?: "",
contentCardEntity.link.text ?: ""
)
SensorsBridge.trackGameDetailModuleClick(
gameId = trackData.gameId,
gameName = trackData.gameName,
gameType = trackData.gameType,
contentType = "组件内容",
moduleType = trackData.moduleType,
moduleName = trackData.moduleName,
sequence = trackData.sequence,
linkText = contentCardEntity.link.text ?: "",
linkType = contentCardEntity.link.type ?: "",
linkId = contentCardEntity.id,
gameStatus = getGameStatus()
)
val dialog = contentCardEntity.dialog
if (dialog != null) {// 展示内容卡片提示弹窗
DialogHelper.showDialog(
context = context,
title = dialog.title ?: "",
content = dialog.body ?: "",
confirmText = context.getString(com.gh.gamecenter.common.R.string.confirm),
cancelText = context.getString(com.gh.gamecenter.common.R.string.cancel),
confirmClickCallback = {
jumpToContentCardLink(context, contentCardEntity, viewModel)
}
root.tag = contentCardEntity.id
cardBackground?.colorTransparent?.let {
root.setRoundedColorBackgroundByColorInt(
it.RGBAToARGB().hexStringToIntColor(),
8F
)
} else {
jumpToContentCardLink(context, contentCardEntity, viewModel)
}
}
titleTv.text = contentCardEntity.title
ImageUtils.display(iconIv, contentCardEntity.icon)
viewModel.contentCardClickedLiveData.observe(lifecycleOwner) {
if (it.peekContent() == contentCardEntity.link.type) {
it.getContentWithHandled()?.let { containerView.performClick() }
val showContentTv =
contentCardEntity.showDes && (contentCardEntity.des.isNotEmpty() || contentCardEntity.link.type == TYPE_GIFT)
contentTv.visibleIf(showContentTv) {
contentTv.text =
if (contentCardEntity.link.type == TYPE_GIFT && contentCardEntity.des.isEmpty()) "${contentCardEntity.libao?.total}个游戏礼包" else contentCardEntity.des
contentTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
}
val showContentBannerView =
contentCardEntity.showDes && contentCardEntity.link.type == TYPE_SERVER && contentCardEntity.server != null && contentCardEntity.des.isEmpty()
contentBannerView.goneIf(!showContentBannerView) {
val server = contentCardEntity.server ?: return@goneIf
val nowTime = System.currentTimeMillis()
var timeDiff = 0L
var closestIndex = 0
val bannerTextDataList = server.calendar.mapIndexed { index, serverCalendarEntity ->
val diff = abs(nowTime - serverCalendarEntity.getTime() * 1000L)
if (diff < timeDiff || index == 0) {
timeDiff = diff
closestIndex = index
}
val serverTime =
if (TimeUtils.isToday(serverCalendarEntity.getTime()))
serverCalendarEntity.getFormatTime("今天 HH:mm")
else if (TimeUtils.isTomorrow(serverCalendarEntity.getTime()))
serverCalendarEntity.getFormatTime("明天 HH:mm")
else
serverCalendarEntity.getFormatTime("MM-dd HH:mm")
TextBannerView.BannerTextData("${serverTime ?: ""} ${serverCalendarEntity.type}")
}
contentBannerView.setDataList(bannerTextDataList, closestIndex)
contentBannerView.startBannerLoop()
}
redDotTv.goneIf(!((contentCardEntity.link.type == TYPE_SERVER && contentCardEntity.server?.total != 0) || contentCardEntity.link.type == TYPE_GIFT)) {
if ((contentCardEntity.link.type == TYPE_SERVER) && (contentCardEntity.server?.calendar?.isNotEmpty() == true))
redDotTv.text = contentCardEntity.server?.total.toString()
if ((contentCardEntity.link.type == TYPE_GIFT) && (contentCardEntity.libao != null))
redDotTv.text = contentCardEntity.libao?.total.toString()
}
val showNewTag = contentCardEntity.link.type == TYPE_ARCHIVE && contentCardEntity.archive != null && contentCardEntity.showNewTag
newIv.goneIf(!showNewTag)
root.setOnClickListener {
NewFlatLogUtils.logGameDetailGameContentCardClick(
contentCardEntity.title ?: "",
viewModel.game?.name ?: "",
viewModel.game?.id ?: "",
contentCardEntity.link.type ?: "",
contentCardEntity.link.link ?: "",
contentCardEntity.link.text ?: ""
)
SensorsBridge.trackGameDetailModuleClick(
gameId = trackData.gameId,
gameName = trackData.gameName,
gameType = trackData.gameType,
contentType = "组件内容",
moduleType = trackData.moduleType,
moduleName = trackData.moduleName,
sequence = trackData.sequence,
linkText = contentCardEntity.link.text ?: "",
linkType = contentCardEntity.link.type ?: "",
linkId = contentCardEntity.id,
gameStatus = getGameStatus()
)
val dialog = contentCardEntity.dialog
if (dialog != null) {// 展示内容卡片提示弹窗
DialogHelper.showDialog(
context = context,
title = dialog.title ?: "",
content = dialog.body ?: "",
confirmText = context.getString(com.gh.gamecenter.common.R.string.confirm),
cancelText = context.getString(com.gh.gamecenter.common.R.string.cancel),
confirmClickCallback = {
jumpToContentCardLink(context, contentCardEntity, viewModel)
}
)
} else {
jumpToContentCardLink(context, contentCardEntity, viewModel)
}
}
viewModel.contentCardClickedLiveData.observe(lifecycleOwner) {
if (it.peekContent() == contentCardEntity.link.type) {
it.getContentWithHandled()?.let { root.performClick() }
}
}
}
}
private fun jumpToContentCardLink(context: Context, contentCardEntity: ContentCardEntity, viewModel: GameDetailViewModel) {
fun jumpToContentCardLink(context: Context, contentCardEntity: ContentCardEntity, viewModel: GameDetailViewModel) {
val path = "游戏详情->内容卡片"
when (contentCardEntity.link.type) {
TYPE_GIFT,
@ -211,7 +212,7 @@ class GameDetailContentCardSingleItemViewHolder(
ServersCalendarActivity.getIntent(
context,
viewModel.game!!, contentCardEntity.server!!,
MeEntity()
viewModel.meLiveData.value ?: MeEntity()
)
)
}

View File

@ -1,10 +1,17 @@
package com.gh.gamecenter.gamedetail.detail.viewholder
import androidx.lifecycle.LifecycleOwner
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.databinding.ItemGameDetailContentCardTripleBinding
import com.gh.gamecenter.databinding.LayoutGameDetailContentCardSmallBinding
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.gamedetail.GameDetailViewModel
import com.gh.gamecenter.gamedetail.detail.viewholder.GameDetailContentCardDoubleItemViewHolder.Companion.bindContentCard
import com.gh.gamecenter.gamedetail.detail.viewholder.GameDetailContentCardSingleItemViewHolder.Companion.jumpToContentCardLink
import com.gh.gamecenter.gamedetail.entity.ContentCardEntity
import com.gh.gamecenter.gamedetail.entity.ContentCardEntity.Companion.TYPE_ARCHIVE
import com.gh.gamecenter.gamedetail.entity.ContentCardEntity.Companion.TYPE_GIFT
import com.gh.gamecenter.gamedetail.entity.ContentCardEntity.Companion.TYPE_SERVER
import com.gh.gamecenter.gamedetail.entity.GameDetailData
class GameDetailContentCardTripleItemViewHolder(
@ -17,15 +24,16 @@ class GameDetailContentCardTripleItemViewHolder(
super.bindView(data)
data.linkContentCard?.let {
val getGameStatus = { gameStatus }
bindContentCard(
GameDetailContentCardSingleItemViewHolder.bindContentCard(
lifecycleOwner,
viewModel,
it.first(),
data.cardBackground,
binding.firstCardContainer,
baseTrackData.apply { supSequence = 0 },
getGameStatus
)
bindContentCard(
bindSmallContentCard(
lifecycleOwner,
viewModel,
it[1].apply { showDes = false },
@ -33,7 +41,7 @@ class GameDetailContentCardTripleItemViewHolder(
baseTrackData.apply { supSequence = 1 },
getGameStatus
)
bindContentCard(
bindSmallContentCard(
lifecycleOwner,
viewModel,
it[2].apply { showDes = false },
@ -43,4 +51,85 @@ class GameDetailContentCardTripleItemViewHolder(
)
}
}
companion object {
fun bindSmallContentCard(
lifecycleOwner: LifecycleOwner,
viewModel: GameDetailViewModel,
contentCardEntity: ContentCardEntity,
itemBinding: LayoutGameDetailContentCardSmallBinding,
trackData: GameDetailModuleTrackData,
getGameStatus: () -> String
) {
with(itemBinding) {
val context = root.context
if (contentCardEntity.id == root.tag) {
root.background = com.gh.gamecenter.common.R.drawable.bg_shape_f8_radius_8.toDrawable(context)
titleTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
return@with
}
root.tag = contentCardEntity.id
titleTv.text = contentCardEntity.title
ImageUtils.display(iconIv, contentCardEntity.icon)
redDotTv.goneIf(!((contentCardEntity.link.type == TYPE_SERVER && contentCardEntity.server?.total != 0) || contentCardEntity.link.type == TYPE_GIFT)) {
if ((contentCardEntity.link.type == TYPE_SERVER) && (contentCardEntity.server?.calendar?.isNotEmpty() == true))
redDotTv.text = contentCardEntity.server?.total.toString()
if ((contentCardEntity.link.type == TYPE_GIFT) && (contentCardEntity.libao != null))
redDotTv.text = contentCardEntity.libao?.total.toString()
}
val showNewTag = contentCardEntity.link.type == TYPE_ARCHIVE && contentCardEntity.archive != null && contentCardEntity.showNewTag
newIv.goneIf(!showNewTag)
root.setOnClickListener {
NewFlatLogUtils.logGameDetailGameContentCardClick(
contentCardEntity.title ?: "",
viewModel.game?.name ?: "",
viewModel.game?.id ?: "",
contentCardEntity.link.type ?: "",
contentCardEntity.link.link ?: "",
contentCardEntity.link.text ?: ""
)
SensorsBridge.trackGameDetailModuleClick(
gameId = trackData.gameId,
gameName = trackData.gameName,
gameType = trackData.gameType,
contentType = "组件内容",
moduleType = trackData.moduleType,
moduleName = trackData.moduleName,
sequence = trackData.sequence,
linkText = contentCardEntity.link.text ?: "",
linkType = contentCardEntity.link.type ?: "",
linkId = contentCardEntity.id,
gameStatus = getGameStatus()
)
val dialog = contentCardEntity.dialog
if (dialog != null) {// 展示内容卡片提示弹窗
DialogHelper.showDialog(
context = context,
title = dialog.title ?: "",
content = dialog.body ?: "",
confirmText = context.getString(com.gh.gamecenter.common.R.string.confirm),
cancelText = context.getString(com.gh.gamecenter.common.R.string.cancel),
confirmClickCallback = {
jumpToContentCardLink(context, contentCardEntity, viewModel)
}
)
} else {
jumpToContentCardLink(context, contentCardEntity, viewModel)
}
}
viewModel.contentCardClickedLiveData.observe(lifecycleOwner) {
if (it.peekContent() == contentCardEntity.link.type) {
it.getContentWithHandled()?.let { root.performClick() }
}
}
}
}
}
}

View File

@ -27,7 +27,7 @@ class GameDetailDeveloperWordItemViewHolder(
.fromHtmlCompat(PicassoImageGetter(contentTv), ExtraTagHandler())
)
contentTv.post {
expandTv.isVisible = (contentTv.lineCount == 3 && contentTv.layout.getEllipsisCount(2) > 0) || contentTv.lineCount > 3
expandTv.isVisible = (contentTv.lineCount == 3 && (contentTv.layout?.getEllipsisCount(2) ?: 0) > 0) || contentTv.lineCount > 3
}
expandTv.background = R.drawable.bg_ui_surface_expand_gradient.toDrawable(context)
expandTv.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))

View File

@ -120,14 +120,14 @@ class GameDetailInfoItemViewHolder(
nameTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
infoTv.setTextColor(if (data.name == TYPE_ICP) com.gh.gamecenter.common.R.color.text_theme.toColor(context) else com.gh.gamecenter.common.R.color.text_secondary.toColor(context))
actionTv.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
nameTv.text = if (data.name == TYPE_CONTACT) data.contact?.hint else normalTypeNameMap[data.name]
nameTv.text = if (data.name == TYPE_CONTACT) data.text else normalTypeNameMap[data.name]
infoTv.text = when (data.name) {
TYPE_INTERNET -> if (data.value == "yes") "" else ""
TYPE_UPDATE_TIME -> data.value.toLongOrNull()?.let { TimeUtils.getFormatTime(it) }
else -> data.value
}
actionTv.goneIf(data.name != TYPE_CONTACT || data.contact == null) {
val actionString = if (data.contact?.type == "qq") ACTION_CONTACT else if (data.contact?.key.isNullOrEmpty()) ACTION_COPY else ACTION_JOIN
actionTv.goneIf(data.name != TYPE_CONTACT) {
val actionString = if (data.type == "qq") ACTION_CONTACT else if (data.key.isNotEmpty()) ACTION_JOIN else ACTION_COPY
actionTv.text = actionString
actionTv.setOnClickListener {
when (actionString) {
@ -141,7 +141,7 @@ class GameDetailInfoItemViewHolder(
ACTION_JOIN -> {
if (ShareUtils.isQQClientAvailable(context)) {
DirectUtils.directToQqGroup(context, data.contact?.key)
DirectUtils.directToQqGroup(context, data.key)
} else {
data.value.copyTextAndToast("已复制")
}
@ -191,11 +191,11 @@ class GameDetailInfoItemViewHolder(
const val TYPE_CREDIT_CODE = "credit_code"
const val TYPE_SIZE = "size"
const val TYPE_SUPPLIER = "supplier"
const val TYPE_CONTACT = "contact"
const val TYPE_CONTACT = "qq/qq_qun"
const val ACTION_COPY = "复制"
const val ACTION_JOIN = "加入"
const val ACTION_CONTACT = "咨询"
const val ACTION_CONTACT = "添加"
// 固定信息类型
val staticTypeNameMap = mapOf(

View File

@ -53,7 +53,7 @@ class GameDetailServerItemViewHolder(
context,
viewModel.game!!,
entity,
null
viewModel.meLiveData.value
)
context.startActivity(intent)
viewModel.game?.let {

View File

@ -36,7 +36,7 @@ class GameDetailUpdateItemViewHolder(
}
contentTv.text = entity.updateDes
contentTv.post {
expandTv.isVisible = contentTv.lineCount == 3 && contentTv.layout.getEllipsisCount(2) > 0
expandTv.isVisible = contentTv.lineCount == 3 && (contentTv.layout?.getEllipsisCount(2) ?: 0) > 0
}
expandTv.background = R.drawable.bg_ui_surface_expand_gradient.toDrawable(context)
expandTv.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))

View File

@ -0,0 +1,176 @@
package com.gh.gamecenter.gamedetail.dialog
import android.content.Context
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.gh.common.util.DetailDownloadUtils
import com.gh.common.util.DirectUtils
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.DetailViewHolder
import com.gh.gamecenter.common.base.fragment.BaseBottomDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.core.utils.CurrentActivityHolder
import com.gh.gamecenter.databinding.DialogFragmentDspGameDetailBinding
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.GameEntity
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.json.JSONObject
class DspGameDetailDialogFragment : BaseBottomDialogFragment<DialogFragmentDspGameDetailBinding>() {
private var gameEntity: GameEntity? = null
private val dataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
updateDownloadStatus()
}
override fun onDataInit(downloadEntity: DownloadEntity) {
updateDownloadStatus()
}
}
private fun updateDownloadStatus() {
gameEntity?.let {
DetailDownloadUtils.updateViewHolder(
DetailViewHolder(
mBinding.detailLlBottom,
null,
it,
false,
"",
"",
"",
null,
false,
null,
null
)
)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gameEntity = arguments?.getParcelable(EntranceConsts.KEY_GAME_ENTITY)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
gameEntity?.let {
val apk = it.getApk().firstOrNull()
mBinding.ivIcon.displayGameIcon(it)
mBinding.tvName.text = it.name
mBinding.tvVersion.text = getString(R.string.version_name, apk?.version ?: "")
mBinding.titleView.setOnRightClickListener { dismiss() }
mBinding.tvDeveloper.text = getString(R.string.developer_name, it.developer ?: "")
mBinding.tvPermissions.setOnClickListener {
logClickEvent("游戏详情-应用权限")
DirectUtils.directToWebView(
context = requireContext(),
url = gameEntity?.permissionsUrl ?: "",
title = "应用权限")
}
mBinding.tvPrivacyPolicy.setOnClickListener {
logClickEvent("游戏详情-隐私政策")
DirectUtils.directToWebView(
context = requireContext(),
url = gameEntity?.privacyUrl ?: "",
title = "隐私政策"
)
}
mBinding.detailProgressbar.goneIf(apk == null)
if (apk != null) {
mBinding.detailProgressbar.setOnTouchListener { _, motionEvent ->
if (motionEvent.action == MotionEvent.ACTION_DOWN) {
logClickEvent("游戏详情-按钮")
}
return@setOnTouchListener false
}
DetailDownloadUtils.updateViewHolder(
DetailViewHolder(
mBinding.detailLlBottom,
null,
it,
false,
"",
"",
"",
null,
false,
null
)
)
}
}
}
override fun onResume() {
super.onResume()
DownloadManager.getInstance().addObserver(dataWatcher)
}
override fun onPause() {
super.onPause()
DownloadManager.getInstance().removeObserver(dataWatcher)
}
override fun onDestroyView() {
super.onDestroyView()
logClickEvent("游戏详情-关闭弹窗")
}
private fun logClickEvent(text: String) {
gameEntity?.let {
val dspLogMap = it.tempDspLogMap as? HashMap<String, String>
dspLogMap?.let {
dspLogMap["text"] = text
SensorsBridge.trackDspAdClick(JSONObject(dspLogMap as Map<*, *>?))
}
}
}
// 接受安装、卸载消息
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(busFour: EBPackage) {
if (gameEntity != null && gameEntity!!.getApk().size > 0) {
for (apk in gameEntity!!.getApk()) {
if (busFour.packageName == apk.packageName) {
updateDownloadStatus()
}
}
}
}
companion object {
fun show(context: Context, gameEntity: GameEntity) {
if (context is AppCompatActivity) {
context.supportFragmentManager
} else {
(CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
}?.let {
val fragment = DspGameDetailDialogFragment().apply {
arguments = Bundle().apply {
putParcelable(EntranceConsts.KEY_GAME_ENTITY, gameEntity)
}
}
fragment.show(it, fragment::class.java.simpleName)
}
}
}
}

View File

@ -29,6 +29,8 @@ data class GameDetailData(
val linkInfoTag: GameDetailInfoTag? = null,
@SerializedName("link_content_card")
val linkContentCard: List<ContentCardEntity>? = null,
@SerializedName("card_background")
val cardBackground: CardBackground? = null,
@SerializedName("link_advertising")
val linkAdvertising: GameDetailLink? = null,
@SerializedName("link_comprehensive")
@ -136,6 +138,13 @@ data class GameDetailData(
}
}
data class CardBackground(
val color: String = "",
val transparent: String = "",
@SerializedName("color-transparent")
val colorTransparent: String = "", // RGBA格式
)
data class Order(
@SerializedName("before_download")
val beforeDownload: Int = -1, // 下载前排序
@ -408,12 +417,14 @@ data class GameDetailInfo(
@Parcelize
data class InfoItem(
val name: String = "",
val type: String = "",
val text: String = "",
val key: String = "", // QQ群KEY
val value: String = "",
@SerializedName("is_first")
val isFirst: Boolean = true, // 是否显示在一级页面
val order: Int = -1, // 排序
val permissions: List<Permission>? = null,
val contact: Contact? = null
): Parcelable
}

View File

@ -143,13 +143,11 @@ class ServersCalendarActivity : ToolBarActivity() {
setToolbarMenu(R.menu.menu_server_calendar_more)
mBinding.subscribe.visibility = View.GONE
mBinding.subscribeHint1.visibility = View.GONE
mBinding.subscribeHint2.visibility = View.GONE
mBinding.subscribe.setOnClickListener(null)
} else {// 用户已登录并处于未订阅状态
clearMenu()
mBinding.subscribe.visibility = View.VISIBLE
mBinding.subscribeHint1.visibility = View.VISIBLE
mBinding.subscribeHint2.visibility = View.VISIBLE
mBinding.subscribe.setOnClickListener {
CheckLoginUtils.checkLogin(this, "游戏详情-开服日历表-开启订阅") {
mViewModel.subscribeServer()

View File

@ -238,7 +238,7 @@ class ServersCalendarDetailNoDataDialog : BottomSheetDialogFragment() {
wechatRemindCheckIv.alpha = 1.0F
wechatRemind.setOnClickListener {
wechatRemindCheckIv.isChecked = !wechatRemindCheckIv.isChecked
viewModel.wechatRemind = wechatRemindCheckIv.isChecked
// viewModel.wechatRemind = wechatRemindCheckIv.isChecked
SPUtils.setBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, wechatRemindCheckIv.isChecked)
}

View File

@ -30,14 +30,14 @@ class ServersCalendarDetailNoDataViewModel(
var appRemind: Boolean = false
var wechatRemind: Boolean = false
val wechatRemind: Boolean = false
val timeInSeconds: Int = (serverTimeInMills / 1000).toInt()
fun initData(notifySetting: ServerCalendarNotifySetting?) {
if (isDataInit) return
appRemind = notifySetting?.byApp ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_APP, true)
wechatRemind = notifySetting?.byWechat ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, true)
// wechatRemind = notifySetting?.byWechat ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, true)
isDataInit = true
}

View File

@ -308,7 +308,7 @@ class ServersCalendarDetailsRemindDialog : BottomSheetDialogFragment() {
viewBinding.wechatRemind.setOnClickListener {
viewBinding.wechatRemindCheckIv.isChecked = !viewBinding.wechatRemindCheckIv.isChecked
viewModel.wechatRemind = viewBinding.wechatRemindCheckIv.isChecked
// viewModel.wechatRemind = viewBinding.wechatRemindCheckIv.isChecked
SPUtils.setBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, viewBinding.wechatRemindCheckIv.isChecked)
}

View File

@ -36,7 +36,7 @@ class ServersCalendarDetailsRemindViewModel(
var appRemind: Boolean = false
var wechatRemind: Boolean = false
val wechatRemind: Boolean = false
val selectedNotifySeconds: Int get() = (selectedNotifyTime / 1000).toInt()
@ -58,7 +58,7 @@ class ServersCalendarDetailsRemindViewModel(
selectedAdvancedTime = ServersCalendarAdvancedTime.valueOf(notifySetting?.secondsAdvance)
selectedNotifyTime = (notifySetting?.notifyTime ?: calendarEntity.getTime()) * 1000
appRemind = notifySetting?.byApp ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_APP, true)
wechatRemind = notifySetting?.byWechat ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, true)
// wechatRemind = notifySetting?.byWechat ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, true)
isDataInit = true
}

View File

@ -63,7 +63,7 @@ class HistoryApkListAdapter(
holder.binding.expandTv.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(mContext))
holder.binding.updateDescTv.text = apkEntity.updateDesc
holder.binding.updateDescTv.post {
holder.binding.expandTv.isVisible = holder.binding.updateDescTv.lineCount == 3 && holder.binding.updateDescTv.layout.getEllipsisCount(2) > 0
holder.binding.expandTv.isVisible = holder.binding.updateDescTv.lineCount == 3 && (holder.binding.updateDescTv.layout?.getEllipsisCount(2) ?: 0) > 0
}
holder.binding.versionTv.text = "版本${apkEntity.version}"
holder.binding.releaseDateTv.text = TimeUtils.getFormatTime(apkEntity.updateTime)

View File

@ -30,7 +30,9 @@ import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.CustomLinkMovementMethod
import com.gh.gamecenter.common.view.DrawableView
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.core.utils.CenterImageSpan
import com.gh.gamecenter.core.utils.NumberUtils
import com.gh.gamecenter.core.utils.SpanBuilder
import com.gh.gamecenter.databinding.RatingCommentItemBinding
import com.gh.gamecenter.entity.RatingComment
import com.gh.gamecenter.feature.entity.GameEntity
@ -249,7 +251,7 @@ class RatingCommentItemViewHolder(val binding: RatingCommentItemBinding, val pat
if (game.getApk().size > 0 && game.getApk()[0].version == commentData.gameVersion) {
version.text = "当前版本"
} else {
version.text = ("版本:" + commentData.gameVersion)
version.text = commentData.gameVersion
}
}
}

View File

@ -7,6 +7,7 @@ import android.text.Spanned
import android.text.TextPaint
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
@ -15,6 +16,7 @@ import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import com.gh.common.util.*
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.NewLogUtils
@ -27,7 +29,8 @@ import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.DrawableView
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.core.utils.NumberUtils
import com.gh.gamecenter.core.utils.SpanBuilder
import com.gh.gamecenter.databinding.ItemArticleDetailCommentBinding
import com.gh.gamecenter.entity.RatingComment
import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity
@ -165,7 +168,36 @@ class RatingDetailCommentItemViewHolder(val binding: ItemArticleDetailCommentBin
if (game != null && game.getApk().size > 0 && game.getApk()[0].version == commentData.gameVersion) {
version.text = "当前版本"
} else {
version.text = ("版本:" + commentData.gameVersion)
version.text = commentData.gameVersion
}
version.buttonDrawable = R.drawable.ic_version.toDrawable(context)
version.post {
ConstraintSet().apply {
clone(bottomContainer)
if ((version.layout?.lineCount ?: 1) > 1) {
version.gravity = Gravity.TOP
connect(version.id, ConstraintSet.START, device.id, ConstraintSet.START)
connect(version.id, ConstraintSet.TOP, device.id, ConstraintSet.BOTTOM, 6F.dip2px())
connect(version.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, 16F.dip2px())
connect(device.id, ConstraintSet.BOTTOM, version.id, ConstraintSet.TOP)
connect(device.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, 16F.dip2px())
clear(device.id, ConstraintSet.END)
clear(likeCountTv.id, ConstraintSet.TOP)
connect(likeCountTv.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, 4F.dip2px())
bottomContainer.updateLayoutParams<ConstraintLayout.LayoutParams> { height = ConstraintLayout.LayoutParams.WRAP_CONTENT }
} else {
version.gravity = Gravity.CENTER_VERTICAL
connect(version.id, ConstraintSet.START, device.id, ConstraintSet.END)
connect(version.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0)
connect(version.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, 0)
connect(device.id, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
connect(device.id, ConstraintSet.END, version.id, ConstraintSet.START)
connect(device.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0)
connect(likeCountTv.id, ConstraintSet.TOP, timeTv.id, ConstraintSet.TOP, 0)
connect(likeCountTv.id, ConstraintSet.BOTTOM, timeTv.id, ConstraintSet.BOTTOM, 0)
bottomContainer.updateLayoutParams<ConstraintLayout.LayoutParams> { height = 48F.dip2px() }
}
}.applyTo(bottomContainer)
}
if (commentData.me.isCommented) {

View File

@ -513,12 +513,13 @@ class RatingEditActivity : ToolBarActivity(), KeyboardHeightObserver {
ToastUtils.showToast("请先给游戏打分", if (mIsKeyBoardShow) Gravity.CENTER else -1)
return@OnReturnValue
}
if (content.isEmpty()) {
val contentLength = content.count { !it.isWhitespace() }
if (contentLength == 0) {
ToastUtils.showToast("评论内容不能为空喔~", if (mIsKeyBoardShow) Gravity.CENTER else -1)
return@OnReturnValue
}
if (content.length > 10000) {
if (contentLength > 10000) {
ToastUtils.showToast("评论最多10000个字", if (mIsKeyBoardShow) Gravity.CENTER else -1)
return@OnReturnValue
}

View File

@ -14,6 +14,7 @@ import androidx.fragment.app.Fragment
import com.gh.common.util.LogUtils
import com.gh.download.cache.ExoCacheManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.gamecenter.common.observer.MuteCallback
import com.gh.gamecenter.common.observer.VolumeObserver
import com.gh.gamecenter.common.utils.*
@ -40,7 +41,6 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
private var mMuteCallback: MuteCallback
private var mVolumeObserver: VolumeObserver? = null
var gameName = ""
var video: CoverTabEntity.Video? = null
var viewModel: GameDetailViewModel? = null
var uuid = UUID.randomUUID().toString()
@ -50,6 +50,7 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
private var mLastGetContentLengthTime = 0L
var showOrHideCoverFilter: ((Boolean) -> Unit)? = null
var scrollCalculatorHelper: GameDetailScrollCalculatorHelper? = null
init {
post {
@ -87,11 +88,21 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
}
setBackFromFullScreenListener {
// if (it.id == R.id.fullscreen) {
// MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-退出全屏", combinedTitleAndId)
// } else if (it.id == R.id.back) {
// MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-点击返回", combinedTitleAndId)
// }
if (it.id == R.id.fullscreen || it.id == R.id.back) {
SensorsBridge.trackGameDetailVideoClick(
gameName = viewModel?.game?.name ?: "",
gameId = viewModel?.game?.id ?: "",
gameType = viewModel?.game?.categoryChinese ?: "",
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
action = if (it.id == R.id.fullscreen) "退出全屏" else "点击返回",
playType = if (mIsAutoPlay) "自动播放" else "主动播放",
isFullScreen = mIfCurrentIsFullscreen,
sequence = viewModel?.coverTabSequence ?: 1,
tabName = viewModel?.coverTabName ?: "",
playLength = (currentPositionWhenPlaying / 1000).toString()
)
}
clearFullscreenLayout()
}
@ -126,13 +137,22 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
fun startPlayLogic(isAutoPlay: Boolean) {
mIsAutoPlay = isAutoPlay
violenceUpdateMuteStatus()
// if (isAutoPlay) {
// MtaHelper.onEvent("游戏详情_顶部视频", "视频播放方式", "自动播放")
// MtaHelper.onEvent("游戏详情_顶部视频", "顶部视频-自动播放", combinedTitleAndId)
// } else {
// MtaHelper.onEvent("游戏详情_顶部视频", "视频播放方式", "手动播放")
// }
if (isAutoPlay) {
SensorsBridge.trackGameDetailVideoClick(
gameName = viewModel?.game?.name ?: "",
gameId = viewModel?.game?.id ?: "",
gameType = viewModel?.game?.categoryChinese ?: "",
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
action = "自动播放",
playType = if (mIsAutoPlay) "自动播放" else "主动播放",
isFullScreen = mIfCurrentIsFullscreen,
sequence = viewModel?.coverTabSequence ?: 1,
tabName = viewModel?.coverTabName ?: "",
playLength = (currentPositionWhenPlaying / 1000).toString()
)
val seekTime = ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(video?.url))
seekOnStart = seekTime
mTouchingProgressBar = false
@ -237,6 +257,7 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
// 不需要弹弹窗,直接播放
override fun showWifiDialog() {
scrollCalculatorHelper?.currentPlayer = this
startPlayLogic(false)
//val trafficVideo = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SettingsFragment.TRAFFIC_VIDEO_SP_KEY, false)
//if (trafficVideo) {
@ -289,7 +310,7 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
override fun onStopTrackingTouch(seekBar: SeekBar?) {
super.onStopTrackingTouch(seekBar)
uploadVideoStreamingPlaying("拖动")
uploadVideoStreamingPlaying("拖动进度条", seekBar)
}
override fun isShowNetConfirm(): Boolean {
@ -533,7 +554,7 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
}
override fun releaseVideos() {
uploadVideoStreamingPlaying("结束播放")
uploadVideoStreamingPlaying("播放完毕")
CustomManager.releaseAllVideos(getKey())
}
@ -556,11 +577,21 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
override fun onClick(v: View) {
when (v.id) {
R.id.start -> {
// if (currentState == GSYVideoView.CURRENT_STATE_PLAYING) {
// MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-点击暂停", combinedTitleAndId)
// } else {
// MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-点击播放", combinedTitleAndId)
// }
SensorsBridge.trackGameDetailVideoClick(
gameName = viewModel?.game?.name ?: "",
gameId = viewModel?.game?.id ?: "",
gameType = viewModel?.game?.categoryChinese ?: "",
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
action = if (currentState == GSYVideoView.CURRENT_STATE_PLAYING) "点击暂停" else "点击播放",
playType = if (mIsAutoPlay) "自动播放" else "主动播放",
isFullScreen = mIfCurrentIsFullscreen,
sequence = viewModel?.coverTabSequence ?: 1,
tabName = viewModel?.coverTabName ?: "",
playLength = (currentPositionWhenPlaying / 1000).toString()
)
// 手动触发暂停/播放,后续行为都算主动播放
mIsAutoPlay = false
super.onClick(v)
}
@ -568,13 +599,11 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
}
}
private fun getMtaKeyPrefix() = if (mIfCurrentIsFullscreen) "顶部视频(全屏)" else "顶部视频"
fun getCurrentPosition(): Long {
return mCurrentPosition
}
fun uploadVideoStreamingPlaying(action: String) {
fun uploadVideoStreamingPlaying(action: String, seekBar: SeekBar? = null) {
if (video == null || video?.url.isNullOrEmpty()) return
runOnIoThread(isHeavyWightTask = true) {
val isLandscape = mOrientationUtils != null
@ -598,6 +627,26 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
action, video?.videoId, video?.title, viewModel?.game?.id, viewModel?.game?.name,
videoPlayModel, videoPlayStatus(), mContentLength, videoTotalTime, videoPlayTs, progress.toInt()
)
if (action != "开始播放" && action != "暂停播放" && action != "退出全屏") {
val playLength = when (action) {
"播放完毕" -> (duration / 1000).toString()
"拖动进度条" -> ((seekBar?.progress ?: 0) * duration / 100000).toString()
else -> (currentPositionWhenPlaying / 1000).toString()
}
SensorsBridge.trackGameDetailVideoClick(
gameName = viewModel?.game?.name ?: "",
gameId = viewModel?.game?.id ?: "",
gameType = viewModel?.game?.categoryChinese ?: "",
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
action = action,
playType = if (mIsAutoPlay) "自动播放" else "主动播放",
isFullScreen = mIfCurrentIsFullscreen,
sequence = viewModel?.coverTabSequence ?: 1,
tabName = viewModel?.coverTabName ?: "",
playLength = playLength
)
}
}
}
}

View File

@ -62,6 +62,7 @@ import com.gh.gamecenter.feature.exposure.ExposureType
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
import com.gh.gamecenter.feature.utils.SentryHelper
import com.gh.gamecenter.game.commoncollection.detail.CustomCommonCollectionDetailActivity
import com.gh.gamecenter.gamedetail.dialog.DspGameDetailDialogFragment
import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity
import com.gh.gamecenter.home.PageConfigure
import com.gh.gamecenter.home.custom.adapter.CustomPageAdapter
@ -112,6 +113,7 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB
private var bottomTabName = ""
private var tabIndex = -1
private var showFloatingWindow = true
private var showPullDownPush = true
private lateinit var pageLocation: PageLocation
@ -129,6 +131,7 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB
}
}
override fun onDataInit(downloadEntity: DownloadEntity) {
adapter.notifyDownload(downloadEntity)
}
@ -143,6 +146,7 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB
bottomTabName = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_NAME, "") ?: ""
tabIndex = arguments?.getInt(EntranceConsts.KEY_TAB_INDEX, -1) ?: -1
showFloatingWindow = arguments?.getBoolean(EntranceConsts.KEY_SHOW_FLOATING_WINDOW, true) ?: true
showPullDownPush = arguments?.getBoolean(EntranceConsts.KEY_SHOW_PULL_DOWN_PUSH, true) ?: true
val tabName = arguments?.getString(EntranceConsts.KEY_TAB_NAME, "") ?: ""
val multiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: ""
val multiTabNavName = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, "") ?: ""
@ -264,7 +268,11 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB
if (status == LoadStatus.INIT_LOADING) {
binding.reuseLoading.root.goneIf(false)
binding.root.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext()))
binding.root.setBackgroundColor(
com.gh.gamecenter.common.R.color.ui_surface.toColor(
requireContext()
)
)
} else {
binding.reuseLoading.root.goneIf(true)
binding.root.setBackgroundColor(Color.TRANSPARENT)
@ -322,15 +330,27 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB
}
gameDetailDestination.observe(viewLifecycleOwner, EventObserver { (entrance, game) ->
if (game.isMiniGame()) {
MiniGameItemHelper.launchMiniGame(game)
} else {
GameDetailActivity.startGameDetailActivity(
requireContext(),
game.id,
entrance,
traceEvent = game.exposureEvent,
)
when {
game.isDspGame -> {
if (game.interactionType == 2) {
DspGameDetailDialogFragment.show(requireContext(), game)
} else if (game.interactionType == 4) {
MiniGameItemHelper.launchMiniGame(game)
}
}
game.isMiniGame() -> {
MiniGameItemHelper.launchMiniGame(game)
}
else -> {
GameDetailActivity.startGameDetailActivity(
requireContext(),
game.id,
entrance,
traceEvent = game.exposureEvent,
)
}
}
})
@ -587,7 +607,9 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB
private fun buildPriorityChain() {
val videoHandler = VideoHandler(24, scrollCalculatorHelper)
priorityChain.addHandler(pullDownPushHandler)
if (showPullDownPush) {
priorityChain.addHandler(pullDownPushHandler)
}
if (showFloatingWindow) {
val floatingWindowHandler = CustomFloatingWindowHandler(23)
@ -775,7 +797,11 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB
adapter.notifyItemRangeChanged(0, adapter.itemCount)
binding.gameList.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext()))
searchBarBinding?.searchTv?.setHintTextColor(com.gh.gamecenter.common.R.color.text_instance.toColor(requireContext()))
searchBarBinding?.searchTv?.setHintTextColor(
com.gh.gamecenter.common.R.color.text_instance.toColor(
requireContext()
)
)
onScrollChanged(true)
}
}
@ -812,6 +838,7 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable, IB
RefreshState.TwoLevel -> {
setScrollEnabled(false)
}
RefreshState.None -> {
setScrollEnabled(true)
}

View File

@ -86,6 +86,7 @@ class CustomPageViewModel(
addSource(repository.wechatMiniGameCPMItemLiveData, ::notifyWechatMiniGameCPMSubjectItemChanged)
addSource(repository.customRecentAcceleratorItemLiveData, ::notifyItemChanged)
addSource(repository.pkDataListLiveData, ::notifyPKDataChanged)
addSource(repository.dspSubjectLiveData, ::notifyDspSubjectItemChanged)
}
val dataList: LiveData<List<CustomPageItem>> = _dataList
@ -580,6 +581,43 @@ class CustomPageViewModel(
_dataList.value = newData
}
/**
* 通过 dsp 接口异步获取数据插入
*/
private fun notifyDspSubjectItemChanged(result: Pair<String, List<GameEntity>>) {
val (subjectId, gameList) = result
val oldData = dataList.value ?: return
val index = oldData.indexOfFirst {
it is CustomDspPlaceholderItem && subjectId == it.data.id
}
if (index == -1) {
return
}
val subjectItem = (oldData[index] as CustomDspPlaceholderItem).apply {
data.data = gameList.toMutableList()
data.isDspSubject = true
}
val position = subjectItem.position
val componentPosition = subjectItem.componentPosition
val newData = oldData.toMutableList()
val customPageItemList = repository.convertColumnDetailSubjectItems(
position,
componentPosition,
subjectItem.link,
subjectItem.data
)
if (customPageItemList.isEmpty()) return
newData[index] = customPageItemList[0]
newData.addAll(index + 1, customPageItemList.subList(1, customPageItemList.size))
newData.forEachIndexed { pos, customPageItem ->
customPageItem.position = pos
}
repository.notifyPositionChanged(newData.size)
gamePositionAndPackageHelper.clear()
gamePositionAndPackageHelper.putAll(getPositionAndPackageMap(newData))
_dataList.value = newData
}
private fun notifyPKDataChanged(pkList: List<PKEntity>) {
if (pkList.isEmpty()) return

View File

@ -187,7 +187,7 @@ class CustomFoldSlideLargeImageItemAdapter(
binding.gStar.goneIf(!(data.data.showStar && game.commentCount > 3)) {
binding.tvStar.text = if (game.star == 10.0F) "10" else game.star.toString()
}
BindingAdapters.setGameTags(binding.llLabel, game)
BindingAdapters.setGameTags(binding.llLabel, game, "")
binding.cvContainer.setCardBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(itemView.context))
try {

View File

@ -91,7 +91,7 @@ class CustomGamePluginAdapter(
binding.gameRating.textSize =
(if (gameEntity.commentCount > 3) 12 else 10).toFloat()
BindingAdapters.setGameName(binding.gameName, gameEntity, true)
BindingAdapters.setGameTags(binding.labelList, gameEntity)
BindingAdapters.setGameTags(binding.labelList, gameEntity, "")
binding.gameRating.setDrawableStart(
if (gameEntity.commentCount > 3) com.gh.gamecenter.feature.R.drawable.game_horizontal_rating.toDrawable() else null,
null,

View File

@ -142,7 +142,7 @@ class CustomGameRefreshVerticalAdapter(
false
)
BindingAdapters.setGame(iconIv, gameEntity)
BindingAdapters.setGameTags(gameTagContainer, gameEntity)
BindingAdapters.setGameTags(gameTagContainer, gameEntity, "")
GameItemViewHolder.initServerType(gameNameTv, serverTypeTv, gameEntity)
gameDesTv.text = gameEntity.decoratedDes
GameItemViewHolder.initGameSubtitleAndAdLabel(

View File

@ -59,7 +59,8 @@ class CustomGameVerticalAdapter(
briefStyle = _data.briefStyle,
adIconActive = _data.adIconActive,
showSubscript = _data.showIndexIconSubscript,
showSubTitle = _data.showIndexSubtitle
showSubTitle = _data.showIndexSubtitle,
subjectTag = _data.tag ?: ""
)
holder.itemView.setOnClickListener {
@ -116,7 +117,8 @@ class CustomGameVerticalAdapter(
briefStyle: String,
adIconActive: Boolean,
showSubscript: Boolean,
showSubTitle: Boolean
showSubTitle: Boolean,
subjectTag: String = ""
) {
val context = itemView.context
@ -141,16 +143,23 @@ class CustomGameVerticalAdapter(
with(ui) {
gameNameTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
serverTypeTv.setTextColor(com.gh.gamecenter.common.R.color.primary_theme.toColor(context))
downloadTv.background = com.gh.gamecenter.common.R.drawable.download_button_normal_style.toDrawable(context)
downloadTv.background =
com.gh.gamecenter.common.R.drawable.download_button_normal_style.toDrawable(context)
gameDesTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
BindingAdapters.setGameName(
gameNameTv,
gameEntity,
false
)
iconIv.displayGameIconWithIfShowSubscript(gameEntity, showSubscript)
BindingAdapters.setGameTags(gameTagContainer, gameEntity)
BindingAdapters.setGameTagsWithSellingPoint(
gameTagContainer,
sellingPointsBinding,
gameEntity,
subjectTag
)
GameItemViewHolder.initServerType(gameNameTv, serverTypeTv, gameEntity)
val styleResId = R.style.CustomPageGameRating

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