Compare commits

..

112 Commits

Author SHA1 Message Date
bd02a0b4be refactor: update va commit id. 2024-09-09 18:06:09 +08:00
7a498763bb feat: 去掉Va保活通知栏,简单的为每一个游戏bindservice到va_core进程。 2024-09-09 18:01:35 +08:00
8b839a5b13 Merge branch 'feat/GHZSCY-5596' into 'dev'
feat: 广告位管理第三方广告相关优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-5596

See merge request halo/android/assistant-android!1884
2024-09-09 17:59:03 +08:00
7d2ed0eac0 feat: 广告位管理第三方广告相关优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-5596 2024-09-09 17:59:03 +08:00
5c70bc2237 Merge branch 'fix/remove_diverter_param' into 'dev'
fix: 移除分流器无用的接口参数

See merge request halo/android/assistant-android!1883
2024-09-09 17:11:22 +08:00
03b36096b8 fix: 移除分流器无用的接口参数 2024-09-09 17:00:57 +08:00
4191dbc4f0 Merge branch 'feat/GHZSCY-5891' into 'dev'
feat: 新增页面分流器—客户端 https://jira.shanqu.cc/browse/GHZSCY-5891

See merge request halo/android/assistant-android!1876
2024-09-09 13:40:52 +08:00
cc20b6e38a Merge branch 'merge/GHZSCY-6494' into 'dev'
refactor: 敏感词文件,身份证区域文件,兼容Android Pie的apache http dex文件移到插件zip包...

See merge request halo/android/assistant-android!1881
2024-09-09 11:19:45 +08:00
3ad5890238 fix: 【光环助手】提示显示问题 https://jira.shanqu.cc/browse/GHZSCY-6632 2024-09-09 11:14:38 +08:00
6179d7055b feat: vasdk compileSdk version set to 34 2024-09-09 11:07:02 +08:00
f18391adc2 fix: 64位插件下载没有game_id的问题 https://jira.shanqu.cc/browse/GHZSCY-6494 2024-09-09 10:51:37 +08:00
3a2f15a436 refactor: 敏感词文件,身份证区域文件,兼容Android Pie的apache http dex文件移到插件zip包 https://jira.shanqu.cc/browse/GHZSCY-6494
d44e4c30
2024-09-09 10:51:37 +08:00
c0e8160955 refactor: 打包去掉插件下载到assets https://jira.shanqu.cc/browse/GHZSCY-6494 2024-09-09 10:50:46 +08:00
9038131c96 refactor: 版本号meta信息维护 2024-09-09 10:50:03 +08:00
4430ae1107 refactor: 整理va_version,根据product flavor来选择-debug后缀, 打包根据git tag 来选择打正式包还是测试包 2024-09-09 10:48:18 +08:00
d6aa2690b2 feat: 临时提交meta信息 2024-09-09 10:47:54 +08:00
e42fd24b71 feat: 插件下载上报埋点 2024-09-09 10:47:49 +08:00
452a1ede24 feat: 调整64位va插件下载时机 2024-09-09 10:46:57 +08:00
ceb227f645 feat: 新增页面分流器—客户端 https://jira.shanqu.cc/browse/GHZSCY-5891 2024-09-06 17:13:44 +08:00
479ea5778b Merge branch 'fix/GHZSCY-6589' into 'dev'
fix:【光环助手】游戏专题-游戏排序问题 https://jira.shanqu.cc/browse/GHZSCY-6589

See merge request halo/android/assistant-android!1879
2024-09-06 17:06:53 +08:00
8969d6fd5d fix:【光环助手】游戏专题-游戏排序问题 https://jira.shanqu.cc/browse/GHZSCY-6589 2024-09-06 16:41:27 +08:00
cc43f2e0fd Merge branch 'feat/GHZSCY-6492-select-only' into 'dev'
feat: 媒体文件上传控件优化(一) https://jira.shanqu.cc/browse/GHZSCY-6282

See merge request halo/android/assistant-android!1877
2024-09-06 15:40:18 +08:00
ab12491f2f feat: 媒体文件上传控件优化(一) https://jira.shanqu.cc/browse/GHZSCY-6282 2024-09-06 15:38:46 +08:00
ae24ab0c87 Merge branch 'fix/GHZSCY-6566' into 'dev'
fix: 批量删除畅玩游戏时会有部分游戏去到下载列表 https://jira.shanqu.cc/browse/GHZSCY-6566

See merge request halo/android/assistant-android!1868
2024-09-05 17:44:06 +08:00
383712900c Merge branch 'release' into 'dev'
merge release to dev

See merge request halo/android/assistant-android!1872
2024-09-04 11:48:42 +08:00
cea62b55e2 Merge branch 'hotfix/v5.37.4-1094/GHZSCY-6631' into 'release'
fix: 特定场景下首次启动APP未直接打开设置跳转的页面 https://jira.shanqu.cc/browse/GHZSCY-6631

See merge request halo/android/assistant-android!1871
2024-09-04 11:40:58 +08:00
ff6cdb1ba3 Merge branch 'hotfix/v5.37.4-1094/GHZSCY-6613' into 'release'
fix: 客户端启动过程中若首页视频自动播放会置顶显示在开屏广告上 https://jira.shanqu.cc/browse/GHZSCY-6613

See merge request halo/android/assistant-android!1870
2024-09-04 11:40:43 +08:00
b6e531fa96 fix: 特定场景下首次启动APP未直接打开设置跳转的页面 https://jira.shanqu.cc/browse/GHZSCY-6631 2024-09-04 11:06:39 +08:00
e7a37df690 fix: 客户端启动过程中若首页视频自动播放会置顶显示在开屏广告上 https://jira.shanqu.cc/browse/GHZSCY-6613
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-09-03 17:57:38 +08:00
5308ccfabe Merge branch 'fix/GHZSCY-6615' into 'dev'
fix: 空间不足时,下载游戏提示网络不佳且重复请求 https://jira.shanqu.cc/browse/GHZSCY-6615

See merge request halo/android/assistant-android!1867
2024-09-02 15:56:16 +08:00
2ee49b9a20 Merge branch 'fix/GHZSCY-6628' into 'dev'
fix: 修复页面跳转闪退问题 https://jira.shanqu.cc/browse/GHZSCY-6628

See merge request halo/android/assistant-android!1869
2024-09-02 15:49:19 +08:00
3af3d413c0 fix: 修复页面跳转闪退问题 https://jira.shanqu.cc/browse/GHZSCY-6628
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-09-02 14:32:13 +08:00
9bf24977ca Merge branch 'release' into dev
# Conflicts:
#	dependencies.gradle
2024-09-02 13:35:01 +08:00
bf1dd958cd Merge branch 'fix/clean_apk_culprit' into 'dev'
fix: 修复清理安装包时会检索到插件 APK 的问题

See merge request halo/android/assistant-android!1865
2024-09-02 11:37:00 +08:00
30c34b799b fix: 修复清理安装包时会检索到插件 APK 的问题
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-09-02 11:33:52 +08:00
b202b580bb fix: 空间不足时,下载游戏提示网络不佳且重复请求 https://jira.shanqu.cc/browse/GHZSCY-6615
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-09-02 10:48:14 +08:00
239b056abb Merge branch 'hotfix/v5.37.4-1094/illegal_state_exception' into 'release'
fix: 修复请求所有文件访问权限弹窗弹出偶发的闪退问题...

See merge request halo/android/assistant-android!1864
2024-08-30 10:05:24 +08:00
315be3797c fix: 修复请求所有文件访问权限弹窗弹出偶发的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/406178/?project=22 2024-08-30 09:49:52 +08:00
6d29da5172 Merge branch 'fix/jump_to_home_from_external_error' into 'dev'
fix: 从外部跳转光环首页时,SkipActivity未关闭的问题

See merge request halo/android/assistant-android!1863
2024-08-29 16:21:57 +08:00
8aeb0d6f09 fix: 从外部跳转光环首页时,SkipActivity未关闭的问题 2024-08-29 16:19:22 +08:00
bdcca58770 build: fix 编译时无法拉取远端依赖的问题 2024-08-29 15:48:16 +08:00
4115383b68 Merge branch 'feat/GHZSCY-6517' into 'dev'
feat: 优化畅玩安装完成提示启动弹窗—客户端 https://jira.shanqu.cc/browse/GHZSCY-6517

See merge request halo/android/assistant-android!1845
2024-08-29 10:44:05 +08:00
9075bfa214 feat: 优化畅玩安装完成提示启动弹窗—客户端 https://jira.shanqu.cc/browse/GHZSCY-6517 2024-08-29 10:44:04 +08:00
3fa63e331b Merge branch 'feature-GHZS-5576' into 'dev'
feat: 搜索业务-热门榜单新增触发搜索的榜单类型—客户端 https://jira.shanqu.cc/browse/GHZSCY-5576

See merge request halo/android/assistant-android!1738
2024-08-29 10:43:47 +08:00
aef19fcd49 feat: 搜索业务-热门榜单新增触发搜索的榜单类型—客户端 https://jira.shanqu.cc/browse/GHZSCY-5576 2024-08-29 10:43:47 +08:00
15d7252974 Merge branch 'feature-GHZS-5572' into 'dev'
feat: 搜索业务-新增搜索发现取代热门标签—客户端 https://jira.shanqu.cc/browse/GHZSCY-5572

See merge request halo/android/assistant-android!1732
2024-08-29 10:33:27 +08:00
f6fa060f3a feat: 搜索业务-新增搜索发现取代热门标签—客户端 https://jira.shanqu.cc/browse/GHZSCY-5572 2024-08-29 10:33:27 +08:00
7cfe48b3c8 Merge branch 'feat/GHZSCY-6601' into 'dev'
feat: 游戏详情弹窗补充神策埋点—客户端 https://jira.shanqu.cc/browse/GHZSCY-6601

See merge request halo/android/assistant-android!1862
2024-08-29 10:11:43 +08:00
6bd3c1011d feat: 游戏搜索-排序专题 曝光补充游戏ID—客户端 https://jira.shanqu.cc/browse/GHZSCY-6602 2024-08-29 10:08:57 +08:00
3447ecffd8 feat: 游戏详情弹窗补充神策埋点—客户端 https://jira.shanqu.cc/browse/GHZSCY-6601 2024-08-29 10:08:57 +08:00
644b93ab02 fix: 编译问题 2024-08-28 15:10:24 +08:00
6b44140ff3 fix: 批量删除畅玩游戏时会有部分游戏去到下载列表 https://jira.shanqu.cc/browse/GHZSCY-6566
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-27 17:56:41 +08:00
78d26b4cc1 Merge branch 'feat/GHZSCY-6506' into 'dev'
feat: 消息推送:神策埋点新增用户属性极光推送注册id—客户端 https://jira.shanqu.cc/browse/GHZSCY-6506

See merge request halo/android/assistant-android!1861
2024-08-27 14:07:17 +08:00
5565539445 feat: 消息推送:神策埋点新增用户属性极光推送注册id—客户端 https://jira.shanqu.cc/browse/GHZSCY-6506 2024-08-27 14:07:16 +08:00
1ee0f292ba Revert "fix: 修复因为低级失误导致 sentry.onEvent 方法失效的问题"
This reverts commit 0731cc1fde.
2024-08-26 16:06:59 +08:00
f609637f0b Merge branch 'feat/GHZSCY-6578' into 'release'
feat:【光环助手】设备信息显示 https://jira.shanqu.cc/browse/GHZSCY-6578

See merge request halo/android/assistant-android!1860
2024-08-26 14:07:55 +08:00
9d10add8eb feat:【光环助手】设备信息显示 https://jira.shanqu.cc/browse/GHZSCY-6578 2024-08-26 14:07:54 +08:00
f5a39f982e chore: 版本更新至 5.37.4 2024-08-26 10:33:09 +08:00
1a063bb0c1 Merge branch 'hotfix/v5.37.3-1093/sentry_culprit' into 'release'
fix: 修复因为低级失误导致 sentry.onEvent 方法失效的问题,捕抓更多 deadSystemException

See merge request halo/android/assistant-android!1859
2024-08-26 10:30:37 +08:00
86340d603f fix: 捕抓应用安装完成后上报用户玩过的游戏有机率遇到的 deadSystemException 并上报 sentry https://sentry.shanqu.cc/organizations/lightgame/issues/404487
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-26 10:21:42 +08:00
0731cc1fde fix: 修复因为低级失误导致 sentry.onEvent 方法失效的问题
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-26 10:20:00 +08:00
86dfee2ff9 Merge branch 'hotfix/v5.37.3-1093/null_pointer_exception' into 'release'
fix: 修复获取BusinessId时出现的空指针闪退问题...

See merge request halo/android/assistant-android!1858
2024-08-26 09:58:11 +08:00
edf82952ce fix: 修复获取BusinessId时出现的空指针闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/404588/?project=22 2024-08-26 09:54:22 +08:00
062f0149d0 Merge branch 'fix/start_activity_error' into 'dev'
fix: 修复页面重建时,部分跳转失效的问题

See merge request halo/android/assistant-android!1857
2024-08-23 16:41:38 +08:00
a504f00bac fix: 修复页面重建时,部分跳转失效的问题
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-23 16:25:24 +08:00
4412d159bb Merge branch 'fix/sentry-404504-3' into 'release'
fix: 下载悬浮窗Detach执行图标贴边动画造成的崩溃...

See merge request halo/android/assistant-android!1856
2024-08-23 16:05:11 +08:00
e119b7fecd fix: 下载悬浮窗Detach执行图标贴边动画造成的崩溃... 2024-08-23 16:05:11 +08:00
72460b88be Merge remote-tracking branch 'origin/release' into dev
# Conflicts:
#	dependencies.gradle
2024-08-23 11:38:24 +08:00
3fe7c8252e chore: 版本更新至 5.37.3 2024-08-22 11:19:28 +08:00
36ebab77ee Merge branch 'hotfix/v5.37.2-1092/dead_system_exception' into 'release'
fix: catch 部分高频调用的包信息获取方法,遇到 DeadSystemException 时上报 sentry

See merge request halo/android/assistant-android!1853
2024-08-22 11:18:35 +08:00
e84db01984 Merge branch 'hotfix/v5.37.2-1092/view_binding' into 'release'
fix: 用 sentry 记录 SearchToolbarTabWrapperFragment ViewBinding bind 异常时 gid,尝试重试一次

See merge request halo/android/assistant-android!1852
2024-08-22 11:16:36 +08:00
ae605d4a55 fix: 用 sentry 记录 SearchToolbarTabWrapperFragment ViewBinding bind 异常时 gid,尝试重试一次
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-22 11:11:12 +08:00
882bf4b551 fix: catch 部分高频调用的包信息获取方法,遇到 DeadSystemException 时上报 sentry
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-22 10:05:09 +08:00
968164af55 Merge branch 'hotfix/v5.37.2-1092/class_cast_exception' into 'release'
fix: 修复帮助中心分类弹窗弹出时的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/405504/?project=22

See merge request halo/android/assistant-android!1850
2024-08-21 10:05:47 +08:00
0119e6f123 fix: 修复帮助中心分类弹窗弹出时的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/405504/?project=22 2024-08-21 09:59:40 +08:00
7093552259 Merge branch 'FIX/sentry-404504-2' into 'release'
fix: 悬浮窗Detach时机问题造成的崩溃2

See merge request halo/android/assistant-android!1847
2024-08-20 14:02:02 +08:00
2be52d21ee fix: 悬浮窗Detach时机问题造成的崩溃2 2024-08-20 14:02:02 +08:00
cb093cc3f0 Merge branch 'hotfix/v5.37.1-1091/jobservice_abstract_method_error' into 'release'
fix: 修复 94d0e2b5f19760d22381ead093c9e89778a14bf4 这个版本没有修复的...

See merge request halo/android/assistant-android!1844
2024-08-19 16:08:04 +08:00
3cbdd94189 fix: 修复 94d0e2b5f19760d22381ead093c9e89778a14bf4 这个版本没有修复的 java.lang.AbstractMethodError: abstract method "void android.app.job.IJobService.onNetworkChanged(android.app.job.JobParameters)" 错误。 2024-08-19 16:02:01 +08:00
11013319bf chore: 版本更新至 5.37.2 2024-08-19 12:40:31 +08:00
aa954b3af9 Merge branch 'hotfix/v5.37.1-1091/mmkv_wrong_usage' into 'release'
fix: 修复2.0.3光环启动32位游戏,概率没有初始化mmkv

See merge request halo/android/assistant-android!1843
2024-08-19 11:59:16 +08:00
93f279a384 fix: 修复2.0.3光环启动32位游戏,概率没有初始化mmkv 2024-08-19 11:45:12 +08:00
4e54833cc9 Merge branch 'hotfix/v5.37.1-1091/login_crash' into 'release'
fix: 临时处理登录后 token 为空引起的闪退,并上报相关日志到 sentry

See merge request halo/android/assistant-android!1842
2024-08-19 11:11:31 +08:00
f95862da8b Merge branch 'hotfix/v5.37.1-1091/class_cast_exception' into 'release'
fix: 修复偶发的类型转换异常闪退...

See merge request halo/android/assistant-android!1841
2024-08-19 10:50:23 +08:00
1515df34b5 fix: 临时处理登录后 token 为空引起的闪退,并上报相关日志到 sentry
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-19 10:44:14 +08:00
8a8ef40dc1 fix: 修复偶发的类型转换异常闪退 https://sentry.shanqu.cc/organizations/lightgame/issues/404710/events/?project=22 2024-08-19 09:27:35 +08:00
b6194fed26 Merge branch 'chen/202408/fix-quick-login-crash' into 'release'
fix:点击快速登录有概率出现crash https://sentry.shanqu.cc/organizations/lightgame/issues/404488/?project=22

See merge request halo/android/assistant-android!1840
2024-08-16 17:39:58 +08:00
02752f6c81 fix:点击快速登录有概率出现crash https://sentry.shanqu.cc/organizations/lightgame/issues/404488/?project=22 2024-08-16 17:34:17 +08:00
b33d367dfd Merge branch 'fix/sentry-404504' into 'release'
fix: 悬浮窗Detach时机问题造成的崩溃

See merge request halo/android/assistant-android!1839
2024-08-16 14:04:09 +08:00
d5046ab431 fix: 悬浮窗Detach时机问题造成的崩溃 2024-08-16 14:04:09 +08:00
a53168c9bc Merge branch 'hotfix/v5.37.1-1091/add_game_popup_crash' into 'release'
fix: 修复退出添加游戏弹窗后弹出引导弹窗出现的闪退问题...

See merge request halo/android/assistant-android!1838
2024-08-16 10:47:28 +08:00
3cbc8c2f74 fix: 修复退出添加游戏弹窗后弹出引导弹窗出现的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/404461/?project=22 2024-08-16 10:42:10 +08:00
7398b83004 Merge branch 'chen/202408/fix-bottom-tab_parse_lottie_crash' into 'release'
fix:当后台bottom tab上传的lottie文件格式不合法时,解析出现crash

See merge request halo/android/assistant-android!1837
2024-08-15 17:19:19 +08:00
8013426ded fix:当后台bottom tab上传的lottie文件格式不合法时,解析出现crash 2024-08-15 17:16:14 +08:00
b86d85b15f chore: 版本更新至 5.37.1-1091
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-15 14:42:55 +08:00
5a73afbab3 Merge branch 'hotfix/v5.37.0-1090-get_meta_data_crash' into 'release'
fix: catch 获取应用 meta data 时的 DeadSystemException 并上报至 sentry...

See merge request halo/android/assistant-android!1836
2024-08-15 14:30:46 +08:00
e19ee25839 fix: catch 获取应用 meta data 时的 DeadSystemException 并上报至 sentry https://sentry.shanqu.cc/organizations/lightgame/issues/390674
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-15 14:26:12 +08:00
61bf39e95f Merge branch 'hotfix/v5.37.0-1090/index_out_of_bounds_exception' into 'release'
fix: 修复数组越界闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/404202/?project=22

See merge request halo/android/assistant-android!1835
2024-08-15 14:20:39 +08:00
ef82ae2378 fix: 修复数组越界闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/404202/?project=22 2024-08-15 14:17:32 +08:00
2dcccc22a8 Merge branch 'fix/sentry-404211' into 'release'
fix: 工作线程显示悬浮窗造成的崩溃问题

See merge request halo/android/assistant-android!1834
2024-08-15 14:09:06 +08:00
cb0e77d204 fix: 工作线程显示悬浮窗造成的崩溃问题 2024-08-15 14:09:06 +08:00
a6ea178609 Merge branch 'fix/sentry-404179' into 'release'
fix: 悬浮窗权限申请弹窗在Activity销毁后弹出造成的崩溃问题

See merge request halo/android/assistant-android!1833
2024-08-15 14:08:58 +08:00
6a62f08891 fix: 悬浮窗权限申请弹窗在Activity销毁后弹出造成的崩溃问题 2024-08-15 14:08:58 +08:00
ddb48481d2 Merge branch 'hotfix/v5.37.0-1090-dual_download_error_issue' into 'release'
fix: 双下载时使用本地下载进行下载时临时屏蔽特殊弹窗和下载悬浮窗 https://jira.shanqu.cc/browse/GHZSCY-6521

See merge request halo/android/assistant-android!1832
2024-08-14 11:36:51 +08:00
48a55a6b31 fix: 双下载时使用本地下载进行下载时临时屏蔽特殊弹窗和下载悬浮窗 2024-08-14 11:35:20 +08:00
50fcb0c3ea fix: 方法入参异常造成的编译问题 2024-08-13 16:20:01 +08:00
e97722b6fb Merge branch 'feat/cw-apk-udpate' into 'release'
feat: 优化畅玩游戏更新逻辑

See merge request halo/android/assistant-android!1790
2024-08-13 15:19:22 +08:00
2163b2ff9e Merge branch 'fix/GHZSCY-6438' into 'release'
fix: 已安装游戏替换问题 https://jira.shanqu.cc/browse/GHZSCY-6438

See merge request halo/android/assistant-android!1831
2024-08-13 15:06:03 +08:00
b9558e2732 Merge branch 'fix/GHZSCY-6414' into 'release'
fix: 安装流程相关优化—2024年8月2日测试组 https://jira.shanqu.cc/browse/GHZSCY-6414

See merge request halo/android/assistant-android!1830
2024-08-13 14:25:51 +08:00
e9139c66e1 fix: 已安装游戏替换问题 https://jira.shanqu.cc/browse/GHZSCY-6438
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-08 17:43:31 +08:00
f492d2ea97 fix: 安装流程相关优化—2024年8月2日测试组 https://jira.shanqu.cc/browse/GHZSCY-6414
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-02 14:49:59 +08:00
46955685bb feat: 优化畅玩游戏更新逻辑 2024-07-30 16:54:30 +08:00
248 changed files with 6456 additions and 1983 deletions

View File

@ -72,6 +72,7 @@ android_build:
only:
- dev
- release
- feat/GHZSCY-6578
# 代码检查
sonarqube_analysis:
@ -156,4 +157,5 @@ oss-upload&send-email:
- /usr/local/bin/python /ci-android-mail-jira-comment.py
only:
- dev
- release
- release
- feat/GHZSCY-6578

View File

@ -107,7 +107,6 @@ android {
buildConfigField "String", "WEIBO_APPKEY", "\"${WEIBO_APPKEY}\""
// 一体包的32位畅玩游戏助手包名
buildConfigField "String", "EXT_PACKAGE_NAME", "\"${rootProject.ext.EXT_PACKAGE_NAME}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
}
// gradle 2.2以上默认同时启用v1和v2优先用于Android N
@ -218,6 +217,9 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${DEV_QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "DEV_CSJ_APPID", "\"${DEV_CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}-debug\""
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}-debug")
}
// publish 发布时候使用的 flavor接口仅包含正式环境
@ -231,6 +233,9 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "DEV_CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}")
}
tea {
@ -244,7 +249,10 @@ android {
buildConfigField "String", "DEV_CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
manifestPlaceholders.put("APPLOG_SCHEME", "rangersapplog.byAx6uYt".toLowerCase())
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}")
}
kuaishou {
@ -257,6 +265,9 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "DEV_CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}")
}
gdt {
@ -269,6 +280,9 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "DEV_CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}")
}
sm {
@ -281,6 +295,9 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "DEV_CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}")
}
// 港澳台
@ -371,7 +388,7 @@ dependencies {
implementation "com.lg:easyfloat:${easyFloat}"
implementation ("com.lg:apksig:${apksig}") {
implementation("com.lg:apksig:${apksig}") {
exclude group: 'com.google.protobuf'
}
@ -387,7 +404,7 @@ dependencies {
implementation project(':vspace-bridge:vspace')
implementation(project(':feature:xapk-installer'))
implementation (project(':module_common')) {
implementation(project(':module_common')) {
exclude group: 'androidx.swiperefreshlayout'
}
@ -462,9 +479,11 @@ dependencies {
implementation(project(':feature:sentry'))
}
implementation(project(':feature:media_select'))
implementation(project(":module_va_api"))
implementation(project(":va-archive-common"))
if(!gradle.ext.excludeOptionalModules || gradle.ext.enableVa) {
if (!gradle.ext.excludeOptionalModules || gradle.ext.enableVa) {
implementation(project(":module_va_impl"))
}
debugImplementation "com.bytedance.tools.codelocator:codelocator-core:2.0.3"

View File

@ -194,7 +194,6 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
VirtualAppManager.AIDL_SERVER_REMOTE_GUIDE_ACTIVITY
)
)
intent.putExtra("isOpTest", true)
requireActivity().startActivity(intent)
}

View File

@ -2,6 +2,7 @@
<resources>
<string name="title_install_external_game">從SD卡安裝</string>
<string name="text_install">安裝</string>
<string name="text_update">更新</string>
<string name="text_uninstall">卸載</string>
<string name="text_start">啟動</string>
</resources>

View File

@ -185,6 +185,8 @@
android:name="io.sentry.breadcrumbs.system-events"
android:value="false" />
<meta-data android:name="module_version" android:value="${VA_VERSION_NAME}" />
<service android:name="com.gh.ndownload.NDownloadService" />
<activity
@ -454,9 +456,6 @@
android:name="com.gh.gamecenter.video.game.GameVideoActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.editor.LocalMediaActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.servers.GameServersActivity"
@ -574,10 +573,6 @@
android:name=".personal.DeliveryInfoActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.editor.PreviewVideoActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.video.publish.VideoPublishActivity"
android:screenOrientation="portrait" />

View File

@ -227,18 +227,7 @@ object AdDelegateHelper {
/**
* 是否大于开屏广告展示间隔时长
*/
private fun isMatchStartUpAdDisplayRule(): Boolean {
mSplashAd?.displayRule?.run {
if (adDisplayInterval > 0) {
val lastShowTime = SPUtils.getLong(Constants.SP_LAST_SPLASH_AD_SHOW_TIME, 0L)
val durationInMinutes = (System.currentTimeMillis() - lastShowTime).toFloat() / 1000 / 60
return durationInMinutes > adDisplayInterval
} else {
return true
}
}
return true
}
private fun isMatchStartUpAdDisplayRule(): Boolean = isMatchAdDisplayRule(mSplashAd, Constants.SP_LAST_SPLASH_AD_SHOW_TIME)
/**
* 是否大于广告管理展示间隔时长
@ -292,6 +281,7 @@ object AdDelegateHelper {
adViewHeightInDp: Float,
startAdContainer: ViewGroup,
sdkStartAdContainer: ViewGroup,
sdkJumpBtn: TextView,
adsViewGroup: FrameLayout,
handler: BaseActivity.BaseHandler,
isHotLaunch: Boolean,
@ -313,6 +303,7 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -330,6 +321,7 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -353,6 +345,7 @@ object AdDelegateHelper {
adViewHeightInDp: Float,
startAdContainer: ViewGroup,
sdkStartAdContainer: ViewGroup,
sdkJumpBtn: TextView,
adsViewGroup: FrameLayout,
handler: BaseActivity.BaseHandler,
isHotLaunch: Boolean,
@ -374,6 +367,7 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -403,13 +397,14 @@ object AdDelegateHelper {
sdkStartAdContainer.visibility = View.VISIBLE
requestCsjSplashAd(
activity,
thirdPartyAd.slotId,
adViewWidthInPx,
adViewHeightInPx,
adViewWidthInDp,
adViewHeightInDp,
sdkStartAdContainer,
sdkJumpBtn,
timeout,
isHotLaunch,
sdkSplashCallback
)
}
@ -420,27 +415,65 @@ object AdDelegateHelper {
*/
private fun requestCsjSplashAd(
activity: Activity,
slotId: String,
adViewWidthInPx: Int,
adViewHeightInPx: Int,
adViewWidthInDp: Float,
adViewHeightInDp: Float,
startAdContainer: ViewGroup,
sdkJumpBtn: TextView,
timeout: Int,
isHotLaunch: Boolean,
callback: (isSuccess: Boolean) -> Unit,
) {
if (mCsjAdImpl == null) {
val thirdPartyAd = if (isHotLaunch) mSplashAd?.hotStartThirdPartyAd else mSplashAd?.thirdPartyAd
if (mCsjAdImpl == null || thirdPartyAd == null) {
callback.invoke(false)
} else {
sdkJumpBtn.setOnClickListener {
callback.invoke(true)
if (activity is BaseActivity) {
activity.baseHandler.removeMessages(MainActivity.COUNTDOWN_SDK_AD)
}
}
val onAdShowAction = {
sdkJumpBtn.visibility = View.VISIBLE
if (activity is BaseActivity) {
activity.baseHandler.sendEmptyMessageDelayed(MainActivity.COUNTDOWN_SDK_AD, 1000)
}
SensorsBridge.trackEvent("ThirdPartyAdShow",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", mSplashAd?.typeChinese ?: "",
"ad_placement", "光环启动",
"launch_type", if (isHotLaunch) "热启动" else "冷启动",
"ad_space_id", mSplashAd?.id ?: "",
"ad_space_name", mSplashAd?.name ?: ""
)
}
val onAdClickAction = {
callback.invoke(true)
SensorsBridge.trackEvent("ThirdPartyAdClick",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", mSplashAd?.typeChinese ?: "",
"ad_placement", "光环启动",
"launch_type", if (isHotLaunch) "热启动" else "冷启动",
"ad_space_id", mSplashAd?.id ?: "",
"ad_space_name", mSplashAd?.name ?: ""
)
}
mCsjAdImpl?.requestSplashAd(
activity,
slotId,
thirdPartyAd.slotId,
adViewWidthInPx,
adViewHeightInPx,
adViewWidthInDp,
adViewHeightInDp,
startAdContainer,
timeout,
onAdShowAction,
onAdClickAction,
callback,
)
}
@ -457,6 +490,7 @@ object AdDelegateHelper {
adViewHeightInDp: Float,
startAdContainer: ViewGroup,
sdkStartAdContainer: ViewGroup,
sdkJumpBtn: TextView,
adsViewGroup: FrameLayout,
handler: BaseActivity.BaseHandler,
isHotLaunch: Boolean,
@ -474,6 +508,7 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -621,9 +656,11 @@ object AdDelegateHelper {
slotId: String,
adContainerView: ViewGroup,
expressViewWidth: Float,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit,
callback: (isSuccess: Boolean) -> Unit,
) {
mCsjAdImpl?.requestFlowAd(fragment, adContainerView, slotId, expressViewWidth, callback)
mCsjAdImpl?.requestFlowAd(fragment, adContainerView, slotId, expressViewWidth, onAdShowAction, onAdClickAction, callback)
}
/**
@ -634,6 +671,8 @@ object AdDelegateHelper {
containerView: ViewGroup,
ad: AdConfig.ThirdPartyAd,
expressViewWidthInDp: Float,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit,
callback: (isSuccess: Boolean) -> Unit
) {
@ -656,6 +695,28 @@ object AdDelegateHelper {
slotId,
expressViewWidthInDp,
expressViewHeightInDp,
onAdShowAction,
onAdClickAction,
callback
)
}
/**
* 获取第三方 全屏/插屏 广告
*/
fun requestFullScreenAd(
fragment: Fragment,
ad: AdConfig.ThirdPartyAd,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit,
callback: (isSuccess: Boolean) -> Unit
) {
val slotId = ad.slotId
mCsjAdImpl?.requestFullScreenAd(
fragment,
slotId,
onAdShowAction,
onAdClickAction,
callback
)
}

View File

@ -3,42 +3,105 @@ package com.gh.ad
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import androidx.fragment.app.Fragment
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.provider.ILaunchAd
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.AdConfig
@Route(path = RouteConsts.provider.vaAd, name = "畅玩启动页广告")
class LaunchAdImpl : ILaunchAd {
override fun init(context: Context?) {
}
override fun requestAd(fragment: Fragment, container: ViewGroup, maskView: View) {
override fun requestAd(fragment: Fragment, container: ViewGroup, maskView: View, topViewStub: ViewStub, bottomViewStub: ViewStub, adClickAction: () -> Unit): View {
if (AdDelegateHelper.shouldShowHelperLaunchAd()) {
val launchAd = AdDelegateHelper.vGameLaunchAd
val showThirdPartyAd = launchAd?.displayRule?.adSource == AdDelegateHelper.AD_TYPE_SDK
val thirdPartyAd = launchAd?.thirdPartyAd
if (showThirdPartyAd && thirdPartyAd != null) {
AdDelegateHelper.requestThirdPartyBannerAd(
fragment,
container,
thirdPartyAd,
DisplayUtils.getScreenWidthInDp(fragment.requireActivity()),
) { isSuccess ->
maskView.goneIf(!isSuccess)
if (isSuccess) {
SPUtils.setLong(Constants.SP_LAST_HELPER_LAUNCH_AD_SHOW_TIME, System.currentTimeMillis())
}
val onAdShowAction = {
SensorsBridge.trackEvent("ThirdPartyAdShow",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", launchAd.typeChinese,
"ad_placement", AD_PLACEMENT,
"ad_space_id", launchAd.id,
"ad_space_name", launchAd.name
)
}
val onAdClickAction = {
SensorsBridge.trackEvent("ThirdPartyAdClick",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", launchAd.typeChinese,
"ad_placement", AD_PLACEMENT,
"ad_space_id", launchAd.id,
"ad_space_name", launchAd.name
)
adClickAction.invoke()
}
if (launchAd.type == AdConfig.TYPE_BANNER) {
requestBannerAd(fragment, container, maskView, thirdPartyAd, onAdShowAction, onAdClickAction)
return topViewStub.inflate()
} else if (launchAd.type == AdConfig.TYPE_INTERSTITIAL) {
requestFullScreenAd(fragment, thirdPartyAd, onAdShowAction, onAdClickAction)
return bottomViewStub.inflate()
}
}
}
return bottomViewStub.inflate()
}
private fun requestBannerAd(
fragment: Fragment,
container: ViewGroup,
maskView: View,
thirdPartyAd: AdConfig.ThirdPartyAd,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit,
) {
AdDelegateHelper.requestThirdPartyBannerAd(
fragment,
container,
thirdPartyAd,
DisplayUtils.getScreenWidthInDp(fragment.requireActivity()),
onAdShowAction,
onAdClickAction
) { isSuccess ->
maskView.goneIf(!isSuccess)
if (isSuccess) {
SPUtils.setLong(Constants.SP_LAST_HELPER_LAUNCH_AD_SHOW_TIME, System.currentTimeMillis())
}
}
}
private fun requestFullScreenAd(
fragment: Fragment,
thirdPartyAd: AdConfig.ThirdPartyAd,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit
) {
AdDelegateHelper.requestFullScreenAd(
fragment,
thirdPartyAd,
onAdShowAction,
onAdClickAction
) { isSuccess ->
if (isSuccess) {
SPUtils.setLong(Constants.SP_LAST_HELPER_LAUNCH_AD_SHOW_TIME, System.currentTimeMillis())
}
}
}
companion object {
private const val AD_PLACEMENT = "畅玩启动"
}
}

View File

@ -21,6 +21,8 @@ import com.gh.common.view.RichEditor
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.entity.LocalVideoEntity
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnIoThread
@ -30,6 +32,7 @@ import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.qa.editor.*
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.qa.entity.EditorInsertEntity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.upload.UploadManager
@ -502,7 +505,7 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> constructor(
startActivityForResult(
LocalMediaActivity.getIntent(
this@BaseRichEditorActivity,
LocalMediaActivity.ChooseType.VIDEO,
ChooseType.VIDEO,
maxChooseCount,
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
), INSERT_MEDIA_VIDEO_CODE
@ -531,7 +534,7 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> constructor(
val maxChooseCount = if (imageCount + 10 <= MAX_IMAGE_COUNT) 10 else MAX_IMAGE_COUNT - imageCount
val intent = LocalMediaActivity.getIntent(
this@BaseRichEditorActivity,
LocalMediaActivity.ChooseType.IMAGE,
ChooseType.IMAGE,
maxChooseCount,
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
)

View File

@ -20,7 +20,7 @@ import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.entity.ForumDetailEntity
import com.gh.gamecenter.entity.LocalVideoEntity
import com.gh.gamecenter.common.entity.LocalVideoEntity
import com.gh.gamecenter.entity.QuoteCountEntity
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.retrofit.RetrofitManager
@ -39,8 +39,6 @@ import retrofit2.HttpException
import java.io.File
import java.io.FileOutputStream
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.LinkedHashMap
import kotlin.collections.set

View File

@ -6,8 +6,8 @@ class DownloadChainBuilder {
private var processEndCallback: ((asVGame: Boolean, Any?) -> Unit)? = null
fun setProcessEndCallback(callback: (asVGame: Boolean, Any?) -> Unit): DownloadChainBuilder {
processEndCallback = callback
fun setProcessEndCallback(gameId: String, callback: (asVGame: Boolean, Any?) -> Unit): DownloadChainBuilder {
processEndCallback = VaPluginDownloadWrapper(gameId = gameId, callback = callback) // 其他需要添加行为的装饰者可以一直包装A(B(C(callback))), 执行顺序 A->B->C->callback
return this
}

View File

@ -0,0 +1,12 @@
package com.gh.common.chain
import com.gh.vspace.VHelper
class VaPluginDownloadWrapper(val gameId: String, val callback: (Boolean, Any?) -> Unit) : (Boolean, Any?) -> Unit {
override fun invoke(asVGame: Boolean, any: Any?) {
callback.invoke(asVGame, any)
if (asVGame) {
VHelper.initVaPlugin(gameId)
}
}
}

View File

@ -7,6 +7,7 @@ import android.os.Build;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.gh.common.util.PackageHelper;
@ -46,6 +47,7 @@ import org.json.JSONObject;
import java.io.IOException;
import java.util.Locale;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.functions.Function;
@ -78,10 +80,10 @@ public class Config {
private static NewApiSettingsEntity.NightMode mNightModeSetting;
private static SimulatorEntity mNewSimulatorEntity;
private static VSetting mVSetting;
private static VNewSetting mVNewSetting;
private volatile static VNewSetting mVNewSetting;
private static AppEntity mNew32UpdateEntity;
public static BehaviorSubject<VNewSetting> vNewSettingSubject = BehaviorSubject.create();
private static BehaviorSubject<VNewSetting> vNewSettingSubject = BehaviorSubject.create();
private static GameGuidePopupEntity mGameGuidePopupEntity;
private static SharedPreferences mDefaultSharedPreferences;
@ -212,6 +214,16 @@ public class Config {
return mVNewSetting;
}
@NonNull
public static Observable<VNewSetting> getVNewSettingObservable() {
if (mVNewSetting != null) {
return Observable.just(mVNewSetting);
} else {
return vNewSettingSubject.hide();
}
}
@Nullable
public static AppEntity getNew32UpdateEntity() {
return mNew32UpdateEntity;

View File

@ -172,7 +172,7 @@ public class BindingAdapters {
builder.addHandler(new OverseaDownloadHandler());
builder.addHandler(new CheckDownloadHandler());
builder.setProcessEndCallback((asVGame, isSubscribe) -> {
builder.setProcessEndCallback(gameEntity.getId(), (asVGame, isSubscribe) -> {
download(v.getContext(),
progressBar,
gameEntity,
@ -197,7 +197,7 @@ public class BindingAdapters {
builder.addHandler(new GamePermissionHandler());
builder.addHandler(new VersionNumberHandler());
builder.setProcessEndCallback((asVGame, isSubscribe) -> {
builder.setProcessEndCallback(gameEntity.getId(), (asVGame, isSubscribe) -> {
DownloadDialog.showDownloadDialog(
v.getContext(),
gameEntity,

View File

@ -0,0 +1,52 @@
package com.gh.common.prioritychain
import android.content.Context
import android.view.LayoutInflater
import android.widget.FrameLayout
import com.airbnb.lottie.LottieAnimationView
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.databinding.LayoutCommunityHomeVideoGuideBinding
class CommunityHomeGuideHandler(
priority: Int,
private val context: Context,
private val decorView: FrameLayout?,
private val videoLottie: LottieAnimationView?
) : PriorityChainHandler(priority) {
init {
updateStatus(STATUS_VALID)
}
override fun onProcess(): Boolean {
return if (SPUtils.getBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, true)) {
showHomeVideoGuide(context, decorView, videoLottie)
processNext()
true
} else {
processNext()
false
}
}
companion object {
fun showHomeVideoGuide(context: Context, decorView: FrameLayout?, videoLottie: LottieAnimationView?) {
val guideLayoutBinding = LayoutCommunityHomeVideoGuideBinding.inflate(
LayoutInflater.from(context),
decorView,
true
)
guideLayoutBinding.root.setOnClickListener { view ->
decorView?.removeView(view)
SPUtils.setBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, false)
videoLottie?.playAnimation()
SPUtils.setLong(
Constants.SP_COMMUNITY_HOME_VIDEO_LOTTIE_LAST_PLAY_TIME,
System.currentTimeMillis()
)
}
}
}
}

View File

@ -0,0 +1,16 @@
package com.gh.common.prioritychain
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
class VideoHandler(priority: Int, val scrollCalculatorHelper: ScrollCalculatorHelper): PriorityChainHandler(priority) {
init {
updateStatus(STATUS_VALID)
}
override fun onProcess(): Boolean {
scrollCalculatorHelper.enableAndPlayIfValid()
return true
}
}

View File

@ -2,12 +2,15 @@ package com.gh.common.provider
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.fragment.app.FragmentActivity
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.common.util.EntranceUtils
import com.gh.gamecenter.common.avoidcallback.Callback
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IEntranceUtilsProvider
import com.lightgame.utils.AppManager
@Route(path = RouteConsts.provider.entranceUtils, name = "EntranceUtils暴露服务")
class EntranceUtilsProviderImpl : IEntranceUtilsProvider {
@ -20,11 +23,16 @@ class EntranceUtilsProviderImpl : IEntranceUtilsProvider {
}
override fun jumpActivityWithCallback(context: Context, bundle: Bundle, callback: () -> Unit) {
EntranceUtils.jumpActivity(context, null, bundle, object : Callback {
override fun onActivityResult(resultCode: Int, data: Intent?) {
callback()
}
})
if (context is FragmentActivity && !context.supportFragmentManager.isDestroyed) {
EntranceUtils.jumpActivityCompat(context, bundle, null, object : Callback {
override fun onActivityResult(resultCode: Int, data: Intent?) {
callback()
}
})
} else {
EntranceUtils.jumpActivityCompat(AppManager.getInstance().currentActivity(), bundle)
}
}
override fun init(context: Context?) {

View File

@ -74,7 +74,7 @@ public class CheckLoginUtils {
Bundle bundle = new Bundle();
bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance);
bundle.putString(EntranceConsts.KEY_TO, LoginActivity.class.getName());
EntranceUtils.jumpActivity(context, nextToBundle, bundle, (resultCode, data) -> {
EntranceUtils.jumpActivityCompat(context, bundle, nextToBundle, (resultCode, data) -> {
if (isTriggerNextStep && listener != null && isLogin()) {
listener.onLogin();
}

View File

@ -37,10 +37,10 @@ import com.gh.gamecenter.core.utils.*
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.minigame.MiniGameItemHelper
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
import com.gh.gamecenter.teenagermode.TeenagerModeActivity
import com.gh.vspace.VHelper
import com.lightgame.download.DownloadConfig
@ -697,7 +697,7 @@ object DownloadItemUtils {
// 为 downloadButton 添加游戏实体,供点击的时候上报用
downloadBtn.putObject(gameEntity)
val gamePermissionDialogFragment = (context as AppCompatActivity).supportFragmentManager.findFragmentByTag(
val gamePermissionDialogFragment = (context as? AppCompatActivity)?.supportFragmentManager?.findFragmentByTag(
GamePermissionDialogFragment::class.java.name
) as GamePermissionDialogFragment?
gamePermissionDialogFragment?.dismissAllowingStateLoss()
@ -910,7 +910,7 @@ object DownloadItemUtils {
addHandler(CheckStoragePermissionHandler())
addHandler(VersionNumberHandler())
}
.setProcessEndCallback { _, _ ->
.setProcessEndCallback(gameEntity.id) { _, _ ->
DownloadDialog.showDownloadDialog(view.context, gameEntity, traceEvent, entrance, location)
}
.buildHandlerChain()
@ -953,7 +953,7 @@ object DownloadItemUtils {
addHandler(LandPageAddressHandler())
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback { asVGame, isSubscribe ->
.setProcessEndCallback(gameEntity.id) { asVGame, isSubscribe ->
download(context, gameEntity, downloadBtn, entrance, location, asVGame, isSubscribe as Boolean, traceEvent)
}
.buildHandlerChain()
@ -972,7 +972,7 @@ object DownloadItemUtils {
addHandler(OverseaDownloadHandler())
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback { asVGame, isSubscribe ->
.setProcessEndCallback(gameEntity.id) { asVGame, isSubscribe ->
download(context, gameEntity, downloadBtn, entrance, location, asVGame, isSubscribe as Boolean, traceEvent)
}
.buildHandlerChain()
@ -991,7 +991,7 @@ object DownloadItemUtils {
addHandler(ValidateVSpaceHandler())
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback { asVGame, isSubscribe ->
.setProcessEndCallback(gameEntity.id) { asVGame, isSubscribe ->
download(context, gameEntity, downloadBtn, entrance, location, asVGame, isSubscribe as Boolean, traceEvent)
}
.buildHandlerChain()
@ -1004,7 +1004,7 @@ object DownloadItemUtils {
addHandler(DownloadDialogHelperHandler())
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback { _, isSubscribe ->
.setProcessEndCallback(gameEntity.id) { _, isSubscribe ->
plugin(context, gameEntity, downloadBtn, entrance, location, isSubscribe as Boolean, traceEvent)
}
.buildHandlerChain()
@ -1089,7 +1089,7 @@ object DownloadItemUtils {
DownloadChainBuilder()
.apply {
addHandler(LandPageAddressHandler())
}.setProcessEndCallback { asVGame, _ ->
}.setProcessEndCallback(gameEntity.id) { asVGame, _ ->
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk) {
DialogUtils.checkDownload(
context,

View File

@ -69,11 +69,13 @@ object DownloadObserver {
DownloadDataHelper.uploadDownloadEvent(downloadEntity)
}
if (DownloadStatus.hijack == downloadEntity.status) {
val status = downloadEntity.status
if (DownloadStatus.hijack == status) {
// 链接被劫持
processHijack(downloadEntity)
return
} else if (DownloadStatus.notfound == downloadEntity.status) {
} else if (DownloadStatus.notfound == status) {
// 404 Not Found
// 删除任务
downloadEntity.status = DownloadStatus.cancel
@ -127,10 +129,9 @@ object DownloadObserver {
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
return
} else if (DownloadStatus.neterror == downloadEntity.status
|| DownloadStatus.timeout == downloadEntity.status
|| DownloadStatus.diskioerror == downloadEntity.status
|| DownloadStatus.diskisfull == downloadEntity.status
} else if (DownloadStatus.neterror == status
|| DownloadStatus.diskioerror == status
|| DownloadStatus.timeout == status
) {
if (mRetryableHashMap[downloadEntity.url] == true
&& NetworkUtils.isWifiConnected(HaloApp.getInstance().application)
@ -139,9 +140,7 @@ object DownloadObserver {
mRetryableHashMap[downloadEntity.url] = false
Utils.log(TAG, "下载重试->" + downloadEntity.toJson())
} else {
if (DownloadStatus.diskisfull == downloadEntity.status) {
ToastUtils.toast("磁盘已满,请清理空间后重试下载")
} else if (DownloadStatus.diskioerror == downloadEntity.status) {
if (DownloadStatus.diskioerror == status) {
ToastUtils.toast("磁盘 IO 异常,请稍后重试")
} else {
ToastUtils.toast("网络不稳定,下载任务已暂停")
@ -150,17 +149,21 @@ object DownloadObserver {
DataLogUtils.uploadNeterrorLog(mApplication, downloadEntity)
Utils.log(TAG, "下载自动暂停->" + downloadEntity.toJson())
}
} else if (DownloadStatus.redirected == downloadEntity.status) {
} else if (DownloadStatus.diskisfull == status) {
ToastUtils.toast("磁盘已满,请清理空间后重试下载")
downloadManager.pause(downloadEntity.url)
} else if (DownloadStatus.redirected == status) {
Utils.log(TAG, "重定向完毕")
DownloadDataHelper.uploadRedirectEvent(downloadEntity)
} else if (DownloadStatus.unqualified == downloadEntity.status) {
} else if (DownloadStatus.unqualified == status) {
// 未成年
RealNameHelper.showRealNameUnqualifiedDialog(downloadEntity)
// 删除任务
downloadEntity.status = DownloadStatus.cancel
downloadManager.cancel(downloadEntity.url)
} else if (DownloadStatus.unavailable == downloadEntity.status) {
} else if (DownloadStatus.unavailable == status) {
// 未接入防沉迷系统
val currentActivity = AppManager.getInstance().currentActivity()
@ -179,7 +182,7 @@ object DownloadObserver {
// 删除任务
downloadEntity.status = DownloadStatus.cancel
downloadManager.cancel(downloadEntity.url)
} else if (DownloadStatus.isCertificating == downloadEntity.status) {
} else if (DownloadStatus.isCertificating == status) {
// 未接入防沉迷系统
val currentActivity = AppManager.getInstance().currentActivity()
@ -207,21 +210,21 @@ object DownloadObserver {
// 删除任务
downloadEntity.status = DownloadStatus.cancel
downloadManager.cancel(downloadEntity.url)
} else if (DownloadStatus.uncertificated == downloadEntity.status) {
} else if (DownloadStatus.uncertificated == status) {
// 未实名
RealNameHelper.showRealNameUncertificatedDialog(downloadEntity)
// 删除任务
downloadEntity.status = DownloadStatus.cancel
downloadManager.cancel(downloadEntity.url)
} else if (DownloadStatus.banned == downloadEntity.status) {
} else if (DownloadStatus.banned == status) {
ToastUtils.showToast("网络异常")
// 删除任务
downloadEntity.status = DownloadStatus.cancel
downloadManager.cancel(downloadEntity.url)
}
if (DownloadStatus.done == downloadEntity.status) {
if (DownloadStatus.done == status) {
if (mDoneDebouncePair?.first != downloadEntity.url) {
mDoneDebouncePair = Pair(downloadEntity.url, System.currentTimeMillis())
performDownloadCompleteAction(downloadEntity, downloadManager)
@ -241,7 +244,7 @@ object DownloadObserver {
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
// 如果已下载大小发生变化,表示成功恢复下载,则重置重试标记
if (downloadEntity.status == DownloadStatus.downloading) {
if (status == DownloadStatus.downloading) {
mRetryableHashMap[downloadEntity.url] = true
}
}

View File

@ -27,7 +27,6 @@ import com.gh.gamecenter.core.utils.ClassUtils;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.SPUtils;
import com.halo.assistant.HaloApp;
import com.lightgame.utils.AppManager;
import com.lightgame.utils.Utils;
import org.json.JSONException;
@ -38,7 +37,18 @@ import java.util.Set;
public class EntranceUtils {
public static void jumpActivity(Context context, Bundle bundle) {
jumpActivityCompat(context, bundle, null, null);
}
public static void jumpActivityCompat(Context context, Bundle bundle) {
jumpActivityCompat(context, bundle, null,null);
}
public static void jumpActivityCompat(Context context,
Bundle bundle,
@Nullable Bundle nextToBundle,
@Nullable Callback callback) {
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
if (HaloApp.getInstance().isRunningForeground || HaloApp.getInstance().isAlreadyUpAndRunning) {
@ -48,66 +58,27 @@ public class EntranceUtils {
if (clazz == null) clazz = MainActivity.class;
if (ToolbarFragment.class.isAssignableFrom(clazz)) { // 兼容ToolbarFragment
ToolBarActivity.startFragmentNewTask(context, (Class<? extends ToolbarFragment>) clazz, bundle);
} else {
Intent intent1 = new Intent(context, clazz);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 如果 activity 名称有 singleton 的就添加 reorder_to_front 标签 (有点粗暴有点蠢,但暂时就先这样吧 :C )
if (clazz.getSimpleName().toLowerCase().contains("singleton")) {
intent1.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
}
intent1.putExtras(bundle);
context.startActivity(intent1);
}
} else {
// 应用未在运行
context.startActivity(SplashScreenActivity.getSplashScreenIntent(context, bundle));
}
}
public static void jumpActivity(Context context, Bundle bundle) {
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
if (AppManager.getInstance().findActivity(MainActivity.class) != null) {
// 应用正在运行前台或后台且MainActivity在栈中
String to = bundle.getString(KEY_TO);
Class<?> clazz = ClassUtils.forName(to);
if (clazz == null) clazz = MainActivity.class;
if (ToolbarFragment.class.isAssignableFrom(clazz)) { // 兼容ToolbarFragment
ToolBarActivity.startFragmentNewTask(context, (Class<? extends ToolbarFragment>) clazz, bundle);
} else {
Intent intent1 = new Intent(context, clazz);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 如果 activity 名称有 singleton 的就添加 reorder_to_front 标签 (有点粗暴有点蠢,但暂时就先这样吧 :C )
if (clazz.getSimpleName().toLowerCase().contains("singleton")) {
intent1.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
}
intent1.putExtras(bundle);
context.startActivity(intent1);
}
} else {
// 应用未在运行
context.startActivity(SplashScreenActivity.getSplashScreenIntent(context, bundle));
}
}
public static void jumpActivity(Context context, Bundle nextToBundle, Bundle bundle, Callback callback) {
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
if (AppManager.getInstance().findActivity(MainActivity.class) != null) {
// 应用正在运行前台或后台且MainActivity在栈中
String to = bundle.getString(KEY_TO);
Class<?> clazz = ClassUtils.forName(to);
if (clazz == null) clazz = MainActivity.class;
if (ToolbarFragment.class.isAssignableFrom(clazz)) { // 兼容ToolbarFragment
ToolBarActivity.startFragmentNewTask(context, (Class<? extends ToolbarFragment>) clazz, bundle);
} else {
} else if (callback != null ) {
Intent intent1 = new Intent(context, clazz);
//TODO:添加FLAG_ACTIVITY_NEW_TASK会导致一跳转页面callback就被调用
//intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.putExtras(bundle);
AvoidOnResultManager.Companion.getInstance((AppCompatActivity) context)
.startForResult(intent1, callback);
if (context instanceof AppCompatActivity) {
AvoidOnResultManager.Companion.getInstance((AppCompatActivity) context)
.startForResult(intent1, callback);
} else {
// 不要回调,正常跳转
context.startActivity(intent1);
}
} else {
Intent intent1 = new Intent(context, clazz);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 如果 activity 名称有 singleton 的就添加 reorder_to_front 标签 (有点粗暴有点蠢,但暂时就先这样吧 :C )
if (clazz.getSimpleName().toLowerCase().contains("singleton")) {
intent1.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
}
intent1.putExtras(bundle);
context.startActivity(intent1);
}
} else {
// 应用未在运行

View File

@ -7,6 +7,7 @@ import com.gh.gamecenter.common.utils.toObject
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.entity.SubjectEntity
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.schedulers.Schedulers
@ -111,6 +112,14 @@ object GameSubstituteRepositoryHelper {
}
}
// 检查是否已安装该游戏 id
if (PackagesManager.getInstalledDataByGameId(game.id) != null) {
// 将该位置的游戏标记为需要替换
positionOfGameToBeReplacedList.add(index)
thisPositionNeedToBeReplaced = true
break
}
// 若此游戏所包含的 apk 没有已安装,那么再检查是否已安装有预设相关包名
if (!thisPositionNeedToBeReplaced) {
var relatedPackageList = arrayListOf<String>()

View File

@ -308,7 +308,7 @@ object GameUtils {
}
/**
* 是否以畅玩游戏的形式来处理
* 是否默认以畅玩游戏的形式来处理
*/
@JvmStatic
fun shouldPerformAsVGame(gameEntity: GameEntity): Boolean {

View File

@ -30,6 +30,9 @@ object NewFlatLogUtils {
private const val EVENT_LOGIN_FROM_GHZS_SHOW = "halo_fun_login_from_ghzs_show"
private const val EVENT_LOGIN_FROM_GHZS_CLICK = "halo_fun_login_from_ghzs_click"
private const val EVENT_INSTALLED_LAUNCH_DIALOG_SHOW = "halo_fun_installed_launch_dialog_show"
private const val EVENT_INSTALLED_LAUNCH_DIALOG_CLICK = "halo_fun_installed_launch_dialog_click"
private fun log(jsonObject: JSONObject, logStore: String = "event", uploadImmediately: Boolean = false) {
Utils.log("NewFlatLogUtils", jsonObject.toString(4))
LoghubUtils.log(jsonObject, logStore, uploadImmediately, true)
@ -550,6 +553,8 @@ object NewFlatLogUtils {
// 搜索-点击搜索榜单内容
@JvmStatic
fun logSearchClickRankDetail(
key: String,
rankType: String,
rankName: String,
rankSequence: String,
linkId: String,
@ -558,6 +563,8 @@ object NewFlatLogUtils {
) {
val json = json {
KEY_EVENT to "search_click_rank_detail"
"key" to key
"rank_type" to rankType
"rank_name" to rankName
"rank_sequence" to rankSequence
"link_id" to linkId
@ -2688,4 +2695,73 @@ object NewFlatLogUtils {
parseAndPutMeta()(this)
}.let(::log)
}
fun logHaloFunInstalledLaunchDialogShow() {
json {
KEY_EVENT to EVENT_INSTALLED_LAUNCH_DIALOG_SHOW
parseAndPutMeta()(this)
}.let(::log)
}
fun logHaloFunInstalledLaunchDialogClick(
gameId: String,
gameName: String,
buttonType: String
) {
json {
KEY_EVENT to EVENT_INSTALLED_LAUNCH_DIALOG_CLICK
"game_id" to gameId
"game_name" to gameName
"button_type" to buttonType
parseAndPutMeta()(this)
}.let(::log)
}
// 用户访问分流器
fun logByPassBrowsing(
source: String,
bypassName: String,
bypassId: String,
branchId: String,
branchName: String,
inProcessTime: Int,
bypassVisitTime: Int,
linkType: String,
linkId: String,
linkText: String,
bypassStatus: Int
) {
json {
KEY_EVENT to "BypassBrowsing"
"source" to source
"bypass_name" to bypassName
"bypass_id" to bypassId
"branch_id" to branchId
"branch_name" to branchName
"inprocess_time" to inProcessTime
"bypass_visit_time" to bypassVisitTime
"link_type" to linkType
"link_id" to linkId
"link_text" to linkText
"bypass_status" to bypassStatus
parseAndPutMeta()(this)
}.let(::log)
}
// 分流失败
fun logFailByPass(
source: String,
bypassName: String,
bypassId: String,
defeatedReason: String
) {
json {
KEY_EVENT to "FailBypass"
"source" to source
"bypass_name" to bypassName
"bypass_id" to bypassId
"defeated_reason" to defeatedReason
parseAndPutMeta()(this)
}.let(::log)
}
}

View File

@ -90,7 +90,7 @@ object PackageChangeHelper : DefaultLifecycleObserver {
pendingPackageTriple = null
pendingGhId = null
PackageRepository.addInstalledGame(packageName)
PackageRepository.addInstalledGame(PackageRepository.packageFilterManager, packageName)
// 添加到额外的包名白名单,下次启动同时查询此包的安装情况
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = true)

View File

@ -81,23 +81,28 @@ object PackageHelper {
// 评论黑名单包名列表,避免用户安装了 Xposed Installer 这样的工具,也能在包含该安装包的游戏详情页评论
private var _commentPackageNameBlackList = arrayListOf<String>()
val commentPackageNameBlackList: ArrayList<String> = _commentPackageNameBlackList
val commentPackageNameBlackList: ArrayList<String>
get() = _commentPackageNameBlackList
// 关闭下载的包列表
private var _downloadPackageNameBlackList = arrayListOf<String>()
val downloadPackageNameBlackList: ArrayList<String> = _downloadPackageNameBlackList
val downloadPackageNameBlackList: ArrayList<String>
get() = _downloadPackageNameBlackList
// 本地已安装的包去掉关闭下载的包后的列表
private var _validLocalPackageNameSet = hashSetOf<String>()
val validLocalPackageNameSet: HashSet<String> = _validLocalPackageNameSet
val validLocalPackageNameSet: HashSet<String>
get() = _validLocalPackageNameSet
// 游戏包名匹配列表
private var _relatedPackageList = arrayListOf<SettingsEntity.GameWithPackages>()
val relatedPackageList: ArrayList<SettingsEntity.GameWithPackages> = _relatedPackageList
val relatedPackageList: ArrayList<SettingsEntity.GameWithPackages>
get() = _relatedPackageList
// 接口控制的已安装应用列表获取开关状态 (UI 显示)
private var _installedPackageApiSwitchStatusLiveData = MutableLiveData<Boolean>()
val installedPackageApiSwitchStatusLiveData: LiveData<Boolean> = _installedPackageApiSwitchStatusLiveData
val installedPackageApiSwitchStatusLiveData: LiveData<Boolean>
get() = _installedPackageApiSwitchStatusLiveData
// 本地已安装包的列表
var localPackageNameSet = hashSetOf<String>()
@ -541,6 +546,7 @@ object PackageHelper {
Utils.log(TAG, "addInstalledButMissingPackages 需要请求接口获取的包数量为 ${installedPackageNameSet.size}")
PackageRepository.addInstalledGames(
packageFilterManager = PackageRepository.packageFilterManager,
pkgNameList = ArrayList(installedPackageNameSet),
updateInstallStatus = true
)

View File

@ -14,6 +14,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.text.TextUtils;
import android.util.AndroidException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -222,8 +223,19 @@ public class PackageUtils {
if (metaDate != null) {
return metaDate.get(name);
}
} catch (NameNotFoundException e) {
// e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
if (e instanceof AndroidException) {
// 有些设备会出现 DeadSystemException
SentryHelper.INSTANCE.onEvent(
"GET_META_DATA_ERROR",
"packageName",
packageName,
"exception_digest",
e.getLocalizedMessage()
);
}
}
return null;
}
@ -627,8 +639,19 @@ public class PackageUtils {
try {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionName;
} catch (NameNotFoundException e) {
// do nothing
} catch (Exception e) {
e.printStackTrace();
if (e instanceof AndroidException) {
// 有些设备会出现 DeadSystemException
SentryHelper.INSTANCE.onEvent(
"GET_VERSION_NAME_ERROR",
"packageName",
packageName,
"exception_digest",
e.getLocalizedMessage()
);
}
}
return null;
}
@ -640,8 +663,18 @@ public class PackageUtils {
try {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionCode;
} catch (NameNotFoundException e) {
// do nothing
} catch (Exception e) {
e.printStackTrace();
if (e instanceof AndroidException) {
// 有些设备会出现 DeadSystemException
SentryHelper.INSTANCE.onEvent(
"GET_VERSION_CODE_ERROR",
"packageName",
packageName,
"exception_digest",
e.getLocalizedMessage()
);
}
}
return 0;
}
@ -654,8 +687,18 @@ public class PackageUtils {
try {
PackageManager packageManager = context.getApplicationContext().getPackageManager();
return packageManager.getApplicationIcon(packageName);
} catch (NameNotFoundException e) {
// do nothing
} catch (Exception e) {
e.printStackTrace();
if (e instanceof AndroidException) {
// 有些设备会出现 DeadSystemException
SentryHelper.INSTANCE.onEvent(
"GET_ICON_ERROR",
"packageName",
packageName,
"exception_digest",
e.getLocalizedMessage()
);
}
}
return null;
}
@ -706,8 +749,19 @@ public class PackageUtils {
jsonObject.put("version", packageInfo.versionName);
}
return jsonObject;
} catch (JSONException | NameNotFoundException e) {
} catch (Exception e) {
e.printStackTrace();
if (e instanceof AndroidException) {
// 有些设备会出现 DeadSystemException
SentryHelper.INSTANCE.onEvent(
"GET_APP_BASIC_INFO_BY_PACKAGE_NAME",
"packageName",
packageName,
"exception_digest",
e.getLocalizedMessage()
);
}
return jsonObject;
}
}

View File

@ -82,6 +82,7 @@ object ViewPagerFragmentHelper {
const val TYPE_TOOLKIT = "toolkit" // 工具箱
fun createFragment(parentFragment: Fragment?, bundle: Bundle, linkEntity: LinkEntity, isTabWrapper: Boolean): Fragment {
val superiorChain = if (parentFragment is ISuperiorChain) parentFragment else null
return when (linkEntity.type) {
// 游戏详情页
TYPE_GAME -> {
@ -90,11 +91,12 @@ object ViewPagerFragmentHelper {
}
// 我的光环
TYPE_MY_HALO -> {
val superiorChain = if (parentFragment is ISuperiorChain) parentFragment else null
HaloPersonalFragment().setSuperiorChain(superiorChain).with(bundle)
}
// 社区首页
TYPE_COMMUNITY_HOME -> CommunityHomeFragment().with(bundle)
TYPE_COMMUNITY_HOME -> {
CommunityHomeFragment().setSuperiorChain(superiorChain).with(bundle)
}
// 视频信息流
TYPE_VIDEO_STREAM -> {
bundle.putBoolean(EntranceConsts.KEY_IS_HOME_VIDEO, true)
@ -148,11 +150,11 @@ object ViewPagerFragmentHelper {
NewQuestionDetailFragment().with(bundle)
}
// 其他原来带Toolbar的Fragment
else -> createToolbarWrapperFragment(bundle, linkEntity, isTabWrapper)
else -> createToolbarWrapperFragment(parentFragment, bundle, linkEntity, isTabWrapper)
}
}
private fun createToolbarWrapperFragment(bundle: Bundle, entity: LinkEntity, isTabWrapper: Boolean): Fragment {
private fun createToolbarWrapperFragment(parentFragment: Fragment?, bundle: Bundle, entity: LinkEntity, isTabWrapper: Boolean): Fragment {
var className = ReloadFragment::class.java.name
when (entity.type) {

View File

@ -59,8 +59,8 @@ class GameDetailActivity : DownloadToolbarActivity() {
}
override fun getBusinessId(): Pair<String, String> {
val fragment = targetFragment as GameDetailFragment
return if (fragment.arguments != null) {
val fragment = targetFragment as? GameDetailFragment?
return if (fragment?.arguments != null) {
Pair(fragment.requireArguments().getString(EntranceConsts.KEY_GAMEID) ?: "", "")
} else {
super.getBusinessId()

View File

@ -48,6 +48,7 @@ import com.gh.gamecenter.common.entity.SensorsEvent
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.ImageUtils.getIdealImageUrl
import com.gh.gamecenter.common.view.DragListener
import com.gh.gamecenter.common.view.DraggableBigImageView
import com.gh.gamecenter.common.view.Gh_RelativeLayout
import com.gh.gamecenter.core.runOnIoThread
@ -815,18 +816,18 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener {
if (mBigImageView == null) {
mBigImageView = imageView
}
imageView.setDragListener(object : DraggableBigImageView.DragListener {
override fun onRelease(draggableBigImageView: DraggableBigImageView, scale: Float) {
imageView.setDragListener(object : DragListener {
override fun onRelease(scale: Float) {
updateOriginPosition(mViewPager.currentItem)
performExitAnimation(draggableBigImageView, scale, isFadeOnly())
performExitAnimation(imageView, scale, isFadeOnly())
}
override fun onDrag(draggableBigImageView: DraggableBigImageView, fraction: Float) {
override fun onDrag(fraction: Float) {
mBackgroundView.alpha = 1 - fraction
mIndicatorMask.visibility = View.GONE
}
override fun onRestore(draggableBigImageView: DraggableBigImageView, fraction: Float) {
override fun onRestore(fraction: Float) {
mBackgroundView.alpha = 1F
// mIndicatorMask.goneIf(mUrlList?.size == 1)
if (mUrlList?.size != 1 || mAnswerEntity != null) {

View File

@ -138,7 +138,6 @@ import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.HttpException;
@ -149,6 +148,8 @@ public class MainActivity extends BaseActivity {
public static final String SHOW_AD = "show_ad";
public static final int COUNTDOWN_AD = 100;
private int mCountdownMaxCount = 3;
public static final int COUNTDOWN_SDK_AD = 101;
public static final int COUNTDOWN_SDK_MAX_COUNT = 5;
private int mCountdownCount = 0;
private static final String CURRENT_PAGE = "current_page";
@ -490,6 +491,7 @@ public class MainActivity extends BaseActivity {
if (AdDelegateHelper.INSTANCE.shouldShowStartUpAd(false)) {
ViewGroup startAdContainer = findViewById(R.id.startAdContainer);
ViewGroup sdkStartAdContainer = findViewById(R.id.sdkStartAdContainer);
TextView sdkJumpBtn = findViewById(R.id.sdkJumpBtn);
FrameLayout adsFl = findViewById(R.id.adsFl);
View icpContainer = findViewById(R.id.sdkStartAdIcpContainer);
if (icpContainer != null) {
@ -518,6 +520,7 @@ public class MainActivity extends BaseActivity {
screenHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsFl,
(BaseHandler) mBaseHandler,
false,
@ -537,13 +540,19 @@ public class MainActivity extends BaseActivity {
@Override
protected void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == COUNTDOWN_AD) {
if (msg.what == COUNTDOWN_AD || msg.what == COUNTDOWN_SDK_AD) {
mCountdownCount++;
if (mCountdownMaxCount < mCountdownCount) {
int maxCount;
if (msg.what == COUNTDOWN_AD) {
maxCount = mCountdownMaxCount;
} else {
maxCount = COUNTDOWN_SDK_MAX_COUNT;
}
if (maxCount < mCountdownCount) {
AdDelegateHelper.INSTANCE.setShowingSplashAd(false);
hideSplashAd();
if (msg.obj instanceof StartupAdEntity) {
if (msg.what == COUNTDOWN_AD && msg.obj instanceof StartupAdEntity) {
StartupAdEntity ad = (StartupAdEntity) msg.obj;
LinkEntity linkEntity = ad.getJump();
SensorsBridge.trackEvent(
@ -556,14 +565,26 @@ public class MainActivity extends BaseActivity {
linkEntity.getLink(),
"link_text",
linkEntity.getText());
} else if (msg.what == COUNTDOWN_SDK_AD) {
SPUtils.setLong(Constants.SP_LAST_SPLASH_AD_SHOW_TIME, System.currentTimeMillis());
}
} else {
TextView jumpBtn = findViewById(R.id.jumpBtn);
jumpBtn.setText(getString(R.string.splash_jump, mCountdownMaxCount - mCountdownCount));
Message newMsg = Message.obtain();
newMsg.what = COUNTDOWN_AD;
newMsg.obj = msg.obj;
mBaseHandler.sendMessageDelayed(newMsg, 1000);
if (msg.what == COUNTDOWN_AD) {
TextView jumpBtn = findViewById(R.id.jumpBtn);
if (jumpBtn != null) {
jumpBtn.setText(getString(R.string.splash_jump, maxCount - mCountdownCount));
}
Message newMsg = Message.obtain();
newMsg.what = COUNTDOWN_AD;
newMsg.obj = msg.obj;
mBaseHandler.sendMessageDelayed(newMsg, 1000);
} else {
TextView jumpBtn = findViewById(R.id.sdkJumpBtn);
if (jumpBtn != null) {
jumpBtn.setText(getString(R.string.splash_jump, maxCount - mCountdownCount));
}
mBaseHandler.sendEmptyMessageDelayed(COUNTDOWN_SDK_AD, 1000);
}
}
}
}
@ -601,7 +622,11 @@ public class MainActivity extends BaseActivity {
ExtensionsKt.removeFromParent(startSdkAdContainer, true);
AdDelegateHelper.INSTANCE.cancelSplashAd(this);
}
TextView jumpBtn = findViewById(R.id.sdkJumpBtn);
if (jumpBtn != null) {
jumpBtn.setVisibility(View.GONE);
ExtensionsKt.removeFromParent(jumpBtn, true);
}
View startSdkAdIcpContainer = findViewById(R.id.sdkStartAdIcpContainer);
if (startSdkAdIcpContainer != null) {
startSdkAdIcpContainer.setVisibility(View.GONE);

View File

@ -10,6 +10,8 @@ import android.view.inputmethod.EditorInfo
import android.widget.*
import androidx.core.widget.doAfterTextChanged
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import com.gh.common.util.*
import com.gh.common.util.LogUtils
import com.gh.gamecenter.DisplayType.*
@ -118,7 +120,7 @@ open class SearchActivity : BaseActivity() {
trackSearchPageShow()
}
protected open fun trackSearchPageShow(){
protected open fun trackSearchPageShow() {
val bottomTab = intent.getStringExtra(EntranceConsts.KEY_BOTTOM_TAB_NAME) ?: ""
val multiTabId = intent.getStringExtra(EntranceConsts.KEY_MULTI_TAB_NAV_ID) ?: ""
val multiTabName = intent.getStringExtra(EntranceConsts.KEY_MULTI_TAB_NAV_NAME) ?: ""
@ -179,10 +181,11 @@ open class SearchActivity : BaseActivity() {
open fun search(type: SearchType, key: String?) {
mSearchType = type
mIsAutoSearchDisabled = true
// 自动搜索,默认搜索,热门搜索,历史搜索,主动搜索
// 自动搜索,默认搜索,热门搜索,历史搜索,主动搜索,榜单搜索
when (type) {
SearchType.AUTO -> handleAutoSearch(key)
SearchType.DEFAULT -> handleDefaultSearch(key)
SearchType.RANK -> handleRankSearch(key)
SearchType.HOT -> handleHotSearch(key)
SearchType.HISTORY -> handleHistorySearch(key)
SearchType.MANUAL -> handleManualSearch()
@ -213,6 +216,22 @@ open class SearchActivity : BaseActivity() {
}
}
protected open fun handleRankSearch(key: String?) {
mSearchKey = key
searchEt.setText(key)
searchEt.setSelection(searchEt.text.length)
updateDisplayType(GAME_DETAIL)
LogUtils.uploadSearchGame("searching", "搜索页", key, "榜单搜索")
SensorsBridge.trackSearchButtonClick(
GlobalActivityManager.getCurrentPageEntity().pageId,
GlobalActivityManager.getCurrentPageEntity().pageName,
key ?: "",
TRACK_SEARCH_TYPE_DEFAULT,
mSourceEntrance
)
}
protected open fun handleDefaultSearch(key: String?) {
mSearchKey = key
searchEt.setText(key)
@ -286,38 +305,72 @@ open class SearchActivity : BaseActivity() {
protected open fun provideDao(): ISearchHistoryDao = SearchHistoryDao(this)
open fun updateDisplayType(type: DisplayType) {
val transaction = supportFragmentManager.beginTransaction()
when (type) {
DEFAULT -> {
val fragment = supportFragmentManager.findFragmentByTag(SearchDefaultFragment::class.java.name)
?: SearchDefaultFragment().apply {
arguments = Bundle().also { it.putBoolean(SearchDefaultFragment.KEY_IS_GAME_SEARCH, true) }
}
transaction.replace(R.id.search_result, fragment, SearchDefaultFragment::class.java.name)
val transaction = when (type) {
DEFAULT -> showFragment(
SearchDefaultFragment::class.java.name,
{ SearchDefaultFragment() }
) {
it.arguments = Bundle().also { bundle ->
bundle.putBoolean(SearchDefaultFragment.KEY_IS_GAME_SEARCH, true)
}
}
GAME_DIGEST -> {
val digestListFragment =
supportFragmentManager.findFragmentByTag(SearchGameIndexFragment::class.java.name) as? SearchGameIndexFragment
?: SearchGameIndexFragment()
digestListFragment.setParams(mSearchKey ?: "", mSearchType.value)
transaction.replace(R.id.search_result, digestListFragment, SearchGameIndexFragment::class.java.name)
GAME_DIGEST -> showFragment(
SearchGameIndexFragment::class.java.name,
{ SearchGameIndexFragment() }
) {
removeFragment(SearchGameResultFragment::class.java.name)
it.setParams(mSearchKey ?: "", mSearchType.value)
}
GAME_DETAIL -> {
val detailListFragment =
supportFragmentManager.findFragmentByTag(SearchGameResultFragment::class.java.name) as? SearchGameResultFragment
?: SearchGameResultFragment()
detailListFragment.setParams(mSearchKey ?: "", mSearchType.value)
transaction.replace(R.id.search_result, detailListFragment, SearchGameResultFragment::class.java.name)
GAME_DETAIL -> showFragment(
SearchGameResultFragment::class.java.name,
{ SearchGameResultFragment() }
) {
removeFragment(SearchGameIndexFragment::class.java.name)
it.setParams(mSearchKey ?: "", mSearchType.value)
}
else -> {
//do nothing
}
else -> null
}
mDisplayType = type
transaction.commitAllowingStateLoss()
transaction?.let {
mDisplayType = type
it.commitAllowingStateLoss()
}
}
protected fun <T : Fragment> showFragment(
tag: String,
onFragmentCreate: () -> T,
onFragmentCreated: ((T) -> Unit)? = null,
): FragmentTransaction {
val transaction = supportFragmentManager.beginTransaction()
var createNewFragment = false
var fragment = supportFragmentManager
.findFragmentByTag(tag)
if (fragment == null) {
createNewFragment = true
fragment = onFragmentCreate.invoke()
}
onFragmentCreated?.invoke(fragment as T)
if (createNewFragment) {
transaction.add(R.id.search_result, fragment, tag)
transaction.addToBackStack(null)
} else if (!fragment.isAdded) {
transaction.show(fragment)
transaction.addToBackStack(null)
}
return transaction
}
protected fun removeFragment(tag: String) {
val fragment = supportFragmentManager
.findFragmentByTag(tag) ?: return
supportFragmentManager
.beginTransaction()
.remove(fragment)
.commit()
supportFragmentManager.popBackStack()
}
@Subscribe(threadMode = ThreadMode.MAIN)
@ -325,6 +378,7 @@ open class SearchActivity : BaseActivity() {
when (search.type) {
SearchType.HISTORY.value -> search(SearchType.HISTORY, search.key)
SearchType.HOT.value -> search(SearchType.HOT, search.key)
SearchType.RANK.value -> search(SearchType.RANK, search.key)
"click" -> DataCollectionUtils.uploadSearchClick(
this, mSearchKey, mSearchType.value, "搜索页面",
@ -341,9 +395,8 @@ open class SearchActivity : BaseActivity() {
}
override fun handleBackPressed(): Boolean {
val fragment = supportFragmentManager.findFragmentByTag(SearchDefaultFragment::class.java.name)
if (fragment == null) {
updateDisplayType(DEFAULT)
if (supportFragmentManager.fragments.size == 1) {
finish()
return true
}
return super.handleBackPressed()
@ -417,7 +470,8 @@ enum class SearchType(var value: String) {
DEFAULT("default"),
HISTORY("history"),
MANUAL("initiative"),
HOT("remen");
HOT("remen"),
RANK("rank");
fun toChinese() = when (this) {
AUTO -> "自动搜索"
@ -425,6 +479,7 @@ enum class SearchType(var value: String) {
HISTORY -> "历史搜索"
MANUAL -> "主动搜索"
HOT -> "热门搜索"
RANK -> "榜单搜索"
}
companion object {

View File

@ -419,7 +419,7 @@ public class SkipActivity extends BaseActivity {
} else {
Bundle newBundle = new Bundle();
newBundle.putString(EntranceConsts.KEY_TO, LoginActivity.class.getName());
EntranceUtils.jumpActivity(this, null, newBundle, (resultCode, data) -> {
EntranceUtils.jumpActivityCompat(this, newBundle, null, (resultCode, data) -> {
if(CheckLoginUtils.isLogin()) {
VHelper.INSTANCE.updateAuthorizeInfo(true);
}
@ -442,7 +442,7 @@ public class SkipActivity extends BaseActivity {
break;
default:
EntranceUtils.jumpActivity(this, new Bundle()); // 跳转至首页
return;
break;
}
}
} else if ("market".equals(uri.getScheme())) {

View File

@ -12,8 +12,10 @@ import com.gh.ad.AdDelegateHelper
import com.gh.ad.AdDelegateHelper.requestSplashAd
import com.gh.ad.AdDelegateHelper.shouldShowStartUpAd
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.SPUtils
import java.util.*
/**
@ -37,6 +39,7 @@ class SplashAdActivity : BaseActivity() {
if (shouldShowStartUpAd(true)) {
val startAdContainer = findViewById<ViewGroup>(R.id.startAdContainer)
val sdkStartAdContainer = findViewById<ViewGroup>(R.id.sdkStartAdContainer)
val sdkJumpBtn = findViewById<TextView>(R.id.sdkJumpBtn)
val adsFl = findViewById<FrameLayout>(R.id.adsFl)
val icpContainer = findViewById<View>(R.id.sdkStartAdIcpContainer)
@ -58,6 +61,7 @@ class SplashAdActivity : BaseActivity() {
screenHeightInDp,
startAdContainer!!,
sdkStartAdContainer!!,
sdkJumpBtn!!,
adsFl!!,
(mBaseHandler as BaseHandler),
true
@ -81,15 +85,23 @@ class SplashAdActivity : BaseActivity() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg.what == MainActivity.COUNTDOWN_AD) {
if (msg.what == MainActivity.COUNTDOWN_AD || msg.what == MainActivity.COUNTDOWN_SDK_AD) {
mCountdownCount++
if (COUNTDOWN_MAX_COUNT < mCountdownCount) {
val maxCount = if (msg.what == MainActivity.COUNTDOWN_AD) {
COUNTDOWN_MAX_COUNT
} else {
MainActivity.COUNTDOWN_SDK_MAX_COUNT
}
if (maxCount < mCountdownCount) {
AdDelegateHelper.isShowingSplashAd = false
if (msg.what == MainActivity.COUNTDOWN_SDK_AD) {
SPUtils.setLong(Constants.SP_LAST_SPLASH_AD_SHOW_TIME, System.currentTimeMillis())
}
finishActivity()
} else {
val jumpBtn = findViewById<TextView>(R.id.jumpBtn)
jumpBtn.text = getString(R.string.splash_jump, COUNTDOWN_MAX_COUNT - mCountdownCount)
mBaseHandler.sendEmptyMessageDelayed(MainActivity.COUNTDOWN_AD, 1000)
val jumpBtn = findViewById<TextView>(if (msg.what == MainActivity.COUNTDOWN_AD) R.id.jumpBtn else R.id.sdkJumpBtn)
jumpBtn?.text = getString(R.string.splash_jump, maxCount - mCountdownCount)
mBaseHandler.sendEmptyMessageDelayed(msg.what, 1000)
}
}
}

View File

@ -17,8 +17,11 @@ import androidx.core.text.color
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.common.dialog.NewPrivacyPolicyDialogFragment
import com.gh.common.util.*
import com.gh.common.util.DeviceTokenUtils
import com.gh.common.util.DialogUtils
import com.gh.common.util.GameSubstituteRepositoryHelper.updateGameSubstituteRepository
import com.gh.common.util.PackageHelper
import com.gh.common.util.PackageUtils
import com.gh.common.util.UsageStatsHelper.checkAndPostUsageStats
import com.gh.download.DownloadManager
import com.gh.gamecenter.common.base.activity.BaseActivity
@ -28,6 +31,7 @@ import com.gh.gamecenter.common.tracker.TrackerLogger
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.provider.IAppProvider
import com.gh.gamecenter.core.provider.IPackageUtilsProvider
import com.gh.gamecenter.core.provider.IPushProvider
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.SPUtils
@ -55,6 +59,9 @@ class SplashScreenActivity : BaseActivity() {
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
mIsNewForThisVersion = HaloApp.getInstance().isNewForThisVersion
HaloApp.getInstance().isBrandNewInstall = SPUtils.getBoolean(Constants.SP_BRAND_NEW_USER, true)
if (HaloApp.getInstance().isBrandNewInstall) {
SPUtils.setLong(Constants.SP_BRAND_NEW_FIRST_LAUNCH_TIME, System.currentTimeMillis())
}
// 用户不是新版本,但应用最后更新时间不是上次的时间代表用户重新安装了当前版本
if (!mIsNewForThisVersion) {
@ -274,6 +281,12 @@ class SplashScreenActivity : BaseActivity() {
SensorsBridge.init(HaloApp.getInstance(), HaloApp.getInstance().channel)
SensorsBridge.setOAID(HaloApp.getInstance().oaid)
val pushProvider = ARouter.getInstance().build(RouteConsts.provider.push).navigation() as? IPushProvider
val registrationId = pushProvider?.getRegistrationId(this)
if (!registrationId.isNullOrEmpty()) {
SensorsBridge.profileAppend(KEY_REGISTRATION_ID, registrationId)
}
}
private fun prefetchData() {
@ -305,6 +318,7 @@ class SplashScreenActivity : BaseActivity() {
companion object {
private const val KEY_REGISTRATION_ID = "registration_id"
const val HONOR_CULPRIT_ID = 12324
const val HONOR_CULPRIT_CHANNEL = "荣耀通道"

View File

@ -24,6 +24,7 @@ import com.gh.gamecenter.R;
import com.gh.gamecenter.adapter.viewholder.KcSelectGameViewHolder;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.common.utils.BitmapUtils;
import com.gh.gamecenter.common.utils.FileUtils;
import com.gh.gamecenter.core.utils.MtaHelper;
import com.gh.gamecenter.core.utils.TimeUtils;
import com.gh.gamecenter.core.utils.ToastUtils;
@ -85,7 +86,7 @@ public class CleanApkAdapter extends BaseRecyclerAdapter<KcSelectGameViewHolder>
Observable.create(emitter -> {
// 扫描和获取apk数据 分步操作 尽量避免 StackoverflowError
FindAllAPKPath(Environment.getExternalStorageDirectory());
FindAllAPKPath(mContext.getFilesDir());
FindAllAPKPath(new File(FileUtils.getDownloadDir(mContext)));
LoadApkData();
emitter.onComplete();
})

View File

@ -652,12 +652,12 @@ class DetailViewHolder(
builder.addHandler(LandPageAddressHandler())
builder.addHandler(OverseaDownloadHandler())
builder.addHandler(CheckDownloadHandler())
builder.setProcessEndCallback { asVGame: Boolean, isSubscribe: Any? ->
builder.setProcessEndCallback(mGameEntity.id) { asVGame: Boolean, isSubscribe: Any? ->
download(asVGame, isSubscribe as Boolean)
}
} else {
builder.addHandler(VersionNumberHandler())
builder.setProcessEndCallback { _: Boolean?, _: Any? ->
builder.setProcessEndCallback(mGameEntity.id) { _: Boolean?, _: Any? ->
DownloadDialog.showDownloadDialog(
mViewHolder.context,
mGameEntity,

View File

@ -4,11 +4,10 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.DisplayType
import com.gh.gamecenter.R
import com.gh.gamecenter.SearchActivity
import com.gh.gamecenter.SearchType
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.search.SearchDefaultFragment
@ -64,19 +63,16 @@ class AmwaySearchActivity : SearchActivity() {
}
override fun updateDisplayType(type: DisplayType) {
val transaction = supportFragmentManager.beginTransaction()
when (type) {
DisplayType.DEFAULT -> {
val fragment = supportFragmentManager.findFragmentByTag(SearchDefaultFragment::class.java.name)
?: AmwaySearchDefaultFragment()
transaction.replace(R.id.search_result, fragment, SearchDefaultFragment::class.java.name)
}
val transaction = when (type) {
DisplayType.DEFAULT -> showFragment(
SearchDefaultFragment::class.java.name,
{ AmwaySearchDefaultFragment() }
)
else -> {
val fragment = supportFragmentManager.findFragmentByTag(AmwaySearchListFragment::class.java.name)
?: AmwaySearchListFragment()
transaction.replace(R.id.search_result, fragment, AmwaySearchListFragment::class.java.name)
}
else -> showFragment(
AmwaySearchListFragment::class.java.name,
{ AmwaySearchListFragment() }
)
}
mDisplayType = type
transaction.commitAllowingStateLoss()

View File

@ -33,7 +33,7 @@ class AmwaySearchDefaultFragment : SearchDefaultFragment() {
mViewModel.playedGames.observeNonNull(viewLifecycleOwner) {
defaultViewModel?.isExistHotSearch = it.isNotEmpty()
updateView()
mBinding.hotList.run {
mBinding.searchDiscoveryList.run {
layoutManager = LinearLayoutManager(context)
adapter = AmwaySearchAdapter(context, mViewModel, "安利墙搜索-最近玩过").apply { setData(it) }
}
@ -45,7 +45,7 @@ class AmwaySearchDefaultFragment : SearchDefaultFragment() {
override fun provideDao(): ISearchHistoryDao = AmwaySearchDao()
override fun initView() {
mBinding = mAmwayBinding.searchContent
mBinding.hotHeadContainer.headTitle.text = "最近玩过"
mBinding.searchDiscoveryHeadContainer.headTitle.text = "最近玩过"
mBinding.historyFlexContainer.setLimitHeight(mFlexMaxHeight)
updateHistorySearchView(null)

View File

@ -57,6 +57,7 @@ class AmwaySearchListFragment : ToolbarFragment() {
when (it) {
LoadStatus.INIT_LOADING -> {
hideError()
hideRvList()
showLoading()
hideNoDataHint()
}
@ -64,16 +65,19 @@ class AmwaySearchListFragment : ToolbarFragment() {
hideError()
hideLoading()
hideNoDataHint()
showRvList()
}
LoadStatus.INIT_FAILED -> {
hideLoading()
hideNoDataHint()
showError()
hideRvList()
}
LoadStatus.INIT_EMPTY -> {
showNoDataHint()
hideError()
hideLoading()
hideRvList()
}
else -> {
// do nothing
@ -82,6 +86,14 @@ class AmwaySearchListFragment : ToolbarFragment() {
}
}
private fun hideRvList() {
mBinding.recyclerView.visibility = View.GONE
}
private fun showRvList() {
mBinding.recyclerView.visibility = View.VISIBLE
}
private fun showLoading() {
mBinding.loadingContainer.visibility = View.VISIBLE
}

View File

@ -44,6 +44,7 @@ class AmwaySearchViewModel(application: Application) : AndroidViewModel(applicat
mTempSearchKey = searchKey
currentSearchKey = searchKey
loadStatus.postValue(LoadStatus.INIT_LOADING)
searchGames.postValue(emptyList())
RetrofitManager
.getInstance().api
.getSearchGame(Config.API_HOST + "games:search?keyword=" + searchKey + "&view=anliwall" + "&channel=" + HaloApp.getInstance().channel + "&version=" + BuildConfig.VERSION_NAME)

View File

@ -160,7 +160,7 @@ class DownloadFragment : BaseFragment_TabLayout() {
val ownerAd = downloadManagerAd?.ownerAd
val showOnFailed = downloadManagerAd?.displayRule?.onFailedAction == "show"
if ((showThirdPartyAd && thirdPartyAd != null) || (!showThirdPartyAd && thirdPartyAd != null && ownerAd == null)) {
initThirdPartyAd(thirdPartyAd) { isSuccess ->
initThirdPartyAd(downloadManagerAd, thirdPartyAd) { isSuccess ->
mBinding.maskView.goneIf(!isSuccess)
if (!isSuccess && ownerAd != null && showOnFailed) {
mSlideInterval = ownerAd.adSource?.sliderInterval ?: -1
@ -182,12 +182,34 @@ class DownloadFragment : BaseFragment_TabLayout() {
}
}
private fun initThirdPartyAd(thirdPartyAd: AdConfig.ThirdPartyAd, callback: (isSuccess: Boolean) -> Unit) {
private fun initThirdPartyAd(adConfig: AdConfig?, thirdPartyAd: AdConfig.ThirdPartyAd, callback: (isSuccess: Boolean) -> Unit) {
val onAdShowAction = {
SensorsBridge.trackEvent("ThirdPartyAdShow",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", adConfig?.typeChinese ?: "",
"ad_placement", "下载管理",
"ad_space_id", adConfig?.id ?: "",
"ad_space_name", adConfig?.name ?: ""
)
}
val onAdClickAction = {
SensorsBridge.trackEvent("ThirdPartyAdClick",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", adConfig?.typeChinese ?: "",
"ad_placement", "下载管理",
"ad_space_id", adConfig?.id ?: "",
"ad_space_name", adConfig?.name ?: ""
)
}
AdDelegateHelper.requestThirdPartyBannerAd(
this,
mBinding.adContainer,
thirdPartyAd,
DisplayUtils.getScreenWidthInDp(requireActivity()),
onAdShowAction,
onAdClickAction,
callback
)
}
@ -226,7 +248,7 @@ class DownloadFragment : BaseFragment_TabLayout() {
if (it.isNullOrEmpty() && adConfig.displayRule.adSource == AdDelegateHelper.AD_TYPE_OWNER &&
adConfig.displayRule.onFailedAction == "show" && adConfig.thirdPartyAd != null) {
// 自有广告游戏为空时,显示第三方广告
initThirdPartyAd(adConfig.thirdPartyAd) { isSuccess ->
initThirdPartyAd(adConfig, adConfig.thirdPartyAd) { isSuccess ->
mBinding.maskView.goneIf(!isSuccess)
if (isSuccess) {
SPUtils.setLong(Constants.SP_LAST_DOWNLOAD_MANAGER_AD_SHOW_TIME, System.currentTimeMillis())

View File

@ -6,7 +6,7 @@ class AdConfig(
@SerializedName("_id")
val id: String = "",
val name: String,
val location: String, // 广告插入位置。光环启动halo_launch 下载管理download_manager 游戏搜索game_search 助手启动helper_launch
val location: String, // 广告插入位置。光环启动halo_launch 下载管理download_manager 游戏搜索game_search 畅玩启动helper_launch
val type: String, // 广告位类型。开屏广告launch 信息流广告native banner 广告banner 插屏广告interstitial
val position: Int, // 定位,不存在的时候返回:-1
@SerializedName("display_rules")
@ -18,12 +18,19 @@ class AdConfig(
@SerializedName("owner_ads")
val ownerAd: OwnerAdEntity? = null,
) {
companion object {
const val TYPE_LAUNCH = "launch"
const val TYPE_NATIVE = "native"
const val TYPE_BANNER = "banner"
const val TYPE_INTERSTITIAL = "interstitial"
}
val typeChinese
get() = when (type) {
"launch" -> "开屏"
"native" -> "信息流"
"banner" -> "banner"
"interstitial" -> "插屏"
TYPE_LAUNCH -> "开屏"
TYPE_NATIVE -> "信息流"
TYPE_BANNER -> "banner"
TYPE_INTERSTITIAL -> "插屏"
else -> ""
}

View File

@ -26,7 +26,8 @@ data class BottomTab(
@SerializedName("is_default_page")
var default: Boolean = false, // 是否为默认显示页
var guide: Guide? = null, // 引导文案
var isTransparentStyle: Boolean = false // 本地字段透明底部Tab
var diverter: DiverterEntity? = null, // 分流器
var isTransparentStyle: Boolean = false, // 本地字段透明底部Tab
) : Parcelable {
@Parcelize
data class SearchStyle(

View File

@ -0,0 +1,31 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
class DiverterData(
@SerializedName("diverter_id")
val diverterId: String = "",
@SerializedName("diverter_name")
val diverterName: String = "",
@SerializedName("branch_id")
val branchId: String = "",
@SerializedName("branch_name")
val branchName: String = "",
@SerializedName("branch_type")
val branchType: String = "",
@SerializedName("inprocess_time")
val inprocessTime: Int = 0,
@SerializedName("bypass_visit_time")
val bypassVisitTime: Int = 0,
@SerializedName("link_id")
val linkId: String = "",
@SerializedName("link_type")
val linkType: String = "",
@SerializedName("link_text")
val linkText: String = "",
@SerializedName("bypass_status")
val bypassStatus: Int = 0,
): Parcelable

View File

@ -0,0 +1,15 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
class DiverterEntity(
@SerializedName("module_id")
val moduleId: String = "",
@SerializedName("module_index")
val moduleIndex: Int = -1,
@SerializedName("diverter_data")
val diverterData: DiverterData = DiverterData()
): Parcelable

View File

@ -23,6 +23,9 @@ import com.airbnb.lottie.SimpleColorFilter
import com.airbnb.lottie.model.KeyPath
import com.airbnb.lottie.value.LottieValueCallback
import com.gh.common.browse.BrowseTimer
import com.gh.common.iinterface.ISuperiorChain
import com.gh.common.prioritychain.CommunityHomeGuideHandler
import com.gh.common.prioritychain.PriorityChain
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.NewLogUtils
@ -42,7 +45,6 @@ import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.databinding.FragmentCommunityHomeBinding
import com.gh.gamecenter.databinding.LayoutCommunityHomeVideoGuideBinding
import com.gh.gamecenter.databinding.TabItemCommunityBinding
import com.gh.gamecenter.eventbus.EBSkip
import com.gh.gamecenter.eventbus.EBTypeChange
@ -81,6 +83,8 @@ class CommunityHomeFragment : LazyFragment() {
private var mNavigationBitmap: Bitmap? = null
private var mShowVideo = true
private var mBottomTabId = ""
private val mPriorityChain by lazy { PriorityChain() }
private var mSuperiorChain: ISuperiorChain? = null
private val browseTimer = BrowseTimer()
.withResult {
@ -156,6 +160,12 @@ class CommunityHomeFragment : LazyFragment() {
})
}
private fun addHomeVideoGuideHandler() {
val decorView = activity?.window?.decorView as? FrameLayout
val communityHomeGuideHandler = CommunityHomeGuideHandler(21, requireContext(), decorView, mBinding?.videoLottie)
mPriorityChain.addHandler(communityHomeGuideHandler)
}
override fun initRealView() {
super.initRealView()
@ -166,24 +176,7 @@ class CommunityHomeFragment : LazyFragment() {
mMainWrapperViewModel?.bottomTabListLiveData?.observe(this) { tabList ->
mBinding?.videoAndSearchContainer?.goneIf(!mShowVideo) {
val decorView = requireActivity().window.decorView as? FrameLayout
if (SPUtils.getBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, true)) {
val guideLayoutBinding = LayoutCommunityHomeVideoGuideBinding.inflate(
LayoutInflater.from(requireContext()),
decorView,
true
)
guideLayoutBinding.root.setOnClickListener { view ->
decorView?.removeView(view)
SPUtils.setBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, false)
mBinding?.videoLottie?.playAnimation()
SPUtils.setLong(
Constants.SP_COMMUNITY_HOME_VIDEO_LOTTIE_LAST_PLAY_TIME,
System.currentTimeMillis()
)
}
}
addHomeVideoGuideHandler()
// 每日首次进入社区tab视频lottie播放3次
val lastPlayTime = SPUtils.getLong(Constants.SP_COMMUNITY_HOME_VIDEO_LOTTIE_LAST_PLAY_TIME, 0L) / 1000
@ -285,6 +278,14 @@ class CommunityHomeFragment : LazyFragment() {
DisplayUtils.transparentStatusBar(requireActivity())
DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn)
NewLogUtils.logCommunityHomeEvent("view_community")
mSuperiorChain?.registerInferiorChain(mPriorityChain)
}
override fun onFragmentPause() {
super.onFragmentPause()
mSuperiorChain?.unregisterInferiorChain(mPriorityChain)
}
fun setCurrentItem(index: Int) {
@ -836,6 +837,11 @@ class CommunityHomeFragment : LazyFragment() {
fun getTopBgView() = mBinding?.topBg
fun setSuperiorChain(superiorChain: ISuperiorChain?): CommunityHomeFragment {
this.mSuperiorChain = superiorChain
return this
}
companion object {
var TAB_SELECTED_COLOR: Int = R.color.text_primary
var TAB_DEFAULT_COLOR: Int = R.color.community_forum_more

View File

@ -129,39 +129,40 @@ class ForumOrUserSearchActivity : SearchActivity() {
}
override fun updateDisplayType(type: DisplayType) {
val transaction = supportFragmentManager.beginTransaction()
when (type) {
DisplayType.DEFAULT -> {
val fragment = supportFragmentManager.findFragmentByTag(SearchDefaultFragment::class.java.name)
?: ForumOrUserSearchDefaultFragment()
fragment.arguments = intent.extras
transaction.replace(R.id.search_result, fragment, SearchDefaultFragment::class.java.name)
val key = searchEt.text.toString().trim { it <= ' ' }
if (key.isNotEmpty()) {
val stayTime = (System.currentTimeMillis() - startPageTime) / 1000
if (mEntrance != ForumDetailFragment.ENTRANCE) {
NewFlatLogUtils.logViewSearchList(stayTime, key)
} else {
NewFlatLogUtils.logViewBbsSearchList(stayTime, key, mBbsId)
val transaction = when (type) {
DisplayType.DEFAULT -> showFragment(
SearchDefaultFragment::class.java.name,
onFragmentCreate = { ForumOrUserSearchDefaultFragment() }
) { fragment ->
fragment.arguments = intent.extras
}.apply {
val key = searchEt.text.toString().trim { it <= ' ' }
if (key.isNotEmpty()) {
val stayTime = (System.currentTimeMillis() - startPageTime) / 1000
if (mEntrance != ForumDetailFragment.ENTRANCE) {
NewFlatLogUtils.logViewSearchList(stayTime, key)
} else {
NewFlatLogUtils.logViewBbsSearchList(stayTime, key, mBbsId)
}
}
}
}
else -> {
if (mEntrance != ForumDetailFragment.ENTRANCE) {
val fragment =
supportFragmentManager.findFragmentByTag(ForumOrUserSearchFragment::class.java.name) as? ForumOrUserSearchFragment
?: ForumOrUserSearchFragment()
fragment.setSearchKeyAndType(mSearchKey ?: "", mSearchType.value)
transaction.replace(R.id.search_result, fragment, ForumOrUserSearchFragment::class.java.name)
else -> if (mEntrance != ForumDetailFragment.ENTRANCE) {
showFragment(
ForumOrUserSearchFragment::class.java.name,
{ ForumOrUserSearchFragment() }
) { fragment ->
fragment.setSearchKeyAndType(mSearchKey ?: "", mSearchType.value)
}
} else {
val fragment =
supportFragmentManager.findFragmentByTag(ForumContentSearchListFragment::class.java.name) as? ForumContentSearchListFragment
?: ForumContentSearchListFragment()
fragment.setSearchKeyAndType(mSearchKey ?: "", mSearchType.value)
fragment.arguments = intent.extras
transaction.replace(R.id.search_result, fragment, ForumContentSearchListFragment::class.java.name)
}
showFragment(
ForumOrUserSearchFragment::class.java.name,
{ ForumContentSearchListFragment() }
) { fragment ->
fragment.setSearchKeyAndType(mSearchKey ?: "", mSearchType.value)
fragment.arguments = intent.extras
}
}.apply {
startPageTime = System.currentTimeMillis()
}
}

View File

@ -42,14 +42,14 @@ class ForumOrUserSearchDefaultFragment : SearchDefaultFragment() {
override fun initView() {
mBinding = FragmentSearchDefaultBinding.bind(mCachedView)
mBinding.hotTagHeadContainer.root.visibility = View.GONE
mBinding.hotTagFlexContainer.visibility = View.GONE
mBinding.searchDiscoveryTagHeadContainer.root.visibility = View.GONE
mBinding.searchDiscoveryTagFlexContainer.visibility = View.GONE
if (mEntrance == "论坛首页" || mEntrance == "搜索栏") {
mBinding.hotHeadContainer.headTitle.text = "热门论坛"
mBinding.searchDiscoveryHeadContainer.headTitle.text = "热门论坛"
mViewModel.getForumSearchHotContent()
} else {
mBinding.hotHeadContainer.root.visibility = View.GONE
mBinding.hotList.visibility = View.GONE
mBinding.searchDiscoveryHeadContainer.root.visibility = View.GONE
mBinding.searchDiscoveryList.visibility = View.GONE
}
val params = mBinding.historyHeadContainer.root.layoutParams as ConstraintLayout.LayoutParams
params.topMargin = 0.5f.dip2px()

View File

@ -23,7 +23,7 @@ class ReloadFragment: BaseLazyFragment() {
super.onCreate(savedInstanceState)
mBinding.reuseLoading.root.visibility = View.VISIBLE
mBinding.reuseNoConnection.connectionReloadTv.setOnClickListener {
MainWrapperRepository.getInstance().getDataUnion()
MainWrapperRepository.getInstance().init()
mBinding.reuseNoConnection.root.visibility = View.GONE
mBinding.reuseLoading.root.visibility = View.VISIBLE
}

View File

@ -2,8 +2,8 @@ package com.gh.gamecenter.game.columncollection.detail
import android.content.Intent
import android.os.Bundle
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
@ -34,8 +34,8 @@ class ColumnCollectionDetailActivity : ToolBarActivity() {
}
override fun getBusinessId(): Pair<String, String> {
val fragment = targetFragment as ColumnCollectionDetailFragment
return if (fragment.arguments != null) {
val fragment = targetFragment as? ColumnCollectionDetailFragment?
return if (fragment?.arguments != null) {
Pair(fragment.requireArguments().getString(EntranceConsts.KEY_COLLECTION_ID) ?: "", "")
} else {
super.getBusinessId()

View File

@ -36,8 +36,8 @@ class CommonCollectionDetailActivity : ToolBarActivity() {
}
override fun getBusinessId(): Pair<String, String> {
val fragment = targetFragment as CommonCollectionDetailFragment
return if (fragment.arguments != null) {
val fragment = targetFragment as? CommonCollectionDetailFragment?
return if (fragment?.arguments != null) {
Pair(
fragment.requireArguments().getString(EntranceConsts.KEY_COLLECTION_ID) ?: "",
fragment.requireArguments().getString(EntranceConsts.KEY_BLOCK_ID) ?: ""

View File

@ -36,8 +36,8 @@ class CustomCommonCollectionDetailActivity : ToolBarActivity() {
}
override fun getBusinessId(): Pair<String, String> {
val fragment = targetFragment as CustomCommonCollectionDetailFragment
return if (fragment.arguments != null) {
val fragment = targetFragment as? CustomCommonCollectionDetailFragment?
return if (fragment?.arguments != null) {
Pair(
fragment.requireArguments().getString(EntranceConsts.KEY_COLLECTION_ID) ?: "",
fragment.requireArguments().getString(EntranceConsts.KEY_BLOCK_ID) ?: ""

View File

@ -3,7 +3,6 @@ package com.gh.gamecenter.game.upload
import android.app.Dialog
import android.content.Intent
import android.os.Bundle
import android.provider.MediaStore
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.TextPaint
@ -29,18 +28,21 @@ import com.gh.gamecenter.WebActivity
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.callback.OnListClickListener
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.choosepic.ChoosePicAdapter
import com.gh.gamecenter.core.utils.GsonUtils
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.SpanBuilder
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.FragmentGameUploadBinding
import com.gh.gamecenter.feature.entity.InstallGameEntity
import com.gh.gamecenter.feature.game.SelectGameAdapter
import com.gh.gamecenter.feature.selector.ChooseType
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.internal.utils.PathUtils
import io.reactivex.disposables.Disposable
import okhttp3.MediaType
import okhttp3.RequestBody
@ -86,7 +88,6 @@ class GameUploadFragment : ToolbarFragment() {
mViewModel.upLoadSuccess.observe(viewLifecycleOwner, Observer {
if (it) {
ToastUtils.showToast("上传成功")
MtaHelper.onEvent("游戏上传", "游戏上传", "上传成功")
mUploadDialog.dismiss()
requireActivity().finish()
} else {
@ -119,17 +120,11 @@ class GameUploadFragment : ToolbarFragment() {
5,
object : OnListClickListener {
override fun <T : Any?> onListClick(view: View?, position: Int, data: T) {
MtaHelper.onEvent("游戏上传", "游戏图片", "添加图片")
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "游戏上传")
startActivityForResult(intent, MEDIA_STORE_REQUEST)
}
}
) {
MtaHelper.onEvent(
"游戏上传",
"游戏图片",
"删除图片"
)
}
mAdapter?.setPicItem(R.layout.game_upload_pic_item)
mAdapter?.setSuggestAddPicIcon(R.drawable.icon_pic_add)
@ -147,19 +142,13 @@ class GameUploadFragment : ToolbarFragment() {
1,
object : OnListClickListener {
override fun <T : Any?> onListClick(view: View?, position: Int, data: T) {
MtaHelper.onEvent("游戏上传", "游戏图标", "添加图片")
PermissionHelper.checkStoragePermissionBeforeAction(requireActivity()) {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "游戏上传")
startActivityForResult(intent, MEDIA_ICON_STORE_REQUEST)
}
}
}
) {
MtaHelper.onEvent(
"游戏上传",
"游戏图标",
"删除图片"
)
}
mIconAdapter?.setPicItem(R.layout.game_upload_pic_item)
mIconAdapter?.setSuggestAddPicIcon(R.drawable.icon_pic_add)
@ -194,18 +183,15 @@ class GameUploadFragment : ToolbarFragment() {
private fun initListener() {
mBinding.chooseGameLl.setOnClickListener {
MtaHelper.onEvent("游戏上传", "安装包", "点我选择")
showSelectDialog()
}
mBinding.gameIsNetworkingRg.setOnCheckedChangeListener { _, checkedId ->
when (checkedId) {
R.id.gameNetworkingRb -> {
mIsOnline = "yes"
MtaHelper.onEvent("游戏上传", "是否联网", "需要联网")
}
R.id.gameNoNetworkingRb -> {
mIsOnline = "no"
MtaHelper.onEvent("游戏上传", "是否联网", "无需联网")
}
}
}
@ -213,15 +199,12 @@ class GameUploadFragment : ToolbarFragment() {
when (checkedId) {
R.id.gameLanguageChineseRb -> {
mGameLanguage = "中文"
MtaHelper.onEvent("游戏上传", "游戏语言", "中文")
}
R.id.gameLanguageEnglishRb -> {
mGameLanguage = "英文"
MtaHelper.onEvent("游戏上传", "游戏语言", "英文")
}
R.id.gameLanguageOtherRb -> {
mGameLanguage = "其他"
MtaHelper.onEvent("游戏上传", "游戏语言", "其他")
}
}
}
@ -229,21 +212,17 @@ class GameUploadFragment : ToolbarFragment() {
when (checkedId) {
R.id.gameTypeLocalRb -> {
mGameType = "local"
MtaHelper.onEvent("游戏上传", "游戏类型", "单机")
}
R.id.gameTypeOnlineRb -> {
mGameType = "online"
MtaHelper.onEvent("游戏上传", "游戏类型", "网游")
}
R.id.gameTypeOtherRb -> {
mGameType = "other"
MtaHelper.onEvent("游戏上传", "游戏类型", "其他")
}
}
}
mBinding.addGameLabeTv.setOnClickListener {
MtaHelper.onEvent("游戏上传", "游戏标签", "添加标签")
if (mTags.size < mMaxTagSize) {
showAddTagDialog()
} else {
@ -256,7 +235,6 @@ class GameUploadFragment : ToolbarFragment() {
}
private fun commit() {
MtaHelper.onEvent("游戏上传", "提交", "提交")
if (TextUtils.isEmpty(mBinding.tvChoose.text.toString())) {
ToastUtils.showToast("请先选择游戏安装包")
return
@ -471,11 +449,9 @@ class GameUploadFragment : ToolbarFragment() {
}
back.setOnClickListener {
MtaHelper.onEvent("游戏上传", "安装包", "关闭")
mSelectGameDialog?.cancel()
}
manualBtn.setOnClickListener {
MtaHelper.onEvent("游戏上传", "安装包", "从设备上选择")
val intent = CleanApkActivity.getIntent(requireContext(), true)
PermissionHelper.checkManageAllFilesOrStoragePermissionBeforeAction(requireActivity()) {
startActivityForResult(intent, CHOOSE_LOCAL_APK)
@ -499,32 +475,20 @@ class GameUploadFragment : ToolbarFragment() {
super.onActivityResult(requestCode, resultCode, data)
if (data == null) return
if (requestCode == MEDIA_STORE_REQUEST || requestCode == MEDIA_ICON_STORE_REQUEST) {
val selectedImage = data.data ?: return
val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
val selectedPaths = Matisse.obtainResult(data) ?: return
val picturePath = PathUtils.getPath(requireContext(), selectedPaths[0])
val cursor = requireContext().contentResolver.query(selectedImage, filePathColumn, null, null, null)
?: return
cursor.moveToFirst()
Utils.log("picturePath = $picturePath")
try {
val columnIndex = cursor.getColumnIndex(filePathColumn[0])
val picturePath = cursor.getString(columnIndex)
cursor.close()
Utils.log("picturePath = $picturePath")
val file = File(picturePath)
if (file.length() > 5 * 1024 * 1024) {
ToastUtils.showToast(getString(R.string.pic_max_hint, 5))
val file = File(picturePath)
if (file.length() > 5 * 1024 * 1024) {
ToastUtils.showToast(getString(R.string.pic_max_hint, 5))
} else {
if (requestCode == MEDIA_STORE_REQUEST) {
mAdapter!!.addFileList(picturePath)
} else {
if (requestCode == MEDIA_STORE_REQUEST) {
mAdapter!!.addFileList(picturePath)
} else {
mIconAdapter!!.addFileList(picturePath)
}
mIconAdapter!!.addFileList(picturePath)
}
} catch (e: Exception) {
ToastUtils.showToast(e.message ?: "")
}
} else if (requestCode == CHOOSE_LOCAL_APK) {
val packageName = data.getStringExtra(EntranceConsts.KEY_PACKAGENAME) ?: ""
@ -610,7 +574,6 @@ class GameUploadFragment : ToolbarFragment() {
labelTv.text = tag
labelView.findViewById<View>(R.id.picDelIv).setOnClickListener {
if (mTags.contains(tag)) {
MtaHelper.onEvent("游戏上传", "游戏标签", "删除标签")
mBinding.gameLabelFl.removeView(labelView)
mTags.remove(tag)
mBinding.gameLabelFl.goneIf(mTags.isEmpty())

View File

@ -182,7 +182,9 @@ class AddGamesDialogFragment : BaseDialogFragment() {
return super.onBack()
}
private fun showGuidePopupWindow(): PopupWindow {
private fun showGuidePopupWindow(): PopupWindow? {
if (!isAdded) return null
val guideBinding = LayoutGameCollectionAddGamesGuideBinding.inflate(layoutInflater)
val popupWindow = BugFixedPopupWindow(
guideBinding.root,

View File

@ -7,8 +7,8 @@ import android.view.View
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.core.utils.DisplayUtils
/**
* 游戏单详情
@ -30,8 +30,8 @@ class GameCollectionDetailActivity : ToolBarActivity() {
}
override fun getBusinessId(): Pair<String, String> {
val fragment = targetFragment as GameCollectionDetailFragment
return if (fragment.arguments != null) {
val fragment = targetFragment as? GameCollectionDetailFragment?
return if (fragment?.arguments != null) {
Pair(fragment.requireArguments().getString(EntranceConsts.KEY_GAME_COLLECTION_ID) ?: "", "")
} else {
super.getBusinessId()

View File

@ -16,12 +16,13 @@ import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.common.util.NewLogUtils
import com.gh.gamecenter.common.utils.PermissionHelper
import com.gh.gamecenter.R
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.setRootBackgroundDrawable
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.databinding.DialogChooseGameCollectionCoverTypeBinding
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity.Companion.REQUEST_CODE_IMAGE
import com.gh.gamecenter.qa.editor.LocalMediaActivity
import com.halo.assistant.HaloApp
class ChooseGameCollectionCoverTypeDialog : BaseDialogFragment() {
@ -42,7 +43,7 @@ class ChooseGameCollectionCoverTypeDialog : BaseDialogFragment() {
startActivityForResult(
LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.IMAGE,
ChooseType.IMAGE,
1,
"创建游戏单"
), REQUEST_CODE_IMAGE

View File

@ -285,7 +285,7 @@ class GameCollectionSquareAdapter(
}
})
addOnScrollListener(ScrollEventListener(this).apply {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
binding.bannerIndicator.onPageScrolled(

View File

@ -410,7 +410,7 @@ class GameCollectionSquareFragment : LazyListFragment<GamesCollectionEntity, Gam
}
})
addOnScrollListener(ScrollEventListener(this).apply {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
mDefaultBinding.headerContainer.bannerIndicator.onPageScrolled(

View File

@ -63,7 +63,10 @@ import com.gh.gamecenter.databinding.*
import com.gh.gamecenter.entity.GameUpdateEntity
import com.gh.gamecenter.entity.RecommendPopupEntity
import com.gh.gamecenter.eventbus.*
import com.gh.gamecenter.feature.entity.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.PluginLocation
import com.gh.gamecenter.feature.entity.SimpleGame
import com.gh.gamecenter.feature.entity.TagStyleEntity
import com.gh.gamecenter.feature.eventbus.EBConcernChanged
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.utils.ApkActiveUtils
@ -71,11 +74,7 @@ import com.gh.gamecenter.feature.utils.SentryHelper
import com.gh.gamecenter.forum.detail.ForumDetailActivity
import com.gh.gamecenter.gamedetail.cloudarchive.CloudArchiveFragment
import com.gh.gamecenter.gamedetail.desc.DescFragment
import com.gh.gamecenter.gamedetail.dialog.GameBigEventDialog
import com.gh.gamecenter.gamedetail.dialog.GameDetailMoreDialog
import com.gh.gamecenter.gamedetail.dialog.GameTagsDialog
import com.gh.gamecenter.gamedetail.dialog.SpecialDownloadDialogFragment
import com.gh.gamecenter.gamedetail.dialog.SpecialDownloadVisibilityViewModel
import com.gh.gamecenter.gamedetail.dialog.*
import com.gh.gamecenter.gamedetail.entity.ContentCardEntity
import com.gh.gamecenter.gamedetail.entity.NewGameDetailEntity
import com.gh.gamecenter.gamedetail.entity.Video
@ -149,8 +148,6 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
private var mShowConcernOnMenu = false
private var mSpecialDownloadDetailFragmentIsShowing = false
private val mFragmentsList = ArrayList<Fragment>()
private val mTabTitleList = ArrayList<String>()
private val mTabTypeList = ArrayList<String>() // tab 类型的列表,用于确定某个类型的 tab 在第几个位置
@ -167,7 +164,7 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
|| downloadEntity.status == DownloadStatus.redirected
) {
// 特殊下载弹窗
if (isSpecialDownloadDialogAvailable() && !mSpecialDownloadDetailFragmentIsShowing) {
if (isSpecialDownloadDialogAvailable(downloadEntity) && !isSpecialDownloadDetailFragmentIsShowing()) {
updateSpecialDownloadDialogIcon(true)
if (downloadEntity.status == DownloadStatus.add) {
@ -868,8 +865,10 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
showConcernIconAtBottomBarIfAvailable()
if (isSpecialDownloadDialogAvailable()
&& DownloadManager.getInstance().getDownloadEntitySnapshot(mGameEntity) != null) {
val downloadEntitySnapshot = DownloadManager.getInstance().getDownloadEntitySnapshot(mGameEntity)
if (isSpecialDownloadDialogAvailable(downloadEntitySnapshot)
&& downloadEntitySnapshot != null) {
updateSpecialDownloadDialogIcon(true)
}
@ -1180,6 +1179,11 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
}
private fun doShowAlertDialog(dialog: GameEntity.Dialog) {
SensorsBridge.trackEvent("GameDetailDialogShow",
"game_id", mGameEntity?.id ?: "",
"game_name", mGameEntity?.name ?: "",
"game_type", mGameEntity?.categoryChinese ?: ""
)
DialogHelper.showDialogWithHtmlContent(
requireContext(),
dialog.title,
@ -1187,8 +1191,33 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
dialog.confirmButton.text.toString(),
dialog.closeButtonText,
{
SensorsBridge.trackEvent("GameDetailDialogClick",
"game_id", mGameEntity?.id ?: "",
"game_name", mGameEntity?.name ?: "",
"game_type", mGameEntity?.categoryChinese ?: "",
"link_type", dialog.confirmButton.type ?: "",
"link_id", dialog.confirmButton.link ?: "",
"link_text", dialog.confirmButton.linkText ?: "",
"button_name", dialog.confirmButton.text.toString()
)
dialog.confirmButton.text = dialog.confirmButton.linkText
DirectUtils.directToLinkPage(requireContext(), dialog.confirmButton, mEntrance, "")
},
{
SensorsBridge.trackEvent("GameDetailDialogClick",
"game_id", mGameEntity?.id ?: "",
"game_name", mGameEntity?.name ?: "",
"game_type", mGameEntity?.categoryChinese ?: "",
"button_name", dialog.closeButtonText
)
},
{
SensorsBridge.trackEvent("GameDetailDialogClick",
"game_id", mGameEntity?.id ?: "",
"game_name", mGameEntity?.name ?: "",
"game_type", mGameEntity?.categoryChinese ?: "",
"button_name", "关闭弹窗"
)
}
)
}
@ -2562,12 +2591,15 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
* 2. 未获取到游戏详情数据
* 3. 当前游戏 APK 不为 1 个
* 4. 当前游戏类型不为畅玩
* 5. 当前游戏不是双下载时使用本地下载进行下载
*/
private fun isSpecialDownloadDialogAvailable(): Boolean {
private fun isSpecialDownloadDialogAvailable(downloadEntity: DownloadEntity? = null): Boolean {
if (Config.getNewApiSettingsEntity()?.install?.questionTip?.linkEntity == null) return false
if (mNewGameDetailEntity == null || mGameEntity == null) return false
if (mGameEntity?.getApk()?.size != 1) return false
if (GameUtils.shouldPerformAsVGame(mGameEntity!!)) return false
if (downloadEntity?.asVGame() == true) return false
if (downloadEntity?.isSimulatorGame() == true) return false
if (downloadEntity?.isLocalDownloadInDualDownloadMode() == true) return false
return true
}
@ -2585,7 +2617,6 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
if (fragment == null) {
if (!visibilityViewModel.specialDownloadVisibleLiveData.hasObservers()) {
visibilityViewModel.specialDownloadVisibleLiveData.observe(viewLifecycleOwner) {
mSpecialDownloadDetailFragmentIsShowing = it
updateSpecialDownloadDialogIcon(visible = !it)
}
}
@ -2601,6 +2632,10 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
}
}
private fun isSpecialDownloadDetailFragmentIsShowing(): Boolean {
return childFragmentManager.findFragmentByTag(TAG_SPECIAL_DOWNLOAD_DIALOG) != null
}
override fun scrollToTop() {
val fragment = mFragmentsList.safelyGetInRelease(mBodyBinding.gamedetailVp.currentItem)
if (fragment is IScrollable && fragment.isAdded) {

View File

@ -17,6 +17,7 @@ import com.gh.common.iinterface.ISuperiorChain
import com.gh.common.prioritychain.CustomFloatingWindowHandler
import com.gh.common.prioritychain.PriorityChain
import com.gh.common.prioritychain.PullDownPushHandler
import com.gh.common.prioritychain.VideoHandler
import com.gh.common.util.DefaultSearchHintHelper
import com.gh.common.util.DialogUtils
import com.gh.common.util.DirectUtils
@ -468,9 +469,9 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable {
binding = FragmentCustomBinding.bind(mCachedView)
buildPriorityChain()
scrollCalculatorHelper = ScrollCalculatorHelper(binding.gameList, R.id.autoVideoView, 0, false)
scrollCalculatorHelper = ScrollCalculatorHelper(binding.gameList, R.id.autoVideoView, 0)
buildPriorityChain()
adapter = CustomPageAdapter(
viewModel,
@ -512,9 +513,11 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable {
private fun buildPriorityChain() {
val floatingWindowHandler = CustomFloatingWindowHandler(23)
val videoHandler = VideoHandler(24, scrollCalculatorHelper)
priorityChain.addHandler(pullDownPushHandler)
priorityChain.addHandler(floatingWindowHandler)
priorityChain.addHandler(videoHandler)
(parentFragment as? BaseTabWrapperFragment)?.addTabGuideHandlerIfExists(priorityChain)
viewModel.floatingWindows.observe(viewLifecycleOwner, EventObserver {

View File

@ -146,7 +146,7 @@ class CustomFoldSlideLargeImageItemAdapter(
if (dataList.isEmpty()) {
0
} else {
Int.MAX_VALUE / 2 - Int.MAX_VALUE % dataList.size
Int.MAX_VALUE / 2 - (Int.MAX_VALUE / 2) % dataList.size
}
class ImageItemViewHolder(

View File

@ -140,7 +140,7 @@ class CommonContentHomeSLideListUi(
}
binding.recyclerView.addOnScrollListener(scrollEventListener.apply {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrollStateChanged(state: Int) {
super.onPageScrollStateChanged(state)

View File

@ -163,7 +163,7 @@ class CommonContentHomeSlideWithCardsUi(
}
})
binding.recyclerView.addOnScrollListener(ScrollEventListener(binding.recyclerView).apply {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
_currentDataPosition = adapter.getDataPosition(position)
}

View File

@ -101,7 +101,7 @@ class NotificationColumnViewHolder(
private val scrollEventListener by lazy(LazyThreadSafetyMode.NONE) {
ScrollEventListener(binding.rvNotification).apply {
setOnPageChangeCallback(object : OnPageChangeCallback() {
registerOnPageChangeCallback(object : OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
if (positionOffset > 0F) {
// position 代表的是正在移出屏幕的 itemView 的位置

View File

@ -132,7 +132,7 @@ class HomeSlideListViewHolder(
updateImmersiveColor(slideList[0].placeholderColor.hexStringToIntColor())
binding.recyclerView.addOnScrollListener(ScrollEventListener(binding.recyclerView).apply {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
var lastStatePosition = -1
var lastScrollState = RecyclerView.SCROLL_STATE_IDLE

View File

@ -153,7 +153,7 @@ class HomeSlideWithCardsViewHolder(
}
})
binding.recyclerView.addOnScrollListener(ScrollEventListener(binding.recyclerView).apply {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
var lastStatePosition = -1
var lastScrollState = RecyclerView.SCROLL_STATE_IDLE

View File

@ -5,7 +5,6 @@ import android.os.Handler
import android.os.Looper
import android.view.View
import android.view.ViewGroup
import androidx.core.view.forEach
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.gamecenter.common.constant.Constants
@ -15,13 +14,28 @@ import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.video.detail.CustomManager
import com.halo.assistant.HaloApp
class ScrollCalculatorHelper(val mListRv: RecyclerView, private val mPlayId: Int, private val mRangeTop: Int) {
class ScrollCalculatorHelper(
val mListRv: RecyclerView,
private val mPlayId: Int,
private val mRangeTop: Int,
private var isEnabled: Boolean = true
) {
private var mFirstVisible = -1
private var mLastVisible = 0
private var mRunnable: PlayRunnable? = null
private val mPlayHandler = Handler(Looper.getMainLooper())
var currentPlayer: AutomaticVideoView? = null
fun enableAndPlayIfValid() {
isEnabled = true
if (mListRv.isAttachedToWindow
&& mListRv.scrollState == RecyclerView.SCROLL_STATE_IDLE) {
playVideo(mListRv)
}
}
fun onScrollStateChanged(scrollState: Int) {
if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
playVideo(mListRv)
@ -80,7 +94,7 @@ class ScrollCalculatorHelper(val mListRv: RecyclerView, private val mPlayId: Int
}
private fun playVideo(view: RecyclerView?) {
if (view == null) return
if (view == null || !view.isAttachedToWindow || !isEnabled) return
val layoutManager = view.layoutManager
var gsyBaseVideoPlayer: AutomaticVideoView
for (i in mFirstVisible until mLastVisible + 1) {

View File

@ -130,6 +130,24 @@ object PackagesManager {
return null
}
/**
* 根据游戏 ID 获取已安装的信息
*
* @param gameId 游戏 Id
* @return 如果为空:未安装
*/
fun getInstalledDataByGameId(gameId: String?): GameInstall? {
if (TextUtils.isEmpty(gameId)) {
return null
}
for (gameInstall in mInstalledList) {
if (gameInstall.id == gameId) {
return gameInstall
}
}
return null
}
/**
* 判断包名是否可以更新

View File

@ -3,20 +3,12 @@ package com.gh.gamecenter.minigame
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import com.gh.gamecenter.DisplayType
import com.gh.gamecenter.R
import com.gh.gamecenter.SearchActivity
import com.gh.gamecenter.SearchType
import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.forum.search.ForumSearchDao
import com.gh.gamecenter.search.SearchDefaultFragment
import com.gh.gamecenter.search.SearchGameResultFragment
import com.lightgame.config.CommonDebug
import com.lightgame.listeners.OnBackPressedListener
/**
* 小游戏-搜索页面
@ -26,22 +18,21 @@ class MiniGameSearchActivity : SearchActivity() {
override fun provideDao(): ISearchHistoryDao = MiniGameSearchDao()
override fun updateDisplayType(type: DisplayType) {
val transaction = supportFragmentManager.beginTransaction()
when(type) {
DisplayType.DEFAULT -> {
val fragment = supportFragmentManager.findFragmentByTag(SearchDefaultFragment::class.java.name)
?: MiniGameSearchDefaultFragment().apply {
arguments = Bundle().also { it.putBoolean(SearchDefaultFragment.KEY_IS_GAME_SEARCH, true) }
}
transaction.replace(R.id.search_result, fragment, SearchDefaultFragment::class.java.name)
val transaction = when(type) {
DisplayType.DEFAULT -> showFragment(
SearchDefaultFragment::class.java.name,
{ MiniGameSearchDefaultFragment() }
) {
it.arguments = Bundle().also { bundle ->
bundle.putBoolean(SearchDefaultFragment.KEY_IS_GAME_SEARCH, true)
}
}
else -> {
val digestListFragment =
supportFragmentManager.findFragmentByTag(SearchGameResultFragment::class.java.name) as? MiniGameSearchResultFragment
?: MiniGameSearchResultFragment()
digestListFragment.setParams(mSearchKey ?: "", mSearchType.value)
transaction.replace(R.id.search_result, digestListFragment, SearchGameResultFragment::class.java.name)
else -> showFragment(
SearchGameResultFragment::class.java.name,
{ MiniGameSearchResultFragment() }
) {
it.setParams(mSearchKey ?: "", mSearchType.value)
}
}
mDisplayType = type

View File

@ -11,11 +11,9 @@ import com.gh.gamecenter.room.AppDatabase
import com.halo.assistant.HaloApp
import io.reactivex.schedulers.Schedulers
object PackageFilterManager {
class PackageFilterManager {
private var mPackageKey = "" // 用于下次获取包名-游戏信息的 key
private val mPendingPackageNameSet by lazy { hashSetOf<String>() } // 因遇到异常而等待下次操作更新的包名列表
private val mValidPackageNameSet by lazy { hashSetOf<String>() } // 已被收录的游戏包名列表
val packageKey: String
get() = mPackageKey
@ -34,7 +32,7 @@ object PackageFilterManager {
) {
if (appendOnly) {
// 添加因为异常而没能正常更新的包名列表
packageList.addAll(mPendingPackageNameSet)
packageList.addAll(PackageRepository.mPendingPackageNameSet)
}
RetrofitManager.getInstance()
@ -44,12 +42,12 @@ object PackageFilterManager {
.subscribe(object : BiResponse<PackageFilter>() {
override fun onSuccess(data: PackageFilter) {
mPackageKey = data.key
mPendingPackageNameSet.clear()
PackageRepository.mPendingPackageNameSet.clear()
val partialPackageList = arrayListOf<String>()
if (!appendOnly) {
mValidPackageNameSet.clear()
PackageRepository.mValidPackageNameSet.clear()
tryWithDefaultCatch {
AppDatabase.getInstance().packageFilterDao().deleteAllPackageName()
}
@ -63,13 +61,13 @@ object PackageFilterManager {
}
partialPackageList.add(packageName)
mValidPackageNameSet.add(packageName)
PackageRepository.mValidPackageNameSet.add(packageName)
}
if (appendOnly) {
callbackClosure?.invoke(ArrayList(partialPackageList))
} else {
callbackClosure?.invoke(ArrayList(mValidPackageNameSet))
callbackClosure?.invoke(ArrayList(PackageRepository.mValidPackageNameSet))
}
}
@ -77,7 +75,7 @@ object PackageFilterManager {
super.onFailure(exception)
if (appendOnly) {
mPendingPackageNameSet.addAll(packageList)
PackageRepository.mPendingPackageNameSet.addAll(packageList)
} else {
if (exception is retrofit2.HttpException && exception.code() == 403) {
// 403 代表 key 过期,需要重新获取
@ -91,28 +89,13 @@ object PackageFilterManager {
for (packageEntity in packageEntityList) {
// 依然为已安装状态才加入到有效包名列表中
if (PackageUtils.isInstalled(HaloApp.getInstance(), packageEntity.packageName)) {
mValidPackageNameSet.add(packageEntity.packageName)
PackageRepository.mValidPackageNameSet.add(packageEntity.packageName)
}
}
callbackClosure?.invoke(ArrayList(mValidPackageNameSet))
callbackClosure?.invoke(ArrayList(PackageRepository.mValidPackageNameSet))
}
}
})
}
/**
* 该包名是否有效 (即是否已被光环收录)
*/
fun isPackageValid(packageName: String): Boolean {
return mValidPackageNameSet.contains(packageName)
}
/**
* 是否存在因为接口异常而导致没有查询收录情况的包名列表
*/
fun hasPendingPackage(): Boolean {
return mPendingPackageNameSet.isEmpty()
}
}

View File

@ -6,7 +6,6 @@ import android.text.TextUtils
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.gh.common.filter.RegionSettingHelper
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.common.util.GameUtils
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.R
@ -16,19 +15,29 @@ import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.retrofit.ObservableUtil
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.secondOrNull
import com.gh.gamecenter.common.utils.toArrayList
import com.gh.gamecenter.common.utils.tryCatchInRelease
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.entity.AppEntity
import com.gh.gamecenter.entity.GameUpdateEntity
import com.gh.gamecenter.entity.PackageGame
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.GameInstall
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository.gameInstalled
import com.gh.gamecenter.packagehelper.PackageRepository.gameUpdate
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import io.reactivex.Single
import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import org.json.JSONException
@ -74,6 +83,10 @@ object PackageRepository {
fun changeRecentVaPlayed() {
_recentVaPlayedChanged.postValue(Unit)
}
val packageFilterManager = PackageFilterManager()
var mPendingPackageNameSet = hashSetOf<String>() // 因遇到异常而等待下次操作更新的包名列表
var mValidPackageNameSet = hashSetOf<String>() // 已被收录的游戏包名列表
/**
* 预留方法,如果想手动初始化可以调用
@ -89,18 +102,51 @@ object PackageRepository {
if (gameUpdate.isNotEmpty()) gameUpdate.clear()
if (mInstalledPkgSet.isNotEmpty()) mInstalledPkgSet.clear()
val list = PackageUtils.getAllPackageName(mApplication)
Single.zip<Result<Any?>, Result<Any?>, Result<Any?>>(
Single.create { emitter ->
val list = PackageUtils.getAllPackageName(mApplication)
uploadAppList()
initFilterPackage(list) { filteredList ->
emitter.onSuccess(Result.success(null))
uploadAppList()
mInstalledPkgSet.addAll(filteredList)
notifyInstallPkgData()
loadInstalledGameDigestAndNotifyData(packageFilterManager.packageKey, filteredList)
}
initFilterPackage(list) { filteredList ->
mIsInitialisingData = false
mInstalledPkgSet.addAll(filteredList)
notifyInstallPkgData()
},
Single.create { emitter ->
// 畅玩游戏更新
var allGames = VHelper.getAllVGameSnapshots()
if (allGames.isEmpty()) {
VHelper.refreshVGameSnapshot()
allGames = VHelper.getAllVGameSnapshots()
}
val allGamePkgNames = allGames.map { it.packageName }.toArrayList()
if (allGamePkgNames.isNotEmpty()) {
val packageFilterManager = PackageFilterManager()
updateFilterPackage(packageFilterManager, allGamePkgNames) {
emitter.onSuccess(Result.success(null))
loadInstalledGameDigestAndNotifyData(
packageKey = packageFilterManager.packageKey,
filteredList = allGamePkgNames,
isVGame = true
)
}
}
}
) { t1, t2 -> Result.success(null) }.subscribe(object : SingleObserver<Result<Any?>> {
override fun onSubscribe(d: Disposable) {
}
loadInstalledGameDigestAndNotifyData(filteredList)
}
override fun onSuccess(t: Result<Any?>) {
mIsInitialisingData = false
}
override fun onError(e: Throwable) {
}
})
loadGhzsUpdate()
}
@ -113,18 +159,15 @@ object PackageRepository {
list: MutableList<String>,
callbackClosure: ((list: ArrayList<String>) -> Unit)? = null
) {
PackageFilterManager.updateFilterPackages(list, false) {
callbackClosure?.invoke(it)
}
packageFilterManager.updateFilterPackages(list, false, callbackClosure)
}
private fun updateFilterPackage(
packageFilterManager: PackageFilterManager,
list: MutableList<String>,
callbackClosure: ((list: ArrayList<String>) -> Unit)? = null
) {
PackageFilterManager.updateFilterPackages(list, true) {
callbackClosure?.invoke(it)
}
packageFilterManager.updateFilterPackages(list, true, callbackClosure)
}
/**
@ -135,7 +178,8 @@ object PackageRepository {
PackageUtils.getGhVersionName(),
PackageUtils.getGhVersionCode(),
HaloApp.getInstance().channel,
Build.VERSION.SDK_INT)
Build.VERSION.SDK_INT
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<AppEntity>() {
@ -208,6 +252,7 @@ object PackageRepository {
*/
@SuppressLint("CheckResult")
private fun loadInstalledGameDigestAndNotifyData(
packageKey: String,
filteredList: ArrayList<String>,
isVGame: Boolean = false,
updateInstallStatus: Boolean = false
@ -222,7 +267,7 @@ object PackageRepository {
}, Any())
while (++page <= maxPageCount) {
var observable = mNewApi.getPackageGames(PackageFilterManager.packageKey, page, PAGE_SIZE)
var observable = mNewApi.getPackageGames(packageKey, page, PAGE_SIZE)
.subscribeOn(Schedulers.io())
observable.subscribe(object : BiResponse<List<PackageGame>>() {
@ -238,7 +283,8 @@ object PackageRepository {
}
for (game in validGames) {
val shouldNotifyChanges = validateGameAndPostChanges(gh_id, game, pkgName, isVGame, updateInstallStatus)
val shouldNotifyChanges =
validateGameAndPostChanges(gh_id, game, pkgName, isVGame, updateInstallStatus)
if (!isNotifyUpdate && shouldNotifyChanges) {
isNotifyUpdate = true
}
@ -259,11 +305,13 @@ object PackageRepository {
/**
* 验证游戏并且更新数据
*/
private fun validateGameAndPostChanges(ghId: Any?,
game: GameEntity,
pkgName: String,
isVGame: Boolean,
updateInstallStatus: Boolean): Boolean {
private fun validateGameAndPostChanges(
ghId: Any?,
game: GameEntity,
pkgName: String,
isVGame: Boolean,
updateInstallStatus: Boolean
): Boolean {
if (ghId == null || ghId == game.id) {
gameInstalled.add(GameInstall.transformGameInstall(game, pkgName, isVGame))
mInstalledGameList.add(game)
@ -272,7 +320,8 @@ object PackageRepository {
addCurrentlyInstalledVersionIfValid(game)
if (updateInstallStatus) {
EventBus.getDefault().post(EBPackage(EBPackage.TYPE_INSTALLED, pkgName, game.getApk().firstOrNull()?.version))
EventBus.getDefault()
.post(EBPackage(EBPackage.TYPE_INSTALLED, pkgName, game.getApk().firstOrNull()?.version))
}
if (isCanUpdate || isCanPluggable) {
@ -356,7 +405,8 @@ object PackageRepository {
) {
// 使用了镜像的游戏;插件化关闭的游戏;无需插件化
if (game.shouldUseMirrorInfo()
|| apk.plugin == "close") {
|| apk.plugin == "close"
) {
return false
}
@ -416,8 +466,11 @@ object PackageRepository {
* @param pkgName 已安装的游戏包名
* @param cachedGameEntity 缓存的游戏实体,若存在免去再请求接口的过程
*/
fun addInstalledGame(pkgName: String,
cachedGameEntity: GameEntity? = null) {
fun addInstalledGame(
packageFilterManager: PackageFilterManager,
pkgName: String,
cachedGameEntity: GameEntity? = null
) {
mInstalledPkgSet.add(pkgName)
notifyInstallPkgData()
@ -428,7 +481,8 @@ object PackageRepository {
game = cachedGameEntity,
pkgName = pkgName,
isVGame = false,
updateInstallStatus = false)
updateInstallStatus = false
)
if (containsUpdate) {
notifyGameUpdateData()
@ -436,8 +490,9 @@ object PackageRepository {
} else {
val list = arrayListOf(pkgName)
updateFilterPackage(list) {
updateFilterPackage(packageFilterManager, list) {
loadInstalledGameDigestAndNotifyData(
packageKey = packageFilterManager.packageKey,
filteredList = list,
updateInstallStatus = false
)
@ -453,9 +508,11 @@ object PackageRepository {
* @param isVGame 是否为畅玩游戏
* @param updateInstallStatus 是否更新安装状态
*/
fun addInstalledGames(pkgNameList: ArrayList<String>,
isVGame: Boolean = false,
updateInstallStatus: Boolean = false,
fun addInstalledGames(
packageFilterManager: PackageFilterManager,
pkgNameList: ArrayList<String>,
isVGame: Boolean = false,
updateInstallStatus: Boolean = false,
) {
// 畅玩游戏不添加至本地的已安装包名列表中
if (!isVGame) {
@ -465,8 +522,9 @@ object PackageRepository {
}
notifyInstallPkgData()
updateFilterPackage(pkgNameList) {
updateFilterPackage(packageFilterManager, pkgNameList) {
loadInstalledGameDigestAndNotifyData(
packageKey = packageFilterManager.packageKey,
filteredList = pkgNameList,
isVGame = isVGame,
updateInstallStatus = updateInstallStatus
@ -526,6 +584,13 @@ object PackageRepository {
changeRecentVaPlayed()
}
/**
* 是否存在因为接口异常而导致没有查询收录情况的包名列表
*/
fun hasPendingPackage(): Boolean {
return mPendingPackageNameSet.isEmpty()
}
private fun notifyGameInstallData() {
PackagesManager.initGameInstall(ArrayList(gameInstalled))
gameInstalledLiveData.postValue(ArrayList(gameInstalled))

View File

@ -63,7 +63,13 @@ class PackageViewModel(
* @param cachedGameEntity 缓存的游戏实体
*/
fun addInstalledGame(pkgName: String?, cachedGameEntity: GameEntity? = null) {
if (!TextUtils.isEmpty(pkgName)) mRepository.addInstalledGame(pkgName!!, cachedGameEntity)
if (!TextUtils.isEmpty(pkgName)) {
mRepository.addInstalledGame(
mRepository.packageFilterManager,
pkgName!!,
cachedGameEntity
)
}
}
/**
@ -81,9 +87,7 @@ class PackageViewModel(
// 未同意获取已安装应用权限时不进行数据初始化
if (!PackageHelper.isGetInstalledPackagesAgreed()) return
if (mRepository.gameInstalled.size == 0
|| PackageFilterManager.hasPendingPackage()
) {
if (mRepository.gameInstalled.size == 0 || mRepository.hasPendingPackage()) {
PackageHelper.initPackageRelatedData();
}
}

View File

@ -855,7 +855,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
}
})
addOnScrollListener(ScrollEventListener(this).apply {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
mStubBinding.bannerIndicator.onPageScrolled(

View File

@ -10,20 +10,20 @@ import android.view.View
import androidx.annotation.RequiresApi
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import com.gh.common.util.*
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.ErrorEntity
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.databinding.FragmentBackgroundPreviewBinding
import com.gh.gamecenter.feature.entity.BackgroundImageEntity
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.login.user.UserViewModel
import com.gh.gamecenter.qa.editor.LocalMediaActivity
import com.halo.assistant.HaloApp
import com.zhihu.matisse.Matisse
import io.reactivex.Single
@ -282,7 +282,7 @@ class BackgroundPreviewFragment : ToolbarFragment() {
startActivityForResult(
LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.IMAGE,
ChooseType.IMAGE,
1,
"个性背景"
), MEDIA_STORE_REQUEST

View File

@ -12,12 +12,13 @@ import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.PermissionHelper
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration
import com.gh.gamecenter.databinding.PersonalityBackgroundFragmentBinding
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.login.user.UserViewModel
import com.gh.gamecenter.qa.editor.LocalMediaActivity
import com.halo.assistant.HaloApp
import com.zhihu.matisse.Matisse
@ -75,7 +76,7 @@ class PersonalityBackgroundFragment : ToolbarFragment() {
startActivityForResult(
LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.IMAGE,
ChooseType.IMAGE,
1,
"个性背景"
), MEDIA_STORE_REQUEST

View File

@ -28,6 +28,7 @@ import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.baselist.ListFragment
import com.gh.gamecenter.common.constant.EntranceConsts.KEY_COMMENT_ID
import com.gh.gamecenter.common.eventbus.EBReuse
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.syncpage.SyncDataEntity
import com.gh.gamecenter.common.syncpage.SyncFieldConstants
import com.gh.gamecenter.common.syncpage.SyncPageRepository
@ -38,10 +39,10 @@ import com.gh.gamecenter.databinding.ItemCommentEditImageBinding
import com.gh.gamecenter.eventbus.EBCommentSuccess
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.feature.eventbus.EBDeleteComment
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.GAME_COLLECTION_ID
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.GAME_COLLECTION_TITLE
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.QUESTION_ID
import com.gh.gamecenter.qa.editor.LocalMediaActivity
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
@ -494,7 +495,7 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
val maxChooseCount = 9 - mViewModel.pictureList.size
val intent = LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.IMAGE,
ChooseType.IMAGE,
maxChooseCount,
"评论列表"
)
@ -521,11 +522,6 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
return
}
if (mShowInputOnly) {
// Fuck pm
MtaHelper.onEvent("帖子详情", "评论详情-全部回复", "发送")
}
mViewModel.postPictureAndComment(content, mCommentEntity)
}

View File

@ -1,148 +0,0 @@
package com.gh.gamecenter.qa.editor
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ImageView
import android.widget.PopupWindow
import androidx.core.content.ContextCompat
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.updateStatusBarColor
import com.halo.assistant.HaloApp
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.MimeType
import com.zhihu.matisse.internal.entity.Album
import com.zhihu.matisse.internal.entity.SelectionSpec
import com.zhihu.matisse.internal.model.AlbumCollection
/**
* 选择本地视频/图片
*/
class LocalMediaActivity : ToolBarActivity(), AlbumCollection.AlbumCallbacks {
private var mLocalMediaFragment: LocalMediaFragment? = null
private lateinit var mAlbumsSpinner: VideoAlbumsSpanner
private lateinit var mAlbumsAdapter: VideoAlbumsAdapter
private val mAlbumCollection = AlbumCollection()
private var mIsFirstAlbumLoad = true
private var mChooseType = ""
override fun getLayoutId(): Int = R.layout.activity_video_tablayout_viewpager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
mChooseType = intent.getStringExtra(EntranceConsts.KEY_TYPE) ?: ""
if (mChooseType == ChooseType.VIDEO.value) {
setNavigationTitle("本地视频")
} else {
setNavigationTitle("本地图片")
}
mLocalMediaFragment = LocalMediaFragment().apply { arguments = intent.extras }
supportFragmentManager.beginTransaction()
.replace(R.id.container, mLocalMediaFragment!!, LocalMediaFragment::class.java.name)
.commitAllowingStateLoss()
initAlbumsSpinner()
mTitleTv.setOnClickListener {
mAlbumsSpinner.show(findViewById<View>(R.id.container).height)
setPhotoNavigationTitle(true)
}
}
private fun initAlbumsSpinner() {
mAlbumsSpinner = VideoAlbumsSpanner(this)
mAlbumsAdapter = VideoAlbumsAdapter(this)
mAlbumsSpinner.setPopupAnchorView(findViewById(R.id.normal_toolbar))
mAlbumsSpinner.setAdapter(mAlbumsAdapter)
mAlbumsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
mAlbumCollection.setStateCurrentSelection(position)
mAlbumsAdapter.cursor.moveToPosition(position)
val album = Album.valueOf(mAlbumsAdapter.cursor)
if (album.isAll && SelectionSpec.getInstance().capture) {
album.addCaptureCount()
}
if (mLocalMediaFragment?.isAdded == true) {
mLocalMediaFragment?.loadVideos(album)
}
}
}
mAlbumsSpinner.setDismissListener(PopupWindow.OnDismissListener {
setPhotoNavigationTitle(false)
})
//必须加这行代码,[SelectionSpec]是单例模式下次使用必须先更新MimeType
val mimeType = if (mChooseType == ChooseType.VIDEO.value) {
MimeType.ofVideo()
} else {
MimeType.ofImage()
}
val maxChooseCount = intent.getIntExtra(EntranceConsts.KEY_CHOOSE_MAX_COUNT, 1)
Matisse.from(this).choose(mimeType).showSingleMediaType(true).maxSelectable(maxChooseCount)
mAlbumCollection.onCreate(this, this)
mAlbumCollection.loadAlbums()
}
override fun onAlbumLoad(cursor: Cursor?) {
if (mIsFirstAlbumLoad) {
mIsFirstAlbumLoad = false
mAlbumsAdapter.swapCursor(cursor)
mBaseHandler.post {
cursor?.moveToPosition(mAlbumCollection.currentSelection)
val album = Album.valueOf(cursor)
if (album.isAll && SelectionSpec.getInstance().capture) {
album.addCaptureCount()
}
if (mLocalMediaFragment?.isAdded == true) {
mLocalMediaFragment?.loadVideos(album)
}
}
}
}
override fun onAlbumReset() {
}
private fun setPhotoNavigationTitle(up: Boolean) {
val drawable = ContextCompat.getDrawable(
HaloApp.getInstance().application,
if (up) R.drawable.ic_video_arrow_up else R.drawable.ic_video_arrow_down
)
val arrowIv = findViewById<ImageView>(R.id.arrowIv)
arrowIv?.setImageDrawable(drawable)
}
override fun isAutoResetViewBackgroundEnabled(): Boolean = true
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
}
companion object {
fun getIntent(context: Context, chooseType: ChooseType, maxChooseCount: Int = 1, entrance: String): Intent {
return Intent(context, LocalMediaActivity::class.java).apply {
putExtra(EntranceConsts.KEY_TYPE, chooseType.value)
putExtra(EntranceConsts.KEY_CHOOSE_MAX_COUNT, maxChooseCount)
putExtra(EntranceConsts.KEY_ENTRANCE, entrance)
}
}
}
enum class ChooseType(val value: String) {
VIDEO("video"),
IMAGE("image")
}
}

View File

@ -1,95 +0,0 @@
package com.gh.gamecenter.qa.editor
import android.content.Context
import android.database.Cursor
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.common.util.*
import com.gh.gamecenter.R
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.databinding.LocalVideoItemBinding
import com.zhihu.matisse.internal.entity.Item
import com.zhihu.matisse.internal.ui.adapter.RecyclerViewCursorAdapter
import com.zhihu.matisse.internal.utils.PathUtils
class LocalMediaAdapter(
val context: Context,
val mChooseType: String,
val maxChooseSize: Int,
val entrance: String,
val callback: (ArrayList<Item>) -> Unit
) : RecyclerViewCursorAdapter<LocalVideoPreviewViewHolder>(null) {
private val mSelectedMediaList = arrayListOf<Item>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalVideoPreviewViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.local_video_item, parent, false)
return LocalVideoPreviewViewHolder(LocalVideoItemBinding.bind(view))
}
override fun onBindViewHolder(holder: LocalVideoPreviewViewHolder, cursor: Cursor?, position: Int) {
val item = Item.valueOf(cursor)
holder.binding.durationTv.goneIf(mChooseType == LocalMediaActivity.ChooseType.IMAGE.value)
val path = "file:///${PathUtils.getPath(context, item.contentUri)}"
ImageUtils.displayResizeMedia(holder.binding.preview, path, 200, 200)
holder.binding.durationTv.text = TimeUtils.formatVideoDuration(item.duration / 1000)
val drawable = if (mSelectedMediaList.contains(item)) {
if (maxChooseSize == 1) {
R.drawable.ic_choose_media_selected.toDrawable()
} else {
R.drawable.ic_choose_media_bg.toDrawable()
}
} else {
R.drawable.ic_choose_media_normal.toDrawable()
}
holder.binding.checkImageView.setImageDrawable(drawable)
if (mSelectedMediaList.contains(item) && maxChooseSize > 1) {
holder.binding.chooseCountTv.visibility = View.VISIBLE
holder.binding.chooseCountTv.text = (mSelectedMediaList.indexOf(item) + 1).toString()
} else {
holder.binding.chooseCountTv.visibility = View.GONE
}
holder.itemView.setOnClickListener {
if (mSelectedMediaList.contains(item)) {
mSelectedMediaList.remove(item)
notifyDataSetChanged()
callback.invoke(mSelectedMediaList)
} else {
if (maxChooseSize == 1) {
mSelectedMediaList.clear()
}
if (mSelectedMediaList.size < maxChooseSize) {
mSelectedMediaList.add(item)
notifyDataSetChanged()
callback.invoke(mSelectedMediaList)
} else {
if (mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) {
ToastUtils.showToast("至多选择${maxChooseSize}张图片")
} else {
ToastUtils.showToast("至多选择${maxChooseSize}条视频")
}
}
}
if (entrance == "发帖子" || entrance == "发提问帖" || entrance == "发视频帖") {
val publishContentType = if (entrance == "发帖子") "帖子" else if (entrance == "发提问帖") "提问帖" else "视频帖"
val publishMediaType = if (mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) "图片" else "视频"
NewLogUtils.logChooseMedia("click_radio_button", publishContentType, publishMediaType)
}
}
}
override fun getItemViewType(position: Int, cursor: Cursor?): Int {
return 0
}
fun getSelectedMediaList(): ArrayList<Item> {
return mSelectedMediaList
}
}
class LocalVideoPreviewViewHolder(val binding: LocalVideoItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)

View File

@ -1,191 +0,0 @@
package com.gh.gamecenter.qa.editor
import android.app.Activity
import android.content.Intent
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.GridLayoutManager
import com.gh.common.util.NewLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.tryWithDefaultCatch
import com.gh.gamecenter.common.view.GridSpacingItemDecoration
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.databinding.FragmentLocalMediaBinding
import com.gh.gamecenter.entity.LocalVideoEntity
import com.zhihu.matisse.internal.entity.Album
import com.zhihu.matisse.internal.entity.Item
import com.zhihu.matisse.internal.model.AlbumMediaCollection
import com.zhihu.matisse.internal.model.SelectedItemCollection
import com.zhihu.matisse.internal.ui.BasePreviewActivity
import com.zhihu.matisse.internal.ui.SelectedPreviewActivity
import com.zhihu.matisse.internal.utils.PathUtils
import com.zhihu.matisse.ui.MatisseActivity
class LocalMediaFragment : BaseFragment<Any>(), AlbumMediaCollection.AlbumMediaCallbacks {
private lateinit var mBinding: FragmentLocalMediaBinding
private lateinit var mAdapter: LocalMediaAdapter
private var mAlbumMediaCollection: AlbumMediaCollection? = null
private var mChooseType = ""
override fun getLayoutId(): Int = 0
override fun getInflatedLayout(): View {
mBinding = FragmentLocalMediaBinding.inflate(LayoutInflater.from(requireContext()), null, false)
return mBinding.root
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mChooseType = arguments?.getString(EntranceConsts.KEY_TYPE) ?: ""
// mBinding.reuseNoneData.reuseNoneDataTv.text = "暂无数据~"
mBinding.listRv.layoutManager = GridLayoutManager(requireContext(), 3)
mBinding.listRv.addItemDecoration(GridSpacingItemDecoration(3, 4F.dip2px(), false))
val maxChooseCount = arguments?.getInt(EntranceConsts.KEY_CHOOSE_MAX_COUNT, 1)
mAdapter = LocalMediaAdapter(
requireContext(), mChooseType, maxChooseCount
?: 1, mEntrance
) {
mBinding.previewTv.isEnabled = it.isNotEmpty()
mBinding.confirmTv.isEnabled = it.isNotEmpty()
if (it.isEmpty()) {
mBinding.previewTv.setTextColor(R.color.text_instance.toColor(requireContext()))
mBinding.confirmTv.alpha = 0.6f
} else {
mBinding.previewTv.setTextColor(R.color.text_secondary.toColor(requireContext()))
mBinding.confirmTv.alpha = 1f
}
mBinding.numTv.text = "(${it.size}/${mAdapter.maxChooseSize})"
}
mBinding.numTv.text = "(0/${mAdapter.maxChooseSize})"
mBinding.listRv.adapter = mAdapter
mBinding.listRefresh.isEnabled = false
val publishContentType = if (mEntrance == "发帖子") "帖子" else if (mEntrance == "发提问帖") "提问帖" else "视频帖"
val publishMediaType = if (mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) "图片" else "视频"
mBinding.previewTv.setOnClickListener {
if (mChooseType == LocalMediaActivity.ChooseType.VIDEO.value) {
val intent = PreviewVideoActivity.getIntent(requireContext(), mAdapter.getSelectedMediaList())
requireActivity().startActivityForResult(intent, PREVIEW_VIDEO)
NewLogUtils.logChooseMedia("click_preview", publishContentType, publishMediaType)
} else {
val intent = Intent(requireContext(), SelectedPreviewActivity::class.java)
val bundle = bundleOf(
SelectedItemCollection.STATE_SELECTION to mAdapter.getSelectedMediaList(),
SelectedItemCollection.STATE_COLLECTION_TYPE to SelectedItemCollection.COLLECTION_IMAGE
)
intent.putExtra(BasePreviewActivity.EXTRA_DEFAULT_BUNDLE, bundle)
startActivityForResult(intent, PREVIEW_IMAGE)
}
}
mBinding.confirmTv.setOnClickListener {
NewLogUtils.logChooseMedia("click_confirm", publishContentType, publishMediaType)
val intent = Intent()
if (mChooseType == LocalMediaActivity.ChooseType.VIDEO.value) {
val localVideoList = arrayListOf<LocalVideoEntity>()
mAdapter.getSelectedMediaList().forEach {
val path = PathUtils.getPath(requireContext(), it.contentUri)
if (path == null) {
toast("视频已不存在,请重新选择")
return@forEach
}
val id = MD5Utils.getUrlMD5(path) + System.currentTimeMillis()
val format = getFileFormat(it.mimeType)
localVideoList.add(
LocalVideoEntity(
id,
path,
contentUri = it.contentUri,
duration = it.duration,
format = format,
size = it.size
)
)
}
intent.putExtra(LocalVideoEntity::class.java.name, localVideoList)
} else {
val data = mAdapter.getSelectedMediaList().map { it.contentUri }.toList()
val path = data.map { PathUtils.getPath(requireContext(), it) }.toList()
intent.putParcelableArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION, ArrayList<Uri>(data))
intent.putStringArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION_PATH, ArrayList<String>(path))
}
requireActivity().setResult(Activity.RESULT_OK, intent)
requireActivity().finish()
}
}
private fun getFileFormat(mimeType: String?): String {
var format = ""
tryWithDefaultCatch {
if (mimeType != null) {
val split = mimeType.split("/")
format = if (split.count() >= 2) {
split[1]
} else {
mimeType
}
}
}
return format
}
override fun onAlbumMediaReset() {
mAdapter.swapCursor(null)
}
override fun onAlbumMediaLoad(cursor: Cursor?) {
mAdapter.swapCursor(cursor)
mBinding.reuseNoneData.reuseNoneData.visibility = View.GONE
mBinding.reuseLlLoading.root.visibility = View.GONE
mBinding.reuseNoConnection.root.visibility = View.GONE
mBinding.listRefresh.isRefreshing = false
}
fun loadVideos(album: Album) {
mAlbumMediaCollection?.onDestroy()
mAlbumMediaCollection = AlbumMediaCollection()
mAlbumMediaCollection?.onCreate(requireActivity(), this@LocalMediaFragment)
mAlbumMediaCollection?.load(album)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (data == null) return
if (requestCode == PREVIEW_IMAGE) {
val bundleExtra = data.getBundleExtra(BasePreviewActivity.EXTRA_RESULT_BUNDLE)
val resultApply = data.getBooleanExtra(BasePreviewActivity.EXTRA_RESULT_APPLY, false)
val items = bundleExtra?.getParcelableArrayList<Item>(SelectedItemCollection.STATE_SELECTION)
if (resultApply && !items.isNullOrEmpty()) {
mAdapter.getSelectedMediaList().clear()
mAdapter.getSelectedMediaList().addAll(items)
mAdapter.notifyDataSetChanged()
mBinding.numTv.text = "(${items.size}/${mAdapter.maxChooseSize})"
}
} else if (requestCode == PREVIEW_VIDEO) {
requireActivity().setResult(Activity.RESULT_OK, data)
requireActivity().finish()
}
}
override fun onDestroy() {
super.onDestroy()
mAlbumMediaCollection?.onDestroy()
}
companion object {
const val PREVIEW_VIDEO = 100
const val PREVIEW_IMAGE = 101
}
}

View File

@ -1,36 +0,0 @@
package com.gh.gamecenter.qa.editor
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.R
import com.zhihu.matisse.internal.entity.Item
class PreviewVideoActivity : BaseActivity() {
override fun getLayoutId(): Int {
return R.layout.activity_amway
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DisplayUtils.transparentStatusBar(this)
val containerFragment = supportFragmentManager.findFragmentByTag(PreviewVideoFragment::class.java.name)
?: PreviewVideoFragment().with(intent.extras)
// 若 placeholder 外层为 RelativeLayout 的话,会出现莫名的偏移
supportFragmentManager.beginTransaction()
.replace(R.id.placeholder, containerFragment, PreviewVideoFragment::class.java.name)
.commitAllowingStateLoss()
}
companion object {
fun getIntent(context: Context, videos: ArrayList<Item>): Intent {
val intent = Intent(context, PreviewVideoActivity::class.java)
intent.putExtra(EntranceConsts.KEY_VIDEO_LIST, videos)
return intent
}
}
}

View File

@ -1,229 +0,0 @@
package com.gh.gamecenter.qa.editor
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder
import com.facebook.drawee.generic.RoundingParams
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.gamecenter.common.base.fragment.BaseFragment
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.databinding.FragmentPreviewVideoBinding
import com.gh.gamecenter.databinding.ItemVideoSelectorBinding
import com.gh.gamecenter.entity.LocalVideoEntity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.upload.view.UploadVideoActivity
import com.lightgame.adapter.BaseRecyclerAdapter
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.zhihu.matisse.internal.entity.Item
import com.zhihu.matisse.internal.utils.PathUtils
class PreviewVideoFragment : BaseFragment<Any>() {
private lateinit var mBinding: FragmentPreviewVideoBinding
private var mVideoItems: ArrayList<Item> = arrayListOf()
private var mProcessingDialog: WaitingDialogFragment? = null
private lateinit var mVideoSelectorAdapter: VideoSelectorAdapter
private val mLocalVideoList = arrayListOf<LocalVideoEntity>()
override fun getLayoutId(): Int = 0
override fun getInflatedLayout(): View {
mBinding = FragmentPreviewVideoBinding.inflate(LayoutInflater.from(requireContext()), null, false)
return mBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mVideoItems = arguments?.getParcelableArrayList<Item>(EntranceConsts.KEY_VIDEO_LIST)
?: arrayListOf()
if (mVideoItems.isNotEmpty()) {
mVideoItems.forEach {
val path = PathUtils.getPath(requireContext(), it.contentUri)
val id = MD5Utils.getUrlMD5(path) + System.currentTimeMillis()
val format = getFileFormat(it.mimeType)
val localVideoEntity = LocalVideoEntity(
id = id,
filePath = path,
contentUri = it.contentUri,
duration = it.duration,
format = format,
size = it.size
)
mLocalVideoList.add(localVideoEntity)
}
initVideo(mLocalVideoList[0])
}
mBinding.numTv.text = "(1/${mVideoItems.size})"
mVideoSelectorAdapter = VideoSelectorAdapter(requireContext(), mLocalVideoList) { entity, position ->
mBinding.videoView.release()
mBinding.numTv.text = "(${position + 1}/${mVideoItems.size})"
initVideo(entity)
}
mBinding.videoSelectorRv.adapter = mVideoSelectorAdapter
mBinding.videoSelectorRv.layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
mBinding.videoSelectorRv.addItemDecoration(
GridSpacingItemColorDecoration(
requireContext(),
4,
0,
R.color.transparent
)
)
mBinding.confirmTv.setOnClickListener {
val intent = Intent()
intent.putExtra(LocalVideoEntity::class.java.name, mLocalVideoList)
requireActivity().setResult(Activity.RESULT_OK, intent)
requireActivity().finish()
}
mBinding.changeCoverTv.setOnClickListener {
val item = mVideoItems[mVideoSelectorAdapter.selectPosition]
val intent =
PosterEditActivity.getIntentByPath(requireContext(), PathUtils.getPath(requireContext(), item.uri))
startActivityForResult(intent, UploadVideoActivity.REQUEST_CODE_IMAGE_CROP)
}
mBinding.backBtn.setOnClickListener { requireActivity().finish() }
}
private fun getFileFormat(mimeType: String?): String {
var format = ""
tryWithDefaultCatch {
if (mimeType != null) {
val split = mimeType.split("/")
format = if (split.count() >= 2) {
split[1]
} else {
mimeType
}
}
}
return format
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (data == null || resultCode != Activity.RESULT_OK) return
if (requestCode == UploadVideoActivity.REQUEST_CODE_IMAGE_CROP) {
val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) ?: ""
if (imagePath.isNotEmpty()) {
uploadImage(imagePath)
}
}
}
private fun uploadImage(imagePath: String) {
mProcessingDialog = WaitingDialogFragment.newInstance("图片上传中...", false)
mProcessingDialog?.show(requireActivity().supportFragmentManager, WaitingDialogFragment::class.java.name)
UploadImageUtils.uploadImage(
UploadImageUtils.UploadType.poster,
imagePath,
object : UploadImageUtils.OnUploadImageListener {
override fun onSuccess(imageUrl: String) {
mProcessingDialog?.dismiss()
mLocalVideoList[mVideoSelectorAdapter.selectPosition].poster = imageUrl
mBinding.videoView.updateThumb(imageUrl)
}
override fun onError(e: Throwable?) {
mProcessingDialog?.dismiss()
ToastUtils.showToast("上传失败")
}
override fun onProgress(total: Long, progress: Long) {}
})
}
private fun initVideo(entity: LocalVideoEntity) {
GSYVideoOptionBuilder()
.setIsTouchWiget(false)
.setUrl(entity.filePath)
.setRotateViewAuto(false)
.setCacheWithPlay(false)
.setRotateWithSystem(false)
.setReleaseWhenLossAudio(true)
.setLooping(false)
.setShowFullAnimation(false)
.build(mBinding.videoView)
if (entity.poster.isNotEmpty()) {
mBinding.videoView.updateThumb(entity.poster)
} else {
mBinding.videoView.updateThumb("file:///${PathUtils.getPath(requireContext(), entity.contentUri)}")
}
}
class VideoSelectorAdapter(
context: Context,
val localVideoList: ArrayList<LocalVideoEntity>,
val callback: (LocalVideoEntity, Int) -> Unit
) : BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
var selectPosition: Int = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return VideoSelectorViewHolder(
ItemVideoSelectorBinding.inflate(
LayoutInflater.from(mContext),
parent,
false
)
)
}
override fun getItemCount(): Int = localVideoList.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is VideoSelectorViewHolder) {
val item = localVideoList[position]
holder.binding.previewBorder.goneIf(selectPosition != position)
setPreviewBorder(holder.binding.preview, selectPosition == position)
ImageUtils.display(holder.binding.preview, "file:///${PathUtils.getPath(mContext, item.contentUri)}")
holder.binding.root.setOnClickListener {
selectPosition = position
callback.invoke(localVideoList[selectPosition], selectPosition)
notifyDataSetChanged()
}
}
}
private fun setPreviewBorder(view: SimpleDraweeView, isSelected: Boolean) {
val params = RoundingParams()
params.setBorder(
if (isSelected) R.color.black.toColor() else R.color.transparent.toColor(),
if (isSelected) 1f.dip2px().toFloat() else 0f
)
params.setCornersRadius(4f.dip2px().toFloat())
val build = GenericDraweeHierarchyBuilder.newInstance(mContext.resources)
.setRoundingParams(params)
.build()
view.hierarchy = build
}
}
class VideoSelectorViewHolder(val binding: ItemVideoSelectorBinding) : RecyclerView.ViewHolder(binding.root)
override fun onResume() {
super.onResume()
GSYVideoManager.onResume()
}
override fun onPause() {
super.onPause()
GSYVideoManager.onPause()
}
override fun onDestroy() {
super.onDestroy()
GSYVideoManager.releaseAllVideos()
}
}

View File

@ -24,8 +24,10 @@ import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.entity.LocalVideoEntity
import com.gh.gamecenter.common.entity.NotificationUgc
import com.gh.gamecenter.common.mvvm.Status
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnUiThread
@ -34,6 +36,7 @@ import com.gh.gamecenter.databinding.FragmentVideoPublishBinding
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.qa.dialog.ChooseActivityDialogFragment
@ -41,7 +44,6 @@ import com.gh.gamecenter.qa.dialog.ChooseForumActivity
import com.gh.gamecenter.qa.dialog.ChooseSectionDialogFragment
import com.gh.gamecenter.qa.dialog.InputUrlDialogFragment
import com.gh.gamecenter.qa.editor.GameActivity
import com.gh.gamecenter.qa.editor.LocalMediaActivity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.upload.OnUploadListener
import com.gh.gamecenter.video.upload.UploadManager
@ -117,7 +119,7 @@ class VideoPublishFragment : ToolbarFragment(), KeyboardHeightObserver {
startActivityForResult(
LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.VIDEO,
ChooseType.VIDEO,
1,
"发视频帖"
), BaseRichEditorActivity.INSERT_MEDIA_VIDEO_CODE
@ -285,7 +287,7 @@ class VideoPublishFragment : ToolbarFragment(), KeyboardHeightObserver {
startActivityForResult(
LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.VIDEO,
ChooseType.VIDEO,
1,
"发视频帖"
), BaseRichEditorActivity.INSERT_MEDIA_VIDEO_CODE

View File

@ -29,6 +29,7 @@ import com.gh.gamecenter.entity.DeviceDialogEntity;
import com.gh.gamecenter.entity.DialogEntity;
import com.gh.gamecenter.entity.DiscoveryGameCardEntity;
import com.gh.gamecenter.entity.DiscoveryGameCardLabel;
import com.gh.gamecenter.entity.DiverterEntity;
import com.gh.gamecenter.entity.FollowCommonContentCollection;
import com.gh.gamecenter.entity.FollowDynamicEntity;
import com.gh.gamecenter.entity.FollowUserEntity;
@ -107,6 +108,7 @@ import com.gh.gamecenter.feature.entity.BackgroundImageEntity;
import com.gh.gamecenter.feature.entity.CommentEntity;
import com.gh.gamecenter.feature.entity.CommentnumEntity;
import com.gh.gamecenter.feature.entity.ConcernEntity;
import com.gh.gamecenter.feature.entity.FloatingWindowEntity;
import com.gh.gamecenter.feature.entity.ForumVideoEntity;
import com.gh.gamecenter.feature.entity.GameEntity;
import com.gh.gamecenter.feature.entity.LibaoEntity;
@ -126,7 +128,6 @@ import com.gh.gamecenter.feature.entity.SimulatorEntity;
import com.gh.gamecenter.feature.entity.UserEntity;
import com.gh.gamecenter.feature.entity.ViewsEntity;
import com.gh.gamecenter.feature.entity.WXSubscribeMsgConfig;
import com.gh.gamecenter.feature.entity.FloatingWindowEntity;
import com.gh.gamecenter.gamedetail.entity.BigEvent;
import com.gh.gamecenter.gamedetail.entity.NewGameDetailEntity;
import com.gh.gamecenter.home.custom.model.CustomPageData;
@ -3358,7 +3359,7 @@ public interface ApiService {
* 页面数据聚合[底部tab+多tab导航页+默认数据]
*/
@GET("app/data_union")
Single<DataUnionEntity> getDataUnion();
Single<DataUnionEntity> getDataUnion(@Query("diverter") String diverter);
/**
* 底部tab
@ -3390,4 +3391,16 @@ public interface ApiService {
*/
@GET("game_lists/hot_columns")
Single<List<SubjectEntity>> getHotColumns();
/**
* 分流器列表信息
*/
@GET("app/{module}/diverter")
Single<List<DiverterEntity>> getDiverterList(@Header("Install-First-Access") String isInstallFirstAccess, @Path("module") String module);
/**
* 访问分流页面,更新分流访问次数
*/
@PATCH("app/{module}/diverter_visit_time")
Single<ResponseBody> patchDiverterVisitTime(@Path("module") String module, @Body RequestBody body);
}

View File

@ -1,9 +1,6 @@
package com.gh.gamecenter.search
import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Shader
import android.graphics.Typeface
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.text.TextUtils
@ -18,7 +15,6 @@ import androidx.viewpager.widget.PagerAdapter
import com.gh.common.constant.Config
import com.gh.common.exposure.ExposureManager
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseFragment
@ -34,7 +30,7 @@ import com.gh.gamecenter.databinding.TabItemSearchDefaultRankBinding
import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.db.SearchHistoryDao
import com.gh.gamecenter.eventbus.EBSearch
import com.gh.gamecenter.feature.entity.HotTagEntity
import com.gh.gamecenter.feature.entity.DiscoveryTagEntity
import com.gh.gamecenter.feature.entity.SettingsEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.google.android.flexbox.FlexboxLayout
@ -46,7 +42,7 @@ import org.json.JSONObject
open class SearchDefaultFragment : BaseFragment<Any>() {
private var mHotTagList: List<HotTagEntity>? = null
private var mSearchDiscoveryTagList: List<DiscoveryTagEntity>? = null
protected var mRankList: List<SettingsEntity.Search.RankList>? = null
protected lateinit var mBinding: FragmentSearchDefaultBinding
@ -63,28 +59,28 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
// FlexboxLayout:maxLine 不符合需求
protected val mFlexMaxHeight = DisplayUtils.dip2px(57F)
private val mHotTagClickListener: (Int) -> Unit = {
val tag = mHotTagList!![it]
NewFlatLogUtils.logSearchHotTagClick(
tag.name ?: "",
tag.type ?: "",
tag.link ?: "",
tag.text ?: ""
)
DataLogUtils.uploadHotTagLog(context, tag.name)
PageSwitchDataHelper.pushCurrentPageData(
hashMapOf(
Pair(PageSwitchDataHelper.PAGE_BUSINESS_TYPE, "游戏搜索-热门标签"),
Pair(PageSwitchDataHelper.PAGE_BUSINESS_ID, tag.id ?: ""),
Pair(PageSwitchDataHelper.PAGE_BUSINESS_NAME, tag.name ?: " ")
private val mSearchDiscoveryTagClickListener: (Int) -> Unit = {
val tag = mSearchDiscoveryTagList!![it]
val keyword = tag.keyword
if (keyword.isNotEmpty()) {
PageSwitchDataHelper.pushCurrentPageData(
hashMapOf(
Pair(PageSwitchDataHelper.PAGE_BUSINESS_TYPE, "游戏搜索-搜索发现"),
Pair(PageSwitchDataHelper.PAGE_BUSINESS_ID, tag.id ?: ""),
Pair(PageSwitchDataHelper.PAGE_BUSINESS_NAME, tag.text ?: " ")
)
)
)
SensorsBridge.trackEvent("SearchLabelClick", "label_name", tag.name ?: "", "label_id", tag.id ?: "")
val exposureEvent = ExposureEvent.createEvent(
null,
source = listOf(ExposureSource("首页搜索", ""), ExposureSource("热门标签", tag.name ?: ""))
)
DirectUtils.directToLinkPage(requireContext(), tag, "(搜索-${tag.name})", "", exposureEvent) // 不需要path
SensorsBridge.trackEvent("SearchLabelClick", "label_name", tag.text ?: "", "label_id", tag.id ?: "")
SensorsBridge.trackSearchDiscoveryClick(
labelName = tag.text ?: "",
labelId = tag.id ?: "",
searchContent = keyword,
position = it
)
mViewModel?.add(keyword)
EventBus.getDefault().post(EBSearch("history", keyword))
Util_System_Keyboard.hideSoftKeyboardByIBinder(context, mBinding.historyFlex.windowToken)
}
}
override fun getLayoutId(): Int {
@ -137,16 +133,16 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
}
mViewModel?.isExistRankList = mRankList?.isNotEmpty() == true
mHotTagList = Config.getSettings()?.search?.hotTag
mViewModel?.isExistHotTag = mHotTagList?.isNotEmpty() == true
mSearchDiscoveryTagList = Config.getSettings()?.search?.discoveryTag
mViewModel?.isExistSearchDiscoveryTag = mSearchDiscoveryTagList?.isNotEmpty() == true
updateHistorySearchView(null)
mViewModel?.historySearchLiveData?.observe(viewLifecycleOwner) {
updateHistorySearchView(it)
}
mBinding.hotTagFlexContainer.setLimitHeight(mFlexMaxHeight)
createFlexContent(mBinding.hotTagFlex, getTagListString(), true, clickListener = mHotTagClickListener)
mBinding.searchDiscoveryTagFlexContainer.setLimitHeight(mFlexMaxHeight)
createFlexContent(mBinding.searchDiscoveryTagFlex, getTagListString(), clickListener = mSearchDiscoveryTagClickListener)
initHeadView()
initRankViewPager()
}
@ -175,12 +171,12 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
})
}
}
mBinding.hotHeadContainer.headTitle.text = getString(R.string.search_hot)
mBinding.hotHeadContainer.headTitle.textSize = 16F
mBinding.hotHeadContainer.headActionTv.visibility = View.GONE
mBinding.hotTagHeadContainer.headTitle.text = getString(R.string.search_hot_tag)
mBinding.hotTagHeadContainer.headTitle.textSize = 16F
mBinding.hotTagHeadContainer.headActionTv.visibility = View.GONE
mBinding.searchDiscoveryHeadContainer.headTitle.text = getString(R.string.search_hot)
mBinding.searchDiscoveryHeadContainer.headTitle.textSize = 16F
mBinding.searchDiscoveryHeadContainer.headActionTv.visibility = View.GONE
mBinding.searchDiscoveryTagHeadContainer.headTitle.text = getString(R.string.search_discovery_tag)
mBinding.searchDiscoveryTagHeadContainer.headTitle.textSize = 16F
mBinding.searchDiscoveryTagHeadContainer.headActionTv.visibility = View.GONE
}
protected open fun initRankViewPager() {
@ -315,8 +311,8 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
mBinding.historyHeadContainer.root.visibility =
if (mViewModel?.isExistHistory == true) View.VISIBLE else View.GONE
mBinding.historyFlex.visibility = if (mViewModel?.isExistHistory == true) View.VISIBLE else View.GONE
mBinding.hotTagHeadContainer.root.layoutParams =
(mBinding.hotTagHeadContainer.root.layoutParams as ConstraintLayout.LayoutParams).apply {
mBinding.searchDiscoveryTagHeadContainer.root.layoutParams =
(mBinding.searchDiscoveryTagHeadContainer.root.layoutParams as ConstraintLayout.LayoutParams).apply {
setMargins(
0,
if (mViewModel?.isExistHistory == true) 16F.dip2px() else 0,
@ -324,21 +320,21 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
0
)
}
mBinding.hotHeadContainer.root.layoutParams =
(mBinding.hotHeadContainer.root.layoutParams as ConstraintLayout.LayoutParams).apply {
mBinding.searchDiscoveryHeadContainer.root.layoutParams =
(mBinding.searchDiscoveryHeadContainer.root.layoutParams as ConstraintLayout.LayoutParams).apply {
setMargins(
0,
if (mViewModel?.isExistHistory == false && mViewModel?.isExistHotTag == false) 16F.dip2px() else 0,
if (mViewModel?.isExistHistory == false && mViewModel?.isExistSearchDiscoveryTag == false) 16F.dip2px() else 0,
0,
0
)
}
mBinding.hotTagHeadContainer.root.visibility =
if (mViewModel?.isExistHotTag == true) View.VISIBLE else View.GONE
mBinding.hotTagFlex.visibility = if (mViewModel?.isExistHotTag == true) View.VISIBLE else View.GONE
mBinding.hotHeadContainer.root.visibility =
mBinding.searchDiscoveryTagHeadContainer.root.visibility =
if (mViewModel?.isExistSearchDiscoveryTag == true) View.VISIBLE else View.GONE
mBinding.searchDiscoveryTagFlex.visibility = if (mViewModel?.isExistSearchDiscoveryTag == true) View.VISIBLE else View.GONE
mBinding.searchDiscoveryHeadContainer.root.visibility =
if (mViewModel?.isExistHotSearch == true) View.VISIBLE else View.GONE
mBinding.hotList.visibility = if (mViewModel?.isExistHotSearch == true) View.VISIBLE else View.GONE
mBinding.searchDiscoveryList.visibility = if (mViewModel?.isExistHotSearch == true) View.VISIBLE else View.GONE
mBinding.rankTabLayout.visibility = if (mViewModel?.isExistRankList == true) View.VISIBLE else View.GONE
mBinding.rankTabIndicator.visibility = if (mViewModel?.isExistRankList == true) View.VISIBLE else View.GONE
mBinding.rankViewPager.visibility = if (mViewModel?.isExistRankList == true) View.VISIBLE else View.GONE
@ -360,9 +356,9 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
private fun getTagListString(): List<String> {
val list = ArrayList<String>()
if (mHotTagList != null) {
for (entity in mHotTagList!!) {
entity.name?.let { list.add(it) }
if (mSearchDiscoveryTagList != null) {
for (entity in mSearchDiscoveryTagList!!) {
entity.text?.let { list.add(it) }
}
}
return list
@ -371,7 +367,6 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
fun createFlexContent(
flexView: FlexboxLayout,
contentList: List<String>?,
isHotTag: Boolean = false,
clickListener: (Int) -> Unit
) {
@ -385,16 +380,12 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
val params = FlexboxLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 24F.dip2px())
flexCell.layoutParams = params
if (isHotTag && !mHotTagList.isNullOrEmpty() && mHotTagList!![index].isGuessSearch) {
createSmartHotTagStyle(flexCell, contentList[index])
} else {
flexCell.setSingleLine()
flexCell.ellipsize = TextUtils.TruncateAt.END
flexCell.gravity = Gravity.CENTER
flexCell.textSize = 12F
flexCell.text = StringUtils.shrinkStringWithDot(contentList[index], 6)
flexCell.setTextColor(R.color.text_secondary.toColor(requireContext()))
}
flexCell.setSingleLine()
flexCell.ellipsize = TextUtils.TruncateAt.END
flexCell.gravity = Gravity.CENTER
flexCell.textSize = 12F
flexCell.text = StringUtils.shrinkStringWithDot(contentList[index], 6)
flexCell.setTextColor(R.color.text_secondary.toColor(requireContext()))
flexCell.setPadding(8F.dip2px(), 0, 8F.dip2px(), 0)
flexCell.background = if (mIsDarkModeOn) GradientDrawable().apply {
setStroke(0.5F.dip2px(), Color.parseColor("#21FFFFFF"))
@ -406,30 +397,6 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
}
}
private fun createSmartHotTagStyle(flexCell: TextView, name: String) {
flexCell.setDrawableStart(R.drawable.ic_smart_search)
flexCell.compoundDrawablePadding = 4F.dip2px()
flexCell.gravity = Gravity.CENTER_VERTICAL
flexCell.typeface = Typeface.DEFAULT_BOLD
flexCell.textSize = 12F
flexCell.text = StringUtils.shrinkStringWithDot(name, 6)
flexCell.setTextColor(Color.WHITE)
val colors =
intArrayOf(R.color.text_FFB749.toColor(requireContext()), R.color.text_FF6D3C.toColor(requireContext()))
val position = floatArrayOf(0F, 1F)
val linearGradient = LinearGradient(
0F,
0F,
flexCell.paint.textSize * flexCell.text.length,
0F,
colors,
position,
Shader.TileMode.CLAMP
)
flexCell.paint.shader = linearGradient
flexCell.invalidate()
}
private fun postExposureEvent(index: Int) {
mRankList?.safelyGetInRelease(index)?.content?.forEach {
if (it.link.type == "game") {
@ -444,17 +411,24 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
super.onDarkModeChanged()
initHeadView()
mBinding.rootContainer.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
mBinding.hotList.adapter?.run {
mBinding.searchDiscoveryList.adapter?.run {
notifyItemRangeChanged(0, itemCount)
}
mViewModel?.historySearchLiveData?.value?.let { updateHistorySearchView(it) }
createFlexContent(mBinding.hotTagFlex, getTagListString(), true, clickListener = mHotTagClickListener)
createFlexContent(mBinding.searchDiscoveryTagFlex, getTagListString(), clickListener = mSearchDiscoveryTagClickListener)
}
protected open fun provideDao(): ISearchHistoryDao = SearchHistoryDao(requireContext().applicationContext)
protected open fun provideAdapter(pageRatio: Float): PagerAdapter =
SearchDefaultRankListAdapter(requireContext(), mRankList!!, pageRatio, mIsGameSearch, mSourceEntrance)
SearchDefaultRankListAdapter(
requireContext(),
mViewModel!!,
mRankList!!,
pageRatio,
mIsGameSearch,
mSourceEntrance
)
private fun provideViewModel(): SearchDefaultViewModel {
val factory = SearchDefaultViewModel.Factory(provideDao())

View File

@ -6,23 +6,27 @@ import android.view.ViewGroup
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.SearchType
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.databinding.SearchDefaultRankItemBinding
import com.gh.gamecenter.eventbus.EBSearch
import com.gh.gamecenter.feature.entity.SettingsEntity
import com.lightgame.adapter.BaseRecyclerAdapter
import com.lightgame.utils.Util_System_Keyboard
import org.greenrobot.eventbus.EventBus
import org.json.JSONException
import org.json.JSONObject
class SearchDefaultRankAdapter(
context: Context,
private val mViewModel: SearchDefaultViewModel,
private val mRankList: SettingsEntity.Search.RankList,
private val mIsGameSearch: Boolean,
private val mIsGameSearch: Boolean
) : BaseRecyclerAdapter<SearchDefaultRankAdapter.SearchDefaultRankItemViewHolder>(context) {
override fun getItemCount() = minOf(mRankList.content.size, 10)
override fun getItemCount() = minOf(mRankList.content.size, 20)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
SearchDefaultRankItemViewHolder(parent.toBinding())
@ -34,7 +38,8 @@ class SearchDefaultRankAdapter(
icon.goneIf(!mRankList.isShowIcon) {
icon.display(if (rank.link.type == "game") gameEntity?.icon else rank.icon)
}
name.text = if (rank.link.type == "game") gameEntity?.name else rank.name.ifBlank { rank.link.text }
val labelName = if (rank.link.type == "game") gameEntity?.name else rank.name.ifBlank { rank.link.text }
name.text = labelName
index.run {
typeface = Typeface.createFromAsset(mContext.assets, Constants.DIN_FONT_PATH)
text = (position + 1).toString()
@ -60,18 +65,31 @@ class SearchDefaultRankAdapter(
root.setOnClickListener {
val linkEntity = rank.link
DirectUtils.directToLinkPage(
mContext,
linkEntity,
"游戏搜索-搜索榜单",
"${mRankList.title}-${rank.name}",
rank.exposureEvent
)
if (linkEntity.type == "game_search") {
mViewModel.add(linkEntity.text ?: "")
EventBus.getDefault().post(EBSearch(SearchType.RANK.value, linkEntity.text))
} else {
DirectUtils.directToLinkPage(
mContext,
linkEntity,
"游戏搜索-搜索榜单",
"${mRankList.title}-${rank.name}",
rank.exposureEvent
)
}
Util_System_Keyboard.hideSoftKeyboardByIBinder(mContext, it.windowToken)
// 是否来源于游戏搜索
if (mIsGameSearch) {
val searchType = if (linkEntity.type == "game_search") {
SearchType.RANK.toChinese()
} else {
""
}
NewFlatLogUtils.logSearchClickRankDetail(
linkEntity.link ?: "",
searchType,
rank.name,
(position + 1).toString(),
linkEntity.link ?: "",
@ -89,6 +107,8 @@ class SearchDefaultRankAdapter(
val trackEvent = JSONObject()
try {
trackEvent.put("text", labelName)
trackEvent.put("search_content", linkEntity.link)
trackEvent.put("game_name", gameEntity?.name)
trackEvent.put("game_id", gameEntity?.id)
trackEvent.put("list_name", mRankList.title)

View File

@ -11,6 +11,7 @@ import com.gh.gamecenter.feature.entity.SettingsEntity
class SearchDefaultRankListAdapter(
private val mContext: Context,
private val mViewModel: SearchDefaultViewModel,
private val mRankList: List<SettingsEntity.Search.RankList>,
private val mPageWidth: Float,
private val mIsGameSearch: Boolean,
@ -31,7 +32,7 @@ class SearchDefaultRankListAdapter(
view!!.findViewById<RecyclerView>(R.id.rankContainer).run {
layoutManager = LinearLayoutManager(mContext)
adapter = SearchDefaultRankAdapter(mContext, mRankList[position], mIsGameSearch)
adapter = SearchDefaultRankAdapter(mContext, mViewModel, mRankList[position], mIsGameSearch)
}
return view
}

View File

@ -10,7 +10,7 @@ class SearchDefaultViewModel(private val dao: ISearchHistoryDao) : ViewModel() {
val historySearchLiveData = MutableLiveData<List<String>>()
var isExistHotSearch: Boolean = false
var isExistHotTag: Boolean = false
var isExistSearchDiscoveryTag: Boolean = false
var isExistHistory: Boolean = false
var isExistRankList: Boolean = false

View File

@ -76,7 +76,7 @@ class SearchSubjectItemViewHolder(var binding: SearchSubjectItemBinding) : Recyc
val exposureSources = arrayListOf<ExposureSource>().apply {
add(ExposureSource("首页搜索"))
add(ExposureSource(type, key))
add(ExposureSource("专题", entity.name))
add(ExposureSource("专题", "${entity.name}+${entity.columnId}"))
}
val exposureEvent = ExposureEvent.createEvent(game, exposureSources)
exposureList.add(exposureEvent)

View File

@ -68,6 +68,12 @@ class SearchGameResultAdapter(
val positionAndPackageMap = HashMap<String, Int>()
private val adIdSet = hashSetOf<String>() // 记录展示过的广告id
fun clearAdIdSet() {
adIdSet.clear()
}
override fun setListData(updateData: MutableList<SearchItemData>?) {
exposureEventArray = SparseArray(updateData?.size ?: 0)
// 记录游戏位置
@ -216,6 +222,7 @@ class SearchGameResultAdapter(
is SearchGameAdItemViewHolder -> {
val adEntity = mEntityList[position].ad
val adConfig = mEntityList[position].adConfig
val slotId = adEntity?.slotId ?: ""
val adContainer = holder.binding.adContainer
val screenWidthInDp = DisplayUtils.getScreenWidthInDp(fragment.activity)
@ -224,7 +231,36 @@ class SearchGameResultAdapter(
// 广告 slotId 没有变,不管它
} else {
adContainer.tag = slotId
AdDelegateHelper.requestThirdPartyFlowAd(fragment, slotId, adContainer, screenWidthInDp) {
val onAdShowAction: () -> Unit = {
if (!adIdSet.contains(adConfig?.id)) {
SensorsBridge.trackEvent("ThirdPartyAdShow",
json {
"ad_source" to adEntity?.sourceName
"ad_id" to slotId
"ad_format" to adConfig?.typeChinese
"ad_placement" to "搜索结果"
"ad_space_id" to adConfig?.id
"ad_space_name" to adConfig?.name
"position" to adConfig?.position
}
)
}
adIdSet.add(adConfig?.id ?: "")
}
val onAdClickAction = {
SensorsBridge.trackEvent("ThirdPartyAdClick",
json {
"ad_source" to adEntity?.sourceName
"ad_id" to slotId
"ad_format" to adConfig?.typeChinese
"ad_placement" to "搜索结果"
"ad_space_id" to adConfig?.id
"ad_space_name" to adConfig?.name
"position" to adConfig?.position
}
)
}
AdDelegateHelper.requestThirdPartyFlowAd(fragment, slotId, adContainer, screenWidthInDp, onAdShowAction, onAdClickAction) {
}
}

View File

@ -354,6 +354,7 @@ open class SearchGameResultFragment : ListFragment<GameEntity, SearchGameResultV
this.mKey = key
this.mType = type
mAdapter?.key = key
mAdapter?.clearAdIdSet()
mListViewModel?.updateSearchKeyWithType(key, type)
mListViewModel?.clearSearchSubjects()
mListViewModel?.load(LoadType.REFRESH)

View File

@ -188,7 +188,7 @@ class SearchGameResultViewModel(
list: List<GameEntity>
) {
thirdPartyAdList.forEach {
decoratedItemDataList.add(it.position - 1, SearchItemData(ad = it.thirdPartyAd))
decoratedItemDataList.add(it.position - 1, SearchItemData(ad = it.thirdPartyAd, adConfig = it))
SPUtils.setLong(Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + it.position, System.currentTimeMillis())
}
postResultList(decoratedItemDataList, list)
@ -209,7 +209,7 @@ class SearchGameResultViewModel(
if ((showThirdPartyAd && adConfig?.thirdPartyAd != null)
|| (showOwnerAd && adConfig?.ownerAd == null && adConfig?.thirdPartyAd != null && showOnFailed)
) {
decoratedItemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd))
decoratedItemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd, adConfig = adConfig))
SPUtils.setLong(
Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + adConfig.position,
System.currentTimeMillis()
@ -254,7 +254,7 @@ class SearchGameResultViewModel(
)
} else if (showOnFailed && adConfig.thirdPartyAd != null) {
// 自有广告为空时,显示第三方广告
decoratedItemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd))
decoratedItemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd, adConfig = adConfig))
SPUtils.setLong(
Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + adConfig.position,
System.currentTimeMillis()

View File

@ -9,20 +9,18 @@ import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.core.view.isVisible
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.LazyFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.BugFixedPopupWindow
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.doOnEnd
import com.gh.gamecenter.core.utils.doOnStart
import com.gh.gamecenter.databinding.FragmentGameServerTestV2Binding
import com.gh.gamecenter.databinding.LayoutGameServerTestV2SettingBinding
import com.gh.gamecenter.databinding.LayoutGameServerTestV2SettingGuideBinding
import com.gh.gamecenter.feature.entity.PageLocation
import com.gh.gamecenter.mygame.MyGameActivity
@ -70,7 +68,7 @@ class GameServerTestV2Fragment : LazyFragment() {
if (SPUtils.getBoolean(Constants.SP_SHOW_GAME_SERVER_TEST_V2_SETTING_GUIDE, true)) {
mBaseHandler.post {
showSettingView {
showGuidePopupWindow()
showGuide()
}
}
SPUtils.setBoolean(Constants.SP_SHOW_GAME_SERVER_TEST_V2_SETTING_GUIDE, false)
@ -293,6 +291,19 @@ class GameServerTestV2Fragment : LazyFragment() {
}.start()
}
private fun showGuide() {
mBinding?.settingGuideContainer?.setOnClickListener {
dismissGuide()
}
mBinding?.settingGuideContainer?.isVisible = true
}
private fun dismissGuide() {
mBinding?.settingGuideIv?.animate()?.alpha(0F)?.setDuration(200L)?.doOnEnd {
mBinding?.settingGuideContainer?.isVisible = false
}?.start()
}
private fun getItemTextView(type: String): TextView {
return TextView(requireContext()).apply {
text = type
@ -332,22 +343,11 @@ class GameServerTestV2Fragment : LazyFragment() {
.commitAllowingStateLoss()
}
private fun showGuidePopupWindow(): PopupWindow {
val guideBinding = LayoutGameServerTestV2SettingGuideBinding.inflate(layoutInflater)
return BugFixedPopupWindow(
guideBinding.root,
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
isFocusable = true
isTouchable = true
isOutsideTouchable = true
animationStyle = R.style.popup_window_ease_in_and_out_anim_style
showAsDropDown(mBinding?.optionIv, 0, (-4F).dip2px())
}
}
override fun onBackPressed(): Boolean {
if (mBinding?.settingGuideContainer?.isVisible == true) {
dismissGuide()
return true
}
if (mSettingBinding != null) {
dismissSettingView()
return true

View File

@ -16,11 +16,12 @@ import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.databinding.PieceBottomTabBinding
import com.gh.gamecenter.entity.BottomTab
import com.gh.gamecenter.feature.utils.SentryHelper
/**
* 底部Tab 基类
*/
abstract class BaseBottomTabFragment<T: ViewBinding>: ToolbarFragment() {
abstract class BaseBottomTabFragment<T : ViewBinding> : ToolbarFragment() {
protected var mViewPager: ViewPager2? = null
protected var mBottomTabContainer: LinearLayout? = null
@ -44,6 +45,7 @@ abstract class BaseBottomTabFragment<T: ViewBinding>: ToolbarFragment() {
protected fun initBottomTab(bottomTabList: List<BottomTab>) {
mBottomTabBindingList.clear()
mBottomTabContainer?.run {
visibility = View.VISIBLE
removeAllViews()
bottomTabList.forEachIndexed { index, bottomTab ->
addView(
@ -61,6 +63,13 @@ abstract class BaseBottomTabFragment<T: ViewBinding>: ToolbarFragment() {
.apply {
tabTv.text = bottomTab.name
if (!TextUtils.isEmpty(bottomTab.jsCode)) {
tabLottie.setFailureListener {
SentryHelper.onEvent(
SENTRY_ID_BOTTOM_TAB_LOTTIE_LOAD_FAILED,
BOTTOM_TAB_ID, bottomTab.id,
BOTTOM_TAB_NAME, bottomTab.name
)
}
tabLottie.setAnimationFromJson(bottomTab.jsCode, bottomTab.id + bottomTab.name)
}
if (bottomTab.iconSelector != 0) {
@ -78,6 +87,11 @@ abstract class BaseBottomTabFragment<T: ViewBinding>: ToolbarFragment() {
protected fun initViewPager() {
mViewPager?.run {
isUserInputEnabled = false
// 去掉默认动画
setPageTransformer { page, _ ->
page.translationX = 0F
page.alpha = 1F
}
adapter = provideAdapter()
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
@ -122,6 +136,13 @@ abstract class BaseBottomTabFragment<T: ViewBinding>: ToolbarFragment() {
protected open fun onPageSelected(position: Int) {
checkIndex(position)
}
protected open fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
protected open fun onPageScrollStateChanged(state: Int) {}
companion object {
private const val SENTRY_ID_BOTTOM_TAB_LOTTIE_LOAD_FAILED = "bottom_tab_lottie_load_failed"
private const val BOTTOM_TAB_ID = "bottom_tab_id"
private const val BOTTOM_TAB_NAME = "bottom_tab_name"
}
}

View File

@ -14,32 +14,45 @@ import com.gh.gamecenter.fragment.ReloadFragment
class MainFragmentStateAdapter(private val mFragment: Fragment) : BaseDiffFragmentStateAdapter<BottomTab>(mFragment) {
override fun createFragment(data: BottomTab?, position: Int): Fragment {
if (data != null) {
val bundle = Bundle()
val superiorChain = if (mFragment is ISuperiorChain) mFragment else null
mFragment.arguments?.let { bundle.putAll(it) }
if (data.link == null) return ReloadFragment()
bundle.putBoolean(EntranceConsts.KEY_IS_HOME, true)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_ID, data.id)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_NAME, data.name)
bundle.putInt(EntranceConsts.KEY_POSITION, position)
bundle.putParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST, arrayListOf(ExposureSource("底部tab", data.name)))
bundle.putBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, true)
return when (data.link.type) {
if (data == null) return ReloadFragment()
val bundle = Bundle()
val superiorChain = if (mFragment is ISuperiorChain) mFragment else null
mFragment.arguments?.let { bundle.putAll(it) }
bundle.putBoolean(EntranceConsts.KEY_IS_HOME, true)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_ID, data.id)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_NAME, data.name)
bundle.putInt(EntranceConsts.KEY_POSITION, position)
val exposureSourceList = arrayListOf(ExposureSource("底部tab", data.name))
data.diverter?.let {
exposureSourceList.add(
ExposureSource(
"分流器",
"${it.diverterData.diverterName}+${it.diverterData.diverterId}"
)
)
}
bundle.putParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST, exposureSourceList)
bundle.putBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, true)
return if (data.link == null) {
ReloadFragment()
} else {
when (data.link!!.type) {
ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE -> {
bundle.putParcelable(LinkEntity::class.java.simpleName, data.link)
SearchToolbarTabWrapperFragment().setSuperiorChain(superiorChain).apply { arguments = bundle }
}
ViewPagerFragmentHelper.TYPE_MULTI_TAB_NAV -> {
bundle.putParcelable(BottomTab.SearchStyle::class.java.simpleName, data.searchStyle)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, data.link.link)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, data.link.text)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, data.link!!.link)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, data.link!!.text)
SearchToolbarTabWrapperFragment().setSuperiorChain(superiorChain).apply { arguments = bundle }
}
else -> ViewPagerFragmentHelper.createFragment(mFragment, bundle, data.link, false)
else -> ViewPagerFragmentHelper.createFragment(mFragment, bundle, data.link!!, false)
}
} else {
return ReloadFragment()
}
}

View File

@ -13,6 +13,8 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.text.color
import androidx.core.view.doOnNextLayout
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.ethanhua.skeleton.Skeleton
import com.ethanhua.skeleton.SkeletonScreen
import com.gh.common.iinterface.ISuperiorChain
import com.gh.common.prioritychain.BottomTabGuideHandler
import com.gh.common.prioritychain.GlobalPriorityChainHelper
@ -48,6 +50,8 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
private var mBottomTabGuideHandler: BottomTabGuideHandler? = null
private var mBottomTabGuidePopupWindow: PopupWindow? = null
private var mSkeletonScreen: SkeletonScreen? = null
override fun getLayoutId(): Int = 0
override fun getInflatedLayout(): View = mBinding.root
override fun provideAdapter(): FragmentStateAdapter = mAdapter
@ -63,18 +67,24 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
super.onCreate(savedInstanceState)
DisplayUtils.transparentStatusBar(requireActivity())
initSkeleton()
mBinding.viewShadow.visibility = if (mIsDarkModeOn) View.GONE else View.VISIBLE
buildPriorityChain()
mViewModel?.bottomTabListLiveData?.observe(this) {
mSkeletonScreen?.hide()
mBottomTabList.clear()
mBottomTabList.addAll(it)
mViewPager?.offscreenPageLimit = it.size
initBottomTab(mBottomTabList)
showBottomTabGuideIfExists()
mAdapter.submitList(mBottomTabList)
val defaultBottomTabIndex = mViewModel!!.defaultBottomTabIndex
val defaultBottomTab = mBottomTabList.getOrNull(defaultBottomTabIndex) ?: return@observe
mViewPager?.doOnNextLayout {
setCurrentItem(mViewModel!!.defaultBottomTabIndex)
mViewModel?.handleBypassVisit(defaultBottomTab)
setCurrentItem(defaultBottomTabIndex)
}
}
}
@ -84,6 +94,18 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
mPriorityChain.addHandler(mBottomTabGuideHandler!!)
}
private fun initSkeleton() {
mSkeletonScreen = Skeleton.bind(mBinding.skeleton)
.shimmer(true)
.angle(Constants.SHIMMER_ANGLE)
.color(R.color.ui_skeleton_highlight)
.duration(Constants.SHIMMER_DURATION)
.maskWidth(Constants.MASK_WIDTH)
.gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
.load(R.layout.fragment_main_skeleton)
.show()
}
private fun showBottomTabGuideIfExists() {
val guidePosition = mBottomTabList.indexOfFirst { it.guide != null }
if (guidePosition != -1) {
@ -194,6 +216,9 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
if (bottomTab?.guide != null) {
dismissBottomTabGuide()
}
bottomTab?.let {
mViewModel?.handleBypassVisit(it)
}
playTabAnimation(toCheck)
changeBottomTabStyle(toCheck)
EventBus.getDefault().post(EBReuse(Constants.FINISH_PULL_DOWN_PUSH))

View File

@ -3,23 +3,31 @@ package com.gh.gamecenter.wrapper
import android.annotation.SuppressLint
import androidx.lifecycle.MutableLiveData
import com.gh.common.util.HomeBottomBarHelper
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.ViewPagerFragmentHelper
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.entity.LaunchRedirect
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.core.utils.GsonUtils
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.SingletonHolder
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.entity.BottomTab
import com.gh.gamecenter.entity.DataUnionEntity
import com.gh.gamecenter.entity.DiverterEntity
import com.gh.gamecenter.entity.MultiTabNav
import com.gh.gamecenter.home.custom.model.CustomPageData
import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
class MainWrapperRepository {
private val mNewApi = RetrofitManager.getInstance().newApi
@ -45,9 +53,17 @@ class MainWrapperRepository {
val customPageLiveData = MutableLiveData<CustomPageData?>()
val errorLiveData = MutableLiveData<Exception>()
val diverterList = arrayListOf<DiverterEntity>()
private val mTabSelectEventFlow = MutableSharedFlow<MainSelectedEvent>()
val tabSelectEventFlow = mTabSelectEventFlow as SharedFlow<MainSelectedEvent>
fun init() {
// 若 timeout 后数据未加载完成,则即便还没有回调也使用默认数据生成底部 tab
emitDefaultTabDataAfterTimeout()
getBypassList()
}
/**
* 发送首次启动跳转事件的选中事件
*/
@ -132,10 +148,37 @@ class MainWrapperRepository {
}
}
@SuppressLint("CheckResult")
fun getBypassList() {
val isInstallFirstAccess = TimeUtils.isToday(SPUtils.getLong(Constants.SP_BRAND_NEW_FIRST_LAUNCH_TIME) / 1000).toString()
mNewApi.getDiverterList(isInstallFirstAccess, BYPASS_TYPE_BOTTOM_TAB)
.subscribeOn(Schedulers.io())
.timeout(BYPASS_TIME_OUT, TimeUnit.MILLISECONDS)
.subscribe(object : BiResponse<List<DiverterEntity>>() {
override fun onSuccess(data: List<DiverterEntity>) {
diverterList.clear()
diverterList.addAll(data)
getDataUnion()
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
getDataUnion()
val reasonString = if (exception is TimeoutException) "分流判断超时" else "分流页面返回为空"
SensorsBridge.trackFailByPass("底部tab", "", "", reasonString)
NewFlatLogUtils.logFailByPass("底部tab", "", "", reasonString)
}
})
}
@SuppressLint("CheckResult")
fun getDataUnion() {
processBottomTabData(emptyList())
mNewApi.dataUnion
var diverter = ""
diverterList.forEach {
diverter += "${it.moduleIndex}:${it.diverterData.branchId},"
}
mNewApi.getDataUnion(diverter)
.subscribeOn(Schedulers.io())
.subscribe(object : BiResponse<DataUnionEntity>() {
override fun onSuccess(data: DataUnionEntity) {
@ -156,7 +199,9 @@ class MainWrapperRepository {
private fun processBottomTabData(bottomTabList: List<BottomTab>) {
if (bottomTabList.isNotEmpty()) {
HomeBottomBarHelper.updateDefaultHomeBottomTabData(bottomTabList)
diverterList.forEach {
bottomTabList.getOrNull(it.moduleIndex)?.diverter = it
}
var preSelectedTab: BottomTab? = null
@ -199,11 +244,16 @@ class MainWrapperRepository {
}
}
HomeBottomBarHelper.updateDefaultHomeBottomTabData(bottomTabList)
bottomTabListLiveData.postValue(bottomTabList)
defaultNavId = bottomTabList.find { it.link?.type == ViewPagerFragmentHelper.TYPE_MULTI_TAB_NAV && it.default }?.link?.link ?: ""
defaultCustomPageId = bottomTabList.find { it.link?.type == ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE && it.default }?.link?.link ?: ""
} else {
HomeBottomBarHelper.getDefaultHomeBottomTabData().run {
val defaultIndex = indexOfFirst { it.default }
if (defaultIndex != -1) {
defaultBottomTabIndex = defaultIndex
}
bottomTabListLiveData.postValue(this)
defaultNavId = find { it.link?.type == ViewPagerFragmentHelper.TYPE_MULTI_TAB_NAV && it.default }?.link?.link ?: ""
defaultCustomPageId = find { it.link?.type == ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE && it.default }?.link?.link ?: ""
@ -232,7 +282,19 @@ class MainWrapperRepository {
multiTabNavLiveData.postValue(multiTabNav)
}
companion object : SingletonHolder<MainWrapperRepository>({ MainWrapperRepository() })
private fun emitDefaultTabDataAfterTimeout() {
CoroutineScope(SupervisorJob()).launch {
delay(3000)
if (!mMainDataIsLoaded) {
processBottomTabData(emptyList())
}
}
}
companion object : SingletonHolder<MainWrapperRepository>({ MainWrapperRepository() }) {
private const val BYPASS_TIME_OUT = 1000L
const val BYPASS_TYPE_BOTTOM_TAB = "bottom_tab"
}
}
sealed class MainSelectedEvent {

View File

@ -1,15 +1,22 @@
package com.gh.gamecenter.wrapper
import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.*
import com.gh.common.util.CheckLoginUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.RealNameHelper
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.common.utils.toRequestBody
import com.gh.gamecenter.entity.BottomTab
import com.gh.gamecenter.entity.DiverterEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.livedata.Event
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.retrofit.RetrofitManager
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.map
@ -23,6 +30,7 @@ import retrofit2.HttpException
class MainWrapperViewModel(application: Application, private val mRepository: MainWrapperRepository) :
AndroidViewModel(application) {
private val mApi = RetrofitManager.getInstance().api
private val mNewApi = RetrofitManager.getInstance().newApi
val defaultBottomTabIndex
get() = mRepository.defaultBottomTabIndex
@ -45,6 +53,8 @@ class MainWrapperViewModel(application: Application, private val mRepository: Ma
val bottomDoubleTabAction = MutableLiveData<BottomTab>()
private val byPassVisitBottomTabIdSet = hashSetOf<String>()
/**
* 请求各种弹窗的数据
*/
@ -91,6 +101,57 @@ class MainWrapperViewModel(application: Application, private val mRepository: Ma
})
}
fun handleBypassVisit(bottomTab: BottomTab) {
if (byPassVisitBottomTabIdSet.contains(bottomTab.id)) return
bottomTab.diverter?.let {
val source = "底部tab${bottomTab.name}"
SensorsBridge.trackByPassBrowsing(
source,
it.diverterData.diverterName,
it.diverterData.diverterId,
it.diverterData.branchId,
it.diverterData.branchName,
it.diverterData.inprocessTime,
it.diverterData.bypassVisitTime,
it.diverterData.linkType,
it.diverterData.linkId,
it.diverterData.linkText,
it.diverterData.bypassStatus,
)
NewFlatLogUtils.logByPassBrowsing(
source,
it.diverterData.diverterName,
it.diverterData.diverterId,
it.diverterData.branchId,
it.diverterData.branchName,
it.diverterData.inprocessTime,
it.diverterData.bypassVisitTime,
it.diverterData.linkType,
it.diverterData.linkId,
it.diverterData.linkText,
it.diverterData.bypassStatus,
)
patchBypassVisitTime(it)
byPassVisitBottomTabIdSet.add(bottomTab.id)
}
}
@SuppressLint("CheckResult")
private fun patchBypassVisitTime(diverterEntity: DiverterEntity) {
val data = mapOf(
"module_id" to diverterEntity.moduleId,
"diverter_id" to diverterEntity.diverterData.diverterId
)
mNewApi.patchDiverterVisitTime(MainWrapperRepository.BYPASS_TYPE_BOTTOM_TAB, data.toRequestBody())
.compose(singleToMain())
.subscribe({
Utils.log(TAG, "patchDiverterVisitTime onSuccess: ${it.string()}")
}, {
Utils.log(TAG, "patchDiverterVisitTime onError: ${it.message}")
})
}
class Factory(
private val mApplication: Application,
) : ViewModelProvider.NewInstanceFactory() {
@ -100,6 +161,7 @@ class MainWrapperViewModel(application: Application, private val mRepository: Ma
}
companion object {
private const val TAG = "MainWrapperViewModel"
const val SHOULD_SHOW_OPENING_DIALOG = "show_opening_dialog"
}
}

View File

@ -58,6 +58,7 @@ import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.time.TimeUtil
import com.gh.gamecenter.feature.game.GameItemViewHolder
import com.gh.gamecenter.feature.utils.SentryHelper
import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment
import com.gh.gamecenter.home.custom.CustomPageFragment
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
@ -89,7 +90,18 @@ import kotlin.math.roundToInt
* @see Style
*/
class SearchToolbarTabWrapperFragment : BaseTabWrapperFragment(), ISearchToolbarTab, ISmartRefresh, ISuperiorChain {
private val mBinding by lazy { FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater) }
private val mBinding by lazy {
try {
FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater)
} catch (e: Exception) {
SentryHelper.onEvent("VIEW_BINDING_BIND_ERROR",
"digest", e.localizedMessage,
"gid", HaloApp.getInstance().gid
)
// 玄学,重试一次,该闪退闪退
FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater)
}
}
private val mViewModel: SearchToolbarTabWrapperViewModel by lazy {
viewModelProviderFromParent(
SearchToolbarTabWrapperViewModel.Factory(mMultiTabNavId, mNoTabLinkEntity?.link ?: ""),

View File

@ -14,9 +14,11 @@ import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.blankj.utilcode.util.ThreadUtils;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.SplashScreenActivity;
import com.gh.gamecenter.common.base.GlobalActivityManager;
import com.gh.gamecenter.core.AppExecutor;
import com.gh.ndownload.suspendwindow.NDownloadDrawOverlayPermissionWindowController;
import com.gh.ndownload.suspendwindow.NDownloadSuspendWindowController;
import com.gh.ndownload.suspendwindow.utils.NDownloadSuspendWindowHelper;
@ -282,6 +284,14 @@ public class NDownloadService extends Service {
}
private void showDownloadSuspendWindowIfNeeded(DownloadEntity entry) {
if (ThreadUtils.isMainThread()) {
showDownloadSuspendWindowIfNeededInner(entry);
} else {
AppExecutor.getUiExecutor().execute(() -> showDownloadSuspendWindowIfNeededInner(entry));
}
}
private void showDownloadSuspendWindowIfNeededInner(DownloadEntity entry) {
if (!NDownloadSuspendWindowHelper.shouldSkipDownloadEntity(getApplicationContext(), entry)) {
if (NDownloadSuspendWindowHelper.canDrawOverLayer(getApplicationContext())) {// 已开启悬浮窗权限,直接显示悬浮窗
showDownloadSuspendWindow();

View File

@ -3,7 +3,9 @@ package com.gh.ndownload.suspendwindow
import android.app.Activity
import android.app.Application
import android.app.Application.ActivityLifecycleCallbacks
import android.os.Build
import android.os.Bundle
import androidx.core.view.ViewCompat
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.asVGame
@ -45,7 +47,9 @@ class NDownloadDrawOverlayPermissionWindowController(val application: Applicatio
}
fun show(activity: Activity) {
if (isStarted) return
val decorView = activity.window.decorView
val isAttachedToWindow = ViewCompat.isAttachedToWindow(decorView)
if (isStarted || !isAttachedToWindow) return
isStarted = true
currentActivityRef = WeakReference(activity)
onAttachToUi(activity)
@ -124,10 +128,10 @@ class NDownloadDrawOverlayPermissionWindowController(val application: Applicatio
SensorsBridge.trackDownloadSuspendedWindowGuideShow(
gameId = downloadEntity.gameId,
gameName = downloadEntity.name,
gameSchemeType = if(downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameSchemeType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameType = downloadEntity.categoryChinese,
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
downloadType = if(downloadEntity.asVGame()) "畅玩下载" else "本地下载"
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载"
)
}
}

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