Compare commits

...

425 Commits

Author SHA1 Message Date
7fd7197909 版本升级至 5.1.2 2021-08-31 15:12:20 +08:00
58178d2871 Merge branch 'hotfix-v5.1.1-371-crash' into 'release'
处理一系列闪退

See merge request halo/android/assistant-android!122
2021-08-31 15:09:32 +08:00
880838c263 处理一系列闪退
1. 修复文本自动填充偶发的闪退
2. 修复软键盘弹起时切换页面触发的闪退
3. 修复在后台执行下载任务时偶发的闪退
4. 修复用户发表旧视频内容在新版本个人主页浏览时的闪退
5. 修复游戏详情专区返回时偶发的闪退
2021-08-31 15:06:33 +08:00
17e5c0535d Merge branch 'hotfix-v5.1.1-indicator' into 'release'
修改首页 indicator 不显示问题

See merge request halo/android/assistant-android!121
2021-08-30 18:06:01 +08:00
66fee84b49 Merge branch 'hotfix-v5.1.1-setting' into 'release'
修复"游戏下载设置页面的自动安装游戏选项和自动关注游戏选项默认值错误"问题

See merge request halo/android/assistant-android!120
2021-08-30 18:00:20 +08:00
lyr
bbe0350f2c 修复"游戏下载设置页面的自动安装游戏选项和自动关注游戏选项默认值错误"问题 2021-08-30 17:57:19 +08:00
1a35b5ded0 修改首页indicator不显示问题 2021-08-30 17:35:50 +08:00
efd81a4e0c 版本升级到 5.1.1 2021-08-26 10:22:00 +08:00
493e08ce2c Merge branch 'hotfix-v5.1.0-370-crash' into 'release'
修复 5.1.0-370 的一些闪退

See merge request halo/android/assistant-android!119
2021-08-26 10:21:23 +08:00
1906451060 修复一系列闪退问题
1. 修复因为特殊处理光遇更新判断而触发的其它下载闪退问题
2. 修复帖子视频详情页点击点赞/关注等按钮触发一键登录时的闪退问题
3. 修复下载完成点击通知栏下载完成通知偶发的闪退问题
4. 修复从首页安利墙发表新安利后点击到达评论可能触发的闪退问题
5. 修复论坛详情在页面被内存回收重建时下拉刷新的闪退问题
6. 修复游戏详情页浏览专区在页面被内存回收重建时点返回按钮偶发的闪退问题
7. 修复开测表列表不存在推荐标签时的闪退问题
2021-08-26 10:17:08 +08:00
f865e95ae1 Revert "临时简单处理光遇游戏包点击更新时会卡住的问题"
This reverts commit 86edc8b9
2021-08-25 18:23:30 +08:00
lyr
3e7e98d555 添加在新页面打开全屏webview的JS调用方法 2021-08-23 17:21:44 +08:00
7537963a8e 支持url跳转视频详情 2021-08-21 14:50:33 +08:00
bb9fd24068 【光环助手V5.1.0】新社区2期-埋点文档(0820 产品验收问题) https://git.ghzs.com/pm/halo-app-issues/-/issues/1303 2021-08-20 16:13:21 +08:00
eda1eea2de 完成社区探索之旅(测试汇总8) https://git.ghzs.com/pm/halo-app-issues/-/issues/1285 2021-08-20 10:54:52 +08:00
62e60f4309 修改删除社区帖子toast文案 2021-08-19 15:50:17 +08:00
b1fef73c54 【光环助手V5.1.0】新社区运营验收问题(11) https://git.ghzs.com/pm/halo-app-issues/-/issues/1442 2021-08-18 18:54:56 +08:00
c4fc31c963 【光环助手V5.1.0】新社区运营验收问题(17) https://git.ghzs.com/pm/halo-app-issues/-/issues/1442 2021-08-18 18:49:15 +08:00
812eb842e2 【光环助手V5.1.0】新社区运营验收问题(12) https://git.ghzs.com/pm/halo-app-issues/-/issues/1442 2021-08-18 14:43:51 +08:00
783cb95f24 对接版主删除视频评论接口 2021-08-18 11:04:14 +08:00
5969fa2ca5 网页跳转上传视频的论坛类型改为可选 2021-08-17 15:47:45 +08:00
0187608918 【光环助手V5.1.0】新社区运营验收问题(27) https://git.ghzs.com/pm/halo-app-issues/-/issues/1442 2021-08-17 10:57:55 +08:00
98531376d9 Merge remote-tracking branch 'origin/dev' into dev 2021-08-16 15:30:27 +08:00
86edc8b919 临时简单处理光遇游戏包点击更新时会卡住的问题 2021-08-16 15:30:15 +08:00
7903751d85 【光环助手V5.1.0】新社区运营验收问题(23) https://git.ghzs.com/pm/halo-app-issues/-/issues/1442 2021-08-16 14:28:35 +08:00
2620a29a2b Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-16 11:25:57 +08:00
5cc40c09dc 【光环助手V5.1.0】新社区运营验收问题(17) https://git.ghzs.com/pm/halo-app-issues/-/issues/1442 2021-08-16 11:25:46 +08:00
5c02d37852 启动事件添加应用安装来源信息 2021-08-16 11:19:04 +08:00
2e5d445d65 完成暑期好游安利大赏(0813测试汇总反馈 1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1284 2021-08-16 10:19:28 +08:00
c8bae7d89b 【光环助手V5.1.0】新社区运营验收问题(2) https://git.ghzs.com/pm/halo-app-issues/-/issues/1442 2021-08-13 16:20:20 +08:00
2c98c38721 修改视频贴详情原创标签被切割 2021-08-11 23:05:33 +08:00
lyr
4f28c54591 论坛活动url增加分类id参数 2021-08-11 22:47:07 +08:00
06b5b885e9 修改视频帖详情展开按钮是否显示 2021-08-11 21:05:42 +08:00
612cc2ca9b Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-11 18:26:56 +08:00
18f7b695e5 修改发布视频帖内容来源改为必选 2021-08-11 18:26:45 +08:00
f29e2fe1de 添加简单的扩大点击区域方法 2021-08-11 18:14:56 +08:00
799c22093f 处理编译警告 2021-08-11 16:38:06 +08:00
2496c1d96e 修复全屏网页网络异常加载失败时无法返回的问题 2021-08-11 16:28:31 +08:00
8ee76af30f 优化内存占用 2021-08-11 15:53:40 +08:00
lyr
2f35442558 【光环助手V5.1.0】 0809产品测试问题(第14点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1439 2021-08-11 15:38:01 +08:00
lyr
81fa6a6233 【光环助手V5.1.0】 0809产品测试问题(第11点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1439 2021-08-11 15:10:51 +08:00
lyr
e5778d5b5f 【光环助手V5.1.0】 0809产品测试问题(第6点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1439 2021-08-10 17:39:35 +08:00
f69d607ecb Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-10 16:48:30 +08:00
79fc2e1f93 【光环助手V5.1.0】 0809产品测试问题(3) https://git.ghzs.com/pm/halo-app-issues/-/issues/1439 2021-08-10 16:48:20 +08:00
6dfdbb9ee8 【光环助手V5.1.0】 0809产品测试问题(5)https://git.ghzs.com/pm/halo-app-issues/-/issues/1439 2021-08-10 15:55:35 +08:00
5d8f7b3f8d 模拟器游戏启动时更新金手指文件 https://git.ghzs.com/pm/halo-app-issues/-/issues/1362 2021-08-10 12:02:17 +08:00
cd4601a9c9 添加下载更新简单文件的方法 2021-08-10 12:00:04 +08:00
462a011401 提交LGLibrary 2021-08-10 09:09:59 +08:00
31d55dae14 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-09 20:03:56 +08:00
8dc9731299 【光环助手V5.1.0】新社区2期-论坛展示-提问帖详情页(0806UI测试1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1347 2021-08-09 20:03:45 +08:00
66c19c644e 【光环助手V5.1.0】 0809运营测试问题(1)https://git.ghzs.com/pm/halo-app-issues/-/issues/1438 2021-08-09 17:24:31 +08:00
2b617e2697 完成单机模拟器-金手指功能后台(0809测试反馈) https://git.ghzs.com/pm/halo-app-issues/-/issues/1362 2021-08-09 16:35:24 +08:00
3a60a497e1 更换版主修改问题提示弹窗文案 2021-08-09 16:15:18 +08:00
a9337aee63 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-09 15:52:49 +08:00
c80cd47ccc 【光环助手V5.1.0】 0803产品运营测试问题(2)(8) https://git.ghzs.com/pm/halo-app-issues/-/issues/1428 2021-08-09 15:52:38 +08:00
lyr
d9c2371488 移除冗余的资源文件 https://git.ghzs.com/halo/android/assistant-android/-/issues/33 2021-08-09 15:42:15 +08:00
096d19751a 【光环助手V5.1.0】0805产品测试问题(9)https://git.ghzs.com/pm/halo-app-issues/-/issues/1434 2021-08-09 15:15:41 +08:00
ef70119090 尝试修复 Loghub 日志上报 logstore 错乱的问题 https://github.com/aliyun/aliyun-log-android-sdk/issues/68 2021-08-09 11:56:16 +08:00
95dba71bb9 添加异步登录 JS Api 2021-08-09 10:17:16 +08:00
ec37c7a6f8 【光环助手V5.1.0】 0806产品运营测试问题(7) https://git.ghzs.com/pm/halo-app-issues/-/issues/1436 2021-08-09 09:28:17 +08:00
dcc9352301 调整 Kotlin 版本 2021-08-06 14:34:43 +08:00
c509c6bb38 添加使用快捷方式启动模拟器游戏失败的日志记录 2021-08-06 12:01:23 +08:00
4784d689f8 处理内嵌网页的一些空指针异常 2021-08-06 12:00:31 +08:00
8903a075be Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-06 11:46:37 +08:00
48d6e91b0e 【光环助手V5.1.0】0805运营测试问题(4,6,8) https://git.ghzs.com/pm/halo-app-issues/-/issues/1435 2021-08-06 11:46:27 +08:00
30865239d2 【光环助手V5.1.0】 0806产品运营测试问题(3)https://git.ghzs.com/pm/halo-app-issues/-/issues/1436 2021-08-06 11:31:09 +08:00
8b7cd92ae8 微调 chucker 编译脚本 2021-08-06 11:21:46 +08:00
69f336553e 调整 Kotlin 版本,修复 XAPK 解压在 4.4 以下设备的闪退问题 2021-08-06 11:07:10 +08:00
ed82f96ae3 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-06 10:17:22 +08:00
8f02016a76 【光环助手V5.1.0】新社区2期-论坛展示-提问帖详情页(0806UI测试1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1347 2021-08-06 10:17:12 +08:00
c06f397d12 【光环助手V5.1.0】0805产品测试问题(9,10,12)https://git.ghzs.com/pm/halo-app-issues/-/issues/1434 2021-08-06 10:08:34 +08:00
lyr
912b7280cd 【光环助手V5.1.0】0805产品测试问题(第5点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1434 2021-08-05 17:15:48 +08:00
50f7dd2c63 【光环助手V5.1.0】0805产品测试问题(3,4) https://git.ghzs.com/pm/halo-app-issues/-/issues/1434 2021-08-05 15:49:12 +08:00
23469543c7 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-05 14:48:14 +08:00
2d3f70dd12 【光环助手V5.1.0】 0804产品运营测试问题(1)(12) https://git.ghzs.com/pm/halo-app-issues/-/issues/1430 2021-08-05 14:48:09 +08:00
lyr
f57d19e797 【光环助手V5.1.0】 0804产品运营测试问题(1)(第10点) https://git.ghzs.com/pm/halo-app-issues/-/issues/1430#note_112113 2021-08-05 10:48:52 +08:00
lyr
8c529f0724 【光环助手V5.1.0】新社区2期-论坛展示-信息流-提问帖评论(0804UI测试)https://git.ghzs.com/pm/halo-app-issues/-/issues/1344#note_111919 2021-08-05 10:19:24 +08:00
lyr
1583f1957a 完成积分体系-光能中心、光能屋UI更改 https://git.ghzs.com/pm/halo-app-issues/-/issues/1300 2021-08-04 18:44:40 +08:00
lyr
676ccb133f 【光环助手V5.1.0】 0803产品运营测试问题(2)(第3点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1428 2021-08-04 15:45:31 +08:00
489b8143a1 【光环助手V5.1.0】 0804产品运营测试问题(1) (1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1430 2021-08-04 15:06:44 +08:00
c4cf3efa21 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-04 14:26:57 +08:00
871cfd638d 【光环助手V5.1.0】新社区2期-论坛展示-帖子详情页(0804UI测试1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1346 2021-08-04 14:26:47 +08:00
lyr
1f3ae1c687 【光环助手V5.1.0】 0803产品运营测试问题(1)(第23、24点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1426 2021-08-04 14:21:55 +08:00
59eb101a48 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-04 12:14:09 +08:00
b3b8b6ba29 【光环助手V5.1.0】 0803产品运营测试问题(2)https://git.ghzs.com/pm/halo-app-issues/-/issues/1428 2021-08-04 12:14:02 +08:00
lyr
42baaa8950 【光环助手V5.1.0】 0803产品运营测试问题(2)(第3点) https://git.ghzs.com/pm/halo-app-issues/-/issues/1428 2021-08-04 11:07:51 +08:00
lyr
61ad70d3b8 【光环助手V5.1.0】新社区2期-论坛展示-信息流-提问帖评论(0804UI测试)https://git.ghzs.com/pm/halo-app-issues/-/issues/1344#note_111919 2021-08-04 10:59:35 +08:00
lyr
04252e3b91 【光环助手V5.1.0】 0803产品运营测试问题(22)https://git.ghzs.com/pm/halo-app-issues/-/issues/1426 2021-08-03 18:20:41 +08:00
lyr
b40543d7b5 【光环助手V5.1.0】 0803产品运营测试问题(16)https://git.ghzs.com/pm/halo-app-issues/-/issues/1426 2021-08-03 17:30:26 +08:00
lyr
903fa49b71 【光环助手V5.1.0】 0803产品运营测试问题(13)https://git.ghzs.com/pm/halo-app-issues/-/issues/1426 2021-08-03 16:28:08 +08:00
lyr
03ae2699c6 【光环助手V5.1.0】 0803产品运营测试问题(11)https://git.ghzs.com/pm/halo-app-issues/-/issues/1426 2021-08-03 16:09:05 +08:00
8ecc4078bb 【光环助手V5.1.0】 0803产品运营测试问题(1,20,27,31) https://git.ghzs.com/pm/halo-app-issues/-/issues/1426 2021-08-03 15:59:02 +08:00
ebaf4f02f0 【光环助手V5.1.0】新社区2期-论坛展示-论坛详情-版主成员(0802UI测试)https://git.ghzs.com/pm/halo-app-issues/-/issues/1345 2021-08-03 11:21:03 +08:00
e85437379a 调整金手指功能数据字段 https://git.ghzs.com/pm/halo-app-issues/-/issues/1362 2021-08-02 17:20:44 +08:00
5a77522a04 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-02 16:18:48 +08:00
e5512604fa 修改上传图片的 type 参数 2021-08-02 16:18:39 +08:00
lyr
146a46f8be 【光环助手V5.1.0】0802产品测试问题(1)https://git.ghzs.com/pm/halo-app-issues/-/issues/1423 2021-08-02 16:04:00 +08:00
91f33f06d3 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-02 15:59:00 +08:00
59da7b6ba6 【光环助手V5.1.0】新社区2期-视频帖发布页(0802UI测试1,2) https://git.ghzs.com/pm/halo-app-issues/-/issues/1355 2021-08-02 15:58:45 +08:00
lyr
0a1ad238f8 【光环助手V5.1.0】新社区2期-论坛详情页引导(0802UI测试)https://git.ghzs.com/pm/halo-app-issues/-/issues/1352#note_111430 2021-08-02 15:43:24 +08:00
302e6c145c 【光环助手V5.1.0】新社区2期-论坛展示-帖子详情页(0802UI测试1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1346 2021-08-02 15:37:40 +08:00
a6bbf0dfc7 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-08-02 15:17:06 +08:00
9c3d22964c 【光环助手V5.1.0】0730产品测试问题(10,22,23,24) https://git.ghzs.com/pm/halo-app-issues/-/issues/1422 2021-08-02 15:16:56 +08:00
ab60ed8473 【光环助手V5.1.0】新社区2期-论坛展示-论坛详情-版主成员(0802UI测试)https://git.ghzs.com/pm/halo-app-issues/-/issues/1345 2021-08-02 14:31:07 +08:00
lyr
9a00e70cc4 【光环助手V5.1.0】0730产品测试问题(5) https://git.ghzs.com/pm/halo-app-issues/-/issues/1422 2021-08-02 11:16:46 +08:00
lyr
35954f52c5 【光环助手V5.1.0】0730产品测试问题(1-3)https://git.ghzs.com/pm/halo-app-issues/-/issues/1422 2021-08-02 10:58:25 +08:00
e13d80d063 【光环助手V5.1.0】0730产品测试问题(11)https://git.ghzs.com/pm/halo-app-issues/-/issues/1422 2021-08-02 10:13:56 +08:00
73ed15689d 基本完成金手指功能 https://git.ghzs.com/pm/halo-app-issues/-/issues/1362 2021-07-30 18:07:23 +08:00
lyr
65d949bade 【光环助手V5.1.0】新社区2期-推荐页引导(0729UI测试)https://git.ghzs.com/pm/halo-app-issues/-/issues/1350#note_110977 2021-07-29 18:08:01 +08:00
33d0d01051 【光环助手V5.1.0】新社区2期-帖子发布页(0729UI测试2) https://git.ghzs.com/pm/halo-app-issues/-/issues/1354 2021-07-29 16:34:29 +08:00
9998ed0a14 【光环助手V5.1.0】新社区2期-发布页引导(帖子、提问)(0729UI测试1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1351 2021-07-29 16:15:41 +08:00
85a8a17acb Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-29 15:44:34 +08:00
4032cb8abe 【光环助手V5.1.0】新社区2期-视频帖发布页(0729UI测试) https://git.ghzs.com/pm/halo-app-issues/-/issues/1355 2021-07-29 15:44:18 +08:00
fc2e87063e Merge remote-tracking branch 'origin/dev' into dev 2021-07-29 14:34:37 +08:00
a63b809642 清理测试内容 2021-07-29 14:34:22 +08:00
c30048c268 正式环境接口切换到 5.0 2021-07-29 11:01:41 +08:00
956d5a39be 修复优化汇总(1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1342#note_110932 2021-07-29 09:59:51 +08:00
76fd1ab7e3 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-28 20:47:40 +08:00
2c4c954c64 【光环助手V5.1.0】新社区2期-论坛展示-信息流-审核中/审核不通过(2) https://git.ghzs.com/pm/halo-app-issues/-/issues/1343 2021-07-28 20:47:23 +08:00
aef4da961d 正式包丢失渠道后的默认渠道改为 GH_LOST 2021-07-28 17:24:17 +08:00
lyr
31e00bb681 【光环助手V5.1.0】新社区2期-埋点文档(版规说明遗漏部分) https://git.ghzs.com/pm/halo-app-issues/-/issues/1303 2021-07-28 16:50:30 +08:00
369fdd24ef 调整内容及评论投诉弹窗交互 https://git.ghzs.com/pm/halo-app-issues/-/issues/1373 2021-07-28 15:28:25 +08:00
2a57994c7f Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-28 15:21:58 +08:00
6f84b14277 处理升级gsyplayer版本报错问题 2021-07-28 15:21:52 +08:00
10ed05c8ef Merge branch 'feature-upgrade_gsyplayer' into dev
# Conflicts:
#	app/build.gradle
2021-07-28 15:05:18 +08:00
lyr
799bd1042f 【光环助手V5.1.0】新社区2期-埋点文档 https://git.ghzs.com/pm/halo-app-issues/-/issues/1303 2021-07-28 14:07:27 +08:00
6f9acab2d2 修改版主申请审核中的字段 2021-07-28 11:29:45 +08:00
lyr
387d8eb5af 优化社区-论坛页代码 2021-07-27 18:31:10 +08:00
c813c5d5ef 暂时放弃 arm64 的 SO 2021-07-27 17:35:58 +08:00
1c6df6741f 完成光环前端优化汇总第五周(3) https://git.ghzs.com/pm/halo-app-issues/-/issues/1388 2021-07-27 16:45:11 +08:00
b54ab5f824 支持网页将图片保存至图库中 https://git.ghzs.com/pm/halo-app-issues/-/issues/1286 2021-07-27 16:31:05 +08:00
lyr
37fdf6c18e 【光环助手V5.1.0】光环前端优化汇总第五周(4)https://git.ghzs.com/pm/halo-app-issues/-/issues/1388 2021-07-27 16:00:00 +08:00
2685de9b3e Merge remote-tracking branch 'origin/dev' into dev 2021-07-27 15:17:59 +08:00
c049100f5f webView 支持选择本地图片 2021-07-27 15:16:13 +08:00
711d1f9d65 【光环助手V5.1.0】新社区2期-论坛展示-论坛详情-版主成员https://git.ghzs.com/pm/halo-app-issues/-/issues/1345 2021-07-27 15:14:11 +08:00
5d7453afec 完成浏览论坛页面时间任务功能 https://git.ghzs.com/pm/halo-app-issues/-/issues/1285 2021-07-27 11:00:21 +08:00
e4acf466ba 版主加精问题评论更换接口 2021-07-26 14:49:45 +08:00
2d66cbd29c 【光环助手V5.1.0】新社区2期-埋点文档(分享面板) https://git.ghzs.com/pm/halo-app-issues/-/issues/1303 2021-07-26 10:07:10 +08:00
8786f7d500 【光环助手V5.1.0】新社区2期-论坛展示-提问帖详情页 https://git.ghzs.com/pm/halo-app-issues/-/issues/1347 2021-07-23 18:07:13 +08:00
3d40d7a819 新增默认选中论坛首页tab的 url scheme https://git.ghzs.com/pm/halo-app-issues/-/issues/1285 2021-07-22 17:50:22 +08:00
6f7862a0f2 新增默认选中论坛首页tab的 url scheme https://git.ghzs.com/pm/halo-app-issues/-/issues/1285 2021-07-22 17:37:43 +08:00
58c6211d54 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-22 17:34:02 +08:00
d10b187a1f 视频帖详情版主修改活动标签 2021-07-22 17:33:52 +08:00
lyr
1ebf34c83a 【光环助手V5.1.0】新社区2期-埋点文档(版规说明页以及处理漏传)https://git.ghzs.com/pm/halo-app-issues/-/issues/1303 2021-07-22 16:53:01 +08:00
01e2a3c708 1.版主修改活动标签 2.审核中、审核不通过点击图片toast提示 2021-07-22 15:08:20 +08:00
lyr
c9b97552d4 【光环助手V5.1.0】新社区2期-埋点文档(论坛详情页)https://git.ghzs.com/pm/halo-app-issues/-/issues/1303 2021-07-21 17:59:30 +08:00
428f39aa86 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-21 11:13:48 +08:00
5fecb723bc 【光环助手V5.1.0】新社区2期-论坛展示-帖子详情页(1(1)(3)(4),2) https://git.ghzs.com/pm/halo-app-issues/-/issues/1346 2021-07-21 11:13:38 +08:00
0168b0d93a 【光环助手V5.1.0】新社区2期-帖子发布页(4) https://git.ghzs.com/pm/halo-app-issues/-/issues/1354 2021-07-21 11:09:39 +08:00
895fa510fe 【光环助手V5.1.0】新社区2期-论坛展示-视频帖详情页 https://git.ghzs.com/pm/halo-app-issues/-/issues/1348 2021-07-21 11:04:00 +08:00
0aae59a1ba 恢复网页跳转旧视频流详情页的能力 2021-07-21 10:39:11 +08:00
574a2b4dc7 完成内容举报弹窗更改 https://git.ghzs.com/pm/halo-app-issues/-/issues/1373 2021-07-21 09:25:30 +08:00
acae608966 添加跳转至个人主页定位到视频分类的 url scheme https://git.ghzs.com/pm/halo-app-issues/-/issues/1284#note_107842 2021-07-20 18:29:37 +08:00
lyr
6ca6c34f92 【光环助手V5.1.0】新社区2期-埋点文档(部分)https://git.ghzs.com/pm/halo-app-issues/-/issues/1303 2021-07-20 17:55:13 +08:00
16724a2d2e 完成部分内容举报弹窗更改(帖子、提问帖、视频帖) https://git.ghzs.com/pm/halo-app-issues/-/issues/1373 2021-07-20 16:15:54 +08:00
8b50cc561a 补充判断用户类型的逻辑 https://git.ghzs.com/pm/halo-app-issues/-/issues/1293 2021-07-20 09:23:01 +08:00
1ec1d482c4 【光环助手V5.1.0】新社区2期-埋点文档(跳出事件埋点) https://git.ghzs.com/pm/halo-app-issues/-/issues/1303 2021-07-19 19:49:47 +08:00
869b9d507b Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-19 16:21:14 +08:00
514fe66347 【光环助手V5.1.0】新社区2期-埋点文档 https://git.ghzs.com/pm/halo-app-issues/-/issues/1303 2021-07-19 16:21:04 +08:00
lyr
657c91a5ef 【光环助手V5.1.0】新社区2期-论坛展示-信息流-提问帖评论 https://git.ghzs.com/pm/halo-app-issues/-/issues/1344 2021-07-19 11:29:36 +08:00
82354a1156 添加从网页跳转至发视频页面的跳转 urlScheme https://git.ghzs.com/pm/halo-app-issues/-/issues/1284 2021-07-16 18:05:40 +08:00
b7619f0c93 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-16 16:38:25 +08:00
598a3ad0b6 【光环助手V5.1.0】新社区2期-埋点文档(部分) https://git.ghzs.com/pm/halo-app-issues/-/issues/1303 2021-07-16 16:38:18 +08:00
bf0be93dc9 完成"返回活动"的小浮窗功能 https://git.ghzs.com/pm/halo-app-issues/-/issues/1284 2021-07-16 15:28:54 +08:00
lyr
90a676f778 【光环助手V5.1.0】新社区2期-论坛展示-信息流-审核中/审核不通过 https://git.ghzs.com/pm/halo-app-issues/-/issues/1343 2021-07-16 10:29:50 +08:00
lyr
a675e4bbe3 【光环助手V5.1.0】光环前端优化汇总第二周(6)https://git.ghzs.com/pm/halo-app-issues/-/issues/1342 2021-07-16 10:08:01 +08:00
5eee8c6785 更改积分"返回活动"浮窗的实现方式 2021-07-15 18:11:12 +08:00
3c54e4313c Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-15 17:08:09 +08:00
67c4b76de9 【光环助手V5.1.0】新社区2期-帖子发布页(UI部分) https://git.ghzs.com/pm/halo-app-issues/-/issues/1354 2021-07-15 17:07:59 +08:00
34fd08d06a 【光环助手V5.1.0】新社区2期-视频帖发布页(UI部分) https://git.ghzs.com/pm/halo-app-issues/-/issues/1355 2021-07-15 17:07:21 +08:00
lyr
d08b95021d 暂时隐藏社区-论坛-其他福利-万能加速器(因为跳转目的地不唯一) 2021-07-15 16:12:16 +08:00
8517a2aef1 应用启动日志上报添加包名/签名等其它信息 2021-07-14 14:57:43 +08:00
lyr
cbe90141f1 修改社区-推荐引导图错位问题 2021-07-14 14:50:10 +08:00
lyr
fd43bc9426 【光环助手V5.1.0】新社区2期-论坛详情页引导 https://git.ghzs.com/pm/halo-app-issues/-/issues/1352 2021-07-14 14:48:52 +08:00
lyr
34a143b632 【光环助手V5.1.0】新社区2期-推荐页引导 https://git.ghzs.com/pm/halo-app-issues/-/issues/1350 2021-07-14 09:39:56 +08:00
d96e02eca0 完成光环前端优化汇总第二周(1~3) https://git.ghzs.com/pm/halo-app-issues/-/issues/1342 2021-07-13 17:52:36 +08:00
lyr
398b1ae58b 【光环助手V5.1.0】新社区2期-引导页 https://git.ghzs.com/pm/halo-app-issues/-/issues/1349 2021-07-13 14:30:59 +08:00
0749205bef 更新离线已收录游戏列表 2021-07-13 14:13:56 +08:00
54ec115fee 修复判断新老用户接口问题 2021-07-13 12:37:08 +08:00
lyr
ebe7b84dc8 【光环助手V5.1.0】个性化推荐合规相关(2)https://git.ghzs.com/pm/halo-app-issues/-/issues/1308 2021-07-13 09:38:49 +08:00
lyr
332abe66f5 【光环助手V5.1.0】光环前端优化汇总第二周(5 优化)https://git.ghzs.com/pm/halo-app-issues/-/issues/1342 2021-07-12 14:33:31 +08:00
lyr
9dccbfbd51 【光环助手V5.1.0】光环前端优化汇总第二周(5)https://git.ghzs.com/pm/halo-app-issues/-/issues/1342 2021-07-12 14:13:22 +08:00
23a8c9e6aa 临时处理 JCenter 依赖问题 2021-07-12 11:32:41 +08:00
1e4408ac6e 【光环助手V5.1.0】新社区2期-消息中心相关优化 https://git.ghzs.com/pm/halo-app-issues/-/issues/1309 2021-07-09 16:06:33 +08:00
lyr
05ece9c999 光环助手v5.0.0-新社区后台问题汇总(20210708)(8 优化) https://git.ghzs.com/pm/halo-app-issues/-/issues/1364 2021-07-09 15:18:23 +08:00
lyr
216905e455 光环助手v5.0.0-新社区APP端问题汇总(20210708)(8) https://git.ghzs.com/pm/halo-app-issues/-/issues/1364 2021-07-09 14:27:37 +08:00
a1bd88dad8 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-09 12:41:14 +08:00
16c66c707f 【光环助手V5.1.0】新社区2期-发布页引导(帖子、提问)https://git.ghzs.com/pm/halo-app-issues/-/issues/1351 2021-07-09 12:40:56 +08:00
lyr
50cdea2b03 光环助手v5.0.0-新社区APP端问题汇总(20210707)(11) https://git.ghzs.com/pm/halo-app-issues/-/issues/1360 2021-07-09 11:12:49 +08:00
95b0fda15a 版本号更新到 5.1.0 2021-07-09 10:29:32 +08:00
9c99273f81 光环助手v5.0.0-新社区APP端问题汇总(20210707)(1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1360 2021-07-08 21:47:46 +08:00
8d9d36a5df 移除部分无用代码 2021-07-08 18:25:18 +08:00
lyr
b22d168e97 光环助手v5.0.0-新社区APP端问题汇总(20210706)(10 优化) https://git.ghzs.com/pm/halo-app-issues/-/issues/1358 2021-07-08 15:53:49 +08:00
lyr
1fee166e3b 光环助手v5.0.0-新社区APP端问题汇总(20210707)(13) https://git.ghzs.com/pm/halo-app-issues/-/issues/1360 2021-07-08 14:54:25 +08:00
lyr
7c098c29b2 光环助手v5.0.0-新社区APP端问题汇总(20210706)(10) https://git.ghzs.com/pm/halo-app-issues/-/issues/1358 2021-07-08 14:42:19 +08:00
ffc9dc46e2 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-08 11:19:41 +08:00
c491a4084c 修改选择活动标签抖动问题 2021-07-08 11:19:24 +08:00
lyr
0f076a03c5 光环助手v5.0.0-新社区APP端问题汇总(20210707)(11) https://git.ghzs.com/pm/halo-app-issues/-/issues/1360 2021-07-08 10:44:33 +08:00
3345ab2044 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-07 20:41:58 +08:00
5874bdd311 发布帖子/问题视频未上传完不能发布 2021-07-07 20:41:34 +08:00
lyr
4669211196 微调我的光环UI 2021-07-07 18:47:29 +08:00
4879e7f0aa Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-07 18:27:53 +08:00
f5283d386b 1.视频详情点赞按钮防重复点击实现限制为2s 2.修改插入视频时间显示错误问题 2021-07-07 18:27:44 +08:00
lyr
602333f923 光环助手v5.0.0-新社区APP端问题汇总(20210707)第6点 https://git.ghzs.com/pm/halo-app-issues/-/issues/1360 2021-07-07 16:39:23 +08:00
e9810c129e 光环助手v5.0.0-新社区后台问题汇总(20210707)(4) https://git.ghzs.com/pm/halo-app-issues/-/issues/1361 2021-07-07 15:22:56 +08:00
c503f15df9 光环助手v5.0.0-新社区APP端问题汇总(20210706)(13) https://git.ghzs.com/pm/halo-app-issues/-/issues/1358 2021-07-07 14:40:02 +08:00
cba1012bbd Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-07 11:47:39 +08:00
646493ae61 光环助手v5.0.0-新社区APP端问题汇总(20210706)(11) https://git.ghzs.com/pm/halo-app-issues/-/issues/1358 2021-07-07 11:47:30 +08:00
b649089ab0 更换发帖子/问题插入视频接口 2021-07-07 11:46:49 +08:00
lyr
e97fd9023e 光环助手v5.0.0-新社区APP端问题汇总(20210706)第2点 https://git.ghzs.com/pm/halo-app-issues/-/issues/1358 2021-07-07 10:42:39 +08:00
478dd658ab 光环助手v5.0.0-新社区APP端问题汇总(20210706)(5) https://git.ghzs.com/pm/halo-app-issues/-/issues/1358 2021-07-06 18:04:47 +08:00
b313feac01 Merge branch 'dev' of git.ghzs.com:halo/android/assistant-android into dev 2021-07-06 17:24:36 +08:00
c04b82549e 视频帖详情页全屏播放顶部增加标题 2021-07-06 17:24:23 +08:00
lyr
842e62f909 微调个人主页和新分类2.0的UI 2021-07-06 16:28:12 +08:00
lyr
51c6de0506 微调社区-论坛UI 2021-07-06 15:20:19 +08:00
lyr
d350f65c1f 修改活动详情测试环境地址 2021-07-05 18:36:49 +08:00
lyr
aaf8e28abc 光环助手v5.0.0-新社区APP端问题汇总(4)https://git.ghzs.com/pm/halo-app-issues/-/issues/1329 2021-07-05 18:28:07 +08:00
7cb921e969 选择活动标签接口增加location参数 2021-07-05 16:35:57 +08:00
lyr
3a4b41058d 光环助手V5.0.0-新社区展示功能(20200702UI测试问题、UI测试问题补充)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_106840 2021-07-05 15:03:28 +08:00
lyr
26308d1852 光环助手V5.0.0-新社区其他相关改动功能(0702 产品测试问题 1)https://git.ghzs.com/pm/halo-app-issues/-/issues/1254#note_106775 2021-07-05 14:24:34 +08:00
af54932309 Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-07-05 11:51:03 +08:00
83799048ed 光环助手v5.0.0-新社区APP端问题汇总(2) https://git.ghzs.com/pm/halo-app-issues/-/issues/1329 2021-07-05 11:50:54 +08:00
ce410ad2a0 更改网页 url 的地址添加来源 query 的实现 2021-07-05 09:30:55 +08:00
933c40458f 社区插入视频length字段的统一单位为秒 2021-07-05 09:24:10 +08:00
6ae7578281 光环助手V5.0.0-新社区展示功能(0702 产品测试问题1,3,4) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-07-03 15:25:57 +08:00
lyr
72b804e26e 处理论坛信息流GIF图边框显示不对的问题 2021-07-02 18:03:48 +08:00
lyr
7e58cdc59e 修改社区-论坛Tab每次进入页面都会被切割的问题 2021-07-02 17:20:20 +08:00
lyr
d782ea90b6 光环助手V5.0.0-新社区展示功能(0701 产品测试问题 3)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_106529 2021-07-02 15:18:49 +08:00
lyr
efffd752f5 提交漏传资源文件 2021-07-02 14:25:20 +08:00
47f3f45fe6 修复推荐优先专题游戏没有推荐信息时简介不显示大小的问题 2021-07-02 10:30:00 +08:00
b4e12e2ec3 Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-07-01 21:08:15 +08:00
2d331f6294 光环助手V5.0.0-新社区展示功能(0701 产品测试问题4) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-07-01 21:08:09 +08:00
lyr
feb4137f84 光环助手V5.0.0-新社区展示功能(V5.0.0-新社区展示功能-UI测试问题汇总 7 gif没有边框问题)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_105161 2021-07-01 18:57:45 +08:00
6a2c4a2967 Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-07-01 18:16:32 +08:00
68fc5532ff 1.用户回答了问题消息通知跳转新回答详情 2.删除无用导包 2021-07-01 18:16:26 +08:00
lyr
43ae479271 光环助手V5.0.0-新社区展示功能(0630 产品测试问题 5)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_106346 2021-07-01 18:12:52 +08:00
7a3ea4d939 处理版本升级时的数据库更新问题 2021-07-01 18:04:43 +08:00
lyr
1bcb343355 光环助手V5.0.0-新社区展示功能(0630 运营测试问题 4)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_106346 2021-07-01 17:54:36 +08:00
1b7a601cee 处理游戏详情点击启动状态的下载按钮的滚动问题 2021-07-01 16:26:53 +08:00
lyr
8bc9544dc2 光环助手V5.0.0-新社区展示功能(0630 运营测试问题 2)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_106346 2021-07-01 16:03:13 +08:00
e586e65e44 Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-07-01 15:47:38 +08:00
8096da5f50 光环助手V5.0.0-新社区展示功能(0701 产品测试问题1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-07-01 15:47:34 +08:00
91868fda15 微调专题游戏推荐语布局 2021-07-01 14:48:23 +08:00
lyr
a71e8824ce 光环助手V5.0.0-新社区展示功能(0629 产品测试问题 15-17)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_106052 2021-07-01 11:04:28 +08:00
6cbc354921 完成前端优化汇总(16) https://git.ghzs.com/pm/halo-app-issues/-/issues/1260 2021-06-30 18:14:50 +08:00
lyr
79e2077a3d 光环助手V5.0.0-新社区展示功能(0629 产品测试问题 14)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_106052 2021-06-30 18:04:55 +08:00
lyr
6ea3ed067d 光环助手V5.0.0-新社区展示功能(V5.0.0-新社区展示功能-UI测试问题汇总 7)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_105805 2021-06-30 17:06:22 +08:00
lyr
84fe33becc 光环助手V5.0.0-新社区其他相关改动功能 (0629UI问题 4) https://git.ghzs.com/pm/halo-app-issues/-/issues/1254#note_106173 2021-06-30 16:38:25 +08:00
276f53fead Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-30 16:15:06 +08:00
0789cc6fdf 光环助手V5.0.0-新社区其他相关改动功能(0625 运营测试问题2(1)) https://git.ghzs.com/pm/halo-app-issues/-/issues/1254 2021-06-30 16:15:01 +08:00
lyr
3a563b7bd0 光环助手V5.0.0-新社区其他相关改动功能 (0628 产品测试问题 2) https://git.ghzs.com/pm/halo-app-issues/-/issues/1254#note_105919 2021-06-30 15:55:30 +08:00
lyr
9b8ba0c3e6 光环助手V5.0.0-新社区其他相关改动功能(20210622UI测试问题、0623 产品测试问题、0625 运营测试问题)https://git.ghzs.com/pm/halo-app-issues/-/issues/1254 2021-06-30 15:38:38 +08:00
258f054161 光环助手V5.0.0-新社区展示功能(0629 运营测试问题22) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-30 15:15:19 +08:00
258c8db0f9 光环助手V5.0.0-新社区展示功能(0629 运营测试问题23,24) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-30 11:44:16 +08:00
741d0b11af Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-29 18:47:13 +08:00
9267896b50 光环助手V5.0.0-新社区展示功能(0629 运营测试问题13) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-29 18:47:03 +08:00
85c0bd69c1 调整 VolumeObserver 入参,避免内存泄漏 2021-06-29 18:45:58 +08:00
2436507b7b Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-29 17:58:06 +08:00
1b68b07e53 光环助手V5.0.0-新社区展示功能(0629 产品测试问题8,10) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-29 17:57:55 +08:00
lyr
c4ba28de1a 光环助手V5.0.0-新社区展示功能(20200629UI测试问题)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_106168 2021-06-29 17:26:37 +08:00
lyr
8daa55c16a 光环助手V5.0.0-新社区展示功能 (0629 产品测试问题 第4、5点) 2021-06-29 17:09:47 +08:00
lyr
5e6c6aca13 光环助手V5.0.0-新社区展示功能(V5.0.0-新社区展示功能-UI测试问题补充 第2点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_105806 2021-06-29 16:31:06 +08:00
lyr
e08ab790ee 优化新分类2.0引导UI 2021-06-29 15:59:26 +08:00
lyr
a394901cb4 光环前端优化汇总(2021年5月)(20210629UI问题补充) https://git.ghzs.com/pm/halo-app-issues/-/issues/1260#note_106103 2021-06-29 15:34:12 +08:00
lyr
2aaa7f2795 优化社区-论坛Tab的UI 2021-06-29 15:07:10 +08:00
7b0d2a25c8 Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-29 09:40:35 +08:00
e3f553a96f 光环助手V5.0.0-新社区展示功能(0628 产品测试问题4) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-29 09:40:25 +08:00
lyr
809a25e3e2 光环助手V5.0.0-新社区展示功能(0628测试问题-热门论坛应该不展示已关注的论坛)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_105293 2021-06-28 18:58:20 +08:00
lyr
88041d9b5f 优化社区-推荐Tab搜索框UI 2021-06-28 16:57:40 +08:00
lyr
a11d741f6f 修复社区推荐Tab和论坛详情在当前论坛列表为空时,发布新内容而未显示的问题 2021-06-28 16:44:06 +08:00
1283def6da 修复首页搜索栏嵌套滚动不灵敏的问题 2021-06-28 16:33:21 +08:00
9c621c31a2 光环助手V5.0.0-新社区展示功能(0621 产品测试问题8(11)) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-28 15:54:50 +08:00
9e3f40723f 光环助手V5.0.0-新社区发布功能(0628 产品测试问题1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1252 2021-06-28 15:13:46 +08:00
682bb9a3e7 Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-28 14:53:55 +08:00
9f2d164c70 光环助手V5.0.0-新社区展示功能(0628 产品测试问题1,2) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-28 14:53:49 +08:00
lyr
bad5130047 修复社区-推荐Tab本人新发布的视频贴时间显示不对问题 2021-06-28 14:23:23 +08:00
e3f831e0ca 修复应用内网页因为地址带特殊字符而出现的加载问题 2021-06-28 11:45:04 +08:00
a1432452f5 Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-28 11:31:30 +08:00
b3cc68b7c6 修改跳转至新的问题详情和回答详情 2021-06-28 11:31:25 +08:00
lyr
cc1f5a269a 修改个人主页发布Tab-切换子tab视频还在播放的问题 2021-06-28 11:27:25 +08:00
a2892a713a Merge branch 'release' into dev-5.0.0
# Conflicts:
#	dependencies.gradle
2021-06-28 11:01:59 +08:00
lyr
c342e22ce4 Merge remote-tracking branch 'origin/dev-5.0.0' into dev-5.0.0 2021-06-28 10:02:51 +08:00
lyr
57dcbab096 光环助手V5.0.0-新社区展示功能(V5.0.0-新社区展示功能-UI测试问题补充)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_105806 2021-06-28 10:02:39 +08:00
5aa4c5bcbc 光环助手V5.0.0-新社区展示功能(0625 产品测试问题3,4(2)) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-28 09:58:16 +08:00
lyr
039e1fc957 光环助手V5.0.0-新社区展示功能(0625 产品测试问题 第2点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_105792 2021-06-25 18:55:30 +08:00
f7b2d10543 Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-25 17:24:13 +08:00
38972eca76 光环助手V5.0.0-新社区发布功能(UI测试问题汇总0621AM 9) https://git.ghzs.com/pm/halo-app-issues/-/issues/1252 2021-06-25 17:24:05 +08:00
lyr
c2d853d709 【光环助手V5.0.0】提问帖相关需求(V5.0.0-新社区提问帖相关需求-UI测试问题汇总 第1点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1251#note_105291 2021-06-25 16:47:04 +08:00
lyr
239209dbf2 光环助手V5.0.0-新社区展示功能(0623 产品测试问题 第1、2、4点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_105473 2021-06-25 16:05:43 +08:00
lyr
ba1f3e20d3 优化新分类2.0引导图逻辑 2021-06-25 15:02:57 +08:00
lyr
92ac62d949 光环助手V5.0.0-新社区展示功能(20210622UI测试问题)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_105369 2021-06-25 15:00:29 +08:00
6499e3b718 修改视频贴详情用户关注按钮样式 2021-06-25 11:12:23 +08:00
5f0d2604ec Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-25 10:09:49 +08:00
5f7bb6cb49 光环助手V5.0.0-新社区发布功能(0622 产品测试问题2(2)) https://git.ghzs.com/pm/halo-app-issues/-/issues/1252 2021-06-25 10:09:40 +08:00
lyr
7360eef244 光环助手V5.0.0-新社区展示功能(V5.0.0-新社区展示功能-UI测试问题汇总 第1-10点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_105161 2021-06-25 09:27:03 +08:00
52e6feab59 修改视频帖详情游戏标签样式 2021-06-24 20:56:09 +08:00
5f44252410 视频贴详情调整视频区域高度 2021-06-24 18:35:13 +08:00
7ae3b1cafb Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-24 18:13:49 +08:00
0fb77e79fd 光环助手V5.0.0-新社区发布功能(UI测试问题补充0624PM 1,2,3) https://git.ghzs.com/pm/halo-app-issues/-/issues/1252 2021-06-24 18:13:39 +08:00
lyr
d4ae878947 光环前端优化汇总(2021年5月)(第9-11点) https://git.ghzs.com/pm/halo-app-issues/-/issues/1260 2021-06-24 17:58:04 +08:00
37a2e3cc0e 光环助手V5.0.0-新社区发布功能(UI测试问题汇总4,9) https://git.ghzs.com/pm/halo-app-issues/-/issues/1252 2021-06-24 17:04:21 +08:00
30595741cc Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-24 16:46:02 +08:00
11f177d572 完成视频贴详情仿bilibili交互效果 2021-06-24 16:45:51 +08:00
9e13675ca5 完成前端优化汇总(0623测试2) https://git.ghzs.com/pm/halo-app-issues/-/issues/1260#note_105557 2021-06-24 15:14:34 +08:00
144a0641b6 完成游戏专题功能优化(20210617测试问题6) 和 20210622UI测试 2021-06-24 15:13:35 +08:00
95266f6f68 【光环助手V5.0.0】提问帖相关需求(0623测试反馈2) https://git.ghzs.com/pm/halo-app-issues/-/issues/1251 2021-06-24 11:27:56 +08:00
9ded647c0d 光环助手V5.0.0-新社区发布功能(0623 产品测试问题1,2) https://git.ghzs.com/pm/halo-app-issues/-/issues/1252 2021-06-24 10:57:49 +08:00
f566e4d5c3 Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-24 10:40:31 +08:00
5df3efd087 光环助手V5.0.0-新社区展示功能(0623 产品测试问题5) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-24 10:40:19 +08:00
98f4361bf0 Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-24 10:09:21 +08:00
000d1e020a 完成关于MOD游戏下载用户的相关限制(APP 接口修改) https://git.ghzs.com/pm/halo-app-issues/-/issues/1293 2021-06-24 10:07:58 +08:00
lyr
93431a7e37 光环助手V5.0.0-新社区展示功能(0622 产品测试问题 2、4、5)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_105293 2021-06-23 17:59:34 +08:00
lyr
d54523cdb6 光环助手V5.0.0-新社区展示功能(0621 产品测试问题 0、1、2、5)https://git.ghzs.com/pm/halo-app-issues/-/issues/1253#note_105232 2021-06-23 15:59:03 +08:00
ed468b7c73 【光环助手V5.0.0】提问帖相关需求(0623测试反馈1,3) https://git.ghzs.com/pm/halo-app-issues/-/issues/1251 2021-06-23 15:55:11 +08:00
bad6211bf5 光环助手V5.0.0-新社区发布功能 0622 产品测试问题 1,2(1)(3)(4)(5) https://git.ghzs.com/pm/halo-app-issues/-/issues/1252 2021-06-23 14:33:27 +08:00
2743d4d0de Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-22 15:43:58 +08:00
a0f5387917 光环助手V5.0.0-新社区展示功能(0621 产品测试问题4,6,7,8(1,3,4,5,7,8,9,11)) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-22 15:43:49 +08:00
1abfcfdb51 abiFilters 添加 arm64 2021-06-22 14:28:42 +08:00
ecc0a75f52 光环助手V5.0.0-新社区展示功能(UI测试问题汇总10-14) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-21 16:53:21 +08:00
c98bf26337 光环助手V5.0.0-新社区发布功能(UI测试问题汇总1,3-9) https://git.ghzs.com/pm/halo-app-issues/-/issues/1252 2021-06-21 15:44:25 +08:00
187d07d02d 完成光环前端优化汇总(5, 14, 16, 17) https://git.ghzs.com/pm/halo-app-issues/-/issues/1260 2021-06-18 17:51:59 +08:00
c47c23ad21 修复因懒加载优化而造成的下载进度更新异常问题 2021-06-18 09:46:30 +08:00
e6ea1738e5 光环前端优化汇总(2021年5月)(4) https://git.ghzs.com/pm/halo-app-issues/-/issues/1260 2021-06-17 16:32:55 +08:00
40983ecebc 光环前端优化汇总(2021年5月)(3) https://git.ghzs.com/pm/halo-app-issues/-/issues/1260 2021-06-17 14:41:35 +08:00
fbe73ce4da Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-16 18:43:48 +08:00
d46617237f 光环助手V5.0.0-新社区展示功能(社区搜索页的搜索结果增加论坛tab) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-16 18:43:43 +08:00
lyr
0894957d73 修改帖子再次编辑修改成功后的闪退问题 2021-06-16 17:58:15 +08:00
lyr
7f2145d0ef 论坛信息流视频在切换tab时暂停播放 2021-06-16 15:19:21 +08:00
lyr
acefbba6bf Merge remote-tracking branch 'origin/dev-5.0.0' into dev-5.0.0 2021-06-16 14:12:31 +08:00
lyr
660a091b7b 【光环助手5.0.0】一键登录相关优化(第1点)https://git.ghzs.com/pm/halo-app-issues/-/issues/1294 2021-06-16 14:12:25 +08:00
103078d63e 修改图片预览页面点击查看帖子异常 2021-06-16 11:00:28 +08:00
lyr
178395f531 优化论坛信息流审核中视频的显示 2021-06-16 09:52:52 +08:00
lyr
8db213d040 【光环助手V5.0.0】提问帖相关需求(0615测试反馈 3)https://git.ghzs.com/pm/halo-app-issues/-/issues/1251#note_104578 2021-06-16 09:51:13 +08:00
lyr
5904d3c5c7 光环助手V5.0.0-新社区展示功能(前置说明 1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1253 2021-06-15 17:53:01 +08:00
1a511ebbad 【光环助手V5.0.0】提问帖相关需求 (0615测试反馈 1) https://git.ghzs.com/pm/halo-app-issues/-/issues/1251 2021-06-15 17:09:04 +08:00
ef97e8cc6d 新社区插入视频后封面上传到oss 2021-06-15 16:07:21 +08:00
7b11b0e99a Merge branch 'dev-5.0.0' of git.ghzs.com:halo/android/assistant-android into dev-5.0.0 2021-06-11 17:47:24 +08:00
01034f093a 处理合并冲突 2021-06-11 17:45:52 +08:00
8e28766449 Merge branch 'feature-bbs' into dev-5.0.0
# Conflicts:
#	app/src/main/AndroidManifest.xml
#	app/src/main/java/com/gh/common/util/NewLogUtils.kt
#	app/src/main/java/com/gh/gamecenter/personalhome/UserHomeFragment.kt
#	app/src/main/java/com/gh/gamecenter/qa/dialog/MoreFunctionPanelDialog.kt
#	app/src/main/res/layout/fragment_home.xml
#	app/src/main/res/values/colors.xml
2021-06-11 17:34:03 +08:00
lyr
d06d0bd15c 一键登录设置每步超时时间为1.5秒 2021-06-11 17:04:31 +08:00
4fe4a33b80 光环前端优化汇总(2021年5月)(13) https://git.ghzs.com/pm/halo-app-issues/-/issues/1260 2021-06-11 14:55:10 +08:00
4a8e91bbff 对接视频帖加精 2021-06-11 10:09:13 +08:00
lyr
b1f6c7d55c 1.优化游戏详情顶部视频和论坛信息流视频的静音设置
2.修改个人主页发布记录的类型、点赞、评论、回答数据字段转化
2021-06-10 18:06:55 +08:00
lyr
727eeac52e 修改游戏详情顶部静音设置 2021-06-10 16:26:35 +08:00
fb26ec0c13 Merge branch 'feature-bbs' of git.ghzs.com:halo/android/assistant-android into feature-bbs 2021-06-10 15:45:55 +08:00
3e25619bf8 论坛修改点赞、评论、回答、收藏数据字段 2021-06-10 15:45:50 +08:00
lyr
e81fa4ff1a 论坛根据类型展示图标 2021-06-10 11:41:02 +08:00
d7d2bf2667 更换问题收藏/取消收藏接口 2021-06-10 11:16:39 +08:00
c763a0a9e6 完成选择论坛-搜索论坛功能 2021-06-10 10:28:06 +08:00
2afb99b603 修改编辑视频帖发布按钮一直是置灰状态 2021-06-09 18:38:06 +08:00
lyr
2e09ac033b 优化视频贴类型转化 2021-06-09 18:25:27 +08:00
lyr
54747143d1 1.修改论坛详情-视频Tab无法下拉刷新问题
2.修改论坛详情-列表视频贴未显示内容问题
3.修改论坛详情-全部Tab的问答贴回答数量不对问题
2021-06-09 18:10:10 +08:00
076c98977d 1.修改问题详情评论不显示作者标签 2.修改问题详情点击顶部返回按钮无法关闭页面 2021-06-09 16:47:19 +08:00
lyr
b96104fcd9 对接社区推荐Tab的接口 2021-06-09 16:26:04 +08:00
lyr
daece9337c 修改浏览记录-帖子Item左下角对应论坛UI未显示问题 2021-06-09 14:35:13 +08:00
lyr
3547330e2a 论坛详情页-内容搜索结果增加视频帖的展示 2021-06-08 18:19:20 +08:00
4d76fb0159 Merge branch 'feature-bbs' of git.ghzs.com:halo/android/assistant-android into feature-bbs 2021-06-08 17:47:04 +08:00
f34a95b36f 1.更换提问、草稿、问题详情接口 2.对接视频帖评论接口 2021-06-08 17:46:56 +08:00
lyr
6e3cb7a196 1.论坛列表增加视频自动播放功能
2.论坛首页搜索适配视频贴展示
2021-06-08 17:39:06 +08:00
03f87c46ba 游戏更新相关接口添加版本和渠道 https://git.ghzs.com/pm/halo-app-issues/-/issues/1267 2021-06-08 17:08:02 +08:00
b052ba1635 移除无用的 activity 声明 2021-06-08 16:34:27 +08:00
d1183199cd 全局更改跳转视频详情的页面 https://git.ghzs.com/pm/halo-app-issues/-/issues/1254 2021-06-08 16:31:30 +08:00
31fed948f0 更换社区-推荐的接口 https://git.ghzs.com/pm/halo-app-issues/-/issues/1246 2021-06-08 15:54:44 +08:00
f1c5b80dc2 完成社区内容浏览日志上报 https://git.ghzs.com/pm/halo-app-issues/-/issues/1246 2021-06-08 15:51:10 +08:00
86f2aec9ca 修复社区内容分享日志上报丢失的问题 2021-06-08 15:28:26 +08:00
a218f13ccb Merge branch 'feature-bbs' of git.ghzs.com:halo/android/assistant-android into feature-bbs 2021-06-08 10:15:23 +08:00
1fc37571df 修改ImageUtils不能加载视频第一帧的图片 2021-06-08 10:15:12 +08:00
lyr
576f06f250 1.修改论坛详情页列表内容显示不对问题
2.修改论坛信息流左下角对应论坛图标显示不出来问题
2021-06-08 10:03:36 +08:00
3805cb52f6 优化论坛插入视频 2021-06-07 21:38:07 +08:00
lyr
f8d7377236 完成新社区-活动Tab 2021-06-07 18:45:43 +08:00
354fa13a35 优化论坛发布功能 2021-06-07 16:02:29 +08:00
3c533b896b 对接我的论坛帖子、我的收藏帖子 2021-06-05 19:18:57 +08:00
11c6618c0f Merge branch 'feature-bbs' of git.ghzs.com:halo/android/assistant-android into feature-bbs 2021-06-05 10:27:55 +08:00
898bf3e432 粗略完成新问题详情页面 2021-06-05 10:27:49 +08:00
lyr
0faf408762 修复新社区相关数据库迁移报错问题 2021-06-04 17:44:49 +08:00
lyr
1dbabe3cda 提交漏传文件 2021-06-04 16:19:07 +08:00
lyr
a107a19d95 完成社区-论坛Tab-官方论坛对接 2021-06-04 15:43:53 +08:00
lyr
58ea0bb51d 完善论坛信息流视频播放逻辑 2021-06-04 14:25:17 +08:00
557f22b23b rxPermission 更新至 androidX 版本 2021-06-03 16:59:40 +08:00
f40a192e82 调整带 chucker 打包脚本 2021-06-03 16:54:55 +08:00
84f3556275 Merge remote-tracking branch 'origin/release' into dev-5.0.0
# Conflicts:
#	dependencies.gradle
2021-06-03 16:47:45 +08:00
cddc2bff2b 添加简单的 Log 工具类 2021-06-03 16:38:58 +08:00
a0b8caa60a 完成5.0.0游戏专题功能优化 https://git.ghzs.com/pm/halo-app-issues/-/issues/1247 2021-06-03 16:38:12 +08:00
lyr
22cadc77f0 1.修改"社区-推荐Tab最近浏览列表首个Item未读红点已读但未消失"问题
2.修改首页底部导航栏动画不对应问题
3.修改论坛详情页关注按钮显示不全问题
2021-06-03 11:28:09 +08:00
28fb45f0b8 添加简单的 Log 工具类 2021-06-03 11:27:44 +08:00
3d7f8b3f41 Merge branch 'feature-bbs' of git.ghzs.com:halo/android/assistant-android into feature-bbs 2021-06-03 10:30:04 +08:00
bba326cfd9 1.收藏/取消收藏问题 2.修改我的论坛 2021-06-03 10:29:57 +08:00
lyr
f76bb7070a 优化社区-推荐Tab列表显示 2021-06-03 10:28:36 +08:00
lyr
a6a3d769bd 完成论坛详情页的视频Tab和个人主页的发布Tab的视频分类 2021-06-02 18:16:56 +08:00
ea5627b3da 区分正式包和测试包的默认渠道,避免正式包因渠道丢失而回落到测试渠道 2021-06-02 16:58:41 +08:00
d9807227ec 处理列表分割线显示异常问题 2021-06-01 09:26:26 +08:00
5cc1cfa1a6 完成视频详情推荐列表 2021-05-29 17:17:09 +08:00
35cdd140cc Merge branch 'feature-bbs' of git.ghzs.com:halo/android/assistant-android into feature-bbs 2021-05-29 16:07:48 +08:00
87597dbd1f 完成帖子、问题发布功能 2021-05-29 16:07:33 +08:00
lyr
398f7b6642 1.完成首页和游戏详情的视频播放设置
2.完成个人主页的发布Tab(除视频分类)
2021-05-28 18:52:10 +08:00
lyr
f0ca0a2ab1 推荐页的浏览记录和论坛页的关注论坛增加未读状态 2021-05-27 15:44:01 +08:00
c9ffd4e5bd Merge branch 'feature-bbs' of git.ghzs.com:halo/android/assistant-android into feature-bbs 2021-05-26 20:45:06 +08:00
d354b07fe2 1.抽离公共评论详情页面 2.粗略完成问题详情页面UI(未对接接口) 2021-05-26 20:44:46 +08:00
lyr
71a61fdef0 粗略完成社区的推荐Tab和论坛Tab 2021-05-26 19:00:06 +08:00
2eded37321 首页社区 TAB 和首页视频 TAB 互换位置 2021-05-26 10:20:06 +08:00
f35baf0e5b 完成[安装方式管理]新增[白名单]功能 https://git.ghzs.com/pm/halo-app-issues/-/issues/1264 2021-05-25 17:54:02 +08:00
4d64281c78 修改视频贴保存草稿功能 2021-05-24 21:20:04 +08:00
95f1692434 Merge branch 'feature-bbs' of git.ghzs.com:halo/android/assistant-android into feature-bbs 2021-05-24 15:51:12 +08:00
f1302dd1bb 完成视频详情顶部区域 2021-05-24 15:51:00 +08:00
lyr
fec34f1efa 提交遗漏资源文件 2021-05-24 10:45:18 +08:00
lyr
def3b55e51 完成光环助手V5.0.0-新社区其他相关改动功能(第七点UI) 2021-05-24 09:57:08 +08:00
4549f78ede 粗略完成发视频贴功能 2021-05-23 21:04:00 +08:00
3054b56f4a Merge branch 'feature-bbs' of git.ghzs.com:halo/android/assistant-android into feature-bbs 2021-05-20 14:13:28 +08:00
4cf5e0f2f4 论坛富文本支持插入视频功能 2021-05-20 14:13:22 +08:00
lyr
1a18c82f06 个人主页-根据个人信息内容设置背景的高度 2021-05-19 18:51:49 +08:00
lyr
b8fbb429f1 个人主页-根据个人信息内容设置背景的高度 2021-05-19 18:40:25 +08:00
lyr
005229fe8a 提交个人主页游戏tab改动遗漏部分 2021-05-19 18:31:25 +08:00
lyr
3a0bbbf3d0 完成个人主页游戏tab改动 2021-05-19 18:09:34 +08:00
lyr
868e6d31b8 提交论坛详情页顶部UI遗漏部分 2021-05-18 15:31:29 +08:00
lyr
a687f8d877 完成论坛详情页顶部UI调整 2021-05-18 15:15:38 +08:00
3e86a40215 发提问改为富文本 2021-05-13 20:34:09 +08:00
6a68839d0f 1完成选择活动弹窗UI 2.发布帖子、提问删除添加标签入口 2021-05-13 15:31:18 +08:00
2140c9eb40 Update README.md 2021-05-13 15:13:41 +08:00
942af39b2d 大致完成选择论坛、选择本地视频、预览视频、发视频UI 2021-05-12 16:55:18 +08:00
lyr
a0331f0437 光环前端优化汇总(2021年5月)第6点 https://git.ghzs.com/pm/halo-app-issues/-/issues/1260 2021-05-12 14:46:31 +08:00
lyr
63798c94cf Merge branch 'dev' into dev-5.0.0
# Conflicts:
#	app/src/main/java/com/gh/common/util/QuickLoginHelper.kt
2021-05-10 17:06:12 +08:00
lyr
6c2ff6a94f 光环助手V4.9.0-优化登录功能(第3期)(0507 补充运营意见--增加一键登录说明弹窗) https://git.ghzs.com/pm/halo-app-issues/-/issues/1206#note_100061
(cherry picked from commit 3f5f3bf57c)
2021-05-08 16:15:54 +08:00
2211f562c7 抽离发布帖子、回答公共代码 2021-05-08 15:52:41 +08:00
7a0165375c 去掉视频预加载打印日志 2021-04-29 14:46:47 +08:00
e280ea008a 升级gsyVideoPlayer版本并修改预加载 2021-04-28 11:08:04 +08:00
681 changed files with 77113 additions and 10804 deletions

View File

@ -1,69 +1,68 @@
# 光环助手Android客户端
### APK打包配置
### 概述
* 使用[ApkChannelPackage](https://github.com/ltlovezh/ApkChannelPackage)的方案
* 打包命令,视情况使用:
光环助手Android客户端目前使用 Kotlin 作为主要开发语言,以 MVVM 作为参考架构模式进行开发
> 打包Tinker基准包`./scripts/tinker_release_base.sh`
### 约束
> 以Tinker基准包打渠道包`./scripts/tinker_release_channel.sh`
为编写易读易维护且较健壮的代码,可参考以下约束
> 以Tinker基准包打补丁包`./scripts/tinker_release_patch.sh`
1. 尽量将逻辑代码放置于 ViewModel 中View 中只执行 UI 操作
2. 尽量使 View 在被销毁之后仍能恢复状态,处理方式可参考 [保存界面状态](https://developer.android.com/topic/libraries/architecture/saving-states)
3. 尽量参考原有文件结构及命名规范,即以 大模块 - 小模块 的形式生成包关系
4. 遵循最小改动原则,在提交代码前务必先检查变动的代码,尽量以可控的变动规模来构成一个 commit ,以便日后追踪问题
5. 代码规范可参考 [AOSP Java 风格](https://source.android.com/setup/contribute/code-style)
6. 尽量使用 Kotlin 来写新文件
7. RecyclerView 的 ViewHolder 尽量使用 DataBinding 来减少代码以及提高可读性
8. Commit 前请确保不带入非项目必须文件,可手动修改 [.gitignore](https://stackoverflow.com/questions/8527597/how-do-i-ignore-files-in-a-directory-in-git) 文件忽略
9. 新页面请勿使用 ButterKnife 来进行 View 获取和绑定,请使用 ViewBinding
10. No AsyncTask!
### 混淆配置
### 公用部分
* 配置文件Android默认配置+proguard-rules.txt等
* 参考libraries下每个项目独立的配置文件`proguard-project.txt`
本项目使用 LiveData 实现了一个简单通用的基础列表分页功能,具体可见 `ListFragment`, `ListViewModel` 等类,理想情况下只需少量代码即可新建一个简单分页列表
### apk大小优化
### 首次拉取项目代码
* 限制resConfig资源集
* 开启ShrinkResources
* 开启混淆使用minifyEnabled(仅在release开启
* pngquant对png压缩、png/jpg->webp(未尝试)
`git clone -b dev git@git.ghzs.com:halo/android/assistant-android.git --recursive`
### git 版本管理
本项目使用简化版的 git flow 来管理分支,细节请看 [光环安卓简单 git 规范](https://git.ghzs.com/halo/android/assistant-android/-/wikis/%E5%85%89%E7%8E%AF%E5%AE%89%E5%8D%93%E7%AE%80%E5%8D%95-git-%E8%A7%84%E8%8C%83)
### API 环境配置
本项目使用 Build Variants 来切换 API 环境
* internal 为测试环境
* publish 为正式环境
### 图片资源配置
* 新增图片资源时,默认只添加最高规格的 xxxhdpi 文件
* 新增图片资源时,需要将其转换为 .webp 格式 (包括含透明图层的图片默认质量为90%) (转换后体积变大的文件除外)
### 第三方appkey等配置
* 修改`gradle.properties`文件将各种key填入其中实现统一管理
* 通过gradle文件内的resValue/buildConfigField/manifestPlaceHolder方式实现编译期间修改具体情况请参考``./build.gradle``和``./app/build.gradle``配置
### 拉取代码步骤
### 混淆配置
1. 拉取主项目代码: `git clone -b dev git@gitlab.ghzhushou.com:halo/assistant-android.git`
2. 初始化公用类库: `bash ./scripts/submodules_init.sh`
* 本项目使用了微信的 [AndResGuard](https://github.com/shwenzhang/AndResGuard) 作为资源混淆压缩方案,新增需要使用 `getIdentifier` 获取的资源文件时需要添加至白名单
* 本项目默认使用 R8 作为混淆工具,往 proguard-rules.txt 添加 proguard 新配置项时请检查可用性(如语法等)
### submodule管理方式(只拉取master)
### APK打包配置
* 提交代码需要cd到submodule文件夹去做修改
* 更新远端代码,`bash ./scripts/submodules_update.sh`
* 本项目使用了 [VasDolly](https://github.com/Tencent/VasDolly) 作为渠道包实现方案
* 打包命令,具体参数请见相应文件:
> 打内部测试包:`./scripts/test_build.sh`
> 打正式发布包:`./scripts/build_with_simple_backup.sh`
### TODO
* GSON 序列化用统一一个, GsonUtil fromJson
* CleanApkAdapter 转化字符串size工具函数 比如SpeedUtils
* getString 解决 字符串hardcode问题
* ~~Adapter 里面clicklistener 用接口传参将点击操作委托给controller~~
* ~~Adapter ViewHolder的功能部分重写到ViewHolder类本身~~
* ~~activity 统一入口未完成(外部入口相关)去除多余activity使用统一toolbar~~
* ~~release / debug compile不同的类库不需要再做什么开关~~
* ~~Toolbar分离有图形按钮/没有图形按钮~~
### TODO Since 3.1
- 解决 Utils 工具类引发的内存泄漏问题
- 把原有 EventBus 的消息 Type 统一到一个文件内
- 将实现细节从 View(Fragment、Activity) 剥离并以 MVVM 结构改造
- ~~将 ListViewModel 所对应的 ListRepository 合并到 ListViewModel 中~~
- 依照光环助手界面功能以大模块 - 小模块的方式去修改包结构,包内文件建议以包名摘要作为前缀
- ~~使用 RxJava 的 Debounce 和 Map 操作优化搜索触发机制 参考资料:[1](https://proandroiddev.com/building-an-autocompleting-edittext-using-rxjava-f69c5c3f5a40),[2](https://medium.com/@kurtisnusbaum/rxandroid-basics-part-2-6e877af352)~~
- ~~把 ListViewModel 的数据结构类型转换方式换为抽象方法,让继承的类实现,避免出现无响应的问题~~
- ~~rxjava2 如果接口返回为空 会发生异常:java.lang.NullPointerException: Null is not a valid element (答案编辑) 解决方法->com.gh.gamecenter.retrofit.Response~~
- constraintLayout 1.1.2 导致布局出现异常(问题编辑标签选择弹窗)
- 搞清楚 GameManager 的用途,看能不能去掉
- 重构一下 MainActivity
* 把原有 EventBus 的消息 Type 统一一个文件内
* 将实现细节从 View(Fragment、Activity) 剥离并以 MVVM 结构改造
* 重构 MainActivity

View File

@ -46,6 +46,8 @@ android {
}
ndk {
// 如果不添加 `arm64` 调用系统的 PackageManager 的方法读取安装包信息的时候会出现 native 层闪退,草
// 微博 SDK 没有 64位的 SO添加了 arm64 又会无法使用微博登录,下个版本更新微博 SDK 刻不容缓了!
abiFilters "armeabi-v7a", "x86"
}
@ -216,11 +218,9 @@ dependencies {
testImplementation 'junit:junit:4.12'
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakcanary}"
debugImplementation "com.facebook.stetho:stetho:${stetho}"
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stetho}"
debugImplementation "com.squareup.okhttp3:logging-interceptor:${okHttp}"
debugImplementation "com.gu.android:toolargetool:${toolargetool}"
debugImplementation "com.github.nichbar:WhatTheStack:$whatTheStack"
debugImplementation "com.github.nichbar:WhatTheStack:${whatTheStack}"
implementation "androidx.core:core-ktx:${core}"
implementation "androidx.fragment:fragment-ktx:${fragment}"
@ -276,8 +276,6 @@ dependencies {
implementation "com.daimajia.swipelayout:library:${swipeLayout}"
implementation "com.sina.weibo.sdk:core:${weiboSDK}"
// bugly with tinker support
// implementation "com.tencent.bugly:crashreport_upgrade:${buglyTinkerSupport}"
@ -300,11 +298,11 @@ dependencies {
implementation "com.squareup.picasso:picasso:${picasso}"
// for video streaming
implementation("com.shuyu:gsyVideoPlayer-java:$gsyVideo", {
implementation("com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-java:$gsyVideo", {
exclude module: "gsyvideoplayer-androidvideocache"
exclude group: "tv.danmaku.ijk.media"
})
implementation "com.shuyu:GSYVideoPlayer-exo2:$gsyVideo"
implementation "com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-exo_player2:$gsyVideo"
implementation "android.arch.work:work-runtime:${workManager}"
@ -317,22 +315,22 @@ dependencies {
implementation "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:${mta}"
implementation "com.github.nichbar:AndroidRomChecker:${romChecker}"
debugImplementation "com.github.nichbar.chucker:library:$chucker"
releaseImplementation "com.github.nichbar.chucker:library-no-op:$chucker"
teaImplementation "com.bytedance.applog:RangersAppLog-Lite-cn:$bytedanceApplog"
debugImplementation "com.github.nichbar.chucker:library:${chucker}"
releaseImplementation "com.github.nichbar.chucker:library-no-op:${chucker}"
teaImplementation "com.bytedance.applog:RangersAppLog-Lite-cn:${bytedanceApplog}"
// implementation "com.bytedance.ies.ugc.aweme:opensdk-china-external:$bytedanceAweme"
// implementation "com.bytedance.ies.ugc.aweme:opensdk-common:$bytedanceAweme"
implementation "com.aliyun.dpa:oss-android-sdk:${oss}"
implementation "com.airbnb.android:lottie:$lottie"
implementation "com.airbnb.android:lottie:${lottie}"
implementation "net.lingala.zip4j:zip4j:${zip4j}"
// plugin 需要字符串,故不能用值
implementation "io.sentry:sentry-android:3.2.0"
implementation("com.github.piasy:BigImageViewer:$bigImageViewer", {
implementation("com.github.piasy:BigImageViewer:${bigImageViewer}", {
exclude group: 'com.squareup.okhttp3'
exclude group: 'androidx.swiperefreshlayout'
exclude group: 'com.github.bumptech.glide'
@ -340,10 +338,13 @@ dependencies {
})
implementation "com.github.PhilJay:MPAndroidChart:${chart}"
implementation "com.github.hsiafan:apk-parser:$apkParser"
implementation "org.nanohttpd:nanohttpd:$nanohttpd"
implementation "com.github.hsiafan:apk-parser:${apkParser}"
implementation "org.nanohttpd:nanohttpd:${nanohttpd}"
implementation "com.aliyun.openservices:aliyun-log-android-sdk:$aliyunLog"
implementation "com.aliyun.openservices:aliyun-log-android-sdk:${aliyunLog}"
implementation "com.github.princekin-f:EasyFloat:${easyFloat}"
implementation "io.github.florent37:shapeofview:$shapeOfView"
implementation project(':libraries:LGLibrary')
// implementation project(':libraries:MTA')
@ -461,6 +462,11 @@ andResGuard {
"R.drawable.ic_search_no_18",
"R.drawable.ic_search_no_19",
"R.drawable.ic_search_no_20",
"R.drawable.ic_recommend_activity",
"R.drawable.ic_recommend_discount",
"R.drawable.ic_recommend_function",
"R.drawable.ic_recommend_gift",
"R.drawable.ic_recommend_role",
"R.drawable.login_btn_bg",
"R.drawable.ic_quick_login_check",
"R.drawable.ic_quick_login_uncheck",

Binary file not shown.

View File

@ -131,4 +131,12 @@
### 中国移动一键登录
-dontwarn com.cmic.sso.sdk.**
-keep class com.cmic.sso.sdk.* { *; }
-keep class com.cmic.sso.sdk.* { *; }
### EasyFloat
-keep class com.lzf.easyfloat.* {*;}
### 避免 WebChromeClient 被混淆
-keepclassmembers class * extends android.webkit.WebChromeClient{
public void openFileChooser(...);
}

View File

@ -2,8 +2,6 @@ package com.gh.gamecenter;
import android.app.Application;
import com.facebook.stetho.Stetho;
import com.facebook.stetho.okhttp3.StethoInterceptor;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
@ -17,9 +15,6 @@ import okhttp3.logging.HttpLoggingInterceptor;
public class Injection {
public static boolean appInit(Application application) {
// init stetho
Stetho.initializeWithDefaults(application);
// 监控Bundle大小,预防溢出(需要调试的时候再开启吧!)
// TooLargeTool.startLogging(application);
return true;
@ -30,7 +25,6 @@ public class Injection {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addNetworkInterceptor(interceptor);
builder.addNetworkInterceptor(new StethoInterceptor());
return builder;
}

View File

@ -30,14 +30,8 @@
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<!-- bugly with tinker -->
<!-- <uses-permission android:name="android.permission.READ_LOGS" />-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!--可选,穿山甲提供“获取地理位置权限”和“不给予地理位置权限,开发者传入地理位置参数”两种方式上报用户位置,两种方式均可不选,添加位置权限或参数将帮助投放定位广告-->
<!--请注意:无论通过何种方式提供给穿山甲用户地理位置,均需向用户声明地理位置权限将应用于穿山甲广告投放,穿山甲不强制获取地理位置信息-->
<!--<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />-->
<!-- 如果有视频相关的广告且使用textureView播放请务必添加否则黑屏 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
@ -47,9 +41,11 @@
com.shuyu.gsyvideoplayer.armv7a,
com.shuyu.gsyvideoplayer.x86,
com.shuyu.gsy.base,
shuyu.com.androidvideocache,
com.google.android.exoplayer2,
tv.danmaku.ijk.media.exo2,
pl.droidsonroids.gif" />
pl.droidsonroids.gif,
com.lzf.easyfloat"/>
<!-- 去掉 SDK 一些流氓权限 -->
<uses-permission
@ -70,9 +66,9 @@
android:icon="@mipmap/logo"
android:label="@string/app_name"
android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_config"
android:resizeableActivity="true"
android:theme="@style/AppCompatTheme.APP"
android:networkSecurityConfig="@xml/network_security_config"
tools:replace="android:allowBackup"
tools:targetApi="n">
@ -85,7 +81,6 @@
android:name="io.sentry.breadcrumbs.system-events"
android:value="false" />
<!--android:launchMode = "singleTask"-->
<activity
android:name="com.gh.gamecenter.SplashScreenActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
@ -112,7 +107,6 @@
android:launchMode="singleTask"
android:screenOrientation="portrait" />
<!--android:theme = "@android:style/Theme.Black.NoTitleBar.Fullscreen" 退出时屏幕抖动 -->
<activity
android:name="com.gh.gamecenter.ImageViewerActivity"
android:theme="@style/Theme.Transparent" />
@ -170,6 +164,10 @@
android:name="com.gh.gamecenter.WebActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.SingletonWebActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.FullScreenWebActivity"
android:screenOrientation="portrait" />
@ -475,7 +473,7 @@
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.editor.VideoActivity"
android:name="com.gh.gamecenter.qa.editor.LocalMediaActivity"
android:screenOrientation="portrait" />
<activity
@ -513,6 +511,12 @@
android:theme="@style/Theme.Transparent"
android:windowSoftInputMode="adjustNothing" />
<activity
android:name=".qa.dialog.ChooseForumActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.Transparent"
android:windowSoftInputMode="adjustNothing" />
<activity
android:name="com.gh.gamecenter.video.detail.VideoDetailActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
@ -552,10 +556,6 @@
android:name=".forum.select.ForumSelectActivity"
android:screenOrientation="portrait" />
<activity
android:name=".forum.follow.ForumMyFollowActivity"
android:screenOrientation="portrait" />
<activity
android:name=".forum.detail.ForumDetailActivity"
android:screenOrientation="portrait" />
@ -564,6 +564,10 @@
android:name=".forum.moderator.ModeratorListActivity"
android:screenOrientation="portrait" />
<activity
android:name=".forum.moderator.ApplyModeratorActivity"
android:screenOrientation="portrait"/>
<activity
android:name=".video.label.VideoLabelActivity"
android:screenOrientation="portrait" />
@ -585,10 +589,6 @@
android:screenOrientation="portrait"
android:theme="@style/TransparentStatusBarAndNavigationBar" />
<activity
android:name=".personalhome.excellentcomments.ExcellentCommentsActivity"
android:screenOrientation="portrait" />
<activity
android:name=".simulatorgame.SimulatorGameActivity"
android:screenOrientation="portrait" />
@ -611,8 +611,8 @@
<activity
android:name=".energy.EnergyCenterActivity"
android:screenOrientation="portrait"
android:launchMode="singleTask" />
android:launchMode="singleTask"
android:screenOrientation="portrait" />
<activity
android:name=".energy.EnergyHouseActivity"
@ -638,13 +638,53 @@
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" />
<activity
android:name=".setting.GameDownloadSettingActivity"
android:screenOrientation="portrait" />
<activity
android:name=".setting.VideoSettingActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.video.detail.ForumVideoDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name=".video.videomanager.VideoDraftActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.questions.newdetail.NewQuestionDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.editor.FullScreenVideoActivity"
android:screenOrientation="landscape"
android:theme="@style/AppFullScreenTheme" />
<activity
android:name=".forum.list.ForumListActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.answer.detail.SimpleAnswerDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.cmic.sso.sdk.activity.LoginAuthActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@android:style/Theme.Dialog"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:launchMode="singleTop">
</activity>
android:theme="@android:style/Theme.Dialog" />
<activity

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
package androidx.swiperefreshlayout.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
public class ViewPagerSwipeRefreshLayout extends SwipeRefreshLayout {
private float startY;
private float startX;
// 记录viewPager是否拖拽的标记
private boolean mIsVpDragger;
private final int mTouchSlop;
public ViewPagerSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// 记录手指按下的位置
startY = ev.getY();
startX = ev.getX();
// 初始化标记
mIsVpDragger = false;
break;
case MotionEvent.ACTION_MOVE:
// 如果viewpager正在拖拽中那么不拦截它的事件直接return false
if(mIsVpDragger) {
return false;
}
// 获取当前手指位置
float endY = ev.getY();
float endX = ev.getX();
float distanceX = Math.abs(endX - startX);
float distanceY = Math.abs(endY - startY);
// 如果X轴位移大于Y轴位移那么将事件交给viewPager处理。
if(distanceX > mTouchSlop && distanceX > distanceY) {
mIsVpDragger = true;
return false;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// 初始化标记
mIsVpDragger = false;
break;
}
// 如果是Y轴位移大于X轴事件交给swipeRefreshLayout处理。
return super.onInterceptTouchEvent(ev);
}
}

View File

@ -1,11 +1,9 @@
package com.gh.base;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@ -15,7 +13,6 @@ import android.os.TransactionTooLargeException;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
@ -49,12 +46,10 @@ import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.LoginActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.SplashScreenActivity;
import com.gh.gamecenter.energy.EnergyCenterActivity;
import com.gh.gamecenter.eventbus.EBShowDialog;
import com.lightgame.BaseAppCompatActivity;
import com.lightgame.download.DownloadEntity;
import com.lightgame.download.FileUtils;
import com.lightgame.utils.Util_System_Keyboard;
import com.lightgame.utils.Utils;
import com.tencent.tauth.Tencent;
@ -96,6 +91,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
private View mTaskBackView;
private WindowManager mWM;
private WindowManager.LayoutParams mWmParams;
public long startPageTime = 0;
protected final Handler mBaseHandler = new BaseHandler(this);
@ -147,6 +143,8 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
Utils.log("ACTIVITY_ENTRANCE -> " + mEntrance);
}
disableAutofill();
if (savedInstanceState != null) {
String xapkUnzipActivity = SPUtils.getString(Constants.SP_XAPK_UNZIP_ACTIVITY);
String xapkUrl = SPUtils.getString(Constants.SP_XAPK_URL);
@ -165,10 +163,12 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
}
}
}
}
if (this.getClass().getName().equals(EnergyCenterActivity.class.getName())) {
SPUtils.setBoolean(Constants.SP_SHOW_TASK_FLOAT, false);
}
@Override
protected void onResume() {
super.onResume();
startPageTime = System.currentTimeMillis();
}
@SuppressWarnings("ConstantConditions")
@ -214,6 +214,15 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
}
}
/**
* 关闭 editText 自动填充帐号 (我们也用不上),开启的时候有小概率出发 TimeoutException
*/
private void disableAutofill() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getWindow().getDecorView().setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
}
}
private View getRootViewWithEnvIndicator(View view) {
RelativeLayout screenRootView = new RelativeLayout(this);
screenRootView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
@ -274,9 +283,9 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
});
} else if (SIGNATURE_CONFLICT.equals(showDialog.getType())) {
DialogHelper.showSignatureConflictDialog(this, () -> {
PackageInstaller.uninstall(BaseActivity.this, showDialog.getPath());
return null;
});
PackageInstaller.uninstall(BaseActivity.this, showDialog.getPath());
return null;
});
} else if (LOGIN_EXCEPTION.equals(showDialog.getType())) {
if (mIsExistLogoutDialog) return;
mIsExistLogoutDialog = true;
@ -308,10 +317,6 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
@Override
protected void onPause() {
super.onPause();
if (mWM != null && mTaskBackView != null && mHasAddTaskFloat) {
mWM.removeView(mTaskBackView);
mHasAddTaskFloat = false;
}
if (isFinishing()) {
onFinish();
@ -323,90 +328,6 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
}
}
@Override
protected void onResume() {
super.onResume();
if (SPUtils.getBoolean(Constants.SP_SHOW_TASK_FLOAT) && !this.getClass().getName().equals(EnergyCenterActivity.class.getName())) {
addTaskBackView();
mHasAddTaskFloat = true;
}
}
private void addTaskBackView() {
mWM = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
mWmParams = new WindowManager.LayoutParams();
mWmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION;
mWmParams.format = PixelFormat.RGBA_8888;
mWmParams.gravity = Gravity.LEFT | Gravity.BOTTOM;
mWmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mWmParams.width = DisplayUtils.dip2px(76F);
mWmParams.height = DisplayUtils.dip2px(36F);
mWmParams.y = SPUtils.getInt(Constants.SP_TASK_FLOAT_LAST_Y, DisplayUtils.dip2px(114F));
mTaskBackView = View.inflate(this, R.layout.layout_task_back, null);
mTaskBackView.setOnClickListener(v -> {
// 如果当前是在键盘输入时,点击"返回任务"要先收起键盘
Util_System_Keyboard.hideSoftKeyboard(this);
startActivity(EnergyCenterActivity.Companion.getIntent(this));
SPUtils.setBoolean(Constants.SP_SHOW_TASK_FLOAT, false);
mWM.removeView(mTaskBackView);
mHasAddTaskFloat = false;
});
setFloatTouchListener();
mWM.addView(mTaskBackView, mWmParams);
}
private void setFloatTouchListener() {
int screenHeight = getResources().getDisplayMetrics().heightPixels;
mTaskBackView.setOnTouchListener(new View.OnTouchListener() {
private int intervalY;
private int startY;
@Override
public boolean onTouch(View v, MotionEvent event) {
final int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
intervalY = y;
startY = y;
break;
case MotionEvent.ACTION_MOVE:
mWmParams.y -= (y - intervalY);
if (mWmParams.y < 0) {
mWmParams.y = 0;
}
if (mWmParams.y > screenHeight) {
mWmParams.y = screenHeight;
}
if (mWM != null && mTaskBackView != null && mHasAddTaskFloat) {
mWM.updateViewLayout(mTaskBackView, mWmParams);
}
intervalY = y;
return true;
case MotionEvent.ACTION_UP:
// 滑动距离少于10视为点击返回false否则视为拖动返回true
if (Math.abs(y - startY) <= 10) {
return false;
} else {
// 记录位置
SPUtils.setInt(Constants.SP_TASK_FLOAT_LAST_Y, mWmParams.y);
return true;
}
}
return false;
}
});
}
/**
* 此回调可用于确认当前 activity 已经执行了 finish() 方法并处于 isFinishing 状态
*/

View File

@ -1,5 +1,6 @@
package com.gh.base
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ClipboardManager
@ -9,32 +10,42 @@ import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.webkit.JavascriptInterface
import android.widget.CheckBox
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
import butterknife.OnClick
import com.gh.common.AppExecutor
import com.gh.common.runOnIoThread
import com.gh.common.util.*
import com.gh.common.view.RichEditor
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.GameEntity
import com.gh.gamecenter.entity.MyVideoEntity
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
import com.gh.gamecenter.entity.LocalVideoEntity
import com.gh.gamecenter.entity.VideoEntity
import com.gh.gamecenter.qa.editor.GameActivity
import com.gh.gamecenter.qa.editor.InsertAnswerWrapperActivity
import com.gh.gamecenter.qa.editor.InsertArticleWrapperActivity
import com.gh.gamecenter.qa.editor.VideoActivity
import com.gh.gamecenter.qa.editor.LocalMediaActivity
import com.gh.gamecenter.qa.entity.AnswerEntity
import com.gh.gamecenter.qa.entity.ArticleEntity
import com.gh.gamecenter.qa.entity.EditorInsertEntity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.upload.UploadManager
import com.google.gson.JsonObject
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import com.lightgame.view.CheckableImageView
import io.reactivex.disposables.Disposable
import kotterknife.bindView
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
abstract class BaseRichEditorActivity : ToolBarActivity() {
abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarActivity(),
KeyboardHeightObserver, UploadVideoListener {
val mRichEditor by bindView<RichEditor>(R.id.rich_editor)
@ -55,24 +66,31 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
private val mEditorParagraphContainer by bindView<View>(R.id.editor_paragraph_container)
private val mEditorLinkContainer by bindView<View>(R.id.editor_link_container)
private val mEditorInsertDetailContainer by bindView<View>(R.id.editor_insert_detail_container)
val mAddLabelContainer by bindView<View>(R.id.add_label_container)
val mAddLabelTv by bindView<TextView>(R.id.add_label_tv)
val mLabelNumTv by bindView<TextView>(R.id.label_num_tv)
val mLabelArrowIv by bindView<ImageView>(R.id.label_arrow)
val mTagsContainer by bindView<FrameLayout>(R.id.tagsContainer)
private val mTagsContainer by bindView<FrameLayout>(R.id.tagsContainer)
private val mUploadVideoGuideContainer by bindView<View>(R.id.uploadVideoGuideContainer)
protected val mOriginalCb by bindView<CheckBox>(R.id.originalCb)
private val mOriginalTipsContainer by bindView<View>(R.id.originalTipsContainer)
private val mOriginalTipsClose by bindView<TextView>(R.id.originalTipsClose)
private var mCurrentParagraphStyle = ""
private var mIsExtendedKeyboardShow = false
private var mAgreePostPic: Boolean = false
private var mGuideDisposable: Disposable? = null
protected lateinit var mViewModel: VM
protected var mIsKeyBoardShow = false
private var mKeyboardHeightProvider: KeyboardHeightProvider? = null
private var mMaxUploadVideoGuideCount = 2
val FILE_HOST = "file:///"
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
DialogUtils.fixWebViewKeyboardNotWorking(this)
if (resultCode != Activity.RESULT_OK) return
var insertData: EditorInsertEntity? = null
val insertData: EditorInsertEntity?
when (requestCode) {
INSERT_ANSWER_CODE -> {
val answer = data?.getParcelableExtra<AnswerEntity>(AnswerEntity::class.java.simpleName)
val answer =
data?.getParcelableExtra<AnswerEntity>(AnswerEntity::class.java.simpleName)
if (answer != null) {
mRichEditor.focusEditor()
insertData = EditorInsertEntity.transform(answer)
@ -80,7 +98,8 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
}
}
INSERT_ARTICLE_CODE -> {
val article = data?.getParcelableExtra<ArticleEntity>(ArticleEntity::class.java.simpleName)
val article =
data?.getParcelableExtra<ArticleEntity>(ArticleEntity::class.java.simpleName)
if (article != null) {
mRichEditor.focusEditor()
insertData = EditorInsertEntity.transform(article)
@ -95,11 +114,22 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
mRichEditor.insertCustomStyleLink(insertData)
}
}
VideoActivity.INSERT_VIDEO_CODE -> {
val video = data?.getParcelableExtra<MyVideoEntity>(MyVideoEntity::class.java.simpleName)
if (video != null) {
REQUEST_CODE_IMAGE -> {
if (data != null) mViewModel.uploadPic(data)
}
INSERT_VIDEO_CODE -> {
val localVideoList =
data?.getParcelableArrayListExtra<LocalVideoEntity>(LocalVideoEntity::class.java.name)
?: arrayListOf()
if (localVideoList.isNotEmpty()) {
mRichEditor.focusEditor()
mRichEditor.insertCustomVideo(video)
uploadVideo(localVideoList)
}
}
REQUEST_CODE_IMAGE_CROP -> {
val imagePath = data?.getStringExtra(CropImageActivity.RESULT_CLIP_PATH)
if (!imagePath.isNullOrEmpty()) {
mViewModel.uploadPoster(imagePath)
}
}
}
@ -107,19 +137,55 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
AppExecutor.uiExecutor.executeWithDelay(Runnable {
Util_System_Keyboard.showSoftKeyboard(this)
}, 100)
}
private fun uploadVideo(localVideoList: ArrayList<LocalVideoEntity>) {
mViewModel.localVideoList.addAll(localVideoList)
runOnIoThread {
localVideoList.forEach {
if (it.poster.startsWith("http")) {
runOnUiThread {
mRichEditor.focusEditor()
mRichEditor.insertPlaceholderVideo(it.id, it.poster)
}
} else {
val videoThumbnail = BitmapUtils.getVideoThumbnail(it.filePath)
val filePath = "${cacheDir.absolutePath}${File.separator}${it.id}.webp"
BitmapUtils.saveBitmap(videoThumbnail, filePath)
it.poster = filePath
runOnUiThread {
mRichEditor.focusEditor()
mRichEditor.insertPlaceholderVideo(it.id, "$FILE_HOST${it.poster}")
}
}
}
mViewModel.uploadVideo()
}
}
@SuppressLint("AddJavascriptInterface", "ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mAddLabelContainer.visibility = if (this is ArticleEditActivity) View.VISIBLE else View.GONE
mViewModel = provideViewModel()
mViewModel.setUploadVideoListener(this)
mKeyboardHeightProvider = KeyboardHeightProvider(this)
mRichEditor.post { mKeyboardHeightProvider?.start() }
mRichEditor.setPadding(20, 15, 20, 15)
// 防止个别手机在Js里无法获取粘贴内容
mRichEditor.addJavascriptInterface(OnPasteListener(), "onPasteListener")
mRichEditor.addJavascriptInterface(OnCursorChangeListener(), "OnCursorChangeListener")
mRichEditor.addJavascriptInterface(OnEditorTextChangeListener(), "OnEditorTextChangeListener")
mRichEditor.addJavascriptInterface(
OnEditorTextChangeListener(),
"OnEditorTextChangeListener"
)
mRichEditor.addJavascriptInterface(OnVideoListener(), "onVideoListener")
mRichEditor.addJavascriptInterface(
OnQuoteCountChangeListener(),
"OnQuoteCountChangeListener"
)
mRichEditor.setInputEnabled(true)
mRichEditor.setPadding(16, 15, 16, 15)
mRichEditor.setOnTouchListener { _, _ ->
if (mIsExtendedKeyboardShow) {
@ -129,14 +195,46 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
mRichEditor.hasFocus()
} else false
}
mOriginalCb.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
mOriginalTipsContainer.alpha = 0f
mOriginalTipsContainer.visibility = View.VISIBLE
ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 0f, 1f).setDuration(200).start()
}
}
observeData()
}
private fun observeData() {
mViewModel.chooseImagesUpload.observe(this, Observer {
mRichEditor.focusEditor()
for (key in it.keys) {
mRichEditor.insertPlaceholderImage(key)
}
})
mViewModel.chooseImagesUploadSuccess.observe(this, Observer {
val jsonArray = JSONArray()
for (key in it.keys) {
val jsonObject = JSONObject()
jsonObject.put("id", key)
jsonObject.put("url", it[key])
jsonArray.put(jsonObject)
}
mRichEditor.replacePlaceholderImage(jsonArray.toString())
})
}
override fun onKeyboardHeightChanged(height: Int, orientation: Int) {
mIsKeyBoardShow = height > 0
if (height > 0) {
closeExtendedKeyboard()
}
}
fun closeExtendedKeyboard() {
mEditorInsertDetailContainer.visibility = View.GONE
mEditorFont.isChecked = false
mEditorLink.isChecked = false
mAddLabelContainer.isSelected = false
mIsExtendedKeyboardShow = false
}
@ -144,41 +242,15 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
mEditorFont.isEnabled = isEnabled
}
fun changeAddLabel(isLabelContainerShow: Boolean) {
if (isLabelContainerShow) {
mLabelNumTv.visibility = View.GONE
mLabelArrowIv.visibility = View.VISIBLE
mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.theme_font))
mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
mAddLabelContainer.background = ContextCompat.getDrawable(this, R.drawable.bg_editor_insert_add_label)
} else {
val selectedLabel = getSelectedLabel()
if (selectedLabel == 0) {
mAddLabelTv.text = "添加标签"
mLabelNumTv.visibility = View.GONE
mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.text_666666))
mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(this, R.drawable.ic_add_label), null, null, null)
} else {
mAddLabelTv.text = "标签"
mLabelNumTv.visibility = View.VISIBLE
mLabelNumTv.text = selectedLabel.toString()
mAddLabelTv.setTextColor(ContextCompat.getColor(this, R.color.theme_font))
mAddLabelTv.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
}
mLabelArrowIv.visibility = View.GONE
mAddLabelContainer.background = ContextCompat.getDrawable(this, R.drawable.border_round_stroke_eee_999)
}
}
open fun getSelectedLabel(): Int = 0
@OnClick(R.id.editor_image, R.id.editor_font, R.id.editor_link, R.id.add_label_container, R.id.editor_font_underline,
R.id.editor_font_bold, R.id.editor_font_italic, R.id.editor_font_strikethrough,
R.id.editor_paragraph_h1, R.id.editor_paragraph_h2, R.id.editor_paragraph_h3,
R.id.editor_paragraph_h4, R.id.editor_font_container, R.id.editor_paragraph_container,
R.id.editor_paragraph_quote, R.id.editor_link_answer, R.id.editor_link_article,
R.id.editor_link_game, R.id.editor_link_video)
@OnClick(
R.id.editor_image, R.id.editor_font, R.id.editor_link, R.id.editor_font_underline,
R.id.editor_font_bold, R.id.editor_font_italic, R.id.editor_font_strikethrough,
R.id.editor_paragraph_h1, R.id.editor_paragraph_h2, R.id.editor_paragraph_h3,
R.id.editor_paragraph_h4, R.id.editor_font_container, R.id.editor_paragraph_container,
R.id.editor_paragraph_quote, R.id.editor_link_answer, R.id.editor_link_article,
R.id.editor_link_game, R.id.editor_link_video, R.id.uploadVideoGuideClose,
R.id.originalTipsClose
)
fun onRichClick(view: View) {
when (view.id) {
R.id.editor_font -> {
@ -187,9 +259,6 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
R.id.editor_link -> {
controlEditorLinkContainer()
}
R.id.add_label_container -> {
controlAddLabelContainer()
}
R.id.editor_font_bold -> {
mEditorFontBold.isChecked = !mEditorFontBold.isChecked
mRichEditor.setBold()
@ -267,32 +336,108 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
}
R.id.editor_link_answer -> {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-回答")
startActivityForResult(InsertAnswerWrapperActivity.getIntent(this), INSERT_ANSWER_CODE)
startActivityForResult(
InsertAnswerWrapperActivity.getIntent(this),
INSERT_ANSWER_CODE
)
}
R.id.editor_link_article -> {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-文章")
startActivityForResult(InsertArticleWrapperActivity.getIntent(this), INSERT_ARTICLE_CODE)
startActivityForResult(
InsertArticleWrapperActivity.getIntent(this),
INSERT_ARTICLE_CODE
)
}
R.id.editor_link_game -> {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-游戏")
startActivityForResult(GameActivity.getIntent(this, GameActivity.INSERT_GAME_TITLE), INSERT_GAME_CODE)
startActivityForResult(
GameActivity.getIntent(this, GameActivity.INSERT_GAME_TITLE),
INSERT_GAME_CODE
)
}
R.id.editor_link_video -> {
PermissionHelper.checkStoragePermissionBeforeAction(this,
object : EmptyCallback {
override fun onCallback() {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频")
startActivityForResult(VideoActivity.getIntent(this@BaseRichEditorActivity), VideoActivity.INSERT_VIDEO_CODE)
}
})
object : EmptyCallback {
override fun onCallback() {
MtaHelper.onEvent(mtaEventName(), "插入链接", "插入链接-视频")
startActivityForResult(
LocalMediaActivity.getIntent(
this@BaseRichEditorActivity,
LocalMediaActivity.ChooseType.VIDEO,
3,
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
), INSERT_VIDEO_CODE
)
NewLogUtils.logChooseMedia(
"view_media",
if (mtaEventName() == "提问帖") "提问帖" else "帖子",
"视频"
)
}
})
}
R.id.editor_image -> {
if (!mAgreePostPic && !NetworkUtils.isWifiOr4GOr3GConnected(this)) {
mAgreePostPic = true
DialogUtils.showAlertDialog(
this,
"警告",
"当前使用移动网络,上传图片会消耗手机流量",
"我知道了", "", { startMediaStore() }, null
)
return
}
startMediaStore()
NewLogUtils.logChooseMedia(
"view_media",
if (mtaEventName() == "提问帖") "提问帖" else "帖子",
"图片"
)
}
R.id.uploadVideoGuideClose -> {
hideUploadVideoGuide()
if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) {
mGuideDisposable!!.dispose()
mGuideDisposable = null
}
}
R.id.originalTipsClose -> {
val animator = ObjectAnimator.ofFloat(mOriginalTipsContainer, "alpha", 1f, 0f).setDuration(200)
animator.doOnEnd {
mOriginalTipsContainer.visibility = View.GONE
}
animator.start()
}
}
}
private fun startMediaStore() {
MtaHelper.onEvent(mtaEventName(), "插入图片", "插入图片")
if (mViewModel.mapImages.size >= 50) {
toast(R.string.answer_edit_max_img_hint)
return
}
try {
PermissionHelper.checkStoragePermissionBeforeAction(this, object : EmptyCallback {
override fun onCallback() {
val intent = LocalMediaActivity.getIntent(
this@BaseRichEditorActivity,
LocalMediaActivity.ChooseType.IMAGE,
10,
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
)
startActivityForResult(intent, REQUEST_CODE_IMAGE)
}
})
} catch (e: Exception) {
toast(R.string.media_image_hint)
e.printStackTrace()
}
}
private fun controlEditorFontContainer() {
mEditorFont.isChecked = !mEditorFont.isChecked
mEditorLink.isChecked = false
mAddLabelContainer.isSelected = false
val isShouldDelay = if (mEditorFont.isChecked) {
Util_System_Keyboard.hideSoftKeyboard(this)
true
@ -301,20 +446,20 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
false
}
mEditorInsertDetailContainer.postDelayed({
mEditorInsertDetailContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
mEditorInsertDetailContainer.visibility =
if (mEditorFont.isChecked) View.VISIBLE else View.GONE
mEditorFontContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
mEditorParagraphContainer.visibility = if (mEditorFont.isChecked) View.VISIBLE else View.GONE
mEditorParagraphContainer.visibility =
if (mEditorFont.isChecked) View.VISIBLE else View.GONE
mEditorLinkContainer.visibility = View.GONE
mTagsContainer.visibility = View.GONE
mIsExtendedKeyboardShow = mEditorFont.isChecked
changeAddLabel(false)
}, if (isShouldDelay) 200 else 0L)
}
private fun controlEditorLinkContainer() {
mEditorLink.isChecked = !mEditorLink.isChecked
mEditorFont.isChecked = false
mAddLabelContainer.isSelected = false
val isShouldDelay = if (mEditorLink.isChecked) {
Util_System_Keyboard.hideSoftKeyboard(this)
true
@ -323,38 +468,16 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
false
}
mEditorInsertDetailContainer.postDelayed({
mEditorInsertDetailContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE
mEditorInsertDetailContainer.visibility =
if (mEditorLink.isChecked) View.VISIBLE else View.GONE
mEditorLinkContainer.visibility = if (mEditorLink.isChecked) View.VISIBLE else View.GONE
mEditorFontContainer.visibility = View.GONE
mEditorParagraphContainer.visibility = View.GONE
mTagsContainer.visibility = View.GONE
mIsExtendedKeyboardShow = mEditorLink.isChecked
changeAddLabel(false)
}, if (isShouldDelay) 200 else 0L)
}
fun controlAddLabelContainer() {
mEditorLink.isChecked = false
mEditorFont.isChecked = false
mAddLabelContainer.isSelected = !mAddLabelContainer.isSelected
val isShouldDelay = if (mAddLabelContainer.isSelected) {
Util_System_Keyboard.hideSoftKeyboard(this)
changeAddLabel(true)
true
} else {
Util_System_Keyboard.showSoftKeyboard(this)
changeAddLabel(false)
false
}
mEditorInsertDetailContainer.postDelayed({
mEditorInsertDetailContainer.visibility = if (mAddLabelContainer.isSelected) View.VISIBLE else View.GONE
mTagsContainer.visibility = if (mAddLabelContainer.isSelected) View.VISIBLE else View.GONE
mEditorLinkContainer.visibility = View.GONE
mEditorFontContainer.visibility = View.GONE
mEditorParagraphContainer.visibility = View.GONE
mIsExtendedKeyboardShow = mAddLabelContainer.isSelected
}, if (isShouldDelay) 200 else 0L)
}
override fun handleBackPressed(): Boolean {
if (mIsExtendedKeyboardShow) {
@ -364,6 +487,53 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
return super.handleBackPressed()
}
override fun onResume() {
super.onResume()
mKeyboardHeightProvider?.setKeyboardHeightObserver(this)
}
override fun onPause() {
super.onPause()
mKeyboardHeightProvider?.setKeyboardHeightObserver(null)
}
//视频上传功能引导
fun showUploadVideoGuide() {
mUploadVideoGuideContainer.postDelayed({
val count = SPUtils.getInt(getVideoGuideKey(), 0)
if (count >= mMaxUploadVideoGuideCount) return@postDelayed
mUploadVideoGuideContainer.alpha = 0f
mUploadVideoGuideContainer.visibility = View.VISIBLE
mUploadVideoGuideContainer.animate().alpha(1f).setDuration(200).start()
mGuideDisposable = countDownTimer(3) { finish, _ ->
if (finish) {
hideUploadVideoGuide()
}
}
SPUtils.setInt(getVideoGuideKey(), count + 1)
}, 1000)
}
fun hideUploadVideoGuide() {
val animate = mUploadVideoGuideContainer.animate().alpha(0f).setDuration(200)
animate.doOnEnd {
mUploadVideoGuideContainer.visibility = View.GONE
}
animate.start()
}
override fun onDestroy() {
super.onDestroy()
mKeyboardHeightProvider?.close()
val path = mViewModel.currentUploadingVideo?.filePath
if (path != null && UploadManager.isUploading(path)) {
UploadManager.cancelTask(path)
}
if (mGuideDisposable != null && !mGuideDisposable!!.isDisposed) {
mGuideDisposable!!.dispose()
mGuideDisposable = null
}
}
private inner class OnCursorChangeListener {
@JavascriptInterface
@ -397,11 +567,12 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
@JavascriptInterface
fun onPaste() {
val clipboard =
HaloApp.getInstance().application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
HaloApp.getInstance().application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clipText = clipboard.text.toString()
if (!TextUtils.isEmpty(clipText)) {
// 替换换行符号否则 插入失败
val text = clipText.replace("[ ]".toRegex(), "&nbsp;").replace("[\r\n]".toRegex(), "<br/>")
val text = clipText.replace("[ ]".toRegex(), "&nbsp;")
.replace("[\r\n]".toRegex(), "<br/>")
mBaseHandler.post { mRichEditor.insertHtml(text) }
}
}
@ -413,11 +584,103 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
val num = if (count > MAX_INPUT_TEXT_NUM) MAX_INPUT_TEXT_NUM - count else count
mEditorTextNumTv.post {
mEditorTextNumTv.text = num.toString()
mViewModel.quoteCountEntity.textCount = num
}
}
}
private inner class OnQuoteCountChangeListener {
@JavascriptInterface
fun onQuoteCountChange(
imageCount: Int,
articleCount: Int,
answerCount: Int,
videoCount: Int,
gameCount: Int
) {
mEditorTextNumTv.post {
mViewModel.quoteCountEntity.apply {
this.imageCount = imageCount
this.articleCount = articleCount
this.answerCount = answerCount
this.videoCount = videoCount
this.gameCount = gameCount
}
}
}
}
private inner class OnVideoListener {
@JavascriptInterface
fun showDeleteDialog(id: String) {
DialogHelper.showDialog(this@BaseRichEditorActivity, "提示", "确定删除吗?", "确定", "取消", {
runOnUiThread {
mRichEditor.delPlaceholderVideo(id)
mViewModel.deleteVideo(id)
}
})
}
@JavascriptInterface
fun updatePoster(id: String, videoId: String, url: String) {
mViewModel.id = id
mViewModel.videoId = videoId
val videoEntity = VideoEntity(url = url)
val intent =
PosterEditActivity.getIntentByVideo(this@BaseRichEditorActivity, videoEntity)
startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
}
@JavascriptInterface
fun deleteUploadingVideo(id: String) {
mViewModel.deleteVideo(id)
}
@JavascriptInterface
fun reUploadVideo(id: String) {
val video = mViewModel.uploadVideoErrorList.find { it.id == id }
if (video != null) {
mViewModel.localVideoList.add(video)
mViewModel.uploadVideoErrorList.remove(video)
mViewModel.uploadVideo()
}
}
}
override fun insertPlaceholderVideo(id: String, poster: String) {
mRichEditor.insertPlaceholderVideo(id, poster)
}
override fun updateVideoProgress(id: String, progress: String) {
mRichEditor.updateVideoProgress(id, progress)
}
override fun videoUploadFinished(id: String, url: String, msg: JsonObject) {
try {
val obj = JSONObject()
obj.put("poster", msg.get("poster").asString)
obj.put("url", msg.get("url").asString)
obj.put("duration", RichEditor.formatVideoDuration(msg.get("length").asLong))
obj.put("id", msg.get("_id").asString)
obj.put("status", "pending")
mRichEditor.videoUploadFinished(id, url, obj.toString())
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
override fun changePoster(id: String, poster: String) {
mRichEditor.changePoster(id, poster)
}
override fun videoUploadFailed(id: String) {
mRichEditor.videoUploadFailed(id)
}
open fun getSelectedLabel(): Int = 0
abstract fun mtaEventName(): String
abstract fun provideViewModel(): VM
abstract fun getVideoGuideKey(): String
companion object {
const val ELEMENT_NAME_BOLD = " b "
@ -434,5 +697,9 @@ abstract class BaseRichEditorActivity : ToolBarActivity() {
const val INSERT_ARTICLE_CODE = 412
const val INSERT_GAME_CODE = 413
const val MAX_INPUT_TEXT_NUM = 10000
const val REQUEST_CODE_IMAGE = 120
const val INSERT_VIDEO_CODE = 121
const val REQUEST_CODE_IMAGE_CROP = 122
}
}

View File

@ -0,0 +1,444 @@
package com.gh.base
import android.app.Application
import android.content.Intent
import android.graphics.Bitmap
import android.media.ThumbnailUtils
import android.provider.MediaStore
import android.text.TextUtils
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import com.gh.base.fragment.WaitingDialogFragment
import com.gh.common.runOnUiThread
import com.gh.common.util.*
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.ErrorEntity
import com.gh.gamecenter.entity.LocalVideoEntity
import com.gh.gamecenter.entity.QuoteCountEntity
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.retrofit.service.ApiService
import com.gh.gamecenter.video.upload.OnUploadListener
import com.gh.gamecenter.video.upload.UploadManager
import com.google.gson.JsonObject
import com.lightgame.utils.Utils
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.internal.utils.PathUtils
import io.reactivex.disposables.Disposable
import okhttp3.ResponseBody
import retrofit2.HttpException
import java.io.File
import java.io.FileOutputStream
import java.util.*
import kotlin.collections.HashMap
import kotlin.collections.LinkedHashMap
import kotlin.collections.List
import kotlin.collections.Map
import kotlin.collections.find
import kotlin.collections.forEach
import kotlin.collections.set
abstract class BaseRichEditorViewModel(application: Application) : AndroidViewModel(application) {
val mApi: ApiService = RetrofitManager.getInstance(application).api
val processDialog = MediatorLiveData<WaitingDialogFragment.WaitingDialogData>()
val uploadingImage = ArrayList<LinkedHashMap<String, String>>()
val chooseImagesUpload = MutableLiveData<LinkedHashMap<String, String>>()
val chooseImagesUploadSuccess = MutableLiveData<LinkedHashMap<String, String>>()
var uploadImageSubscription: Disposable? = null
val mapImages = HashMap<String, String>()
val localVideoList = ArrayList<LocalVideoEntity>()
val uploadVideoErrorList = ArrayList<LocalVideoEntity>()
var currentUploadingVideo: LocalVideoEntity? = null
var type: String = "" //游戏论坛game_bbs 官方论坛official_bbs
private var mUploadVideoListener: UploadVideoListener? = null
val TITLE_MIN_LENGTH = 6
val MIN_TEXT_LENGTH = 6
val MAX_TEXT_LENGTH = 10000
val FILE_HOST = "file:///"
var id = ""//视频标记
var videoId = ""//更改封面视频id
val quoteCountEntity = QuoteCountEntity()//数据上报用
fun setUploadVideoListener(uploadVideoListener: UploadVideoListener) {
this.mUploadVideoListener = uploadVideoListener
}
//检查图片是否符合规则并上传图片
fun uploadPic(data: Intent) {
val uris = Matisse.obtainResult(data)
val pictureList = ArrayList<String>()
for (uri in uris) {
val picturePath = PathUtils.getPath(getApplication(), uri)
if (picturePath != null) {
if (File(picturePath).length() > ImageUtils.getUploadFileMaxSize()) {
val count = ImageUtils.getUploadFileMaxSize() / 1024 / 1024
val application: Application = getApplication()
Utils.toast(
getApplication(),
application.getString(R.string.pic_max_hint, count)
)
continue
}
Utils.log("picturePath = $picturePath")
pictureList.add(picturePath)
} else {
Utils.log("picturePath is null")
}
}
if (pictureList.size == 0) return
val imageType = when (getRichType()) {
RichType.ARTICLE -> UploadImageUtils.UploadType.community_article
RichType.QUESTION -> UploadImageUtils.UploadType.question
else -> UploadImageUtils.UploadType.poster
}
uploadImageSubscription = UploadImageUtils.compressAndUploadImageList(
imageType,
pictureList,
false,
object : UploadImageUtils.OnUploadImageListListener {
override fun onProgress(total: Long, progress: Long) {}
override fun onCompressSuccess(imageUrls: List<String>) {
val chooseImageMd5Map = LinkedHashMap<String, String>()
imageUrls.forEach {
chooseImageMd5Map[MD5Utils.getUrlMD5(it)] = ""
}
uploadingImage.add(chooseImageMd5Map)
chooseImagesUpload.postValue(chooseImageMd5Map)
}
override fun onSingleSuccess(imageUrl: Map<String, String>) {
val map = LinkedHashMap<String, String>()
for (key in imageUrl.keys) {
map[MD5Utils.getUrlMD5(key)] = FILE_HOST + key.decodeURI()
mapImages[TextUtils.htmlEncode(key).decodeURI()] = imageUrl[key] ?: ""
}
chooseImagesUploadSuccess.postValue(map)
}
override fun onSuccess(
imageUrl: LinkedHashMap<String, String>,
errorMap: Map<String, Exception>
) {
val uploadMap = uploadingImage.find {
it.containsKey(
MD5Utils.getUrlMD5(
imageUrl.entries.iterator().next().key
)
)
}
uploadMap?.let {
uploadingImage.remove(uploadMap)
}
val errorSize = pictureList.size - imageUrl.size
if (errorSize > 0) {
val map = LinkedHashMap<String, String>()
for (key in errorMap.keys) {
map[MD5Utils.getUrlMD5(key)] = ""
}
//value为空会删除PlaceholderImage
chooseImagesUploadSuccess.postValue(map)
for (error in errorMap.values) {
if (error is HttpException && error.code() == 403) {
Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败")
return
}
}
Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败")
}
}
override fun onError(errorMap: Map<String, Exception>) {
val errorSize = pictureList.size
if (errorSize > 0) {
val map = LinkedHashMap<String, String>()
for (key in errorMap.keys) {
map[MD5Utils.getUrlMD5(key)] = ""
}
//value为空会删除PlaceholderImage
chooseImagesUploadSuccess.postValue(map)
}
for (error in errorMap.values) {
if (error is HttpException && error.code() == 403) {
val e = error.response()?.errorBody()?.string()?.toObject<ErrorEntity>()
if (e != null && e.code == 403017) {
Utils.toast(
getApplication(),
errorSize.toString() + "张图片的宽或高超过限制,请裁剪后上传"
)
} else {
Utils.toast(getApplication(), errorSize.toString() + "张违规图片上传失败")
}
return
}
}
if (errorSize == 1) {
Utils.toast(getApplication(), "图片上传失败")
} else {
Utils.toast(getApplication(), errorSize.toString() + "张图片上传失败")
}
}
})
}
fun uploadPoster(picturePath: String) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", true))
uploadImageSubscription =
UploadImageUtils.compressAndUploadImage(UploadImageUtils.UploadType.poster,
picturePath,
false,
object : UploadImageUtils.OnUploadImageListener {
override fun onSuccess(imageUrl: String) {
patchVideoPoster(imageUrl)
}
override fun onError(e: Throwable?) {
handleUploadPosterResult(true)
}
override fun onProgress(total: Long, progress: Long) {
}
})
}
private fun patchVideoPoster(poster: String) {
if (id.isEmpty() || videoId.isEmpty()) return
val map = hashMapOf("poster" to poster, "type" to getVideoType())
mApi.patchInsertVideo(videoId, map.toRequestBody())
.compose(observableToMain())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
super.onResponse(response)
mUploadVideoListener?.changePoster(id, poster)
handleUploadPosterResult(false)
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
handleUploadPosterResult(true)
}
})
}
private fun handleUploadPosterResult(isFailure: Boolean = false) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
if (isFailure) {
ToastUtils.showToast("封面更改失败")
}
id = ""
videoId = ""
}
fun deleteVideo(id: String) {
if (localVideoList.isNotEmpty()) {
val video = localVideoList.find { it.id == id }
if (video != null) {
if (UploadManager.isUploading(video.filePath)) {
UploadManager.cancelTask(video.filePath)
}
localVideoList.remove(video)
}
}
if (uploadVideoErrorList.isNotEmpty()) {
val video = uploadVideoErrorList.find { it.id == id }
if (video != null) {
uploadVideoErrorList.remove(video)
}
}
if (currentUploadingVideo?.id == id) {
currentUploadingVideo = null
uploadVideo()
}
}
fun uploadVideo() {
if (currentUploadingVideo != null) return
if (localVideoList.isEmpty()) return
currentUploadingVideo = localVideoList[0]
UploadManager.createUploadTask(currentUploadingVideo?.filePath
?: "", object : OnUploadListener {
override fun onProgressChanged(
uploadFilePath: String,
currentSize: Long,
totalSize: Long,
speed: Long
) {
runOnUiThread {
val percent = (currentSize * 100 / totalSize.toFloat()).roundTo(1)
currentUploadingVideo?.id?.let {
mUploadVideoListener?.updateVideoProgress(it, percent.toString())
}
}
}
override fun onUploadSuccess(uploadFilePath: String, url: String) {
if (currentUploadingVideo != null) {
postVideoPosterAndInfo(uploadFilePath, url)
}
}
override fun onUploadFailure(uploadFilePath: String, errorMsg: String) {
uploadVideoFailure()
}
})
}
private fun postVideoPosterAndInfo(uploadFilePath: String, url: String) {
val localVideoPoster =
getApplication<Application>().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".jpg"
try {
val bmp = ThumbnailUtils.createVideoThumbnail(
uploadFilePath,
MediaStore.Images.Thumbnails.MINI_KIND
)
// bmp 可能为空
FileOutputStream(localVideoPoster).use { out ->
bmp?.compress(Bitmap.CompressFormat.PNG, 100, out)
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
ToastUtils.showToast("视频封面操作失败")
uploadVideoFailure()
return
}
uploadImageSubscription =
UploadImageUtils.compressAndUploadImage(UploadImageUtils.UploadType.poster,
localVideoPoster,
false,
object : UploadImageUtils.OnUploadImageListener {
override fun onSuccess(imageUrl: String) {
postVideoInfo(url, imageUrl)
}
override fun onError(e: Throwable?) {
uploadVideoFailure()
}
override fun onProgress(total: Long, progress: Long) {
}
})
}
private fun postVideoInfo(url: String, poster: String) {
val map = HashMap<String, Any>().apply {
put("poster", poster)
put("url", url)
put("format", currentUploadingVideo?.format ?: "")
put("size", currentUploadingVideo?.size ?: 0)
put("length", (currentUploadingVideo?.duration ?: 0) / 1000)
put("type", getVideoType())
}
val requestBody = map.toRequestBody()
mApi.insertVideo(requestBody)
.compose(observableToMain())
.subscribe(object : Response<JsonObject>() {
override fun onResponse(response: JsonObject?) {
super.onResponse(response)
if (response != null) {
uploadVideoSuccess(poster, url, response)
}
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
uploadVideoFailure()
}
})
}
private fun uploadVideoSuccess(poster: String, url: String, data: JsonObject) {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
currentUploadingVideo?.let {
mUploadVideoListener?.changePoster(it.id, poster)
mUploadVideoListener?.videoUploadFinished(it.id, url, data)
UploadManager.cancelTask(it.filePath)
localVideoList.remove(it)
}
currentUploadingVideo = null
uploadVideo()
}
private fun uploadVideoFailure() {
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("封面上传中...", false))
currentUploadingVideo?.let {
runOnUiThread {
mUploadVideoListener?.videoUploadFailed(it.id)
}
uploadVideoErrorList.add(it)
localVideoList.remove(it)
UploadManager.cancelTask(it.filePath)
}
currentUploadingVideo = null
uploadVideo()
}
fun checkIsAllUploadedAndToast(): Boolean {
if (localVideoList.isNotEmpty() || uploadVideoErrorList.isNotEmpty()) {
ToastUtils.showToast("视频未上传完成,视频内容保存失败")
return false
}
return true
}
private fun getVideoType(): String {
return when (type) {
BbsType.GAME_BBS.value -> {
when (getRichType()) {
RichType.ARTICLE -> BbsType.GAME_BBS_ARTICLE_INSERT.value
RichType.QUESTION -> BbsType.GAME_BBS_QUESTION_INSERT.value
else -> ""
}
}
BbsType.OFFICIAL_BBS.value -> {
when (getRichType()) {
RichType.ARTICLE -> BbsType.OFFICIAL_BBS_ARTICLE_INSERT.value
RichType.QUESTION -> BbsType.OFFICIAL_BBS_QUESTION_INSERT.value
else -> ""
}
}
else -> ""
}
}
abstract fun getRichType(): RichType
}
interface UploadVideoListener {
/**
* 插入视频占位图
*/
fun insertPlaceholderVideo(id: String, poster: String)
/**
* 更新视频进度条
*/
fun updateVideoProgress(id: String, progress: String)
/**
* 上传视频完成
*/
fun videoUploadFinished(id: String, url: String, msg: JsonObject)
/**
* 更换封面图
*/
fun changePoster(id: String, poster: String)
/**
* 上传失败
*/
fun videoUploadFailed(id: String)
}
enum class RichType {
ARTICLE,
QUESTION,
ANSWER
}

View File

@ -1,80 +0,0 @@
package com.gh.base;
import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;
import com.gh.common.notifier.Notifier;
import com.gh.common.util.DataUtils;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.SplashScreenActivity;
import com.halo.assistant.HaloApp;
import com.lightgame.utils.AppManager;
/**
* 1、写点针对生命周期的统计代码
* 2、写点通用的逻辑
* 3、接口解耦
*
* @author CsHeng
* @Date 09/05/2017
* @Time 6:22 PM
*/
public class GHActivityLifecycleCallbacksImpl implements ActivityLifecycleCallbacks {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
AppManager.getInstance().addActivity(activity);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
CurrentActivityHolder.getActivitySet().add(activity);
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
DataUtils.onResume(activity);
//FIXME 这里应该只是部分Activity需要
try {
// 初始化gameMap
if (!(activity instanceof SplashScreenActivity)) {
DownloadManager.getInstance(activity).initGameMap();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onActivityPaused(Activity activity) {
CurrentActivityHolder.getActivitySet().remove(activity);
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
DataUtils.onPause(activity);
}
}
@Override
public void onActivityStopped(Activity activity) {
Notifier.hide();
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
if (activity.isFinishing()) {
AppManager.getInstance().finishActivity(activity);
}
}
}

View File

@ -0,0 +1,95 @@
package com.gh.base
import android.app.Activity
import android.app.Application
import android.os.Bundle
import com.gh.common.notifier.Notifier
import com.gh.common.util.DataUtils
import com.gh.common.util.FloatingBackViewManager
import com.gh.download.DownloadManager
import com.gh.gamecenter.MainActivity
import com.gh.gamecenter.SplashScreenActivity
import com.gh.gamecenter.energy.EnergyCenterActivity
import com.gh.gamecenter.forum.detail.ForumDetailActivity
import com.gh.gamecenter.forum.list.ForumListActivity
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
import com.halo.assistant.HaloApp
import com.lightgame.utils.AppManager
class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
AppManager.getInstance().addActivity(activity)
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityResumed(activity: Activity) {
CurrentActivityHolder.activitySet.add(activity)
// 判断是否需要显示或隐藏返回小浮窗
if (FloatingBackViewManager.getType().isNotEmpty()) {
if (activity is EnergyCenterActivity
&& FloatingBackViewManager.getType() == FloatingBackViewManager.TYPE_TASK
) {
FloatingBackViewManager.disableBackView()
} else if (!shouldShowActivityBackView(activity)
&& FloatingBackViewManager.getType() == FloatingBackViewManager.TYPE_ACTIVITY
) {
FloatingBackViewManager.disableBackView()
} else {
FloatingBackViewManager.showBackView(activity)
}
}
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
DataUtils.onResume(activity)
// FIXME 这里应该只是部分Activity需要
try {
// 初始化gameMap
if (activity !is SplashScreenActivity) {
DownloadManager.getInstance(activity).initGameMap()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private fun shouldShowActivityBackView(activity: Activity): Boolean {
return (activity is MainActivity
|| activity is ArticleDetailActivity
|| activity is ForumVideoDetailActivity
|| activity is ForumDetailActivity
|| activity is ForumListActivity
|| activity is NewQuestionDetailActivity)
}
override fun onActivityPaused(activity: Activity) {
CurrentActivityHolder.activitySet.remove(activity)
FloatingBackViewManager.dismissBackView(activity)
if (HaloApp.isUserAcceptPrivacyPolicy(activity)) {
DataUtils.onPause(activity)
}
}
override fun onActivityStopped(activity: Activity) {
Notifier.hide()
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityDestroyed(activity: Activity) {
if (activity.isFinishing) {
AppManager.getInstance().finishActivity(activity)
}
}
}

View File

@ -61,6 +61,8 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
@NonNull
protected String mEntrance = "";
public long startPageTime = 0;
protected final Handler mBaseHandler = new BaseFragment.BaseHandler(this);
protected static class BaseHandler extends Handler {
@ -225,6 +227,7 @@ public abstract class BaseFragment<T> extends Fragment implements OnRequestCallB
public void onResume() {
super.onResume();
isEverPause = false;
startPageTime = System.currentTimeMillis();
}
@Override

View File

@ -3,6 +3,7 @@ package com.gh.common
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.util.Base64
import android.webkit.JavascriptInterface
import androidx.annotation.Keep
import androidx.appcompat.app.AppCompatActivity
@ -36,11 +37,16 @@ import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import org.json.JSONObject
import retrofit2.HttpException
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.util.*
import kotlin.collections.ArrayList
class DefaultJsApi(var context: Context) {
private var mLoginHandler: CompletionHandler<Any>? = null
@JavascriptInterface
fun isGhzs(msg: Any): String {
return "true"
@ -342,6 +348,50 @@ class DefaultJsApi(var context: Context) {
return NetworkUtils.isWifiConnected(context)
}
@JavascriptInterface
fun enableBackToActivity(msg: Any) {
FloatingBackViewManager.enableBackView(FloatingBackViewManager.TYPE_ACTIVITY, msg.toString())
}
@JavascriptInterface
fun startBBSStayTimeCount(msg: Any) {
BbsStayTimeHelper.enableStayTimeCount(msg.toString().toInt())
}
@JavascriptInterface
fun saveBase64ImageToGallery(msg: Any) {
val base64StringData = msg.toString()
runOnIoThread {
val base64String = base64StringData.replace("data:image/png;base64", "")
tryWithDefaultCatch {
val imageFile = File(HaloApp.getInstance().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".png")
val decodedString = Base64.decode(base64String, Base64.DEFAULT)
val bos = BufferedOutputStream(FileOutputStream(imageFile))
bos.write(decodedString)
bos.flush()
bos.close()
ImageUtils.saveImageToFile(imageFile, "", true)
}
}
}
@JavascriptInterface
fun loginWithCallback(msg: Any, handler: CompletionHandler<Any>) {
mLoginHandler = handler
login(msg)
}
fun onLogin() {
mLoginHandler?.complete(true)
mLoginHandler = null
}
@JavascriptInterface
fun openInNewFullWebview(url: Any) {
runOnUiThread { DirectUtils.directToFullScreenWebPage(context, url.toString(), true) }
}
@Keep
internal data class ImageEvent(var imageList: ArrayList<String> = arrayListOf(), var position: Int = 0)

View File

@ -10,15 +10,16 @@ import com.gh.common.util.DirectUtils.directToFeedback
import com.gh.common.util.DirectUtils.directToGameDetailVideoStreaming
import com.gh.common.util.DirectUtils.directToGameServerCalendar
import com.gh.common.util.DirectUtils.directToGameVideo
import com.gh.common.util.DirectUtils.directToLegacyVideoDetail
import com.gh.common.util.DirectUtils.directToLinkPage
import com.gh.common.util.DirectUtils.directToQa
import com.gh.common.util.DirectUtils.directToVideoDetail
import com.gh.common.util.GsonUtils.gson
import com.gh.gamecenter.LibaoDetailActivity
import com.gh.gamecenter.MainActivity
import com.gh.gamecenter.NewsDetailActivity
import com.gh.gamecenter.WebActivity
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.qa.video.publish.VideoPublishActivity
import com.gh.gamecenter.subject.SubjectActivity
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
import com.lightgame.utils.Utils
@ -63,9 +64,7 @@ object DefaultUrlHandler {
}
"inurl" -> {
intent = Intent(context, WebActivity::class.java)
intent.putExtra(EntranceUtils.KEY_URL, uri.getQueryParameter("url"))
context.startActivity(intent)
DirectUtils.directToWebView(context, uri.getQueryParameter("url") ?: "")
}
"outurl" -> {
@ -141,7 +140,8 @@ object DefaultUrlHandler {
}
EntranceUtils.HOST_USERHOME -> {
val position = uri.getQueryParameter("position")
DirectUtils.directToHomeActivity(context, id, if (position.isNullOrEmpty()) -1 else position.toInt(), entrance, "")
val subtype = uri.getQueryParameter("sub_type") ?: ""
DirectUtils.directToHomeActivity(context, id, subtype, if (position.isNullOrEmpty()) -1 else position.toInt(), entrance, "")
}
EntranceUtils.HOST_VIDEO_MORE -> {
val referer = uri.getQueryParameter("referer") ?: ""
@ -159,11 +159,14 @@ object DefaultUrlHandler {
} else {
id
}
directToVideoDetail(context, id, location, false, gameId, entrance, "", referer, type, act, paginationType, fieldId, sectionName)
directToLegacyVideoDetail(context, id, location, false, gameId, entrance, "", referer, type, act, paginationType, fieldId, sectionName)
}
EntranceUtils.HOST_VIDEO_DETAIL -> {
DirectUtils.directToVideoDetail(context, id, entrance, path)
}
EntranceUtils.HOST_VIDEO_SINGLE -> {
val referer = uri.getQueryParameter("referer") ?: ""
directToVideoDetail(context, id, VideoDetailContainerViewModel.Location.SINGLE_VIDEO.value,
DirectUtils.directToVideoDetail(context, id, VideoDetailContainerViewModel.Location.SINGLE_VIDEO.value,
false, "", entrance, "", if (TextUtils.isEmpty(referer)) "" else referer)
}
EntranceUtils.HOST_VIDEO_STREAMING_HOME -> {
@ -275,11 +278,39 @@ object DefaultUrlHandler {
}
EntranceUtils.HOST_GAME_RATING_DETAIL -> {
DirectUtils.directToGameRatingDetail(context, uri.getQueryParameter(EntranceUtils.KEY_GAME_ID), uri.getQueryParameter(EntranceUtils.KEY_COMMENT_ID), EntranceUtils.ENTRANCE_BROWSER)
DirectUtils.directToGameRatingDetail(
context,
uri.getQueryParameter(EntranceUtils.KEY_GAME_ID),
uri.getQueryParameter(EntranceUtils.KEY_COMMENT_ID),
EntranceUtils.ENTRANCE_BROWSER)
}
EntranceUtils.HOST_FORUM -> {
DirectUtils.directToForum(context)
val position = uri.getQueryParameter(EntranceUtils.KEY_POSITION)?.toInt()
DirectUtils.directToForum(context, position ?: 0)
}
EntranceUtils.HOST_UPLOAD_VIDEO_NEW -> {
val activityName = uri.getQueryParameter("activity_name") ?: ""
val activityId = uri.getQueryParameter("activity_id") ?: ""
val forumName = uri.getQueryParameter("forum_name") ?: ""
val forumId = uri.getQueryParameter("forum_id") ?: ""
val forumIcon = uri.getQueryParameter("forum_icon") ?: ""
val forumType = uri.getQueryParameter("forum_type") ?: BbsType.OFFICIAL_BBS.value
val activityLabelEntity = ActivityLabelEntity(id = activityId, name = activityName)
val communityEntity = CommunityEntity(id = forumId, name = forumName, icon = forumIcon)
context.startActivity(VideoPublishActivity.getIntent(
context,
communityEntity,
activityLabelEntity,
forumType,
false,
entrance,
""
))
}
EntranceUtils.HOST_SUGGESTION -> {
@ -418,10 +449,11 @@ object DefaultUrlHandler {
val name = uri.getQueryParameter("communityName") ?: ""
DirectUtils.directToCommunityColumn(context, CommunityEntity(id, name), columnsId, entrance, "")
}
contains("zone") -> {
contains("zone") && split("/").size > 2 -> {
val gameId = split("/")[2]
DirectUtils.directGameZone(context, gameId, url, entrance)
}
else -> return false
}
}
return true

View File

@ -62,6 +62,7 @@ public class Config {
public static final String PATCHES = "patches";
public static final String DEFAULT_CHANNEL = "GH_TEST2";
public static final String DEFAULT_CHANNEL_FOR_RELEASE = "GH_LOST"; // 正式包的缺省渠道,避免因渠道丢失而回落到测试渠道
private static String SETTINGS_KEY = "settingsKey";
@ -301,7 +302,7 @@ public class Config {
});
RetrofitManager.getInstance(HaloApp.getInstance().getApplication())
.getApi().getNewSettings(Build.MANUFACTURER, Build.MODEL, channel)
.getApi().getNewSettings(Build.MANUFACTURER, Build.MODEL, channel, Build.VERSION.SDK_INT, BuildConfig.VERSION_NAME)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BiResponse<NewSettingsEntity>() {

View File

@ -1,6 +1,7 @@
package com.gh.common.constant;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.TimeUtils;
import com.halo.assistant.HaloApp;
public class Constants {
@ -170,6 +171,9 @@ public class Constants {
//首页视频播放进度
public static final String SP_HOME_VIDEO_PLAY_RECORD = "home_video_play_record";
// 论坛内容视频播放进度
public static final String SP_CONTENT_VIDEO_PLAY_RECORD = "content_video_play_record";
// 用户是否曾经永久拒绝过存储权限
public static final String SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION = "user_has_permanently_denied_storage_permission";
@ -182,17 +186,38 @@ public class Constants {
// 头像挂件ID
public static final String SP_CHOOSE_AVATAR_ID = "choose_avatar_id";
// 是否显示返回任务悬浮图标
public static final String SP_SHOW_TASK_FLOAT = "show_task_float";
// 悬浮图标Y值
public static final String SP_TASK_FLOAT_LAST_Y = "task_float_last_y";
// 是否第一次进入新分类2.0
public static final String SP_FIRST_ENTER_CATEGORY_V2 = "first_enter_category_v2";
// 是否成功取过号
public static final String SP_HAS_GET_PHONE_INFO = "has_get_phone_info";
// 是否点击过更换背景按钮
public static final String SP_HAS_CLICK_CHANGE_BG = "has_click_change_bg";
// 是否显示更换背景提示
public static final String SP_SHOW_CHANGE_BG_TIPS = "show_change_bg_tips" + TimeUtils.getStartTimeOfToday();
// 新分类2.0引导
public static final String SP_SHOW_CATEGORY_GUIDE = "show_category_guide";
// 内容视频播放选项
public static final String SP_CONTENT_VIDEO_OPTION = "content_video_option";
// 首页/游戏详情页视频播放选项
public static final String SP_HOME_OR_DETAIL_VIDEO_OPTION = "home_or_detail_video_option";
// 是否默认静音播放视频
public static final String SP_VIDEO_PLAY_MUTE = "video_play_mute";
//帖子发布页上传视频引导
public static final String SP_ARTICLE_VIDEO_GUIDE = "article_video_guide";
//问题发布页上传视频引导
public static final String SP_QUESTION_VIDEO_GUIDE = "question_video_guide";
// 社区首页引导
public static final String SP_COMMUNITY_HOME_GUIDE = "community_home_guide";
// 社区首页发布按钮引导
public static final String SP_COMMUNITY_HOME_VIDEO_GUIDE = "community_home_video_guide";
// 论坛详情申请版主引导
public static final String SP_FORUM_DETAIL_MODERATOR_GUIDE = "forum_detail_moderator_guide";
//手机号码匹配规则
public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
public static final String REGEX_ACCOUNT = "^[a-zA-Z_]\\w{5,17}$";
@ -298,6 +323,10 @@ public class Constants {
public static final String WITHDRAW_INFO_ADDRESS_DEV = "https://static-web.ghzs.com/shop-dev/index.html#/cash?from=ghzs";
public static final String WITHDRAW_INFO_ADDRESS = "https://static-web.ghzs.com/shop/index.html#/cash?from=ghzs";
// 活动详情
public static final String ACTIVITY_DETAIL_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_activity_dev/common.html?from=ghzs";
public static final String ACTIVITY_DETAIL_ADDRESS = "https://static-web.ghzs.com/ghzs_activity_prod/common.html?from=ghzs";
//最少需要多少数据才能上传
public static final int DATA_AMOUNT = 20;

View File

@ -336,6 +336,13 @@ public class BindingAdapters {
}
}
@BindingAdapter("gameIcon")
public static void setGameIcon(View view, GameEntity gameEntity) {
if (gameEntity != null && view instanceof GameIconView) {
((GameIconView) view).displayGameIcon(gameEntity.getIcon(), gameEntity.getIconSubscript());
}
}
@BindingAdapter("articleType")
public static void setArticleType(TextView view, String articleType) {
NewsUtils.setNewsType(view, articleType, 0, 0);
@ -773,6 +780,34 @@ public class BindingAdapters {
}
}
@BindingAdapter("setVideoDetailGameTags")
public static void setVideoDetailGameTags(LinearLayout layout, GameEntity gameEntity) {
try {
ArrayList<TagStyleEntity> tagStyle = new ArrayList<>();
TestEntity test = gameEntity.getTest();
if (test != null
// 这个判断用于开测表列表
&& !"type_tag".equals(test.getGameTag())) {
TagStyleEntity typeTag = new TagStyleEntity();
typeTag.setName(test.getType() != null ? test.getType() : "");
typeTag.setBackground("FFF3E0");
typeTag.setColor("FA8500");
tagStyle.add(typeTag);
TagStyleEntity timeTag = new TagStyleEntity();
timeTag.setName(GameViewUtils.getGameTestDate(test.getStart()));
timeTag.setBackground("E0FFF9");
timeTag.setColor("00A887");
tagStyle.add(timeTag);
} else {
tagStyle = gameEntity.getTagStyle();
}
GameViewUtils.setLabelList(layout.getContext(), layout, tagStyle, 4);
} catch (Exception e) {
e.printStackTrace();
}
}
@BindingAdapter("isRefreshing")
public static void isRefreshing(SwipeRefreshLayout layout, LoadStatus status) {
if (status != LoadStatus.INIT_LOADING && status != LoadStatus.LIST_LOADING) {

View File

@ -0,0 +1,90 @@
package com.gh.common.dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentTransaction
import com.gh.base.fragment.BaseDialogFragment
import com.gh.common.util.DirectUtils
import com.gh.common.util.SpanBuilder
import com.gh.common.util.dip2px
import com.gh.common.view.CustomLinkMovementMethod
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.DialogApplyModeratorBinding
class ApplyModeratorDialogFragment : BaseDialogFragment() {
private lateinit var binding: DialogApplyModeratorBinding
private var groupNumber = ""
private var groupKey = ""
private var mCallBack: (() -> Unit)? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = DialogApplyModeratorBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val startText = "版主考核群:"
val text = "$startText$groupNumber\n感谢你对论坛建设的支持\n请加入版主考核群并联系群主进行版主资格考核"
binding.desTv.text = SpanBuilder(text)
.click(startText.length, startText.length + groupNumber.length, R.color.theme_font,true) {
DirectUtils.directToQqGroup(
requireContext(),
groupKey
)
}
.build()
binding.desTv.movementMethod = CustomLinkMovementMethod.getInstance()
binding.confirmTv.setOnClickListener {
dismissAllowingStateLoss()
mCallBack?.invoke()
}
}
override fun onStart() {
super.onStart()
val width = requireContext().resources.displayMetrics.widthPixels - 60F.dip2px()
val height = ViewGroup.LayoutParams.WRAP_CONTENT
dialog?.window?.setLayout(width, height)
}
companion object {
@JvmStatic
fun show(
activity: AppCompatActivity,
number: String,
key: String,
callBack: (() -> Unit)?
) {
var dialogFragment =
activity.supportFragmentManager.findFragmentByTag(ApplyModeratorDialogFragment::class.java.simpleName) as? ApplyModeratorDialogFragment
if (dialogFragment != null) {
dialogFragment.groupNumber = number
dialogFragment.groupKey = key
dialogFragment.mCallBack = callBack
val transaction: FragmentTransaction =
activity.supportFragmentManager.beginTransaction()
transaction.show(dialogFragment)
transaction.commit()
} else {
dialogFragment = ApplyModeratorDialogFragment().apply {
groupNumber = number
groupKey = key
mCallBack = callBack
}
dialogFragment.show(
activity.supportFragmentManager,
PrivacyDialogFragment::class.java.simpleName
)
}
}
}
}

View File

@ -17,13 +17,9 @@ import androidx.appcompat.app.AppCompatActivity
import com.gh.common.avoidcallback.AvoidOnResultManager
import com.gh.common.avoidcallback.Callback
import com.gh.common.constant.Constants
import com.gh.common.util.CheckLoginUtils
import com.gh.common.util.DialogUtils
import com.gh.common.util.GsonUtils
import com.gh.common.util.SPUtils
import com.gh.common.util.*
import com.gh.gamecenter.R
import com.gh.gamecenter.UserInfoEditActivity
import com.gh.gamecenter.WebActivity
import com.gh.gamecenter.entity.AuthDialogEntity
import com.gh.gamecenter.entity.AuthDialogLevel
import com.gh.gamecenter.entity.GameEntity
@ -56,7 +52,7 @@ class CertificationDialog(context: Context, private val authDialogEntity: AuthDi
detailedDesTv.paint.isAntiAlias = true
detailedDesTv.setOnClickListener {
context.startActivity(WebActivity.getIntentByUrl(context, authDialogEntity.link))
DirectUtils.directToWebView(context, authDialogEntity.link)
}
when (authDialogEntity.level) {

View File

@ -26,7 +26,7 @@ import com.gh.gamecenter.R
import com.gh.gamecenter.entity.DeviceDialogEntity
import com.gh.gamecenter.entity.GameEntity
import com.google.gson.reflect.TypeToken
import com.halo.assistant.fragment.SettingsFragment.AUTO_INSTALL_SP_KEY
import com.gh.gamecenter.setting.GameDownloadSettingFragment.Companion.AUTO_INSTALL_SP_KEY
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus

View File

@ -15,7 +15,7 @@ import com.gh.gamecenter.room.converter.*
import com.gh.gamecenter.room.dao.*
import com.halo.assistant.HaloApp
@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class], version = 8, exportSchema = false)
@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class], version = 9, exportSchema = false)
@TypeConverters(CountConverter::class,
CommunityConverter::class,
TimeConverter::class,
@ -26,7 +26,9 @@ import com.halo.assistant.HaloApp
ListStringConverter::class,
CommunityVideoConverter::class,
UserConverter::class,
ImageInfoConverter::class)
ImageInfoConverter::class,
VideoInfoConverter::class,
QuestionsConverter::class)
abstract class HistoryDatabase : RoomDatabase() {
@ -79,6 +81,26 @@ abstract class HistoryDatabase : RoomDatabase() {
}
}
val MIGRATION_8_9: Migration = object : Migration(8, 9) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("Alter TABLE ArticleEntity add des TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE ArticleEntity add url TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE ArticleEntity add videoInfo TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE ArticleEntity add poster TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE ArticleEntity add length INTEGER NOT NULL DEFAULT 0")
database.execSQL("Alter TABLE ArticleEntity add status TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE ArticleEntity add content TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE ArticleEntity add questions TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE AnswerEntity add des TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE AnswerEntity add url TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE AnswerEntity add videoInfo TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE AnswerEntity add poster TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE AnswerEntity add length INTEGER NOT NULL DEFAULT 0")
database.execSQL("Alter TABLE AnswerEntity add status TEXT NOT NULL DEFAULT ''")
database.execSQL("Alter TABLE AnswerEntity add content TEXT NOT NULL DEFAULT ''")
}
}
val instance by lazy {
Room.databaseBuilder(HaloApp.getInstance().application, HistoryDatabase::class.java, "USER_TRACK_HISTORY_DATABASE")
.addMigrations(MIGRATION_2_3)
@ -87,6 +109,7 @@ abstract class HistoryDatabase : RoomDatabase() {
.addMigrations(MIGRATION_5_6)
.addMigrations(MIGRATION_6_7)
.addMigrations(MIGRATION_7_8)
.addMigrations(MIGRATION_8_9)
.build()
}
}

View File

@ -0,0 +1,32 @@
package com.gh.common.json
import org.json.JSONArray
import org.json.JSONObject
import java.util.*
//Source: https://stackoverflow.com/questions/41861449/kotlin-dsl-for-creating-json-objects-without-creating-garbage
fun json(build: JsonObjectBuilder.() -> Unit): JSONObject {
return JsonObjectBuilder().json(build)
}
class JsonObjectBuilder {
private val deque: Deque<JSONObject> = ArrayDeque()
fun json(build: JsonObjectBuilder.() -> Unit): JSONObject {
deque.push(JSONObject())
this.build()
return deque.pop()
}
infix fun <T> String.to(value: T) {
// wrap value into json block if it is a lambda
val wrapped = when (value) {
is Function0<*> -> json { value.invoke() }
is Array<*> -> JSONArray().apply { value.forEach { put(it) } }
else -> value
}
deque.peek().put(this, wrapped)
}
}

View File

@ -41,7 +41,7 @@ object LoghubHelper {
// 每次发送前会把日志保存到本地的binlog文件只有发送成功才会删除保证日志上传At Least Once
setPersistent(1)
// 持久化的文件名,需要保证文件所在的文件夹已创建。配置多个客户端时,不应设置相同文件
setPersistentFilePath(HaloApp.getInstance().application.filesDir.absolutePath + "/log.dat")
setPersistentFilePath(HaloApp.getInstance().application.filesDir.absolutePath + "/${logStore}.dat")
// 是否每次AddLog强制刷新高可靠性场景建议打开
setPersistentForceFlush(1)
// 持久化文件滚动个数建议设置成10。

View File

@ -5,13 +5,14 @@ import android.database.ContentObserver
import android.media.AudioManager
import android.os.Handler
import com.gh.common.util.tryCatchInRelease
import com.halo.assistant.HaloApp
class VolumeObserver(var context: Context, handler: Handler, var callback: MuteCallback? = null)
: ContentObserver(handler) {
class VolumeObserver(var callback: MuteCallback? = null)
: ContentObserver(Handler()) {
var previousVolume: Int = 0
init {
val audio = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val audio = HaloApp.getInstance().application.getSystemService(Context.AUDIO_SERVICE) as AudioManager
// 部分设备的 audioManager getStreamVolume 内部会触发空指针 :(
tryCatchInRelease { previousVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC) }
}
@ -19,7 +20,7 @@ class VolumeObserver(var context: Context, handler: Handler, var callback: MuteC
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
val audio = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val audio = HaloApp.getInstance().application.getSystemService(Context.AUDIO_SERVICE) as AudioManager
var currentVolume = 0
tryCatchInRelease {

View File

@ -7,6 +7,7 @@ import android.graphics.Bitmap
import android.net.Uri
import android.text.TextUtils
import com.g00fy2.versioncompare.Version
import com.gh.common.json.json
import com.gh.common.util.*
import com.gh.download.DownloadManager
import com.gh.gamecenter.entity.GameEntity
@ -14,6 +15,7 @@ import com.gh.gamecenter.entity.SimulatorGameRecordEntity
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.retrofit.BiResponse
import com.gh.gamecenter.retrofit.EmptyResponse
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.room.AppDatabase
import com.halo.assistant.HaloApp
@ -25,6 +27,7 @@ import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import org.json.JSONArray
import java.io.ByteArrayOutputStream
import java.io.File
@ -84,6 +87,8 @@ object SimulatorGameManager {
fun launchSimulatorGame(downloadEntity: DownloadEntity, gameEntity: GameEntity) {
val versionFromInstalledApp = PackageUtils.getVersionByPackage(gameEntity.simulator?.apk?.packageName)
val shouldShowUpdate = Version(gameEntity.simulator?.apk?.version).isHigherThan(versionFromInstalledApp)
updateSimulatorConfigFile(gameId = gameEntity.id)
if (shouldShowUpdate) {
SimulatorDownloadManager.getInstance().showDownloadDialog(AppManager.getInstance().recentActiveActivity, gameEntity.simulator,
SimulatorDownloadManager.SimulatorLocation.LAUNCH, gameEntity.id, gameEntity.name
@ -159,7 +164,7 @@ object SimulatorGameManager {
*/
@SuppressLint("CheckResult")
@JvmStatic
fun recordDownloadSimulatorGames(gameId: String, type: String) {
fun recordDownloadSimulatorGame(gameId: String, type: String) {
val requestMap = hashMapOf<String, Any>()
requestMap["game_id"] = gameId
requestMap["package"] = "-"
@ -174,6 +179,31 @@ object SimulatorGameManager {
})
}
/**
* 批量记录设备下载的模拟器游戏
*/
@SuppressLint("CheckResult")
@JvmStatic
fun recordDownloadSimulatorGames() {
val gameArray = JSONArray()
val allGameList = AppDatabase.getInstance(HaloApp.getInstance()).simulatorGameDao().getAllSimulatorGame()
for (game in allGameList) {
if (!game.isDeleted) {
gameArray.put(json {
"game_id" to game.id
"package" to "-"
})
}
}
if (gameArray.length() == 0) return
RetrofitManager.getInstance(HaloApp.getInstance().application).api
.putDownloadSimulatorGames(HaloApp.getInstance().gid, gameArray.toRequestBody())
.compose(singleToMain())
.subscribe(EmptyResponse())
}
@SuppressLint("CheckResult")
private fun refreshSimulatorGame(gameId: String, type: String) {
val simulatorGameDao = AppDatabase.getInstance(HaloApp.getInstance()).simulatorGameDao()
@ -278,4 +308,22 @@ object SimulatorGameManager {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(EmptyResponse())
}
private fun updateSimulatorConfigFile(gameId: String) {
RetrofitManager.getInstance(HaloApp.getInstance().application)
.api
.getGameDigest(gameId)
.map(ApkActiveUtils.filterMapper)
.subscribeOn(Schedulers.io())
.subscribe(object : Response<GameEntity>() {
override fun onResponse(game: GameEntity?) {
game?.let {
if (!TextUtils.isEmpty(game.simulatorGameConfig)) {
val configFilePath = getPathByType(game.simulatorType + "/cheat/" + game.getApk().firstOrNull()?.packageName + ".ini")
FileUtils.downloadAndUpdateFile(game.simulatorGameConfig, configFilePath)
}
}
}
})
}
}

View File

@ -16,8 +16,12 @@ object SyncFieldConstants {
const val ANSWER_COMMENT_COUNT = "ANSWER_COMMENT_COUNT"
const val ARTICLE_COMMENT_COUNT = "ARTICLE_COMMENT_COUNT"
const val ARTICLE_COMMENT_REPLY_COUNT = "ARTICLE_COMMENT_REPLY_COUNT"
const val ANSWER_COMMENT_REPLY_COUNT = "ANSWER_COMMENT_REPLY_COUNT"
// 回答数量
const val ANSWER_COUNT = "ANSWER_COUNT"
// 是否关注
const val IS_FOLLOWER = "IS_FOLLOWER"
}

View File

@ -4,7 +4,7 @@ import android.app.Activity
import android.app.Application
import android.os.Bundle
// TODO SplashActivity 没有回调 onStart 和 onStop
// FYI 快速启动并且不设置 contentView 马上结束的 SplashActivity 没有 onStart 和 onStop 回调
class ActivityLifecycleWatcher(private val mTrack: ITrack) : Application.ActivityLifecycleCallbacks {
override fun onActivityStarted(activity: Activity) {

View File

@ -8,7 +8,7 @@ import java.util.concurrent.atomic.AtomicLong
import kotlin.concurrent.fixedRateTimer
/**
* 应用声明周期观察者
* 应用生命周期观察者
*/
class AppLifecycleWatcher(private val mTrack: ITrack) : DefaultLifecycleObserver {

View File

@ -5,6 +5,10 @@ import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner
import java.util.*
/**
* launch_id 当前启动的唯一 id (每次应用冷启动或被内存回收时变更)
* session_id 当前会话的唯一 id (应用切换至后台超30秒时变更)
*/
object Tracker : ITrack {
private var mSessionId: String = ""
@ -33,8 +37,6 @@ object Tracker : ITrack {
val businessId = if (activity is IBusiness) activity.getBusinessId() else null
TrackerLogger.logActivityStart(
mLaunchId,
mSessionId,
System.identityHashCode(activity).toString(),
activity::class.java.simpleName,
businessId)
@ -44,23 +46,21 @@ object Tracker : ITrack {
val businessId = if (activity is IBusiness) activity.getBusinessId() else null
TrackerLogger.logActivityStop(
mLaunchId,
mSessionId,
System.identityHashCode(activity).toString(),
activity::class.java.simpleName,
businessId)
}
override fun onAppStarted() {
TrackerLogger.logAppStart(mLaunchId, mSessionId)
TrackerLogger.logAppStart()
}
override fun onAppVisible(interval: Long) {
TrackerLogger.logAppVisible(mLaunchId, mSessionId, interval)
TrackerLogger.logAppVisible(interval)
}
override fun onAppStopped() {
TrackerLogger.logAppStop(mLaunchId, mSessionId)
TrackerLogger.logAppStop()
}
override fun onSessionChanged(sessionId: String) {

View File

@ -1,10 +1,13 @@
package com.gh.common.tracker
import android.content.Context
import com.gh.common.exposure.meta.MetaUtil
import com.gh.common.exposure.meta.MetaUtil.getBase64EncodedAndroidId
import com.gh.common.exposure.meta.MetaUtil.getBase64EncodedIMEI
import com.gh.common.loghub.LoghubUtils
import com.gh.common.util.PackageUtils
import com.gh.common.util.tryCatchInRelease
import com.gh.gamecenter.R
import com.lightgame.utils.Utils
import org.json.JSONException
import org.json.JSONObject
@ -13,16 +16,16 @@ object TrackerLogger {
private const val LOG_STORE = "launch_activity"
fun logAppStart(launchId: String, sessionId: String) {
logAppVisible(launchId, sessionId, 0)
fun logAppStart() {
logAppVisible(0)
}
fun logAppVisible(launchId: String, sessionId: String, interval: Long) {
fun logAppVisible(interval: Long) {
val jsonObject = JSONObject()
val payloadObject = JSONObject()
tryCatchInRelease {
payloadObject.put("launch_id", launchId)
payloadObject.put("session_id", sessionId)
payloadObject.put("launch_id", Tracker.launchId)
payloadObject.put("session_id", Tracker.sessionId)
payloadObject.put("interval", interval)
jsonObject.put("event", "app_visible")
@ -32,12 +35,12 @@ object TrackerLogger {
uploadToLoghub(jsonObject, true)
}
fun logAppStop(launchId: String, sessionId: String) {
fun logAppStop() {
val jsonObject = JSONObject()
val payloadObject = JSONObject()
tryCatchInRelease {
payloadObject.put("launch_id", launchId)
payloadObject.put("session_id", sessionId)
payloadObject.put("launch_id", Tracker.launchId)
payloadObject.put("session_id", Tracker.sessionId)
jsonObject.put("event", "app_invisible")
jsonObject.put("payload", payloadObject)
@ -46,16 +49,14 @@ object TrackerLogger {
uploadToLoghub(jsonObject, true)
}
fun logActivityStart(launchId: String,
sessionId: String,
activityId: String,
fun logActivityStart(activityId: String,
activityName: String,
activityBusinessId: Pair<String, String>? = null) {
val jsonObject = JSONObject()
val payloadObject = JSONObject()
tryCatchInRelease {
payloadObject.put("launch_id", launchId)
payloadObject.put("session_id", sessionId)
payloadObject.put("launch_id", Tracker.launchId)
payloadObject.put("session_id", Tracker.sessionId)
payloadObject.put("activity_id", activityId)
payloadObject.put("activity_name", activityName)
if (activityBusinessId != null) {
@ -72,16 +73,14 @@ object TrackerLogger {
uploadToLoghub(jsonObject, false)
}
fun logActivityStop(launchId: String,
sessionId: String,
activityId: String,
fun logActivityStop(activityId: String,
activityName: String,
activityBusinessId: Pair<String, String>? = null) {
val jsonObject = JSONObject()
val payloadObject = JSONObject()
tryCatchInRelease {
payloadObject.put("launch_id", launchId)
payloadObject.put("session_id", sessionId)
payloadObject.put("launch_id", Tracker.launchId)
payloadObject.put("session_id", Tracker.sessionId)
payloadObject.put("activity_id", activityId)
payloadObject.put("activity_name", activityName)
if (activityBusinessId != null) {
@ -99,15 +98,13 @@ object TrackerLogger {
}
@JvmStatic
fun logHomeTabSelected(launchId: String,
sessionId: String,
tabPosition: Int,
fun logHomeTabSelected(tabPosition: Int,
tabContent: String) {
val jsonObject = JSONObject()
val payloadObject = JSONObject()
tryCatchInRelease {
payloadObject.put("launch_id", launchId)
payloadObject.put("session_id", sessionId)
payloadObject.put("launch_id", Tracker.launchId)
payloadObject.put("session_id", Tracker.sessionId)
payloadObject.put("tab_position", tabPosition)
payloadObject.put("tab_content", tabContent)
@ -119,12 +116,25 @@ object TrackerLogger {
}
@JvmStatic
fun logAppLaunch(launchId: String, sessionId: String) {
fun logAppLaunch(context: Context) {
val jsonObject = JSONObject()
val payloadObject = JSONObject()
val signatureHash = PackageUtils.getApkSignatureByPackageName(context, context.packageName)
val sideLoadInfo = PackageUtils.getSideLoadedInfo()
tryCatchInRelease {
payloadObject.put("launch_id", launchId)
payloadObject.put("session_id", sessionId)
payloadObject.put("launch_id", Tracker.launchId)
payloadObject.put("session_id", Tracker.sessionId)
payloadObject.put("signature", signatureHash)
payloadObject.put("package_name", context.packageName)
payloadObject.put("app_name", context.getString(R.string.app_name))
sideLoadInfo?.let {
payloadObject.put("is_side_loaded", sideLoadInfo["is_side_loaded"])
sideLoadInfo["installer_store"]?.let {
payloadObject.put("installer_store", it)
}
}
jsonObject.put("event", "app_launch")
jsonObject.put("payload", payloadObject)
@ -134,12 +144,12 @@ object TrackerLogger {
}
@JvmStatic
fun logAppLaunchSuccessful(launchId: String, sessionId: String) {
fun logAppLaunchSuccessful() {
val jsonObject = JSONObject()
val payloadObject = JSONObject()
tryCatchInRelease {
payloadObject.put("launch_id", launchId)
payloadObject.put("session_id", sessionId)
payloadObject.put("launch_id", Tracker.launchId)
payloadObject.put("session_id", Tracker.sessionId)
jsonObject.put("event", "app_launch_successful")
jsonObject.put("payload", payloadObject)

View File

@ -0,0 +1,134 @@
package com.gh.common.util
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.preference.PreferenceManager
import android.view.LayoutInflater
import android.view.View
import android.view.Window
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import com.gh.base.CurrentActivityHolder
import com.gh.common.json.json
import com.gh.common.util.ToastUtils.showToast
import com.gh.gamecenter.R
import com.gh.gamecenter.SuggestionActivity
import com.gh.gamecenter.entity.SettingsEntity
import com.gh.gamecenter.retrofit.BiResponse
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import org.json.JSONObject
object BbsReportHelper {
fun showReportDialog(contentId: String) {
val sp = PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance())
val suggestion: SettingsEntity.Suggestion? = sp.getString(SuggestionActivity.SUGGESTION_HINT_TYPE, null)?.toObject()
val reportList = suggestion?.report ?: return
CurrentActivityHolder.getCurrentActivity()?.apply {
if (this.isFinishing) return@apply
val dialog = Dialog(this)
val view = LayoutInflater.from(this).inflate(R.layout.dialog_video_complaint, null, false)
val complaintContainer = view.findViewById<LinearLayout>(R.id.complaintContainer)
val otherComplaintContainer: ConstraintLayout = view.findViewById(R.id.otherComplaintContainer)
val complaintCommentEt = view.findViewById<EditText>(R.id.complaintCommentEt)
val backTv = view.findViewById<TextView>(R.id.backTv)
val commitTv = view.findViewById<TextView>(R.id.commitTv)
val finalContext: Context = this
//添加透明阴影,实现类似 clipPadding=false 效果
complaintCommentEt.setShadowLayer(complaintCommentEt.extendedPaddingBottom.toFloat(), 0f, 0f, Color.TRANSPARENT)
complaintCommentEt.setTextChangedListener { s: CharSequence, _: Int?, _: Int?, _: Int? ->
commitTv.setTextColor(ContextCompat.getColor(finalContext, if (s.toString().trim { it <= ' ' }.isEmpty()) R.color.text_999999 else R.color.theme_font))
}
for (option in reportList) {
val reportTv = TextView(this)
reportTv.text = option
reportTv.textSize = 16F
reportTv.setTextColor(R.color.title.toColor())
reportTv.setBackgroundResource(R.drawable.textview_white_style)
reportTv.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT)
reportTv.setPadding(20F.dip2px(), 17F.dip2px(), 20F.dip2px(), 17F.dip2px())
if (option.contains("其它")) {
val drawable = R.drawable.ic_complaint_arrow_right.toDrawable()
drawable!!.setBounds(0, 0, 6F.dip2px(), 10F.dip2px())
reportTv.setCompoundDrawables(null, null, drawable, null)
}
complaintContainer.addView(reportTv)
reportTv.setOnClickListener {
if (option.contains("其它")) {
complaintContainer.visibility = View.GONE
otherComplaintContainer.visibility = View.VISIBLE
complaintCommentEt.requestFocus()
Util_System_Keyboard.showSoftKeyboard(finalContext, complaintCommentEt)
} else {
postReport(contentId, json {
"reason" to reportTv.text.toString()
})
dialog.cancel()
}
}
}
backTv.setOnClickListener {
Util_System_Keyboard.hideSoftKeyboard(finalContext, complaintCommentEt)
complaintContainer.visibility = View.VISIBLE
otherComplaintContainer.visibility = View.GONE
}
commitTv.setOnClickListener {
if (complaintCommentEt.text.toString().isEmpty()) {
showToast("请先输入说明~")
return@setOnClickListener
}
postReport(contentId, json {
"reason" to "其它"
"description" to complaintCommentEt.text.toString()
})
dialog.cancel()
}
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.setContentView(view)
dialog.show()
val window = dialog.window
if (window != null) {
window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
val params = window.attributes
params.width = resources.displayMetrics.widthPixels - 40F.dip2px()
window.attributes = params
}
}
}
@SuppressLint("CheckResult")
private fun postReport(contentId: String, reportContent: JSONObject) {
RetrofitManager.getInstance(HaloApp.getInstance())
.api
.postBbsReport(contentId, reportContent.toRequestBody())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BiResponse<ResponseBody>() {
override fun onSuccess(data: ResponseBody) {
Utils.toast(HaloApp.getInstance(), "举报成功")
}
})
}
}

View File

@ -0,0 +1,128 @@
package com.gh.common.util
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.os.Bundle
import com.gh.base.CurrentActivityHolder
import com.gh.base.GHThreadFactory
import com.gh.gamecenter.MainActivity
import com.gh.gamecenter.forum.detail.ForumDetailActivity
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.qa.comment.CommentActivity
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
import com.gh.gamecenter.retrofit.BiResponse
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
object BbsStayTimeHelper {
private var mStayTimeCount = 0
private var mIsStayTimeCountEnabled = false // 论坛停留时间统计是否开启
private var mStayTimeTimeout = 30
private var mIsStayTimeCountValid = false // 论坛停留时间统计是否有效
const val IS_BBS_CONTENT_VISIBLE = "is_bbs_content_visible"
private val mThreadService: ExecutorService by lazy {
Executors.newSingleThreadExecutor(GHThreadFactory("STAY_TIME_THREAD"))
}
private val mActivityLifecycleCallbacks by lazy {
object : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {}
override fun onActivityStarted(activity: Activity) {}
override fun onActivityResumed(activity: Activity) {
if (isTopActivityBbsRelated(activity) && mIsStayTimeCountEnabled) {
resumeTimeCount()
}
}
override fun onActivityPaused(activity: Activity) {
if (isTopActivityBbsRelated(activity) && mIsStayTimeCountEnabled) {
pauseTimeCount()
}
}
}
}
fun enableStayTimeCount(timeout: Int) {
mIsStayTimeCountEnabled = true
mStayTimeTimeout = timeout
resumeTimeCount()
HaloApp.getInstance().registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks)
}
fun disableStayTimeCount() {
mIsStayTimeCountEnabled = false
mStayTimeCount = 0
HaloApp.getInstance().unregisterActivityLifecycleCallbacks(mActivityLifecycleCallbacks)
}
fun resumeTimeCount() {
if (!mIsStayTimeCountEnabled) return
mIsStayTimeCountValid = true
mThreadService.execute {
while (mIsStayTimeCountEnabled && mIsStayTimeCountValid) {
val topActivity = CurrentActivityHolder.getCurrentActivity() ?: continue
if (isTopActivityBbsRelated(topActivity)) {
tryWithDefaultCatch {
mStayTimeCount++
if (mStayTimeCount >= mStayTimeTimeout) {
postExploreFinish()
}
}
}
Thread.sleep(1000)
}
}
}
fun pauseTimeCount() {
if (!mIsStayTimeCountEnabled) return
mIsStayTimeCountValid = false
}
private fun isTopActivityBbsRelated(activity: Activity): Boolean {
return (activity is MainActivity && activity.intent.extras?.getBoolean(IS_BBS_CONTENT_VISIBLE) == true)
|| activity is ForumDetailActivity
|| activity is ArticleDetailActivity
|| activity is ForumVideoDetailActivity
|| activity is CommentActivity
|| activity is NewQuestionDetailActivity
}
@SuppressLint("CheckResult")
private fun postExploreFinish() {
RetrofitManager.getInstance(HaloApp.getInstance())
.api
.postExplorerFinish()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BiResponse<ResponseBody>() {
override fun onSuccess(data: ResponseBody) {
if (!isPublishEnv()) {
ToastUtils.toast("完成了论坛停留任务(仅测试环境有这个 toast 不要慌)")
}
disableStayTimeCount()
}
})
}
}

View File

@ -8,6 +8,7 @@ import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.media.ExifInterface;
import android.media.MediaMetadataRetriever;
import android.os.Build;
import com.halo.assistant.HaloApp;
@ -307,6 +308,218 @@ public class BitmapUtils {
return outputBitmap;
}
/**
* 模糊Bitmap
*
* @param sentBitmap
* @param radius
* @return
*/
public static Bitmap doBlur(Bitmap sentBitmap, int radius) {
Bitmap.Config config = sentBitmap.getConfig();
if (config == null) {
config = Bitmap.Config.ARGB_8888;
}
Bitmap bitmap = sentBitmap.copy(config, true);
if (radius < 1) {
return (null);
}
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
int wm = w - 1;
int hm = h - 1;
int wh = w * h;
int div = radius + radius + 1;
int r[] = new int[wh];
int g[] = new int[wh];
int b[] = new int[wh];
int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
int vmin[] = new int[Math.max(w, h)];
int divsum = (div + 1) >> 1;
divsum *= divsum;
int dv[] = new int[256 * divsum];
for (i = 0; i < 256 * divsum; i++) {
dv[i] = (i / divsum);
}
yw = yi = 0;
int[][] stack = new int[div][3];
int stackpointer;
int stackstart;
int[] sir;
int rbs;
int r1 = radius + 1;
int routsum, goutsum, boutsum;
int rinsum, ginsum, binsum;
for (y = 0; y < h; y++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
for (i = -radius; i <= radius; i++) {
p = pix[yi + Math.min(wm, Math.max(i, 0))];
sir = stack[i + radius];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rbs = r1 - Math.abs(i);
rsum += sir[0] * rbs;
gsum += sir[1] * rbs;
bsum += sir[2] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
}
stackpointer = radius;
for (x = 0; x < w; x++) {
r[yi] = dv[rsum];
g[yi] = dv[gsum];
b[yi] = dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (y == 0) {
vmin[x] = Math.min(x + radius + 1, wm);
}
p = pix[yw + vmin[x]];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[(stackpointer) % div];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi++;
}
yw += w;
}
for (x = 0; x < w; x++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
yp = -radius * w;
for (i = -radius; i <= radius; i++) {
yi = Math.max(0, yp) + x;
sir = stack[i + radius];
sir[0] = r[yi];
sir[1] = g[yi];
sir[2] = b[yi];
rbs = r1 - Math.abs(i);
rsum += r[yi] * rbs;
gsum += g[yi] * rbs;
bsum += b[yi] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
if (i < hm) {
yp += w;
}
}
yi = x;
stackpointer = radius;
for (y = 0; y < h; y++) {
// Preserve alpha channel: ( 0xff000000 & pix[yi] )
pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (x == 0) {
vmin[y] = Math.min(y + r1, hm) * w;
}
p = x + vmin[y];
sir[0] = r[p];
sir[1] = g[p];
sir[2] = b[p];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[stackpointer];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi += w;
}
}
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
return (bitmap);
}
/**
* 保存图片
*
@ -354,4 +567,25 @@ public class BitmapUtils {
return result;
}
// 获取视频缩略图(比较耗时,建议在子线程中调用)
public static Bitmap getVideoThumbnail(String filePath) {
Bitmap bitmap = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(filePath);
bitmap = retriever.getFrameAtTime();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
} finally {
try {
retriever.release();
} catch (RuntimeException e) {
e.printStackTrace();
}
}
return bitmap;
}
}

View File

@ -1,9 +1,11 @@
package com.gh.common.util;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import com.gh.base.CurrentActivityHolder;
import com.gh.common.constant.Constants;
import com.gh.gamecenter.LoginActivity;
import com.gh.gamecenter.manager.UserManager;
@ -16,14 +18,21 @@ import com.lightgame.utils.Utils;
public class CheckLoginUtils {
public static void checkLogin(final Context context, String entrance, OnLoginListener listener) {
public static void checkLogin(Context context, String entrance, OnLoginListener listener) {
if (!isLogin()) {
if (listener != null) Utils.toast(context, "需要登录");
LogUtils.login("dialog", null, entrance);
LogUtils.login("activity", null, entrance);
if (SPUtils.getBoolean(Constants.SP_HAS_GET_PHONE_INFO) || NetworkUtils.isOpenMobileData(context)) {
QuickLoginHelper.startLogin(context, entrance);
// 需要确保传入的 context 不为 application
if (!(context instanceof Activity)) {
context = CurrentActivityHolder.getCurrentActivity();
}
if (context != null) {
QuickLoginHelper.startLogin(context, entrance);
}
} else {
// 有可能App未启动
Bundle bundle = new Bundle();

View File

@ -21,97 +21,140 @@ import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import org.json.JSONException
import org.json.JSONObject
import retrofit2.HttpException
object CommentHelper {
@JvmStatic
fun showCommunityArticleCommentOptions(view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
articleId: String,
communityId: String,
isShowTop: Boolean = false,
ignoreModerator: Boolean = false,
listener: OnCommentOptionClickListener?) {
showCommentOptions(view = view,
commentEntity = commentEntity,
showConversation = showConversation,
articleId = articleId,
communityId = communityId,
isShowTop = isShowTop,
ignoreModerator = ignoreModerator,
listener = listener)
fun showCommunityArticleCommentOptions(
view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
articleId: String,
communityId: String,
isShowTop: Boolean = false,
ignoreModerator: Boolean = false,
listener: OnCommentOptionClickListener?
) {
showCommentOptions(
view = view,
commentEntity = commentEntity,
showConversation = showConversation,
articleId = articleId,
communityId = communityId,
isShowTop = isShowTop,
ignoreModerator = ignoreModerator,
listener = listener
)
}
@JvmStatic
fun showAnswerCommentOptions(view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
answerId: String,
listener: OnCommentOptionClickListener?) {
showCommentOptions(view = view,
commentEntity = commentEntity,
showConversation = showConversation,
answerId = answerId,
listener = listener)
fun showAnswerCommentOptions(
view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
answerId: String,
listener: OnCommentOptionClickListener?
) {
showCommentOptions(
view = view,
commentEntity = commentEntity,
showConversation = showConversation,
answerId = answerId,
listener = listener
)
}
@JvmStatic
fun showVideoCommentOptions(view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
videoId: String,
isVideoAuthor: Boolean,
listener: OnCommentOptionClickListener?) {
showCommentOptions(view = view,
commentEntity = commentEntity,
showConversation = showConversation,
videoId = videoId,
isVideoAuthor = isVideoAuthor,
listener = listener)
fun showVideoCommentOptions(
view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
videoId: String,
isVideoAuthor: Boolean,
isShowTop: Boolean = false,
listener: OnCommentOptionClickListener?
) {
showCommentOptions(
view = view,
commentEntity = commentEntity,
showConversation = showConversation,
videoId = videoId,
isVideoAuthor = isVideoAuthor,
isShowTop = isShowTop,
listener = listener
)
}
private fun showCommentOptions(view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
articleId: String? = null,
communityId: String? = null,
answerId: String? = null,
videoId: String? = null,
isShowTop: Boolean = false,
ignoreModerator: Boolean = false,
isVideoAuthor: Boolean = false,
listener: OnCommentOptionClickListener? = null) {
fun showQuestionCommentOption(
view: View,
commentEntity: CommentEntity,
questionId: String,
isShowTop: Boolean = false,
listener: OnCommentOptionClickListener?
) {
showCommentOptions(
view = view,
commentEntity = commentEntity,
showConversation = false,
questionId = questionId,
isShowTop = isShowTop,
ignoreModerator = true,
listener = listener
)
}
private fun showCommentOptions(
view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
articleId: String? = null,
communityId: String? = null,
answerId: String? = null,
questionId: String? = null,
videoId: String? = null,
isShowTop: Boolean = false,
ignoreModerator: Boolean = false,
isVideoAuthor: Boolean = false,
listener: OnCommentOptionClickListener? = null
) {
val context = view.context
val dialogOptions = ArrayList<String>()
if (isShowTop && articleId != null && commentEntity.me?.isArticleOrAnswerAuthor == true) {
if (isShowTop && (articleId != null || questionId != null) && commentEntity.me?.isContentAuthor == true) {
dialogOptions.add(if (commentEntity.isTop) "取消置顶" else "置顶")
}
if (questionId != null && commentEntity.me?.isContentAuthor == true) {
if (commentEntity.accept) {
dialogOptions.add("取消采纳")
} else {
dialogOptions.add("采纳")
}
}
dialogOptions.add("复制")
if (commentEntity.user.id != UserManager.getInstance().userId) {
dialogOptions.add("投诉")
}
if (isVideoAuthor || (videoId != null && commentEntity.user.id == UserManager.getInstance().userId)) {
dialogOptions.add("删除评论")
} else if (articleId != null && (commentEntity.user.id == UserManager.getInstance().userId ||
commentEntity.me?.isModerator == true || commentEntity.me?.isArticleOrAnswerAuthor == true)) {
if (questionId != null && commentEntity.me?.isModerator == true && !commentEntity.choiceness) {
dialogOptions.add("加精选")
}
if (commentEntity.user.id == UserManager.getInstance().userId || commentEntity.me?.isModerator == true || commentEntity.me?.isContentAuthor == true) {
dialogOptions.add("删除评论")
}
commentEntity.me?.let {
/*commentEntity.me?.let {
if (ignoreModerator) return@let
if (it.isModerator || (it.moderatorPermissions.hideAnswerComment > Permissions.GUEST
|| it.moderatorPermissions.topAnswerComment > Permissions.GUEST
|| it.moderatorPermissions.hideCommunityArticleComment > Permissions.GUEST
|| it.moderatorPermissions.topCommunityArticleComment > Permissions.GUEST)) {
|| it.moderatorPermissions.topAnswerComment > Permissions.GUEST
|| it.moderatorPermissions.hideCommunityArticleComment > Permissions.GUEST
|| it.moderatorPermissions.topCommunityArticleComment > Permissions.GUEST)
) {
dialogOptions.add("管理")
}
}
}*/
if (commentEntity.parentUser != null && showConversation) {
dialogOptions.add("查看对话")
@ -119,9 +162,11 @@ object CommentHelper {
val inflater = LayoutInflater.from(context)
val layout = inflater.inflate(R.layout.layout_popup_container, null)
val popupWindow = BugFixedPopupWindow(layout,
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT)
val popupWindow = BugFixedPopupWindow(
layout,
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
val container = layout.findViewById<LinearLayout>(R.id.container)
for (text in dialogOptions) {
val item = inflater.inflate(R.layout.layout_popup_option_item, container, false)
@ -134,34 +179,73 @@ object CommentHelper {
popupWindow.dismiss()
listener?.onCommentOptionClick(commentEntity, text)
when (text) {
"管理" -> showControlDialog(context, answerId, articleId, communityId, commentEntity, commentEntity.me!!)
"管理" -> showControlDialog(
context,
answerId,
articleId,
communityId,
commentEntity,
commentEntity.me!!
)
"复制" -> copyText(commentEntity.content, context)
"投诉" -> {
context.ifLogin("回答详情-评论-投诉") {
showReportTypeDialog(context, !videoId.isNullOrEmpty()) { reportType ->
showReportTypeDialog(context) { reportType ->
val commentListener =
object : PostCommentUtils.PostCommentListener {
override fun postSuccess(response: JSONObject?) {
Utils.toast(context, "感谢您的投诉")
}
val commentListener = object : PostCommentUtils.PostCommentListener {
override fun postSuccess(response: JSONObject?) {
Utils.toast(context, "感谢您的投诉")
}
override fun postFailed(error: Throwable?) {
if (error == null) {
Utils.toast(context, "投诉失败,请稍后重试")
} else {
Utils.toast(context, "投诉失败,${error.message}")
override fun postFailed(error: Throwable?) {
if (error == null) {
Utils.toast(context, "投诉失败,请稍后重试")
} else {
Utils.toast(context, "投诉失败,${error.message}")
}
}
}
}
if (answerId != null) {
PostCommentUtils.postAnswerReportData(context, commentEntity.id, answerId, reportType, commentListener)
} else if (articleId != null) {
PostCommentUtils.reportCommunityArticleComment(context, communityId, articleId, commentEntity.id, reportType, commentListener)
} else {
PostCommentUtils.reportVideoComment(context, videoId, commentEntity.id, reportType, commentListener)
when {
answerId != null -> {
PostCommentUtils.postAnswerReportData(
context,
commentEntity.id,
answerId,
reportType,
commentListener
)
}
articleId != null -> {
PostCommentUtils.reportCommunityArticleComment(
context,
communityId,
articleId,
commentEntity.id,
reportType,
commentListener
)
}
questionId != null -> {
PostCommentUtils.reportQuestionComment(
context,
questionId,
commentEntity.id,
reportType,
commentListener
)
}
else -> {
PostCommentUtils.reportVideoComment(
context,
videoId,
commentEntity.id,
reportType,
commentListener
)
}
}
}
}
@ -169,14 +253,37 @@ object CommentHelper {
"查看对话" -> {
if (answerId != null) {
context.startActivity(CommentDetailActivity
.getAnswerCommentIntent(context, commentEntity.id, answerId, null))
context.startActivity(
CommentDetailActivity
.getAnswerCommentIntent(
context,
commentEntity.id,
answerId,
null
)
)
} else if (articleId != null) {
context.startActivity(CommentDetailActivity
.getCommunityArticleCommentIntent(context, articleId, commentEntity.id, communityId, null))
context.startActivity(
CommentDetailActivity
.getCommunityArticleCommentIntent(
context,
articleId,
commentEntity.id,
communityId,
null
)
)
} else {
context.startActivity(CommentDetailActivity
.getVideoCommentIntent(context, commentEntity.id, videoId, isVideoAuthor, null))
context.startActivity(
CommentDetailActivity
.getVideoCommentIntent(
context,
commentEntity.id,
videoId,
isVideoAuthor,
null
)
)
}
}
}
@ -188,12 +295,14 @@ object CommentHelper {
popupWindow.showAutoOrientation(view)
}
private fun showControlDialog(context: Context,
answerId: String? = null,
articleId: String? = null,
communityId: String? = null,
comment: CommentEntity,
me: MeEntity) {
private fun showControlDialog(
context: Context,
answerId: String? = null,
articleId: String? = null,
communityId: String? = null,
comment: CommentEntity,
me: MeEntity
) {
val dialogOptions = arrayListOf<String>()
val highlight = "置顶评论"
val hide = "隐藏评论"
@ -202,19 +311,23 @@ object CommentHelper {
var canHideCommentDirectly = false
if (me.isModerator || me.moderatorPermissions.topAnswerComment > Permissions.GUEST
|| me.moderatorPermissions.topCommunityArticleComment > Permissions.GUEST) {
|| me.moderatorPermissions.topCommunityArticleComment > Permissions.GUEST
) {
dialogOptions.add(highlight)
if (me.moderatorPermissions.topAnswerComment > Permissions.REPORTER
|| me.moderatorPermissions.topCommunityArticleComment > Permissions.REPORTER) {
|| me.moderatorPermissions.topCommunityArticleComment > Permissions.REPORTER
) {
canHighlightCommentDirectly = true
}
}
if (me.isModerator || me.moderatorPermissions.hideAnswerComment > Permissions.GUEST
|| me.moderatorPermissions.hideCommunityArticleComment > Permissions.GUEST) {
|| me.moderatorPermissions.hideCommunityArticleComment > Permissions.GUEST
) {
dialogOptions.add(hide)
if (me.moderatorPermissions.hideAnswerComment > Permissions.REPORTER
|| me.moderatorPermissions.hideCommunityArticleComment > Permissions.REPORTER) {
|| me.moderatorPermissions.hideCommunityArticleComment > Permissions.REPORTER
) {
canHideCommentDirectly = true
}
}
@ -275,7 +388,10 @@ object CommentHelper {
val errorJson = JSONObject(string)
val errorCode = errorJson.getInt("code")
if (errorCode == 403059) {
Utils.toast(HaloApp.getInstance().application, "权限错误,请刷新后重试")
Utils.toast(
HaloApp.getInstance().application,
"权限错误,请刷新后重试"
)
return
} else {
Utils.toast(HaloApp.getInstance().application, e.message())
@ -286,23 +402,31 @@ object CommentHelper {
}
if (answerId != null) {
DialogUtils.showAlertDialog(context, highlight, highlightDialogHintContent,
"确定", "取消", {
RetrofitManager.getInstance(context).api
DialogUtils.showAlertDialog(
context, highlight, highlightDialogHintContent,
"确定", "取消", {
RetrofitManager.getInstance(context).api
.highlightAnswerComment(answerId, comment.id)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(highlightObserver)
}, null)
}, null
)
} else {
DialogUtils.showAlertDialog(context, highlight, highlightDialogHintContent,
"确定", "取消", {
RetrofitManager.getInstance(context).api
.highlightCommunityArticleComment(communityId, articleId, comment.id)
DialogUtils.showAlertDialog(
context, highlight, highlightDialogHintContent,
"确定", "取消", {
RetrofitManager.getInstance(context).api
.highlightCommunityArticleComment(
communityId,
articleId,
comment.id
)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(highlightObserver)
}, null)
}, null
)
}
}
@ -324,7 +448,10 @@ object CommentHelper {
val errorJson = JSONObject(string)
val errorCode = errorJson.getInt("code")
if (errorCode == 403059) {
Utils.toast(HaloApp.getInstance().application, "权限错误,请刷新后重试")
Utils.toast(
HaloApp.getInstance().application,
"权限错误,请刷新后重试"
)
return
} else {
Utils.toast(HaloApp.getInstance().application, e.message())
@ -335,46 +462,40 @@ object CommentHelper {
}
if (answerId != null) {
DialogUtils.showAlertDialog(context, hide, hideDialogHintContent,
"确定", "取消", {
RetrofitManager.getInstance(context).api
DialogUtils.showAlertDialog(
context, hide, hideDialogHintContent,
"确定", "取消", {
RetrofitManager.getInstance(context).api
.hideAnswerComment(answerId, comment.id)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(hideObserver)
}, null)
}, null
)
} else {
DialogUtils.showAlertDialog(context, hide, hideDialogHintContent,
"确定", "取消", {
RetrofitManager.getInstance(context).api
DialogUtils.showAlertDialog(
context, hide, hideDialogHintContent,
"确定", "取消", {
RetrofitManager.getInstance(context).api
.hideCommunityArticleComment(communityId, articleId, comment.id)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(hideObserver)
}, null)
}, null
)
}
}
}
}
}
private fun showReportTypeDialog(context: Context, isVideoComment: Boolean, reportCallback: (reportType: String) -> Unit) {
private fun showReportTypeDialog(
context: Context,
reportCallback: (reportType: String) -> Unit
) {
val reportTypes = arrayListOf("垃圾广告营销", "恶意攻击谩骂", "淫秽色情信息", "违法有害信息", "其它")
if (!isVideoComment) {
DialogUtils.showListDialog(context, reportTypes, null) { text ->
val jsonObject = JSONObject()
try {
jsonObject.put("reason", text)
reportCallback.invoke(jsonObject.toString())
} catch (e: JSONException) {
e.printStackTrace()
}
}
} else {
DialogUtils.showVideoComplaintDialog(context, reportTypes, null) { text ->
reportCallback.invoke(text)
}
DialogUtils.showVideoComplaintDialog(context, reportTypes, null) { text ->
reportCallback.invoke(text)
}
}

View File

@ -271,6 +271,7 @@ public class CommentUtils {
String articleId,
String articleCommunityId,
String videoId,
String questionId,
final CommentEntity commentEntity,
final TextView commentLikeCountTv,
final ImageView commentLikeIv,
@ -294,7 +295,7 @@ public class CommentUtils {
commentLikeCountTv.setText(NumberUtils.transSimpleCount(commentEntity.getVote()));
commentLikeCountTv.setVisibility(View.VISIBLE);
PostCommentUtils.likeComment(context, answerId, articleId, articleCommunityId, videoId, commentEntity.getId(),
PostCommentUtils.likeComment(context, answerId, articleId, articleCommunityId, videoId, questionId, commentEntity.getId(),
new PostCommentUtils.PostCommentListener() {
@Override
public void postSuccess(JSONObject response) {
@ -348,7 +349,7 @@ public class CommentUtils {
String entrance = "视频流-评论-点赞";
CheckLoginUtils.checkLogin(context, entrance, () -> {
PostCommentUtils.likeComment(context, answerId, articleId, articleCommunityId, videoId, commentEntity.getId(),
PostCommentUtils.likeComment(context, answerId, articleId, articleCommunityId, videoId, "", commentEntity.getId(),
new PostCommentUtils.PostCommentListener() {
@Override
public void postSuccess(JSONObject response) {

View File

@ -1,5 +1,6 @@
package com.gh.common.util
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.graphics.Color
@ -35,9 +36,11 @@ object DialogHelper {
uiModificationCallback: ((binding: DialogAlertDefaultBinding) -> Unit)? = null,
trackMtaEvent: Boolean = false,
mtaEvent: String = "",
mtaKey: String = ""): Dialog {
mtaKey: String = "") {
val solidContext = checkDialogContext(context)
if (solidContext is Activity && solidContext.isFinishing) return
val dialog = if (trackMtaEvent) {
TrackableDialog(solidContext, R.style.GhAlertDialog, mtaEvent, mtaKey)
} else {
@ -97,7 +100,6 @@ object DialogHelper {
dialog.setContentView(contentView)
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog.show()
return dialog
}
/**
@ -114,8 +116,8 @@ object DialogHelper {
negativeClickCallback: EmptyCallback,
trackMtaEvent: Boolean = false,
mtaEvent: String = "",
mtaKey: String = ""): Dialog {
return showDialog(
mtaKey: String = "") {
showDialog(
context = context,
title = title,
content = content,

View File

@ -65,6 +65,7 @@ import com.gh.gamecenter.R;
import com.gh.gamecenter.SuggestionActivity;
import com.gh.gamecenter.adapter.viewholder.PrivacyPolicyItemViewHolder;
import com.gh.gamecenter.databinding.DialogBindPhoneBinding;
import com.gh.gamecenter.databinding.DialogEnergySignBinding;
import com.gh.gamecenter.databinding.DialogOverseaConfirmationBinding;
import com.gh.gamecenter.databinding.DialogPackageParseErrorBinding;
import com.gh.gamecenter.databinding.DialogQuickLoginPermissionBinding;
@ -80,6 +81,7 @@ import com.gh.gamecenter.entity.PrivacyPolicyEntity;
import com.gh.gamecenter.entity.SettingsEntity;
import com.gh.gamecenter.entity.SimpleGameEntity;
import com.gh.gamecenter.entity.TrackableEntity;
import com.gh.gamecenter.setting.GameDownloadSettingFragment;
import com.gh.gamecenter.suggest.SuggestType;
import com.halo.assistant.HaloApp;
import com.halo.assistant.fragment.SettingsFragment;
@ -221,7 +223,7 @@ public class DialogUtils {
} else if (NetworkUtils.isWifiConnected(context)
|| filter4GorSize(context, size)) {
callBack.onResponse(false);
} else if (!preferences.getBoolean(SettingsFragment.getTrafficDownloadHintKey(), true)) {
} else if (!preferences.getBoolean(GameDownloadSettingFragment.getTrafficDownloadHintKey(), true)) {
AppExecutor.getUiExecutor().executeWithDelay(() -> Utils.toast(context, "当前使用移动网络下载,请注意流量消耗"), 500);
callBack.onResponse(false);
} else {
@ -296,7 +298,7 @@ public class DialogUtils {
PreferenceManager
.getDefaultSharedPreferences(finalContext)
.edit()
.putBoolean(SettingsFragment.getTrafficDownloadHintKey(), false)
.putBoolean(GameDownloadSettingFragment.getTrafficDownloadHintKey(), false)
.apply();
AppExecutor.getUiExecutor().executeWithDelay(() -> {
// 显示了弹窗以后,即便下面这个 toast 放在 listener.onConfirm 后调用也是显示 listener.onConfirm 里的 toast
@ -1926,7 +1928,7 @@ public class DialogUtils {
return dialog;
}
public static void showEnergyDialog(Context context, String userName, int energy) {
public static void showEnergyDialog(Context context, String userName, long energy) {
context = checkDialogContext(context);
final Dialog dialog = new Dialog(context, R.style.DialogWindowTransparent);
@ -2182,6 +2184,38 @@ public class DialogUtils {
dialog.show();
}
public static void showEnergySignDialog(Context context, int sevenDaySerialSign) {
context = checkDialogContext(context);
final Dialog dialog = new Dialog(context, R.style.DialogWindowTransparent);
DialogEnergySignBinding binding = DialogEnergySignBinding.inflate(LayoutInflater.from(context));
if (sevenDaySerialSign > 7) sevenDaySerialSign = 7;
for (int i = 1; i <= sevenDaySerialSign; i++) {
int index = (i - 1) * 2;
LinearLayout dayContainer = (LinearLayout) binding.signDaysContainer.getChildAt(index);
ImageView dayIv = (ImageView) dayContainer.getChildAt(1);
dayIv.setImageResource(R.drawable.ic_energy_center_signed);
if (i != 7) {
int rIndex = (i - 1) * 2 + 1;
LinearLayout lineContainer = (LinearLayout) binding.signDaysContainer.getChildAt(rIndex);
View straightLine = lineContainer.getChildAt(0);
View dottedLine = lineContainer.getChildAt(1);
if (i != sevenDaySerialSign) {
straightLine.setVisibility(View.VISIBLE);
} else {
dottedLine.setVisibility(View.VISIBLE);
}
}
}
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(binding.getRoot());
dialog.show();
}
/**
* @param context may be is application context
* @return activity context

View File

@ -28,9 +28,9 @@ import com.gh.gamecenter.category.CategoryDirectoryActivity
import com.gh.gamecenter.category2.CategoryV2Activity
import com.gh.gamecenter.download.DownloadFragment.Companion.INDEX_UPDATE
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.eventbus.EBReuse
import com.gh.gamecenter.eventbus.EBSkip
import com.gh.gamecenter.forum.detail.ForumDetailActivity
import com.gh.gamecenter.forum.home.CommunityHomeFragment
import com.gh.gamecenter.fragment.MainWrapperFragment
import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailActivity
import com.gh.gamecenter.game.upload.GameSubmissionActivity
@ -43,13 +43,14 @@ import com.gh.gamecenter.mygame.PlayedGameActivity
import com.gh.gamecenter.personalhome.UserHomeActivity
import com.gh.gamecenter.personalhome.background.PersonalityBackgroundActivity
import com.gh.gamecenter.personalhome.border.AvatarBorderActivity
import com.gh.gamecenter.qa.CommunityFragment
import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity
import com.gh.gamecenter.personalhome.home.UserHistoryViewModel
import com.gh.gamecenter.qa.answer.detail.SimpleAnswerDetailActivity
import com.gh.gamecenter.qa.article.SimpleArticleListActivity
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.qa.column.detail.AskColumnDetailActivity
import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
import com.gh.gamecenter.qa.subject.CommunitySubjectActivity
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.security.BindPhoneActivity
@ -455,20 +456,42 @@ object DirectUtils {
/**
* 跳转至个人主页
* @param position 定位到某个tab 0游戏评论 1问答 2视频
* @param position 定位到某个tab 0游戏 1发布
*/
@JvmStatic
fun directToHomeActivity(context: Context, userId: String?, position: Int, entrance: String? = null, path: String? = null) {
fun directToHomeActivity(context: Context,
userId: String?,
position: Int,
entrance: String? = null,
path: String? = null) {
directToHomeActivity(context, userId, "", position, entrance, path)
}
/**
* 跳转至个人主页
* @param position 定位到某个tab 0游戏 1发布
* @param type 类型
*/
@JvmStatic
fun directToHomeActivity(context: Context,
userId: String?,
type: String = "",
position: Int,
entrance: String? = null,
path: String? = null) {
IntegralLogHelper.log("view_homepage", "个人主页")
val bundle = Bundle()
bundle.putString(KEY_USER_ID, userId)
bundle.putString(KEY_TO, UserHomeActivity::class.java.name)
bundle.putString(KEY_ENTRANCE, BaseActivity.mergeEntranceAndPath(entrance, path))
bundle.putString(KEY_PATH, path)
bundle.putString(KEY_TYPE, UserHistoryViewModel.TYPE.fromValue(type).value)
bundle.putInt(KEY_POSITION, position)
jumpActivity(context, bundle)
}
/**
* 回到首页
*/
@ -635,7 +658,7 @@ object DirectUtils {
if (id.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, AnswerDetailActivity::class.java.name)
bundle.putString(KEY_TO, SimpleAnswerDetailActivity::class.java.name)
bundle.putString(KEY_PATH, path)
bundle.putString(KEY_ANSWER_ID, id)
jumpActivity(context, bundle)
@ -646,7 +669,7 @@ object DirectUtils {
if (id.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, QuestionsDetailActivity::class.java.name)
bundle.putString(KEY_TO, NewQuestionDetailActivity::class.java.name)
bundle.putString(KEY_PATH, path)
bundle.putString(KEY_QUESTIONS_ID, id)
jumpActivity(context, bundle)
@ -657,7 +680,11 @@ object DirectUtils {
if (url.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, WebActivity::class.java.simpleName)
if (url.contains("android_page_type=singleton")) {
bundle.putString(KEY_TO, SingletonWebActivity::class.java.simpleName)
} else {
bundle.putString(KEY_TO, WebActivity::class.java.simpleName)
}
bundle.putString(EntranceUtils.KEY_URL, url)
jumpActivity(context, bundle)
}
@ -753,37 +780,25 @@ object DirectUtils {
}
/**
* 切换到社区页面
* 切换到社区页面(旧社区页面已经没有了,处理为跳转到论坛详情)
*/
@JvmStatic
fun directToCommunity(context: Context, community: CommunityEntity?) {
if (MainActivity::class.java.name != RunningUtils.getTopActivity(context)) {
val intent = Intent(context, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
context.startActivity(intent)
}
UserManager.getInstance().setCommunityData(community)
// 这里换个线程操作是为了做一点延时
AppExecutor.ioExecutor.execute {
EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_ASK))
EventBus.getDefault().post(EBReuse(CommunityFragment.EB_RETRY_PAGE))
}
}
@JvmStatic
fun directToCommunity(context: Context) {
if (MainActivity::class.java.name != RunningUtils.getTopActivity(context)) {
val intent = Intent(context, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
context.startActivity(intent)
}
// 这里换个线程操作是为了做一点延时
AppExecutor.ioExecutor.execute {
EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_ASK))
EventBus.getDefault().post(EBReuse(CommunityFragment.EB_RETRY_PAGE))
}
// if (MainActivity::class.java.name != RunningUtils.getTopActivity(context)) {
// val intent = Intent(context, MainActivity::class.java)
// intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
// context.startActivity(intent)
//
// UserManager.getInstance().setCommunityData(community)
//
// // 这里换个线程操作是为了做一点延时
// AppExecutor.ioExecutor.execute {
// EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_BBS))
// EventBus.getDefault().post(EBReuse(CommunityFragment.EB_RETRY_PAGE))
// }
// } else {
directForumDetail(context, community?.id)
// }
}
@JvmStatic
@ -824,16 +839,45 @@ object DirectUtils {
jumpActivity(context, bundle)
}
@JvmStatic
fun directToVideoDetail(context: Context, videoId: String,
entrance: String? = null,
path: String? = "") {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, ForumVideoDetailActivity::class.java.name)
bundle.putString(KEY_VIDEO_ID, videoId);
bundle.putString(KEY_PATH, path)
jumpActivity(context, bundle)
} else {
DialogUtils.showLowSystemVersionDialog(context)
}
}
/**
* 跳转到旧视频页面
* @param fromLocation 可见 [VideoDetailContainerViewModel.Location]
*/
@JvmStatic
fun directToVideoDetail(context: Context, videoId: String, fromLocation: String, showComment: Boolean = false, gameId: String = "", entrance: String? = null,
path: String? = "", referer: String = "", type: String = "", act: String = "", paginationType: String = "", fieldId: String = "", sectionName: String = "") {
fun directToLegacyVideoDetail(context: Context,
videoId: String,
fromLocation: String,
showComment: Boolean = false,
gameId: String = "",
entrance: String? = null,
path: String? = "",
referer: String = "",
type: String = "",
act: String = "",
paginationType: String = "",
fieldId: String = "",
sectionName: String = "") {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, VideoDetailActivity::class.java.name)
bundle.putString(KEY_VIDEO_ID, videoId);
bundle.putString(KEY_PATH, path)
bundle.putString(KEY_ID, videoId)
bundle.putString(KEY_GAMEID, gameId)
@ -853,7 +897,7 @@ object DirectUtils {
@JvmStatic
fun directToVideoDetail(context: Context, videoId: String, fromLocation: String, showComment: Boolean = false, gameId: String = "", entrance: String? = null, path: String? = "", referer: String = "") {
directToVideoDetail(context, videoId, fromLocation, showComment, gameId, entrance, path, referer, "", "")
directToVideoDetail(context, videoId, entrance, path)
}
/**
@ -1166,9 +1210,10 @@ object DirectUtils {
/**
* 到首页-论坛 tab
* @param position 论坛的子 tab 位置
*/
@JvmStatic
fun directToForum(context: Context) {
fun directToForum(context: Context, position: Int = 0) {
if (RunningUtils.isRunning(context)
&& MainActivity::class.java.name == RunningUtils.getBaseActivity(context)) {
val intent = Intent(context, MainActivity::class.java)
@ -1177,10 +1222,14 @@ object DirectUtils {
// 这里换个线程操作是为了做一点延时
AppExecutor.ioExecutor.execute {
EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_ASK))
EventBus.getDefault().post(EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_BBS))
EventBus.getDefault().post(EBSkip(CommunityHomeFragment.EB_TAB, position))
}
} else {
jumpActivity(context, Bundle().apply { putInt(KEY_POSITION, MainWrapperFragment.INDEX_ASK) })
jumpActivity(context, Bundle().apply {
putInt(KEY_POSITION, MainWrapperFragment.INDEX_BBS)
putInt(KEY_SUB_POSITION, position)
})
}
}
@ -1461,4 +1510,19 @@ object DirectUtils {
url = String.format(Locale.CHINA, "%s&timestamp=%d", url, (Date().time / 1000 / 1000.toFloat()).roundToInt())
directToFullScreenWebPage(context, url, true)
}
/**
* 跳转至活动详情
*/
@JvmStatic
fun directToActivityDetail(context: Context, activityId: String, categoryId: String, entrance: String) {
var url: String = if (isPublishEnv()) {
Constants.ACTIVITY_DETAIL_ADDRESS
} else {
Constants.ACTIVITY_DETAIL_ADDRESS_DEV
}
url = String.format(Locale.CHINA, "%s&id=%s&category_id=%s&timestamp=%d", url, activityId, categoryId, (Date().time / 1000 / 1000.toFloat()).roundToInt())
directToWebView(context, url, entrance)
}
}

View File

@ -240,6 +240,18 @@ public class DisplayUtils {
return defaultValue;
}
public static void hideNavigationBar(Activity activity) {
Window window = activity.getWindow();
View decorView = window.getDecorView();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { // lower api
decorView.setSystemUiVisibility(View.GONE);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
}
}
public static int retrieveNavigationHeight(Context context) {
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");

View File

@ -138,7 +138,7 @@ public class DownloadItemUtils {
holder.gameDownloadBtn.setText("已预约");
holder.gameDownloadBtn.setTextColor(ContextCompat.getColor(holder.gameDes.getContext(), R.color.aaaaaa));
holder.gameDownloadBtn.setBackgroundResource(R.drawable.button_round_f5f5f5);
updateItemViewStatus(holder, false, null);
updateItemViewStatus(holder, false, null, null);
}
}
@ -171,7 +171,7 @@ public class DownloadItemUtils {
// 显示预约
if (gameEntity.isReservable()) {
updateItemViewStatus(holder, false, briefStyle);
updateItemViewStatus(holder, false, briefStyle, gameEntity.getColumnRecommend());
if (!ReservationRepository.thisGameHasBeenReserved(gameEntity.getId())) {
holder.gameDownloadBtn.setText("预约");
holder.gameDownloadBtn.setTextColor(Color.WHITE);
@ -189,7 +189,7 @@ public class DownloadItemUtils {
LinkEntity h5LinkEntity = gameEntity.getH5Link();
String offStatus = gameEntity.getDownloadOffStatus();
updateItemViewStatus(holder, false, briefStyle);
updateItemViewStatus(holder, false, briefStyle, gameEntity.getColumnRecommend());
if (h5LinkEntity != null) {
if ("play".equals(h5LinkEntity.getType())) {
@ -252,7 +252,7 @@ public class DownloadItemUtils {
GameUtils.setDownloadBtnStatus(context, gameEntity, holder.gameDownloadBtn, pluginLocation);
updateItemViewStatus(holder, false, briefStyle);
updateItemViewStatus(holder, false, briefStyle, gameEntity.getColumnRecommend());
}
// 更新插件的条目有多个apk包
@ -276,14 +276,14 @@ public class DownloadItemUtils {
return;
}
}
updateItemViewStatus(holder, false, briefStyle);
updateItemViewStatus(holder, false, briefStyle, gameEntity.getColumnRecommend());
}
// 更改进度条和提示文本的状态
public static void changeStatus(Context context, GameViewHolder holder, DownloadEntity downloadEntity,
boolean isShowPlatform, boolean isNormal) {
updateItemViewStatus(holder, true, null);
updateItemViewStatus(holder, true, null, null);
holder.gameProgressbar.setProgressDrawable(context.getResources().getDrawable(R.drawable.progressbar_bg_style));
String platform = PlatformUtils.getInstance(context).getPlatformName(downloadEntity.getPlatform());
@ -377,22 +377,59 @@ public class DownloadItemUtils {
}
}
private static void updateItemViewStatus(GameViewHolder holder, boolean hasDownload, @Nullable String briefStyle) {
private static void updateItemViewStatus(GameViewHolder holder,
boolean hasDownload,
@Nullable String briefStyle,
@Nullable LinkEntity recommendStyle) {
if (hasDownload) {
if (holder.gameRating != null) holder.gameRating.setVisibility(View.GONE);
holder.gameDes.setVisibility(View.GONE);
holder.gameProgressbar.setVisibility(View.VISIBLE);
holder.gameInfo.setVisibility(View.VISIBLE);
if (holder.recommendContainer != null) {
holder.recommendContainer.setVisibility(View.GONE);
}
} else {
holder.gameProgressbar.setVisibility(View.GONE);
holder.gameInfo.setVisibility(View.GONE);
// 推荐优先,有推荐内容时不执行下面的 star 和 brief 代码块
if (briefStyle != null
&& recommendStyle != null
&& briefStyle.contains("recommend")) {
if (holder.recommendContainer != null) {
holder.recommendContainer.setVisibility(View.VISIBLE);
}
if (holder.gameRating != null) holder.gameRating.setVisibility(View.GONE);
holder.gameDes.setVisibility(View.GONE);
holder.recommendTv.setText(recommendStyle.getText());
if ("none".equals(recommendStyle.getType())) {
holder.recommendIv.setVisibility(View.GONE);
} else {
Context context = holder.recommendContainer.getContext();
int drawableId = context.getResources().getIdentifier(
"ic_recommend_" + recommendStyle.getType(),
"drawable",
context.getPackageName());
holder.recommendIv.setVisibility(View.VISIBLE);
holder.recommendIv.setImageResource(drawableId);
}
return;
} else {
if (holder.recommendContainer != null) {
holder.recommendContainer.setVisibility(View.GONE);
}
}
if (briefStyle != null && briefStyle.contains("star")) {
if (holder.gameRating != null) holder.gameRating.setVisibility(View.VISIBLE);
} else {
if (holder.gameRating != null) holder.gameRating.setVisibility(View.GONE);
}
if (TextUtils.isEmpty(briefStyle) || briefStyle.contains("brief")) {
// 缺省情况下回落到游戏简介
if (TextUtils.isEmpty(briefStyle) || briefStyle.contains("brief") || briefStyle.contains("recommend")) {
holder.gameDes.setVisibility(View.VISIBLE);
} else {
holder.gameDes.setVisibility(View.GONE);
@ -436,7 +473,7 @@ public class DownloadItemUtils {
final ExposureEvent traceEvent,
@Nullable final EmptyCallback clickCallback) {
setOnClickListener(context, downloadBtn, gameEntity, position, adapter, entrance, location, traceEvent, clickCallback, null);
setOnClickListener(context, downloadBtn, gameEntity, position, adapter, entrance, location, traceEvent, clickCallback, null, null);
}
/**
@ -451,6 +488,7 @@ public class DownloadItemUtils {
final String location,
final ExposureEvent traceEvent,
@Nullable final EmptyCallback clickCallback,
@Nullable final EmptyCallback refreshCallback,
@Nullable final EmptyCallback allStateClickCallback) {
if (gameEntity.isReservable()) {
@ -468,7 +506,12 @@ public class DownloadItemUtils {
gameEntity,
() -> {
LogUtils.logReservation(gameEntity, traceEvent);
adapter.notifyItemChanged(position);
if (adapter != null) {
adapter.notifyItemChanged(position);
}
if (refreshCallback != null) {
refreshCallback.onCallback();
}
}
);
dialogFragment.show(((AppCompatActivity) context).getSupportFragmentManager(), "reserve");
@ -486,13 +529,23 @@ public class DownloadItemUtils {
if ("download".equals(gameEntity.getReserveStatus())) {
ReservationHelper.showDeleteReservationDialog(context, () -> {
ReservationHelper.deleteReservation(gameEntity, () -> {
adapter.notifyItemChanged(position);
if (adapter != null) {
adapter.notifyItemChanged(position);
}
if (refreshCallback != null) {
refreshCallback.onCallback();
}
});
});
} else {
ReservationHelper.showCancelReservationDialog(context, () -> {
ReservationHelper.cancelReservation(gameEntity, () -> {
adapter.notifyItemChanged(position);
if (adapter != null) {
adapter.notifyItemChanged(position);
}
if (refreshCallback != null) {
refreshCallback.onCallback();
}
});
});
}
@ -529,7 +582,7 @@ public class DownloadItemUtils {
if (clickCallback != null) {
clickCallback.onCallback();
}
onNormalClick(context, downloadBtn, gameEntity, position, adapter, entrance, location, traceEvent);
onNormalClick(context, downloadBtn, gameEntity, position, adapter, entrance, location, traceEvent, refreshCallback);
};
// 启动不需要请求存储权限
@ -575,7 +628,7 @@ public class DownloadItemUtils {
final RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter,
final String entrance,
final String location) {
onNormalClick(context, downloadBtn, gameEntity, position, adapter, entrance, location, null);
onNormalClick(context, downloadBtn, gameEntity, position, adapter, entrance, location, null, null);
}
public static void onNormalClick(final Context context,
@ -585,7 +638,8 @@ public class DownloadItemUtils {
final RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter,
final String entrance,
final String location,
@Nullable final ExposureEvent traceEvent) {
@Nullable final ExposureEvent traceEvent,
@Nullable final EmptyCallback refreshCallback) {
String str = downloadBtn.getText().toString();
if (gameEntity.getApk().isEmpty()) return;
ApkEntity apk = ExtensionsKt.safelyGetInRelease(gameEntity.getApk(), 0);
@ -656,7 +710,7 @@ public class DownloadItemUtils {
return;
}
}
install(context, gameEntity, position, adapter);
install(context, gameEntity, position, adapter, refreshCallback);
} else if (str.equals(context.getString(R.string.launch))) {
//启动模拟器游戏
if (SimulatorGameManager.isSimulatorGame(gameEntity)) {
@ -737,7 +791,7 @@ public class DownloadItemUtils {
//安装
private static void install(final Context context, GameEntity gameEntity, int position,
RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter) {
RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter, @Nullable final EmptyCallback refreshCallback) {
ApkEntity apkEntity = gameEntity.getApk().get(0);
DownloadEntity downloadEntity = DownloadManager.getInstance(context).getDownloadEntityByUrl(apkEntity.getUrl());
if (downloadEntity != null) {
@ -748,7 +802,12 @@ public class DownloadItemUtils {
if (gameEntity.getEntryMap() != null) {
gameEntity.getEntryMap().remove(apkEntity.getPlatform());
}
adapter.notifyItemChanged(position);
if (adapter != null) {
adapter.notifyItemChanged(position);
}
if (refreshCallback != null) {
refreshCallback.onCallback();
}
} else if (PackageUtils.isCanPluggable(apkEntity)) {
DialogHelper.showPluginDialog(context, () -> {
PackageInstaller.uninstall(context, path);

View File

@ -22,9 +22,9 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBShowDialog
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.setting.GameDownloadSettingFragment.Companion.AUTO_INSTALL_SP_KEY
import com.gh.gamecenter.suggest.SuggestType
import com.halo.assistant.HaloApp
import com.halo.assistant.fragment.SettingsFragment
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus
@ -155,12 +155,12 @@ object DownloadObserver {
SimulatorDownloadManager.getInstance().showDownloadDialog(currentActivity, simulator,
SimulatorDownloadManager.SimulatorLocation.LAUNCH, downloadEntity.gameId, gameName, null)
}
SimulatorGameManager.recordDownloadSimulatorGames(downloadEntity.gameId, simulator.type)
SimulatorGameManager.recordDownloadSimulatorGame(downloadEntity.gameId, simulator.type)
SimulatorGameManager.postPlayedGame(downloadEntity.gameId, downloadEntity.packageName)
} else {
val downloadType = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)
// 是否是自动安装
val isAutoInstall = PreferenceManager.getDefaultSharedPreferences(mApplication).getBoolean(SettingsFragment.AUTO_INSTALL_SP_KEY, true)
val isAutoInstall = PreferenceManager.getDefaultSharedPreferences(mApplication).getBoolean(AUTO_INSTALL_SP_KEY, true)
if (downloadType == Constants.SIMULATOR_DOWNLOAD || isAutoInstall) {
if (FileUtils.isEmptyFile(downloadEntity.path)) {
Utils.toast(mApplication, R.string.install_failure_hint)

View File

@ -130,27 +130,39 @@ object EnergyTaskHelper {
// 完成弹窗
@JvmStatic
fun showCompletePopup(entity: EnergyTaskCompleteEntity) {
val currentActivity = AppManager.getInstance().recentActiveActivity
currentActivity?.run {
val contentView = View.inflate(this, R.layout.popup_energy_task, null)
contentView.run {
findViewById<TextView>(R.id.taskDesc).text = "恭喜你!完成任务:${entity.name}"
findViewById<TextView>(R.id.taskEnergy).text = "+${entity.energy}光能"
isFocusable = true
isFocusableInTouchMode = true
setOnClickListener {
if (currentActivity::class.java.simpleName != EnergyCenterActivity::class.java.simpleName) {
currentActivity.startActivity(EnergyCenterActivity.getIntent(currentActivity))
tryWithDefaultCatch {
val currentActivity = AppManager.getInstance().recentActiveActivity
val popWindow = PopupWindow(LinearLayout.LayoutParams.MATCH_PARENT, 88F.dip2px())
currentActivity?.run {
val contentView = View.inflate(this, R.layout.popup_energy_task, null)
contentView.run {
findViewById<TextView>(R.id.taskDesc).text = "恭喜你!完成任务:${entity.name}"
findViewById<TextView>(R.id.taskEnergy).text = "+${entity.energy}光能"
isFocusable = true
isFocusableInTouchMode = true
setOnClickListener {
if (popWindow != null && popWindow.isShowing) {
popWindow.dismiss()
}
if (currentActivity !is EnergyCenterActivity) {
currentActivity.startActivity(EnergyCenterActivity.getIntent(currentActivity))
}
}
}
}
val popWindow = PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT, 88F.dip2px())
popWindow.showAtLocation(currentActivity.window.decorView, Gravity.TOP, 0, 0)
countDownTimer(3) { finish, _ ->
if (finish && popWindow != null && popWindow.isShowing) {
popWindow.dismiss()
popWindow.contentView = contentView
currentActivity.window.decorView.post {
popWindow.showAtLocation(currentActivity.window.decorView, Gravity.TOP, 0, 0)
}
contentView.postDelayed({
tryCatchInRelease {
if (popWindow != null && popWindow.isShowing) {
popWindow.dismiss()
}
}
}, 3000)
}
}
}

View File

@ -47,11 +47,13 @@ public class EntranceUtils {
public static final String KEY_PACKAGE_MD5 = "package_md5";
public static final String HOST_ARTICLE = "article";
public static final String HOST_UPLOAD_VIDEO = "upload_video";//上传视频
public static final String HOST_UPLOAD_VIDEO_NEW = "upload_video_new"; // 上传视频新(AKA 发视频)
public static final String HOST_VIDEO_SINGLE = "video_single";//指定视频-不能划动
public static final String HOST_VIDEO_MORE = "video_more";//指定视频-能划动
public static final String HOST_VIDEO_STREAMING_HOME = "video_streaming_home";//视频流-首页
public static final String HOST_VIDEO_STREAMING_DESC = "video_streaming_desc";//视频流-游戏介绍进入
public static final String HOST_VIDEO_COLLECTION = "video_collection";//视频合集
public static final String HOST_VIDEO_DETAIL = "video_detail";
public static final String HOST_USERHOME = "userhome";//个人主页
public static final String HOST_VIDEO = "video";
public static final String HOST_FORUM = "forum";
@ -98,6 +100,7 @@ public class EntranceUtils {
public static final String KEY_LINK = "link";
public static final String KEY_NAME = "name";
public static final String KEY_POSITION = "position";
public static final String KEY_SUB_POSITION = "sub_position";
public static final String KEY_ENTRANCE = "entrance";
public static final String KEY_ENTRANCE_LINK = "entrance_link";
public static final String KEY_TARGET = "target";
@ -236,6 +239,11 @@ public class EntranceUtils {
public static final String KEY_IS_QA_FEEDBACK = "is_qa_feedback";
public static final String KEY_IS_CLICK_RECEIVE_BTN = "is_click_receive_btn";
public static final String KEY_SHOW_QUICK_LOGIN = "show_quick_login";
public static final String KEY_VIDEO_LIST = "video_list";
public static final String KEY_CHOOSE_FORUM_TYPE = "choose_forum_type";
public static final String KEY_CHOOSE_MAX_COUNT = "choose_max_count";
public static final String KEY_COMMENT_COUNT = "comment_count";
public static final String KEY_IS_COMMENT_CONVERSATION = "is_comment_conversation";
public static void jumpActivity(Context context, Bundle bundle) {
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
@ -252,6 +260,10 @@ public class EntranceUtils {
} 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);
}

View File

@ -19,10 +19,12 @@ object ErrorHelper {
* [customizedHandler] 返回 true 为已处理该错误码false 则交由 [handleError] 处理
*/
@JvmStatic
fun handleErrorWithCustomizedHandler(context: Context,
errorString: String?,
showHighPriorityHint: Boolean = false,
customizedHandler: (code: Int) -> Boolean) {
fun handleErrorWithCustomizedHandler(
context: Context,
errorString: String?,
showHighPriorityHint: Boolean = false,
customizedHandler: (code: Int) -> Boolean
) {
val errorEntity = errorString?.toObject<ErrorEntity>()
if (customizedHandler(errorEntity?.code ?: 0)) {
@ -75,8 +77,15 @@ object ErrorHelper {
*403057 游戏评论
*403054 更新社区文章
*403047 回答点赞
*403112 发布视频贴
*403113 修改视频贴
*403114 点赞视频贴
*/
private fun handleError(context: Context, showHighPriorityHint: Boolean = false, errorEntity: ErrorEntity) {
private fun handleError(
context: Context,
showHighPriorityHint: Boolean = false,
errorEntity: ErrorEntity
) {
when (errorEntity.code) {
403050,
403051,
@ -89,7 +98,10 @@ object ErrorHelper {
403054,
403069,
403071,
403047 -> handleErrorWithCommunityBannedDialog(context, errorEntity)
403047,
403112,
403113,
403114 -> handleErrorWithCommunityBannedDialog(context, errorEntity)
403057,
403068 -> handleErrorWithCommentBannedDialog(context, errorEntity)
@ -120,12 +132,15 @@ object ErrorHelper {
403082 -> Utils.toast(context, "作者已关闭评论")
403022 -> Utils.toast(context, "不能回复自己")
403056 -> Utils.toast(context, "发布失败,字数已达上限")
403111 -> Utils.toast(context, "提交失败,评论违规")
403020 -> if (showHighPriorityHint) {
DialogUtils.showAlertDialog(context,
"提醒",
"问过于频繁,请先休息一下哦",
"知道了", null, null, null)
DialogUtils.showAlertDialog(
context,
"",
"提问过于频繁,请先休息一下哦",
"知道了", null, null, null
)
} else {
Utils.toast(context, R.string.comment_failed_toofrequent)
}
@ -148,12 +163,14 @@ object ErrorHelper {
"(非永久)"
}
val dialogContext = DialogUtils.checkDialogContext(context)
DialogUtils.showAlertDialog(dialogContext,
"提示",
"你因违反《光环助手评论规则》,已被禁言$bannedType如有疑问请联系客服QQ${Config.getSettings()?.support?.qq}",
"去看看", "关闭", {
dialogContext.startActivity(WebActivity.getCommentRulesIntent(dialogContext))
}, null)
DialogUtils.showAlertDialog(
dialogContext,
"提示",
"你因违反《光环助手评论规则》,已被禁言$bannedType如有疑问请联系客服QQ${Config.getSettings()?.support?.qq}",
"去看看", "关闭", {
dialogContext.startActivity(WebActivity.getCommentRulesIntent(dialogContext))
}, null
)
}
private fun handleErrorWithCommunityBannedDialog(context: Context, errorEntity: ErrorEntity) {
@ -163,18 +180,21 @@ object ErrorHelper {
"(非永久)"
}
val dialogContext = DialogUtils.checkDialogContext(context)
DialogUtils.showAlertDialog(dialogContext,
"提示",
"你因违反《问答版块规则》,已被禁言$bannedType如有疑问请联系客服QQ1562479331",
"去看看", "关闭", {
dialogContext.startActivity(WebActivity.getCommunityRuleIntent(dialogContext))
}, null)
DialogUtils.showAlertDialog(
dialogContext,
"提示",
"你因违反《问答版块规则》,已被禁言$bannedType如有疑问请联系客服QQ1562479331",
"去看看", "关闭", {
dialogContext.startActivity(WebActivity.getCommunityRuleIntent(dialogContext))
}, null
)
}
@JvmStatic
fun handleLoginError(context: Context, httpException: HttpException?) {
try {
val errorEntity: ErrorEntity? = httpException?.response()?.errorBody()?.string()?.toObject()
val errorEntity: ErrorEntity? =
httpException?.response()?.errorBody()?.string()?.toObject()
when {
errorEntity?.code == 403099 -> {
Utils.toast(context, "当前账号正在注销,禁止登录")

View File

@ -6,6 +6,7 @@ import android.content.ClipboardManager
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.os.Build
@ -13,9 +14,9 @@ import android.text.*
import android.text.style.ClickableSpan
import android.text.style.ImageSpan
import android.text.style.URLSpan
import android.util.Log
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.view.*
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.PopupWindow
@ -29,6 +30,7 @@ import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.*
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import androidx.viewpager.widget.ViewPager
import com.airbnb.lottie.LottieAnimationView
import com.facebook.drawee.view.SimpleDraweeView
@ -54,27 +56,32 @@ import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okhttp3.MediaType
import okhttp3.RequestBody
import org.json.JSONArray
import org.json.JSONObject
import java.net.URI
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.regex.Pattern
import kotlin.math.abs
/**
* 创建以 activity 为观察者上下文的 viewModel
*/
inline fun <reified VM : ViewModel> FragmentActivity.viewModelProvider(
provider: ViewModelProvider.Factory? = null
provider: ViewModelProvider.Factory? = null
) =
ViewModelProviders.of(this, provider).get(VM::class.java)
ViewModelProviders.of(this, provider).get(VM::class.java)
/**
* 创建以 activity 为观察者上下文的 viewModel
* 额外的 key: 用于区分单 activity 多 viewModel 的情况 (如首页tab)
*/
inline fun <reified VM : ViewModel> Fragment.viewModelProviderFromParent(
provider: ViewModelProvider.Factory? = null,
key: String = ""
provider: ViewModelProvider.Factory? = null,
key: String = ""
) = if (key.isEmpty()) {
ViewModelProviders.of(requireActivity(), provider).get(VM::class.java)
} else {
@ -85,28 +92,33 @@ inline fun <reified VM : ViewModel> Fragment.viewModelProviderFromParent(
* 创建以 activity 为观察者上下文的 viewModel
*/
inline fun <reified VM : ViewModel> FragmentActivity.viewModelProviderFromParent(
provider: ViewModelProvider.Factory? = null
provider: ViewModelProvider.Factory? = null
) =
ViewModelProviders.of(this, provider).get(VM::class.java)
ViewModelProviders.of(this, provider).get(VM::class.java)
/**
* 创建以 fragment 为观察者上下文的 viewModel
*/
inline fun <reified VM : ViewModel> Fragment.viewModelProvider(
provider: ViewModelProvider.Factory? = null
provider: ViewModelProvider.Factory? = null
) =
ViewModelProviders.of(this, provider).get(VM::class.java)
ViewModelProviders.of(this, provider).get(VM::class.java)
/**
*
* ViewPager Extensions
*
*/
fun ViewPager.doOnPageSelected(action: (position: Int) -> Unit) = addOnPageChangeListener(onSelected = action)
fun ViewPager.doOnPageSelected(action: (position: Int) -> Unit) =
addOnPageChangeListener(onSelected = action)
fun ViewPager.addOnPageChangeListener(onSelected: ((position: Int) -> Unit)? = null) {
val listener = object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
// Do nothing.
}
@ -121,11 +133,17 @@ fun ViewPager.addOnPageChangeListener(onSelected: ((position: Int) -> Unit)? = n
addOnPageChangeListener(listener)
}
fun ViewPager.doOnScroll(onStateChanged: ((state: Int) -> Unit)? = null,
onPageScrolled: ((position: Int, positionOffset: Float, positionOffsetPixels: Int) -> Unit)? = null,
onPageSelected: ((position: Int) -> Unit)? = null) {
fun ViewPager.doOnScroll(
onStateChanged: ((state: Int) -> Unit)? = null,
onPageScrolled: ((position: Int, positionOffset: Float, positionOffsetPixels: Int) -> Unit)? = null,
onPageSelected: ((position: Int) -> Unit)? = null
) {
val listener = object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
onPageScrolled?.invoke(position, positionOffset, positionOffsetPixels)
}
@ -145,13 +163,19 @@ fun ViewPager.doOnScroll(onStateChanged: ((state: Int) -> Unit)? = null,
* Fragment related
*/
inline fun <reified T : Fragment> Fragment.fragmentFromActivity() =
parentFragmentManager.findFragmentByTag(T::class.java.simpleName) as? T
?: parentFragmentManager.fragmentFactory.instantiate(requireContext().classLoader, T::class.java.canonicalName) as T
parentFragmentManager.findFragmentByTag(T::class.java.simpleName) as? T
?: parentFragmentManager.fragmentFactory.instantiate(
requireContext().classLoader,
T::class.java.canonicalName
) as T
inline fun <reified T : Fragment> Fragment.fragmentFromParentFragment() =
childFragmentManager.findFragmentByTag(T::class.java.simpleName) as? T
?: childFragmentManager.fragmentFactory.instantiate(requireContext().classLoader, T::class.java.canonicalName) as T
childFragmentManager.findFragmentByTag(T::class.java.simpleName) as? T
?: childFragmentManager.fragmentFactory.instantiate(
requireContext().classLoader,
T::class.java.canonicalName
) as T
/**
@ -159,7 +183,12 @@ inline fun <reified T : Fragment> Fragment.fragmentFromParentFragment() =
*/
// 监听滚动距离
fun RecyclerView.doOnScrolledSpecificDistance(distanceX: Int = 0, distanceY: Int = 0, singleTimeEvent: Boolean = false, action: () -> Unit) {
fun RecyclerView.doOnScrolledSpecificDistance(
distanceX: Int = 0,
distanceY: Int = 0,
singleTimeEvent: Boolean = false,
action: () -> Unit
) {
val listener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
@ -176,6 +205,18 @@ fun RecyclerView.doOnScrolledSpecificDistance(distanceX: Int = 0, distanceY: Int
addOnScrollListener(listener)
}
/**
* ViewBinding related Extensions
*/
inline fun <reified T : ViewBinding> ViewGroup.toBinding(): T {
return T::class.java.getMethod(
"inflate",
LayoutInflater::class.java,
ViewGroup::class.java,
Boolean::class.java
).invoke(null, layoutInflater, this, false) as T
}
/**
* View Extensions
*/
@ -216,6 +257,46 @@ fun View.setDebouncedClickListener(action: () -> Unit) {
setOnClickListener { debounceActionWithInterval(interval = 300L) { action.invoke() } }
}
val View.layoutInflater: LayoutInflater
get() = LayoutInflater.from(this.context)
fun View.removeFromParent() {
postDelayed(object : Runnable {
override fun run() {
try {
if (parent == null) {
Utils.log(javaClass.simpleName, "getParent() returning Null")
} else {
try {
(parent as ViewGroup).removeView(this@removeFromParent)
} catch (ex: Exception) {
Utils.log(javaClass.simpleName, "Cannot remove from parent layout")
}
}
} catch (ex: Exception) {
Utils.log(javaClass.simpleName, Log.getStackTraceString(ex))
}
}
}, 100)
}
/**
* 扩大 View 的点击区域
*/
fun View.enlargeTouchArea(enlargedSizeInPx: Int = 4F.dip2px()) {
val parent = parent as View
parent.post {
val rect = Rect()
getHitRect(rect)
rect.top -= enlargedSizeInPx
rect.left -= enlargedSizeInPx
rect.bottom += enlargedSizeInPx
rect.right += enlargedSizeInPx
parent.touchDelegate = TouchDelegate(rect, this)
}
}
fun isPublishEnv(): Boolean {
return BuildConfig.FLAVOR != "internal"
}
@ -268,11 +349,11 @@ fun String.insert(index: Int, string: String): String {
*/
fun String.replaceUnsupportedHtmlTag(): String {
return this.replace("<ul", "<hul")
.replace("</ul>", "</hul>")
.replace("<li", "<hli")
.replace("</li>", "</hli>")
.replace("<ol", "<hol")
.replace("</ol>", "</hol>")
.replace("</ul>", "</hul>")
.replace("<li", "<hli")
.replace("</li>", "</hli>")
.replace("<ol", "<hol")
.replace("</ol>", "</hol>")
}
fun String.containHtmlTag(): Boolean {
@ -287,8 +368,8 @@ fun String.containHtmlTag(): Boolean {
fun Fragment.showRegulationTestDialogIfNeeded(action: (() -> Unit)) {
if (UserManager.getInstance().userShouldTakeRegulationBaseOnLastRemind()) {
DialogUtils.showRegulationTestDialog(requireContext(),
{ DirectUtils.directToRegulationTestPage(requireContext()) },
{ action.invoke() })
{ DirectUtils.directToRegulationTestPage(requireContext()) },
{ action.invoke() })
} else {
action()
}
@ -297,8 +378,8 @@ fun Fragment.showRegulationTestDialogIfNeeded(action: (() -> Unit)) {
fun Context.showRegulationTestDialogIfNeeded(action: (() -> Unit)) {
if (UserManager.getInstance().userShouldTakeRegulationBaseOnLastRemind()) {
DialogUtils.showRegulationTestDialog(this,
{ DirectUtils.directToRegulationTestPage(this) },
{ action.invoke() })
{ DirectUtils.directToRegulationTestPage(this) },
{ action.invoke() })
} else {
action()
}
@ -397,14 +478,15 @@ fun String.removeInsertedContent(): String {
// 去除视频相关文本
fun String.removeVideoContent(): String {
val videoRegex = "(?s)<div class=\"insert-video-container\".*?</div>"
val videoRegex =
"(?s)<div data-id.*?class=\"placeholder-video-container\".*? class=\"video-poster-btn\".*?</div>"
return this.replace(videoRegex.toRegex(), "")
}
// 完全地清除所有 Html 格式
fun String.clearHtmlFormatCompletely(): String {
return Html.fromHtml(this).toString().replace('\n', 32.toChar())
.replace(160.toChar(), 32.toChar()).replace(65532.toChar(), 32.toChar()).trim { it <= ' ' }
.replace(160.toChar(), 32.toChar()).replace(65532.toChar(), 32.toChar()).trim { it <= ' ' }
}
// 如果该字符串长度超过固定长度的话,从头开始截取固定长度并返回
@ -463,6 +545,14 @@ fun Any.toRequestBody(): RequestBody {
return RequestBody.create(MediaType.parse("application/json"), json)
}
fun JSONObject.toRequestBody(): RequestBody {
return RequestBody.create(MediaType.parse("application/json"), this.toString())
}
fun JSONArray.toRequestBody(): RequestBody {
return RequestBody.create(MediaType.parse("application/json"), this.toString())
}
// 对在浏览器(WebView)显示的路径进行转码
fun String.decodeURI(): String {
return URI(null, null, this, null).rawPath
@ -526,19 +616,23 @@ fun PopupWindow.showAutoOrientation(anchorView: View, distanceY: Int = 0, distan
* 权限相关
*/
fun Fragment.checkReadPhoneStateAndStoragePermissionBeforeAction(action: (() -> Unit)) {
PermissionHelper.checkReadPhoneStateAndStoragePermissionBeforeAction(requireContext(), object : EmptyCallback {
override fun onCallback() {
action.invoke()
}
})
PermissionHelper.checkReadPhoneStateAndStoragePermissionBeforeAction(
requireContext(),
object : EmptyCallback {
override fun onCallback() {
action.invoke()
}
})
}
fun Fragment.checkReadPhoneStatePermissionBeforeAction(action: (() -> Unit)) {
PermissionHelper.checkReadPhoneStatePermissionBeforeAction(requireContext(), object : EmptyCallback {
override fun onCallback() {
action.invoke()
}
})
PermissionHelper.checkReadPhoneStatePermissionBeforeAction(
requireContext(),
object : EmptyCallback {
override fun onCallback() {
action.invoke()
}
})
}
fun Fragment.checkStoragePermissionBeforeAction(action: (() -> Unit)) {
@ -550,11 +644,13 @@ fun Fragment.checkStoragePermissionBeforeAction(action: (() -> Unit)) {
}
fun FragmentActivity.checkReadPhoneStateAndStoragePermissionBeforeAction(action: (() -> Unit)) {
PermissionHelper.checkReadPhoneStateAndStoragePermissionBeforeAction(this, object : EmptyCallback {
override fun onCallback() {
action.invoke()
}
})
PermissionHelper.checkReadPhoneStateAndStoragePermissionBeforeAction(
this,
object : EmptyCallback {
override fun onCallback() {
action.invoke()
}
})
}
fun FragmentActivity.checkReadPhoneStatePermissionBeforeAction(action: (() -> Unit)) {
@ -585,20 +681,27 @@ fun <T> List<T>.secondOrNull(): T? {
/**
* TextView related.
*/
fun TextView.setTextWithHighlightedTextWrappedInsideWrapper(text: CharSequence,
wrapper: String = Constants.DEFAULT_TEXT_WRAPPER,
@ColorRes
highlightColorId: Int = R.color.theme_font,
copyClickedText: Boolean = false,
highlightedTextClickListener: (() -> Unit)? = null) {
TextHelper.highlightTextThatIsWrappedInsideWrapper(this, text, wrapper, highlightColorId, object : SimpleCallback<String> {
override fun onCallback(arg: String) {
if (copyClickedText) {
arg.copyTextAndToast("已复制:$arg")
fun TextView.setTextWithHighlightedTextWrappedInsideWrapper(
text: CharSequence,
wrapper: String = Constants.DEFAULT_TEXT_WRAPPER,
@ColorRes
highlightColorId: Int = R.color.theme_font,
copyClickedText: Boolean = false,
highlightedTextClickListener: (() -> Unit)? = null
) {
TextHelper.highlightTextThatIsWrappedInsideWrapper(
this,
text,
wrapper,
highlightColorId,
object : SimpleCallback<String> {
override fun onCallback(arg: String) {
if (copyClickedText) {
arg.copyTextAndToast("已复制:$arg")
}
highlightedTextClickListener?.invoke()
}
highlightedTextClickListener?.invoke()
}
})
})
}
fun TextView.setTextChangedListener(action: (s: CharSequence, start: Int, before: Int, count: Int) -> Unit) {
@ -639,7 +742,10 @@ fun <T> List<T>.safelyGetInRelease(index: Int): T? {
* @param shrankText 未展开时的文字
* @param expandedText 展开后的文字
*/
fun ExpandTextView.setTextWithInterceptingInternalUrl(shrankText: CharSequence, expandedText: CharSequence) {
fun ExpandTextView.setTextWithInterceptingInternalUrl(
shrankText: CharSequence,
expandedText: CharSequence
) {
var shrankSsb = shrankText.interceptUrlSpanAndRoundImageSpan()
var expandedSsb = expandedText.interceptUrlSpanAndRoundImageSpan()
@ -647,23 +753,42 @@ fun ExpandTextView.setTextWithInterceptingInternalUrl(shrankText: CharSequence,
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
while (shrankSsb.contains("\n\n")) {
val index = shrankSsb.indexOf("\n\n", 0, true)
shrankSsb = SpannableStringBuilder(shrankSsb.subSequence(0, index)).append(shrankSsb.subSequence(index + "\n".length, shrankSsb.length))
shrankSsb = SpannableStringBuilder(
shrankSsb.subSequence(
0,
index
)
).append(shrankSsb.subSequence(index + "\n".length, shrankSsb.length))
}
while (expandedSsb.contains("\n\n")) {
val index = expandedSsb.indexOf("\n\n", 0, true)
expandedSsb = SpannableStringBuilder(expandedSsb.subSequence(0, index)).append(expandedSsb.subSequence(index + "\n".length, expandedSsb.length))
expandedSsb = SpannableStringBuilder(
expandedSsb.subSequence(
0,
index
)
).append(expandedSsb.subSequence(index + "\n".length, expandedSsb.length))
}
}
// 去掉多余的 P 标签换行
if (expandedSsb.endsWith("\n", true)) {
expandedSsb = SpannableStringBuilder((expandedSsb.subSequence(0, expandedSsb.length - "\n".length)))
expandedSsb =
SpannableStringBuilder((expandedSsb.subSequence(0, expandedSsb.length - "\n".length)))
}
movementMethod = CustomLinkMovementMethod.getInstance()
shrankSsb = TextHelper.updateSpannableStringWithHighlightedSpan(context, shrankSsb, highlightedTextClickListener = null)
expandedSsb = TextHelper.updateSpannableStringWithHighlightedSpan(context, expandedSsb, highlightedTextClickListener = null)
shrankSsb = TextHelper.updateSpannableStringWithHighlightedSpan(
context,
shrankSsb,
highlightedTextClickListener = null
)
expandedSsb = TextHelper.updateSpannableStringWithHighlightedSpan(
context,
expandedSsb,
highlightedTextClickListener = null
)
setShrankTextAndExpandedText(shrankSsb, expandedSsb)
}
@ -671,32 +796,41 @@ fun CharSequence.interceptUrlSpanAndRoundImageSpan(): SpannableStringBuilder {
return SpannableStringBuilder.valueOf(this).apply {
getSpans(0, length, URLSpan::class.java).forEach {
setSpan(
object : ClickableSpan() {
override fun updateDrawState(ds: TextPaint) {
super.updateDrawState(ds)
ds.color = ContextCompat.getColor(HaloApp.getInstance().application, R.color.theme_font)
ds.isUnderlineText = false
}
object : ClickableSpan() {
override fun updateDrawState(ds: TextPaint) {
super.updateDrawState(ds)
ds.color = ContextCompat.getColor(
HaloApp.getInstance().application,
R.color.theme_font
)
ds.isUnderlineText = false
}
override fun onClick(widget: View) {
if (!DefaultUrlHandler.interceptUrl(widget.context, it.url, "")) {
widget.context.startActivity(WebActivity.getIntent(widget.context, it.url, true))
}
override fun onClick(widget: View) {
if (!DefaultUrlHandler.interceptUrl(widget.context, it.url, "")) {
widget.context.startActivity(
WebActivity.getIntent(
widget.context,
it.url,
true
)
)
}
},
getSpanStart(it),
getSpanEnd(it),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
}
},
getSpanStart(it),
getSpanEnd(it),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
removeSpan(it)
}
getSpans(0, length, ImageSpan::class.java).forEach {
setSpan(
CenterImageSpan(it.drawable),
getSpanStart(it),
getSpanEnd(it),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
CenterImageSpan(it.drawable),
getSpanStart(it),
getSpanEnd(it),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
removeSpan(it)
}
@ -805,22 +939,22 @@ inline fun testChannelOnly(f: () -> Unit) {
* 倒计时单位s
*/
inline fun countDownTimer(
timeInSeconds: Long,
crossinline block: (finish: Boolean, remainingTime: Long) -> Unit
timeInSeconds: Long,
crossinline block: (finish: Boolean, remainingTime: Long) -> Unit
): Disposable {
var subscribe: Disposable? = null
subscribe = Observable.interval(0, 1000, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (it < timeInSeconds) {
block.invoke(false, timeInSeconds - it)
} else {
block.invoke(true, 0)
if (subscribe != null && !subscribe!!.isDisposed) {
subscribe?.dispose()
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (it < timeInSeconds) {
block.invoke(false, timeInSeconds - it)
} else {
block.invoke(true, 0)
if (subscribe != null && !subscribe!!.isDisposed) {
subscribe?.dispose()
}
}
}
return subscribe
}
@ -829,17 +963,17 @@ inline fun countDownTimer(
* @start 起始时间
*/
inline fun countUpTimer(
start: Long,
period: Long = 1000,
crossinline block: (millisUntilFinished: Long) -> Unit
start: Long,
period: Long = 1000,
crossinline block: (millisUntilFinished: Long) -> Unit
): Disposable {
var startTime = start
return Observable.interval(0, period, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
startTime += period
block.invoke(startTime)
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
startTime += period
block.invoke(startTime)
}
}
/**
@ -847,10 +981,10 @@ inline fun countUpTimer(
*/
inline fun rxTimer(interval: Long, crossinline block: (times: Long) -> Unit): Disposable {
return Observable.interval(0, interval, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
block.invoke(it)
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
block.invoke(it)
}
}
fun LottieAnimationView.doOnAnimationEnd(action: () -> Unit) {
@ -916,13 +1050,17 @@ fun List<String>?.checkSameFromStringArray(check2: List<String>?): Boolean {
fun EditText.showKeyBoard() {
this.postDelayed({
this.requestFocus()
val inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val inputMethodManager =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showSoftInput(this, 0)
}, 300)
}
fun SeekBar.doOnSeekBarChangeListener(progressChange: ((progress: Int) -> Unit)? = null, onStopTrackingTouch: (() -> Unit)? = null) {
fun SeekBar.doOnSeekBarChangeListener(
progressChange: ((progress: Int) -> Unit)? = null,
onStopTrackingTouch: (() -> Unit)? = null
) {
this.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
progressChange?.invoke(progress)
@ -941,14 +1079,22 @@ fun SeekBar.doOnSeekBarChangeListener(progressChange: ((progress: Int) -> Unit)?
fun <T> observableToMain(): ObservableTransformer<T, T> {
return ObservableTransformer { upstream ->
upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
}
}
fun <T> singleToMain(): SingleTransformer<T, T> {
return SingleTransformer { upstream ->
upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
}
}
fun clickToastByStatus(status: String, action: () -> Unit) {
when (status) {
"pending" -> ToastUtils.showToast("内容审核中")
"fail" -> ToastUtils.showToast("内容审核不通过")
else -> action.invoke()
}
}

View File

@ -0,0 +1,130 @@
package com.gh.common.util
import android.app.Activity
import android.os.Build
import android.view.Gravity
import android.widget.TextView
import com.gh.gamecenter.R
import com.gh.gamecenter.energy.EnergyCenterActivity
import com.lightgame.utils.Util_System_Keyboard
import com.lzf.easyfloat.EasyFloat
import com.lzf.easyfloat.enums.ShowPattern
import com.lzf.easyfloat.enums.SidePattern
/**
* 返回小浮窗管理类
* 支持两种类型,网页活动(type_activity) 和 积分任务(type_task)
*
* 两种浮窗冲突,显示其中一个的时候就会把另一个判定为隐藏
*/
object FloatingBackViewManager {
const val TYPE_ACTIVITY = "type_activity"
const val TYPE_TASK = "type_task"
private const val FLOATING_BACK_VIEW = "floating_back_view"
private var mType = ""
private var mActivityUrl = ""
private var mLastPositionY = 114F.dip2px()
/**
* 显示返回小浮窗
*/
fun showBackView(activity: Activity) {
// 不支持 Android 4.1 的设备
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) return
EasyFloat.with(activity)
.setLayout(R.layout.layout_task_back)
.setTag(FLOATING_BACK_VIEW)
.setAnimator(null)
.setGravity(Gravity.TOP.xor(Gravity.START), 0, mLastPositionY)
.setSidePattern(SidePattern.LEFT)
.setShowPattern(ShowPattern.CURRENT_ACTIVITY)
.registerCallback {
createResult { _, _, view ->
val titleView = view?.findViewById<TextView>(R.id.titleTv)
view?.setOnClickListener {
// 先收起键盘
Util_System_Keyboard.hideSoftKeyboard(activity)
if (mType == TYPE_ACTIVITY) {
titleView?.text = "返回活动"
DirectUtils.directToWebView(activity, mActivityUrl, ("返回活动浮窗"))
} else if (mType == TYPE_TASK) {
titleView?.text = "返回任务"
activity.startActivity(EnergyCenterActivity.getIntent(activity))
}
disableBackView()
}
}
dragEnd { view ->
val statusBarHeight = DisplayUtils.getStatusBarHeight(activity.resources)
// 记录停止拖动的最后位置
val outLocation = IntArray(2)
view.getLocationInWindow(outLocation)
val yOffset = outLocation[1]
if (yOffset <= statusBarHeight) {
// 判断状态栏是否被消费
if (yOffset == view.y.toInt()) {
view.y = statusBarHeight.toFloat()
} else {
view.y = 0F
}
}
mLastPositionY = yOffset
}
}
.show()
}
/**
* 隐藏返回小浮窗
*/
@JvmStatic
fun dismissBackView(activity: Activity) {
EasyFloat.dismiss(activity, FLOATING_BACK_VIEW)
}
fun getType(): String {
return mType
}
/**
* 启用返回小浮窗
* @param type 类型
* @param activityUrl 类型为活动的时候用的地址
*/
fun enableBackView(type: String, activityUrl: String = "") {
mType = type
mActivityUrl = activityUrl
}
/**
* 停用返回小浮窗
* @param type 类型
* @param activityUrl 类型为活动的时候用的地址
*/
@JvmStatic
fun disableBackView() {
mType = ""
mActivityUrl = ""
}
/**
* 返回小浮窗类型是否为活动
*/
fun isTypeActivity() : Boolean {
return mType == TYPE_ACTIVITY
}
}

View File

@ -93,6 +93,13 @@ object GameSubstituteRepositoryHelper {
val game = collection.data?.find { game -> isThisGameUnique(game, gameIdList) }
game?.let {
collection.data?.remove(game)
collection.data?.size?.let { remainingSize ->
// 记录被替换游戏的数量10个以上的时候触发
if (remainingSize % 10 == 0) {
SentryHelper.onEvent("game_substitute", "substituted_size", "${50 - remainingSize}")
}
}
// 产品说要记录补充专题的曝光数,所以这个游戏附带了所在专题的名字
game.subjectName = collection.name
return game

View File

@ -34,6 +34,10 @@ import java.util.TimeZone;
public class GameViewUtils {
public static void setLabelList(Context context, LinearLayout labelLayout, List<TagStyleEntity> tagStyle) {
setLabelList(context, labelLayout, tagStyle, 8);
}
public static void setLabelList(Context context, LinearLayout labelLayout, List<TagStyleEntity> tagStyle, int margin) {
labelLayout.removeAllViews();
if (tagStyle == null || tagStyle.isEmpty()) {
// 没有数据的话默认不显示
@ -42,7 +46,7 @@ public class GameViewUtils {
// labelLayout.addView(getNewGameTagView(context, tagEntity, 0));
} else {
for (int i = 0, size = tagStyle.size(); i < size; i++) {
View view = getNewGameTagView(context, tagStyle.get(i), i == size - 1 ? 0 : DisplayUtils.dip2px(context, 8));
View view = getNewGameTagView(context, tagStyle.get(i), i == size - 1 ? 0 : DisplayUtils.dip2px(context, margin));
labelLayout.addView(view);
if (labelLayout.getChildCount() == 3) {
break;

View File

@ -8,6 +8,7 @@ import android.graphics.drawable.Animatable
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Build
import android.os.Environment
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import com.facebook.common.executors.CallerThreadExecutor
@ -19,6 +20,7 @@ import com.facebook.drawee.controller.ControllerListener
import com.facebook.drawee.drawable.ScalingUtils
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder
import com.facebook.drawee.view.SimpleDraweeView
import com.facebook.imagepipeline.common.ResizeOptions
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
import com.facebook.imagepipeline.image.CloseableImage
import com.facebook.imagepipeline.image.ImageInfo
@ -26,14 +28,16 @@ import com.facebook.imagepipeline.request.ImageRequest
import com.facebook.imagepipeline.request.ImageRequestBuilder
import com.facebook.imagepipeline.request.Postprocessor
import com.gh.common.constant.Config
import com.gh.common.runOnUiThread
import com.gh.common.structure.FixedSizeLinkedHashSet
import com.gh.gamecenter.R
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import com.squareup.picasso.Picasso
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.io.ByteArrayOutputStream
import java.io.*
object ImageUtils {
@ -90,7 +94,7 @@ object ImageUtils {
val jpegConfig = Config.getSettings()?.image?.oss?.jpeg
val webpConfig = Config.getSettings()?.image?.oss?.webp
?: Config.getSettings()?.image?.oss?.gif
if (jpegConfig != null) {
if (imageUrl?.contains("?x-oss-process") == false && jpegConfig != null) {
return if (width == 0 || width == null) {
"$imageUrl$webpConfig"
} else {
@ -308,7 +312,7 @@ object ImageUtils {
if (url == null) return
// 部分自适应宽高图片需要一个 TARGET_WIDTH 来避免加载过小图片
val width = (view?.getTag(TARGET_WIDTH) as? Int) ?: view?.layoutParams?.width
val width = (view?.getTag(TARGET_WIDTH) as? Int) ?: view?.width?.coerceAtLeast(view.layoutParams?.width ?: 0)
val height = view?.layoutParams?.height
var lowResUrl = ""
@ -452,6 +456,19 @@ object ImageUtils {
draweeView.setImageURI("res:///" + res)
}
@JvmStatic
fun displayResizeMedia(draweeView: SimpleDraweeView, url: String, resizeWidthDp: Int, resizeHeightDp: Int) {
val request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
.setResizeOptions(ResizeOptions(resizeWidthDp, resizeHeightDp))
.build()
val controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(request)
.setOldController(draweeView.controller)
.setControllerListener(BaseControllerListener<ImageInfo>())
.build()
draweeView.controller = controller
}
//预加载图片
@JvmStatic
fun prefetchToDiskCache(url: String) {
@ -482,7 +499,7 @@ object ImageUtils {
}
fun getVideoSnapshot(videoUrl: String, progress: Long): String {
return "$videoUrl?x-oss-process=video/snapshot,t_$progress,f_jpg,w_0,h_0"
return "$videoUrl?x-oss-process=video/snapshot,t_$progress,f_jpg,w_0,h_0,ar_auto"
}
/**
@ -494,7 +511,7 @@ object ImageUtils {
val clazz = SimpleDraweeView::class.java
return try {
val field =
clazz.getDeclaredField("sDraweecontrollerbuildersupplier")
clazz.getDeclaredField("sDraweecontrollerbuildersupplier")
field.isAccessible = true
val obj = field[SimpleDraweeView::class.java]
obj != null
@ -502,4 +519,50 @@ object ImageUtils {
false
}
}
/**
* 将图片保存到外部存储空间
* @param imageFile 内部存储的文件
* @param url 图片原地址,用来截取当图片名称用的
* @param useRandomName 是否使用随机名称
*/
fun saveImageToFile(imageFile: File, url: String?, useRandomName: Boolean = false) {
var `in`: InputStream? = null
var out: OutputStream? = null
try {
val fileName: String = if (useRandomName) {
MD5Utils.getContentMD5(System.currentTimeMillis().toString()) + ".png"
} else {
Uri.parse(url).lastPathSegment.toString()
}
val savePath = Environment.getExternalStorageDirectory().absolutePath + "/Pictures/ghzhushou/"
val file = File(savePath)
if (!file.exists()) {
file.mkdirs()
}
val dst = File(savePath, fileName)
if (dst.exists()) {
dst.delete()
}
`in` = FileInputStream(imageFile)
out = FileOutputStream(dst)
val buf = ByteArray(1024)
var len: Int
while (`in`.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
runOnUiThread {
ToastUtils.toast("图片已保存到/Pictures/ghzhushou/")
}
MessageShareUtils.refreshImage(HaloApp.getInstance(), dst)
} catch (e: Exception) {
Utils.log("图片保存失败:$e")
} finally {
tryWithDefaultCatch {
out?.close()
`in`?.close()
}
}
}
}

View File

@ -4,10 +4,16 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.NonNull;
import com.gh.gamecenter.NormalActivity;
import com.gh.gamecenter.SingletonWebActivity;
import com.gh.gamecenter.WebActivity;
import com.gh.gamecenter.category.CategoryListActivity;
import com.gh.gamecenter.entity.CategoryEntity;
import com.halo.assistant.fragment.WebFragment;
/**
* @author CsHeng
@ -17,7 +23,6 @@ import com.gh.gamecenter.entity.CategoryEntity;
public class IntentUtils {
public static Intent getWifiIntent() {
return new Intent("android.settings.WIFI_SETTINGS");
}
@ -61,4 +66,19 @@ public class IntentUtils {
DataUtils.onMtaEvent(context, "分类大全", categoryTitle, category.getName());
context.startActivity(CategoryListActivity.Companion.getIntent(context, categoryTitle, category, "全部"));
}
public static Intent getWebTargetIntent(Context context, Bundle bundle, String url) {
Class<? extends NormalActivity> cls;
if (url.contains("android_page_type=singleton")) {
cls = SingletonWebActivity.class;
} else {
cls = WebActivity.class;
}
Intent intent = new Intent(context, cls);
intent.putExtra(NormalActivity.NORMAL_FRAGMENT_NAME, WebFragment.class.getCanonicalName());
intent.putExtra(NormalActivity.NORMAL_FRAGMENT_BUNDLE, bundle);
return intent;
}
}

View File

@ -91,8 +91,10 @@ public class KeyboardHeightProvider extends PopupWindow {
* of the Activity.
*/
public void start() {
if (!isShowing() && parentView.getWindowToken() != null) {
if (!isShowing()
&& parentView.getWindowToken() != null
&& activity != null
&& !activity.isFinishing()) {
setBackgroundDrawable(new ColorDrawable(0));
showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,6 @@
package com.gh.common.util
import android.app.Activity
import android.app.Application
import android.content.Context
import android.content.Intent
import android.net.Uri
@ -46,7 +45,7 @@ object PackageInstaller {
// TODO 此处可能遇到 activity 是 WXEntryActivity
// TODO 当 activity 全部出栈,但是应用还在下载游戏,下载完会唤不起安装!
if (currentActivity is AppCompatActivity) {
if (currentActivity is AppCompatActivity && !currentActivity.isFinishing) {
InstallPermissionDialogFragment.show(currentActivity, downloadEntity) {
// 取消状态栏下载完成的通知,若存在
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
@ -125,7 +124,7 @@ object PackageInstaller {
// 应用内更新不加 FLAG_ACTIVITY_NEW_TASK 在模拟器上会出现安装完成后安装界面也一并消失的类似闪退的表现
// Application 上下文就更不用说了
val pkgName = PackageUtils.getPackageNameByPath(context, path)
if (pkgName == context.packageName || context is Application) {
if (pkgName == context.packageName || context !is Activity) {
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
installIntent.setDataAndType(uri, "application/vnd.android.package-archive")

View File

@ -45,7 +45,9 @@ import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@ -158,6 +160,42 @@ public class PackageUtils {
return getMetaData(HaloApp.getInstance().getApplication(), packageName, "gh_id");
}
@Nullable
public static Map<String, String> getSideLoadedInfo() {
Context context = HaloApp.getInstance().getApplicationContext();
String packageName = null;
try {
final PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
final PackageManager packageManager = context.getPackageManager();
if (packageInfo != null && packageManager != null) {
packageName = packageInfo.packageName;
// getInstallSourceInfo requires INSTALL_PACKAGES permission which is only given to system
// apps.
final String installerPackageName = packageManager.getInstallerPackageName(packageName);
final Map<String, String> sideLoadedInfo = new HashMap<>();
if (installerPackageName != null) {
sideLoadedInfo.put("is_side_loaded", "false");
// could be amazon, google play etc
sideLoadedInfo.put("installer_store", installerPackageName);
} else {
// if it's installed via adb, system apps or untrusted sources
sideLoadedInfo.put("is_side_loaded", "true");
}
return sideLoadedInfo;
}
} catch (Exception e) {
Utils.log(e.getLocalizedMessage());
}
return null;
}
/*
* 判断是否是插件包
@ -668,29 +706,34 @@ public class PackageUtils {
* 应用是否在前台运行
*/
public static boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getApplicationContext()
.getSystemService(Context.ACTIVITY_SERVICE);
if (activityManager == null) return false;
try {
ActivityManager activityManager = (ActivityManager) context.getApplicationContext()
.getSystemService(Context.ACTIVITY_SERVICE);
if (activityManager == null) return false;
List<ActivityManager.RunningAppProcessInfo> appProcesses =
activityManager.getRunningAppProcesses();
if (appProcesses == null) return false;
List<ActivityManager.RunningAppProcessInfo> appProcesses =
activityManager.getRunningAppProcesses();
if (appProcesses == null) return false;
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm == null) return false;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
if (!pm.isInteractive()) return false;
} else {
if (!pm.isScreenOn()) return false;
}
String packageName = context.getApplicationContext().getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
// The name of the process that this object is associated with.
if (appProcess.processName.equals(packageName) && appProcess.importance
== ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return true;
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm == null) return false;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
if (!pm.isInteractive()) return false;
} else {
if (!pm.isScreenOn()) return false;
}
String packageName = context.getApplicationContext().getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
// The name of the process that this object is associated with.
if (appProcess.processName.equals(packageName) && appProcess.importance
== ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return true;
}
}
} catch (NullPointerException e) {
e.printStackTrace();
return false;
}
return false;
}

View File

@ -117,6 +117,7 @@ public class PostCommentUtils {
String articleId,
String articleCommunityId,
String videoId,
String questionId,
final String commentId,
final PostCommentListener listener) {
@ -126,6 +127,8 @@ public class PostCommentUtils {
observable = RetrofitManager.getInstance(context).getApi().postVoteAnswerComment(answerId, commentId);
} else if (!TextUtils.isEmpty(articleId)) {
observable = RetrofitManager.getInstance(context).getApi().postVoteCommunityArticleComment(articleCommunityId, articleId, commentId);
} else if (!TextUtils.isEmpty(questionId)) {
observable = RetrofitManager.getInstance(context).getApi().postVoteQuestionComment(questionId, commentId);
} else {
observable = RetrofitManager.getInstance(context).getApi().postVoteToVideo(videoId, commentId);
}
@ -258,6 +261,28 @@ public class PostCommentUtils {
}
});
}
public static void reportQuestionComment(final Context context,
final String questionId,
final String commentId,
final String reportData,
final PostCommentListener listener) {
RequestBody body = RequestBody.create(MediaType.parse("application/json"), reportData);
RetrofitManager.getInstance(context).getApi()
.postQuestionCommentReport(questionId, commentId, body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Response<ResponseBody>() {
@Override
public void onResponse(ResponseBody response) {
listener.postSuccess(null);
}
@Override
public void onFailure(HttpException e) {
listener.postFailed(e);
}
});
}
public interface PostCommentListener {
void postSuccess(JSONObject response);

View File

@ -52,6 +52,31 @@ object QuickLoginHelper {
private const val ENTRANCE_PERMISSION_DIALOG = "一键登录权限弹窗"
private const val AUTH_ACTIVITY_NAME = "com.cmic.sso.sdk.activity.LoginAuthActivity"
// 1.取号请求(可提前进行)
@JvmStatic
fun getPhoneInfo(context: Context) {
if (isPublishEnv()) {
AuthnHelper.setDebugMode(false)
} else {
AuthnHelper.setDebugMode(true)
}
AuthnHelper.getInstance(context.applicationContext).getPhoneInfo(
Config.QUICK_LOGIN_APPID,
Config.QUICK_LOGIN_APPKEY,
{ requestCode: Int, jsonObject: JSONObject ->
val code = jsonObject.optString("resultCode")
// “103000”为成功
if (code == "103000") {
if (requestCode == REQUEST_GET_PHONE_INFO_CODE) {
SPUtils.setBoolean(Constants.SP_HAS_GET_PHONE_INFO, true)
}
}
},
REQUEST_GET_PHONE_INFO_CODE
)
}
@JvmStatic
fun startLogin(context: Context, entrance: String) {
// 防止短时间多次调用
@ -64,8 +89,45 @@ object QuickLoginHelper {
} else {
AuthnHelper.setDebugMode(true)
}
if (mAuthnHelper == null) {
initSDK(context, entrance)
}
if (mTokenListener == null) {
initTokenListener(context)
}
if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
loginAuth(context)
} else {
DialogUtils.showQuickLoginPermissionDialog(
context,
{
checkReadPhoneStatePermissionBeforeAction(context, object : EmptyCallback {
override fun onCallback() {
loginAuth(context)
}
})
},
{
startCodeLoginPage(
context,
entrance = ENTRANCE_PERMISSION_DIALOG,
isFinishAuth = false,
isFromPermission = true
)
}
)
}
}
private fun initSDK(context: Context, entrance: String) {
mAuthnHelper = AuthnHelper.getInstance(context.applicationContext)
mAuthnHelper?.run {
// 设置超时时间
overTime = 3000
authThemeConfig = getConfig(context, entrance)
// 授权页面的回调方法
setPageInListener { code, _ ->
@ -80,56 +142,27 @@ object QuickLoginHelper {
startCodeLoginPage(context, true)
}
}
}
}
// token回调
mTokenListener = TokenListener { requestCode: Int, jsonObject: JSONObject ->
val code = jsonObject.optString("resultCode")
// “103000”为成功
if (code == "103000") {
when (requestCode) {
REQUEST_GET_PHONE_INFO_CODE -> {
SPUtils.setBoolean(Constants.SP_HAS_GET_PHONE_INFO, true)
// 2.授权请求
mAuthnHelper!!.loginAuth(Config.QUICK_LOGIN_APPID, Config.QUICK_LOGIN_APPKEY, mTokenListener, REQUEST_LOGIN_AUTH_CODE)
}
REQUEST_LOGIN_AUTH_CODE -> {
// 3.获取token
mToken = jsonObject.optString("token")
}
}
} else if (code != "200020") { // 不成功就调起验证码登录页(200020代表授权页关闭)
toastCode(code)
startCodeLoginPage(context, true)
private fun initTokenListener(context: Context) {
// token回调
mTokenListener = TokenListener { requestCode: Int, jsonObject: JSONObject ->
val code = jsonObject.optString("resultCode")
// “103000”为成功
if (code == "103000") {
if (requestCode == REQUEST_LOGIN_AUTH_CODE) {
// 3.获取token
mToken = jsonObject.optString("token")
}
}
if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
getPhoneInfo(context)
} else {
DialogUtils.showQuickLoginPermissionDialog(
context,
{
checkReadPhoneStatePermissionBeforeAction(context, object : EmptyCallback {
override fun onCallback() {
getPhoneInfo(context)
}
})
},
{
startCodeLoginPage(
context,
entrance = ENTRANCE_PERMISSION_DIALOG,
isFinishAuth = false,
isFromPermission = true
)
}
)
} else if (code != "200020") { // 不成功就调起验证码登录页(200020代表授权页关闭)
toastCode(code)
startCodeLoginPage(context, true)
}
}
}
private fun getPhoneInfo(context: Context) {
private fun loginAuth(context: Context) {
mPreDialog = Dialog(context, R.style.DialogWindowTransparent).apply {
val binding = SetWaitDialogBinding.inflate(LayoutInflater.from(context)).apply {
setWaitMessage.text = "请求登录中"
@ -139,8 +172,8 @@ object QuickLoginHelper {
setCanceledOnTouchOutside(false)
show()
}
// 1.取号请求
mAuthnHelper!!.getPhoneInfo(Config.QUICK_LOGIN_APPID, Config.QUICK_LOGIN_APPKEY, mTokenListener, REQUEST_GET_PHONE_INFO_CODE)
// 2.授权请求
mAuthnHelper?.loginAuth(Config.QUICK_LOGIN_APPID, Config.QUICK_LOGIN_APPKEY, mTokenListener, REQUEST_LOGIN_AUTH_CODE)
}
private fun getConfig(context: Context, entrance: String): AuthThemeConfig{

View File

@ -148,6 +148,11 @@ public class ShareUtils {
if (ShareUtils.shareEntrance == ShareEntrance.inviteFriends) {
IntegralLogHelper.INSTANCE.logInviteResult("成功", mShareType.getName());
}
if (ShareUtils.shareEntrance == ShareUtils.ShareEntrance.askNormal ||
ShareUtils.shareEntrance == ShareEntrance.communityArticle ||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.video) {
NewLogUtils.logShareResult(true);
}
}
@Override
@ -158,6 +163,11 @@ public class ShareUtils {
if (ShareUtils.shareEntrance == ShareEntrance.inviteFriends) {
IntegralLogHelper.INSTANCE.logInviteResult("失败", mShareType.getName());
}
if (ShareUtils.shareEntrance == ShareUtils.ShareEntrance.askNormal ||
ShareUtils.shareEntrance == ShareEntrance.communityArticle ||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.video) {
NewLogUtils.logShareResult(false);
}
}
@Override
@ -168,6 +178,11 @@ public class ShareUtils {
if (ShareUtils.shareEntrance == ShareEntrance.inviteFriends) {
IntegralLogHelper.INSTANCE.logInviteResult("取消", mShareType.getName());
}
if (ShareUtils.shareEntrance == ShareUtils.ShareEntrance.askNormal ||
ShareUtils.shareEntrance == ShareEntrance.communityArticle ||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.video) {
NewLogUtils.logShareResult(false);
}
}
};
@ -215,23 +230,23 @@ public class ShareUtils {
// 分享
switch (way) {
case "qq" :
case "qq":
mShareType = ShareType.qq;
qqShare();
break;
case "qq_zone" :
case "qq_zone":
mShareType = ShareType.qqZone;
qZoneShare();
break;
case "wechat" :
case "wechat":
mShareType = ShareType.wechat;
wechatShare();
break;
case "wechat_moments" :
case "wechat_moments":
mShareType = ShareType.wechatMoments;
wechatMomentsShare();
break;
case "weibo" :
case "weibo":
mShareType = ShareType.weibo;
sinaWeiboShare();
break;
@ -409,6 +424,11 @@ public class ShareUtils {
//QQ分享
public void qqShare() {
Utils.toast(mContext, R.string.share_skip);
mShareType = ShareType.qq;
shareType = "qq_friend";
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
Bundle params = new Bundle();
switch (mShareEntrance) {
@ -445,6 +465,10 @@ public class ShareUtils {
public void wechatShare() {
Utils.toast(mContext, R.string.share_skip);
shareType = "wechat_friend";
mShareType = ShareType.wechat;
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
if (!PackageHelper.INSTANCE.getLocalPackageNameSet().contains("com.tencent.mm")) {
Utils.toast(mContext, "没安装微信,分享失败");
return;
@ -561,6 +585,12 @@ public class ShareUtils {
//QQ空间分享
public void qZoneShare() {
Utils.toast(mContext, R.string.share_skip);
mShareType = ShareType.qqZone;
shareType = "qq_zone";
MtaHelper.onEvent("内容分享", "QQ空间", mTitle);
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
Bundle params = new Bundle();
switch (mShareEntrance) {
@ -601,6 +631,10 @@ public class ShareUtils {
public void wechatMomentsShare() {
Utils.toast(mContext, R.string.share_skip);
mShareType = ShareType.wechatMoments;
shareType = "wechat_moment";
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
if (!PackageHelper.INSTANCE.getLocalPackageNameSet().contains("com.tencent.mm")) {
Utils.toast(mContext, "没安装微信,分享失败");
return;
@ -642,6 +676,10 @@ public class ShareUtils {
//新浪微博分享
public void sinaWeiboShare() {
mShareType = ShareType.weibo;
shareType = "sina_weibo";
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
WbSdk.install(mContext, new AuthInfo(mContext, Config.WEIBO_APPKEY, "http://www.sina.com", WEIBO_SCOPE));
if (mShareEntrance == ShareEntrance.qaDetail) {
@ -668,6 +706,10 @@ public class ShareUtils {
//短信分享
public void shortMessageShare() {
mShareType = ShareType.weibo;
shareType = "sms";
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
String smsBody;
switch (mShareEntrance) {
case news:
@ -714,6 +756,7 @@ public class ShareUtils {
public void copyLink(String copyContent) {
shareType = "copy_link";
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
LogUtils.uploadShareResult(shareType, shareEntrance.getName(), "success", shareUrl, mTitle, mSummary, resourceId);
if (mShareEntrance != ShareEntrance.shareGh) {
ExtensionsKt.copyTextAndToast(copyContent, "复制成功");
safelyDismiss();
@ -749,50 +792,34 @@ public class ShareUtils {
holder.shareLogo.setImageResource(arrLogo[position]);
holder.shareLabel.setText(arrLabel[position]);
holder.itemView.setOnClickListener(v -> {
if (mShareEntrance == ShareEntrance.shareGh) {
MtaHelper.onEvent("我的光环_新", "分享光环", arrLabel[position]);
}
if (listener != null) {
listener.onItemClick(holder.getAdapterPosition());
}
switch (holder.getPosition()) {
case 0:
shareType = "wechat_friend";
MtaHelper.onEvent("内容分享", "微信好友", mTitle);
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
wechatShare();
break;
case 1:
shareType = "wechat_moment";
MtaHelper.onEvent("内容分享", "微信朋友圈", mTitle);
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
wechatMomentsShare();
break;
case 2:
shareType = "qq_friend";
MtaHelper.onEvent("内容分享", "QQ好友", mTitle);
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
qqShare();
break;
case 3:
shareType = "qq_zone";
MtaHelper.onEvent("内容分享", "QQ空间", mTitle);
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
qZoneShare();
break;
case 4:
shareType = "sina_weibo";
MtaHelper.onEvent("内容分享", "新浪微博", mTitle);
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
sinaWeiboShare();
break;
case 5:
MtaHelper.onEvent("内容分享", "短信", mTitle);
shortMessageShare();
break;
case 6:
MtaHelper.onEvent("内容分享", "复制链接", mTitle);
shareType = "copy_link";
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
LogUtils.uploadShareResult(shareType, shareEntrance.getName(), "success", shareUrl, mTitle, mSummary, resourceId);
if (mShareEntrance == ShareEntrance.askInvite) {
copyLink(mTitle + " - 光环助手" + shareUrl);
} else if (mShareEntrance == ShareEntrance.askNormal || mShareEntrance == ShareEntrance.answerNormal) {
@ -815,6 +842,7 @@ public class ShareUtils {
} else {
shareType = "copy_link";
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
LogUtils.uploadShareResult(shareType, shareEntrance.getName(), "success", shareUrl, mTitle, mSummary, resourceId);
}
break;
}

View File

@ -3,6 +3,7 @@ package com.gh.common.util
import android.content.Context
import android.graphics.Color
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.TextPaint
@ -70,6 +71,13 @@ class SpanBuilder(content: CharSequence) {
return this
}
//添加图标
fun image(start: Int, end: Int, drawable: Drawable): SpanBuilder {
val imageSpan = CenterImageSpan(drawable)
spannableString.setSpan(imageSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
return this
}
fun click(start: Int, end: Int, colorRes: Int, isUnderlineText: Boolean = false, onClick: () -> Unit): SpanBuilder {
val clickSpan = object : ClickableSpan() {
override fun updateDrawState(ds: TextPaint) {

View File

@ -27,6 +27,7 @@ import java.lang.Exception
object UploadImageUtils {
enum class UploadType {
community_article,
question,
answer,
suggestion,
@ -179,7 +180,10 @@ object UploadImageUtils {
}
override fun onNext(t: Map<String, String>) {
if (t.isNotEmpty()) postImageList.putAll(t)
if (t.isNotEmpty()){
listener.onSingleSuccess(t)
postImageList.putAll(t)
}
}
override fun onError(ignore: Throwable) {
@ -293,6 +297,7 @@ object UploadImageUtils {
interface OnUploadImageListListener {
fun onSuccess(imageUrl: LinkedHashMap<String, String>, errorMap: Map<String, Exception>) // key:sourceImage value:compressImage
fun onSingleSuccess(imageUrl: Map<String, String>)
fun onCompressSuccess(imageUrls: List<String>) {}
fun onError(errorMap: Map<String, Exception>) // 全部上传失败时回调
fun onProgress(total: Long, progress: Long)

View File

@ -148,7 +148,7 @@ class AvatarBorderView : ConstraintLayout {
}
}
fun displayUserBadge(badgeUrl: String? = "") {
private fun displayUserBadge(badgeUrl: String? = "") {
if (badgeUrl.isNullOrEmpty()) {
badgeView?.setImageURI("")
badgeView?.visibility = View.GONE

View File

@ -30,6 +30,7 @@ class CategoryFilterView @JvmOverloads constructor(context: Context, attrs: Attr
private var sizeFilterArray: ArrayList<SubjectSettingEntity.Size>? = null
private var mOnCategoryFilterSetupListener: OnCategoryFilterSetupListener? = null
private var mOnFilterClickListener: OnFilterClickListener? = null
init {
View.inflate(context, R.layout.layout_category_filter, this)
@ -43,14 +44,17 @@ class CategoryFilterView @JvmOverloads constructor(context: Context, attrs: Attr
mTypeTv.text = mTypeFilterArray[0].value
mTypeContainer.setOnClickListener {
mOnFilterClickListener?.onTypeClick()
showSelectTypePopupWindow(this, mTypeTv, mTypeTv.text.toString())
}
mCatalogContainer.setOnClickListener {
mOnFilterClickListener?.onCategoryClick()
mOnCategoryFilterSetupListener?.onSetupSortCategory()
}
mSizeContainer.setOnClickListener {
mOnFilterClickListener?.onSizeClick()
showSelectSizePopupWindow(this, mSizeTv, mSizeTv.text.toString())
}
}
@ -59,6 +63,14 @@ class CategoryFilterView @JvmOverloads constructor(context: Context, attrs: Attr
mOnCategoryFilterSetupListener = onCategoryFilterSetupListener
}
fun setOnFilterClickListener(onFilterClickListener: OnFilterClickListener) {
mOnFilterClickListener = onFilterClickListener
}
fun resetSortSize() {
mSizeTv.text = "全部大小"
}
private fun toggleHighlightedTextView(targetTextView: TextView, highlightIt: Boolean) {
if (highlightIt) {
targetTextView.background = ContextCompat.getDrawable(targetTextView.context, R.drawable.bg_tag_text)
@ -218,6 +230,12 @@ class CategoryFilterView @JvmOverloads constructor(context: Context, attrs: Attr
fun onSetupSortCategory()
}
interface OnFilterClickListener {
fun onCategoryClick()
fun onTypeClick()
fun onSizeClick()
}
enum class SortType(val value: String) {
RECOMMENDED("热门推荐"),
NEWEST("最新上线"),

View File

@ -1,11 +1,13 @@
package com.gh.common.view
import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.view.View
import android.widget.LinearLayout
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.roundToInt
class CustomDividerItemDecoration(
context: Context,
@ -13,6 +15,7 @@ class CustomDividerItemDecoration(
var notDecorateTheFirstItem: Boolean = false,
var notDecorateTheLastItem: Boolean = false,
var notDecorateTheFirstTwoItems: Boolean = false) : DividerItemDecoration(context, LinearLayout.VERTICAL) {
private val mBounds = Rect()
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
if (onlyDecorateTheFirstItem) {
@ -31,4 +34,42 @@ class CustomDividerItemDecoration(
}
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
if (parent.layoutManager == null || drawable == null) {
return
}
drawVertical(c, parent)
}
private fun drawVertical(canvas: Canvas, parent: RecyclerView) {
canvas.save()
val left: Int
val right: Int
if (parent.clipToPadding) {
left = parent.paddingLeft
right = parent.width - parent.paddingRight
canvas.clipRect(left, parent.paddingTop, right,
parent.height - parent.paddingBottom)
} else {
left = 0
right = parent.width
}
val childCount = parent.childCount
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
parent.getDecoratedBoundsWithMargins(child, mBounds)
var bottom = mBounds.bottom + child.translationY.roundToInt()
val rect = Rect()
getItemOffsets(rect, child, parent, RecyclerView.State())
val top = if (rect.bottom > 0) {
bottom - (drawable?.intrinsicHeight ?: 0)
} else {
bottom = 0
0
}
drawable?.setBounds(left, top, right, bottom)
drawable?.draw(canvas)
}
canvas.restore()
}
}

View File

@ -12,6 +12,8 @@ import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.gh.common.util.DisplayUtils
import com.gh.common.util.dip2px
import com.gh.common.util.sp2px
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.TagStyleEntity
import kotlin.math.ceil
@ -23,16 +25,27 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib
private var mTotalCount = 0
private var mTags = ArrayList<TagStyleEntity>()
private var mItemHeight = DisplayUtils.dip2px(20F)
private var mPadding = DisplayUtils.dip2px(5F)
private var mMargin = DisplayUtils.dip2px(4F)
private var mItemHeight = 0
private var mPadding = 0
private var mMargin = 0
private var mTextSize = 10F
private var mLastItemWidth = DisplayUtils.dip2px(18F)//最后更多按钮宽度
private var mLastItemWidth = 0//最后更多按钮宽度
private var mTotalWidth = 0
private var mStrokeWidth = 0
var onClickListener: OnItemClickListener? = null
init {
gravity = Gravity.CENTER_VERTICAL
val ta = context.obtainStyledAttributes(attrs, R.styleable.FlexLinearLayout)
mItemHeight = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemHeight, 20f.dip2px())
mPadding = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemPadding, 5f.dip2px())
mMargin = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_itemMargin, 4f.dip2px())
mTextSize = ta.getDimension(R.styleable.FlexLinearLayout_itemTextSize, 10f.sp2px().toFloat())
mLastItemWidth = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_lastItemWidth, 18f.dip2px())
mStrokeWidth = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_strokeWidth, 1f.dip2px())
ta.recycle()
}
fun setTags(tags: ArrayList<TagStyleEntity>) {
@ -40,7 +53,7 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib
mTotalCount = tags.size
mTotalWidth = measuredWidth
val paint = Paint()
paint.textSize = DisplayUtils.sp2px(context, mTextSize).toFloat()
paint.textSize = mTextSize
var currentWidth = mLastItemWidth.toFloat()
tags.forEach {
@ -79,7 +92,7 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib
layoutParams = params
setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_game_detail_label_more))
background = createBackgroundDrawable()
scaleType=ImageView.ScaleType.CENTER
scaleType = ImageView.ScaleType.CENTER
}
imageView.setOnClickListener {
onClickListener?.onMoreClickListener()
@ -92,7 +105,7 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib
return TextView(context).apply {
text = tag.name
includeFontPadding = false
textSize = mTextSize
textSize = DisplayUtils.px2sp(context, mTextSize).toFloat()
gravity = Gravity.CENTER
setTextColor(Color.parseColor("#333333"))
@ -112,7 +125,7 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib
private fun createBackgroundDrawable(): GradientDrawable {
val gradientDrawable = GradientDrawable()
gradientDrawable.setColor(Color.TRANSPARENT)
gradientDrawable.setStroke(DisplayUtils.dip2px(context, 1f), Color.parseColor("#C2C6CC"))
gradientDrawable.setStroke(mStrokeWidth, Color.parseColor("#C2C6CC"))
gradientDrawable.cornerRadius = DisplayUtils.dip2px(2f).toFloat()
return gradientDrawable
}

View File

@ -11,6 +11,7 @@ import com.gh.common.util.*
import com.gh.gamecenter.ImageViewerActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.ItemCommunityImageBinding
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.qa.entity.AnswerEntity
import com.gh.gamecenter.qa.entity.CommunityVideoEntity
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
@ -72,7 +73,9 @@ class ImageContainerView : LinearLayout {
mPath = path
index = 0
removeAllViews()
if (entity.getPassVideos().isNullOrEmpty() && entity.images.isNullOrEmpty()) {
if ((entity.user.id == UserManager.getInstance().userId && entity.videos.isNotEmpty())
|| (entity.user.id != UserManager.getInstance().userId && entity.getPassVideos().isNotEmpty())
|| entity.images.isNullOrEmpty()) {
visibility = View.GONE
return
}
@ -155,6 +158,8 @@ class ImageContainerView : LinearLayout {
binding.videoPlay.visibility = View.GONE
displayImage(binding, url, width.toFloat(), height.toFloat(), isChangeRatio)
binding.root.setOnClickListener {
if (mAnswerEntity?.status == "pending" || mAnswerEntity?.status == "fail") return@setOnClickListener
debounceActionWithInterval(it.id, 1000) {
if (mAnswerEntity == null) return@debounceActionWithInterval
val position = if (mAnswerEntity?.type == "community_article") {
@ -162,6 +167,9 @@ class ImageContainerView : LinearLayout {
} else {
if (mAnswerEntity!!.getPassVideos().isNullOrEmpty()) binding.root.tag as Int else (binding.root.tag as Int) - 1
}
if (mAnswerEntity?.communityId.isNullOrEmpty()) {
mAnswerEntity?.communityId = mAnswerEntity?.bbs?.id
}
val intent = ImageViewerActivity.getIntent(context, mAnswerEntity!!.images as ArrayList<String>, position, binding.root,
if (mAnswerEntity?.type == "community_article") mAnswerEntity else null, mEntrance)
context.startActivity(intent)
@ -209,19 +217,35 @@ class ImageContainerView : LinearLayout {
}
if (url.endsWith(".gif")) {
binding.gifBorder.visibility = View.VISIBLE
binding.labelIcon.visibility = View.VISIBLE
binding.labelIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_gif_label))
}
val mediaCount = if (mAnswerEntity?.type == "community_article") {
mAnswerEntity?.images?.size ?: 0
} else {
(mAnswerEntity?.images?.size ?: 0) + (mAnswerEntity?.getPassVideos()?.size ?: 0)
binding.pendingView.run {
when (mAnswerEntity?.status) {
"pending" -> {
visibility = View.VISIBLE
text = R.string.pending_status.toResString()
}
"fail" -> {
visibility = View.VISIBLE
text = R.string.fail_status.toResString()
}
else -> {
visibility = View.GONE
}
}
}
if (!isVideo && index == 2 && mediaCount > 3) {
val imageCount = mAnswerEntity?.images?.size ?: 0
if (!isVideo && index == 2 && imageCount > 3) {
binding.labelIcon.visibility = View.GONE
binding.durationOrNumTv.visibility = View.VISIBLE
binding.durationOrNumTv.text = "+${mAnswerEntity?.images?.size}"
binding.durationOrNumTv.text = "+${imageCount - 3}"
}
hierarchy.actualImageScaleType = ScalingUtils.ScaleType.CENTER_CROP

View File

@ -20,7 +20,8 @@ class LikeView : RelativeLayout {
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
fun runLikeAnimation(event: MotionEvent) {
fun runLikeAnimation(event: MotionEvent?) {
if (event == null) return
val iv = ImageView(context)
val lp = LayoutParams(168, 150)
val widthPixels = context.resources.displayMetrics.widthPixels

View File

@ -11,6 +11,7 @@ import android.util.AttributeSet;
import android.view.Gravity;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@ -463,6 +464,49 @@ public class RichEditor extends WebView {
exec("javascript:RE.replacePlaceholderImage('" + list + "');");
}
/**
* 插入视频占位图
*/
public void insertPlaceholderVideo(String id, String poster) {
exec("javascript:RE.insertPlaceholderVideo('" + id + "','" + poster + "');");
}
/**
* 更新视频进度条
*/
public void updateVideoProgress(String id, String progress) {
exec("javascript:RE.updateVideoProgress('" + id + "','" + progress + "');");
}
/**
* 上传视频完成
*/
public void videoUploadFinished(String id, String url, String msg) {
exec("javascript:RE.videoUploadFinished('" + id + "','" + url + "','" + msg + "');");
}
/**
* 更换封面图
*/
public void changePoster(String id, String poster) {
exec("javascript:RE.changePoster('" + id + "','" + poster + "');");
}
/**
* 删除视频
*/
public void delPlaceholderVideo(String id) {
exec("javascript:RE.delPlaceholderVideo('" + id + "');");
}
/**
* 上传失败
*/
public void videoUploadFailed(String id) {
exec("javascript:RE.videoUploadFailed('" + id + "');");
}
public void removeFormat() {
exec("javascript:RE.removeFormat();");
}

View File

@ -112,10 +112,10 @@ public class TabIndicatorView extends View implements ViewPager.OnPageChangeList
this.mIndicatorSpace = DisplayUtils.dip2px(getContext(), space);
}
private int getIndicatorSpace() {
private int getIndicatorSpace(int position) {
if (mIndicatorSpace != 0) return mIndicatorSpace;
if (mIndicatorWidth != 0) {
View tag = getTabViewByPosition(0);
View tag = getTabViewByPosition(position);
if (tag != null) return (tag.getWidth() - mIndicatorWidth) / 2;
}
return 0;
@ -174,15 +174,15 @@ public class TabIndicatorView extends View implements ViewPager.OnPageChangeList
right += (int) (nextTabView.getRight() * positionOffset + tabView.getRight() * (1.f - positionOffset));
}
left += getIndicatorSpace();
right -= getIndicatorSpace();
left += getIndicatorSpace(position);
right -= getIndicatorSpace(position);
top = tabView.getTop() + getPaddingTop();
bottom = tabView.getBottom() - getPaddingBottom();
range.set(left, top, right, bottom);
} else {
left = tabView.getLeft() + getIndicatorSpace();
right = tabView.getRight() - getIndicatorSpace();
left = tabView.getLeft() + getIndicatorSpace(position);
right = tabView.getRight() - getIndicatorSpace(position);
top = tabView.getTop() + getPaddingTop();
bottom = tabView.getBottom() - getPaddingBottom();
range.set(left, top, right, bottom);

View File

@ -46,7 +46,7 @@ class XapkUnzipThread(private var mDownloadEntity: DownloadEntity,
val absolutePath = Environment.getExternalStorageDirectory().absolutePath
val xapkFile = File(path)
ZipFile(xapkFile).use { zip ->
val unzipClosure: (zip: ZipFile) -> Unit = { zip ->
for (zipEntry in zip.entries().asSequence()) {
val outputFile = if (zipEntry.name.getExtension() == XapkInstaller.XAPK_DATA_EXTENSION_NAME) {
File(absolutePath + File.separator + zipEntry.name)
@ -91,7 +91,7 @@ class XapkUnzipThread(private var mDownloadEntity: DownloadEntity,
bytes = input.read(buffer)
if (canceled) {
mUnzipListener.onCancel(mDownloadEntity)
return
return@use
} else {
// 防止多次短时间内多次触发onProgress方法导致阻塞主线程低端机会出现十分明显的卡顿
debounceActionWithInterval(-1, 500) {
@ -104,6 +104,16 @@ class XapkUnzipThread(private var mDownloadEntity: DownloadEntity,
mUnzipListener.onNext(mDownloadEntity, outputFile.path)
}
}
// Kotlin 1.4.X 在安卓 4.4 以下使用 use 默认关闭 ZipFile 的流时会触发
// java.lang.IncompatibleClassChangeError: interface not implemented 的 Error (Throwable)
// 但实测是不影响解压的,所以这里换用 let 不关闭流,确保不闪退,并且不影响解压结果
// 帮用户解压了,但游戏能不能安装就看天吧 (毕竟支持4.X的游戏也不多了)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
ZipFile(File(path)).use { unzipClosure.invoke(it) }
} else {
ZipFile(File(path)).let { unzipClosure.invoke(it) }
}
mUnzipListener.onSuccess(mDownloadEntity)
} catch (e: Exception) {
if (BuildConfig.DEBUG) throw e
@ -208,18 +218,21 @@ class XapkUnzipThread(private var mDownloadEntity: DownloadEntity,
private fun getUnzipSize(path: String): Long {
var totalSize = 0L
// 这里安卓5.0以下使用use会报错闪退
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ZipFile(File(path)).use {
for (entry in it.entries()) {
totalSize += entry.size
}
}
} else {
for (entry in ZipFile(File(path)).entries()) {
val calculateSizeClosure: (zip: ZipFile) -> Unit = { zip ->
for (entry in zip.entries()) {
totalSize += entry.size
}
}
// Kotlin 1.4.X 在安卓 4.4 以下使用 use 默认ZipFile 的流时会触发
// java.lang.IncompatibleClassChangeError: interface not implemented 的 Error (Throwable)
// 实测是不影响解压,所以这里换用 let 不关闭流,确保不闪退
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
ZipFile(File(path)).use { calculateSizeClosure.invoke(it) }
} else {
ZipFile(File(path)).let { calculateSizeClosure.invoke(it) }
}
return totalSize
}
}

View File

@ -254,6 +254,14 @@ public class DownloadManager implements DownloadStatusListener {
String downloadId = PackageInstaller.createDownloadId(gameEntity.getName());
if (SimulatorGameManager.isSimulatorGame(gameEntity)) {
path = SimulatorGameManager.getPathByType(gameEntity.getSimulatorType()) + "/" + gameEntity.getName() + "." + apkEntity.getFormat();
// 下载模拟器游戏配置文件,地址是 "模拟器游戏类型根目录/cheat/"
if (!TextUtils.isEmpty(gameEntity.getSimulatorGameConfig())) {
String configFilePath = SimulatorGameManager.getPathByType(gameEntity.getSimulatorType()) + "/cheat/" + apkEntity.getPackageName()+ ".ini";
AppExecutor.getIoExecutor().execute(() -> {
FileUtils.downloadFile(gameEntity.getSimulatorGameConfig(), configFilePath);
});
}
} else {
path = PackageInstaller.getDownloadPathWithId(downloadId, apkEntity.getFormat());
}

View File

@ -16,6 +16,7 @@ import com.gh.gamecenter.packagehelper.PackageViewModel
import com.gh.gamecenter.retrofit.EmptyResponse
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.setting.GameDownloadSettingFragment.Companion.CONCERN_GAME_SP_KEY
import com.halo.assistant.HaloApp
import com.halo.assistant.fragment.SettingsFragment
import com.lightgame.download.DownloadEntity
@ -99,7 +100,7 @@ object PackageObserver {
DownloadManager.getInstance(application).cancel(
mDownloadEntity.url, false, true) // 默认不删除安装包 mSp.getBoolean("autodelete", true)
}
if (sp.getBoolean(SettingsFragment.CONCERN_GAME_SP_KEY, true)) { //设置页面控制是否安装后自动关注
if (sp.getBoolean(CONCERN_GAME_SP_KEY, true)) { //设置页面控制是否安装后自动关注
// 安装后关注游戏
val finalDownloadEntity = mDownloadEntity
RetrofitManager.getInstance(application).sensitiveApi

View File

@ -4,26 +4,23 @@ import android.net.Uri
import com.gh.common.runOnIoThread
import com.gh.common.util.DeviceUtils
import com.gh.common.util.NetworkUtils
import com.gh.gamecenter.BuildConfig
import com.gh.common.util.debugOnly
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DataSpec
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.upstream.cache.CacheDataSource
import com.google.android.exoplayer2.upstream.cache.CacheUtil
import com.google.android.exoplayer2.util.Util
import com.google.android.exoplayer2.upstream.cache.CacheWriter
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import tv.danmaku.ijk.media.exo2.ExoSourceManager
import tv.danmaku.ijk.media.exo2.source.GSYDefaultHttpDataSource
import tv.danmaku.ijk.media.exo2.source.GSYExoHttpDataSourceFactory
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
object ExoCacheManager {
private val threads = ConcurrentHashMap<String, AtomicBoolean>()
private val writers = ConcurrentHashMap<String, CacheWriter>()
private fun getPreLength(): Long {
val totalRamSizeOfDevice = DeviceUtils.getTotalRamSizeOfDevice(HaloApp.getInstance().application)
@ -54,40 +51,45 @@ object ExoCacheManager {
val preLength = getPreLength()
if (preLength == 0L) return
runOnIoThread {
Thread.sleep(100)
threads[videoUri] = AtomicBoolean(false)
val contentLength = getContentLength(videoUri)
val cacheLength = if (contentLength >= preLength) preLength else contentLength
val dataSpec = DataSpec(Uri.parse(videoUri), 0, cacheLength, null)
val simpleCache = ExoSourceManager.getCacheSingleInstance(HaloApp.getInstance().application, null)
val dataSourceFactory = GSYExoHttpDataSourceFactory(Util.getUserAgent(HaloApp.getInstance().application,
"ExoCacheManager"), DefaultBandwidthMeter.Builder(HaloApp.getInstance().application).build(),
GSYDefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
GSYDefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, false)
val cacheDataSource = CacheDataSource(simpleCache, dataSourceFactory.createDataSource())
val specBuilder = DataSpec.Builder()
.setUri(Uri.parse(videoUri))
.setLength(cacheLength)
.build()
val dataSource: DataSource = DefaultDataSourceFactory(HaloApp.getInstance()).createDataSource()
val cacheDataSource = CacheDataSource(simpleCache, dataSource)
val cacheWriter = CacheWriter(
cacheDataSource,
specBuilder,
true,
null
) { requestLength, bytesCached, newBytesCached ->
// debugOnly {
// Utils.log("$requestLength-$bytesCached-$newBytesCached")
// }
}
writers[videoUri] = cacheWriter
try {
CacheUtil.cache(dataSpec, simpleCache, CacheUtil.DEFAULT_CACHE_KEY_FACTORY, cacheDataSource, CacheUtil.ProgressListener { requestLength, bytesCached, newBytesCached ->
if (requestLength == bytesCached) {
threads.remove(videoUri)
}
// if (BuildConfig.DEBUG) {
// Utils.log("$requestLength--$bytesCached--$newBytesCached")
// }
}, threads[videoUri])
} catch (e: Throwable) {
threads.remove(videoUri)
cacheWriter.cache()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun cancel(videoUri: String) {
threads[videoUri]?.set(true)
writers[videoUri]?.cancel()
writers.remove(videoUri)
}
fun cancelAll() {
for (entry in threads.entries) {
entry.value.set(true)
val iterator = writers.entries.iterator()
while (iterator.hasNext()) {
val entry = iterator.next()
entry.value.cancel()
iterator.remove()
}
}

View File

@ -11,7 +11,6 @@ import android.graphics.BitmapFactory
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.text.TextUtils
import android.util.Base64
import android.util.DisplayMetrics
@ -48,7 +47,6 @@ import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.github.piasy.biv.view.BigImageView
import com.github.piasy.biv.view.FrescoImageViewFactory
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.io.*
@ -60,7 +58,6 @@ import kotlin.collections.ArrayList
*
* @author 黄壮华
*
* todo BigImageView静态webp/动态webp(ImageInfoExtractor.getImageType(image))判断有问题,导致部分静态webp无法使用缩放功能
*/
class ImageViewerActivity : BaseActivity(), OnPageChangeListener {
@ -189,16 +186,21 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener {
mSavePicBtn.setOnClickListener {
checkStoragePermissionBeforeAction {
mBigImageView?.currentImageFile?.run {
adapter?.saveImageToFile(this, mFinalUrl)
ImageUtils.saveImageToFile(this, mFinalUrl)
}
}
}
mArticleDetailBtn.setOnClickListener {
val intent = ArticleDetailActivity.getIntent(this, CommunityEntity(if (!mAnswerEntity?.communityId.isNullOrEmpty()) mAnswerEntity?.communityId
?: "" else mAnswerEntity?.articleCommunityId ?: "", mAnswerEntity?.communityName
?: ""), mAnswerEntity?.id
?: "", mEntrance, "")
val intent = ArticleDetailActivity.getIntent(
this,
CommunityEntity(
if (!mAnswerEntity?.communityId.isNullOrEmpty()) mAnswerEntity?.communityId ?: "" else mAnswerEntity?.articleCommunityId ?: "",
mAnswerEntity?.communityName ?: ""),
mAnswerEntity?.id
?: "",
mEntrance,
"")
startActivity(intent)
finish()
}
@ -221,44 +223,6 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener {
mViewPager.onDestroy() // 注销EventBus
}
private fun saveImageToFile(src: File, curUrl: String?) {
var `in`: InputStream? = null
var out: OutputStream? = null
try {
val fileName: String = if (mShowBase64Image) {
MD5Utils.getUrlMD5(curUrl!!.substring(0, 50)) + ".png"
} else {
curUrl!!.substring(curUrl.lastIndexOf("/"))
}
val savePath = Environment.getExternalStorageDirectory().absolutePath + "/Pictures/ghzhushou/"
val file = File(savePath)
if (!file.exists()) {
file.mkdirs()
}
val dst = File(savePath, fileName)
if (dst.exists()) {
dst.delete()
}
`in` = FileInputStream(src)
out = FileOutputStream(dst)
val buf = ByteArray(1024)
var len: Int
while (`in`.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
Utils.toast(this@ImageViewerActivity, "图片已保存到/Pictures/ghzhushou/")
MessageShareUtils.refreshImage(this@ImageViewerActivity, dst)
} catch (e: Exception) {
Utils.log("图片保存失败:$e")
} finally {
try {
out?.close()
`in`?.close()
} catch (ignore: Exception) {
}
}
}
@SuppressLint("SetTextI18n")
override fun onPageScrolled(position: Int, positionOffset: Float,
positionOffsetPixels: Int) {
@ -609,7 +573,7 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener {
}
reportTv.setOnClickListener {
checkStoragePermissionBeforeAction {
saveImageToFile(imageView.currentImageFile, finalUrl)
ImageUtils.saveImageToFile(imageView.currentImageFile, finalUrl, mShowBase64Image)
dialog.cancel()
}
dialog.cancel()
@ -621,44 +585,6 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener {
return view
}
fun saveImageToFile(src: File, curUrl: String?) {
var `in`: InputStream? = null
var out: OutputStream? = null
try {
val fileName: String = if (mShowBase64Image) {
MD5Utils.getContentMD5(System.currentTimeMillis().toString()) + ".png"
} else {
Uri.parse(curUrl).lastPathSegment.toString()
}
val savePath = Environment.getExternalStorageDirectory().absolutePath + "/Pictures/ghzhushou/"
val file = File(savePath)
if (!file.exists()) {
file.mkdirs()
}
val dst = File(savePath, fileName)
if (dst.exists()) {
dst.delete()
}
`in` = FileInputStream(src)
out = FileOutputStream(dst)
val buf = ByteArray(1024)
var len: Int
while (`in`.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
Utils.toast(this@ImageViewerActivity, "图片已保存到/Pictures/ghzhushou/")
MessageShareUtils.refreshImage(this@ImageViewerActivity, dst)
} catch (e: Exception) {
Utils.log("图片保存失败:$e")
} finally {
try {
out?.close()
`in`?.close()
} catch (ignore: Exception) {
}
}
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
container.removeView(`object` as View)
}

View File

@ -59,7 +59,9 @@ import com.gh.common.util.NotificationHelper;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.PlatformUtils;
import com.gh.common.util.QuickLoginHelper;
import com.gh.common.util.SPUtils;
import com.gh.common.util.SentryHelper;
import com.gh.common.util.ShareUtils;
import com.gh.common.util.ToastUtils;
import com.gh.common.util.UrlFilterUtils;
@ -92,6 +94,7 @@ import com.github.piasy.biv.BigImageViewer;
import com.github.piasy.biv.loader.fresco.FrescoImageLoader;
import com.google.android.exoplayer2.upstream.cache.Cache;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.halo.assistant.HaloApp;
import com.lightgame.download.DownloadEntity;
@ -307,18 +310,15 @@ public class MainActivity extends BaseActivity {
}
});
//恢复顶部视频默认静音状态
SPUtils.setBoolean(Constants.SP_TOP_VIDEO_VOICE, true);
//恢复视频流非Wifi提醒
SPUtils.setBoolean(Constants.SP_NON_WIFI_TIPS, true);
//重置首页视频播放进度
SPUtils.setString(Constants.SP_HOME_VIDEO_PLAY_RECORD, "");
// 重新打开APP重置"显示返回任务悬浮图标"标志位
SPUtils.setBoolean(Constants.SP_SHOW_TASK_FLOAT, false);
postAttentionVideoRecord();
deleteSimulatorGame();
QuickLoginHelper.getPhoneInfo(this);
}
//上传关注视频浏览记录
@ -453,8 +453,8 @@ public class MainActivity extends BaseActivity {
if (view != null) {
view.setVisibility(View.GONE);
ExtensionsKt.removeFromParent(view);
}
mMainWrapperFragment.getWelcomeDialog();
checkDialog();
}
@ -465,7 +465,9 @@ public class MainActivity extends BaseActivity {
checkNotificationPermission();
// 检查助手更新
AppExecutor.getIoExecutor().execute(() -> {
UpdateManager.getInstance(this).checkUpdate(true, null);
UpdateManager updateManager = UpdateManager.getInstance(this);
updateManager.checkUpdate(true, null);
updateManager.setDismissCallback(() -> mMainWrapperFragment.getWelcomeDialog());
});
}
@ -554,17 +556,27 @@ public class MainActivity extends BaseActivity {
break;
case HOST_LAUNCH_SIMULATOR_GAME:
String json = getIntent().getStringExtra(EntranceUtils.KEY_GAME);
GameEntity gameEntity = GsonUtils.getGson().fromJson(json, new TypeToken<GameEntity>() {
}.getType());
DownloadEntity downloadEntity = SimulatorGameManager.findDownloadEntityByUrl(gameEntity.getApk().get(0).getUrl());
if (downloadEntity != null) {
File file = new File(downloadEntity.getPath());
if (!file.exists()) {
ToastUtils.INSTANCE.showToast("文件已被删除,无法启动");
return;
}
try {
GameEntity gameEntity = GsonUtils.getGson().fromJson(json, new TypeToken<GameEntity>() {
}.getType());
DownloadEntity downloadEntity = SimulatorGameManager.findDownloadEntityByUrl(gameEntity.getApk().get(0).getUrl());
if (downloadEntity != null) {
File file = new File(downloadEntity.getPath());
if (!file.exists()) {
ToastUtils.INSTANCE.showToast("文件已被删除,无法启动");
return;
}
SimulatorGameManager.launchSimulatorGame(downloadEntity, gameEntity);
SimulatorGameManager.launchSimulatorGame(downloadEntity, gameEntity);
}
} catch (JsonSyntaxException exception) {
exception.printStackTrace();
toast("模拟器游戏启动失败,请联系客服反馈相关信息");
SentryHelper.INSTANCE.onEvent(
"SIMULATOR_SHORTCUT_LAUNCH_ERROR",
"raw_json",
json
);
}
break;
case KEY_MARKET_DETAILS:
@ -576,7 +588,6 @@ public class MainActivity extends BaseActivity {
Utils.log(bundle);
if (bundle.getInt(EntranceUtils.KEY_POSITION) != -1) {
Utils.log("abc");
EventBus.getDefault().post(new EBSkip(MainActivity.EB_SKIP_MAIN, bundle.getInt(EntranceUtils.KEY_POSITION)));
}
}
@ -585,8 +596,6 @@ public class MainActivity extends BaseActivity {
/**
* 应用跳转
*
* @param packageName
*/
private void redirectGameDetail(String packageName) {
String filterQuery = UrlFilterUtils.getFilterQuery("package", packageName, "type", "package_redirect");
@ -749,7 +758,7 @@ public class MainActivity extends BaseActivity {
private void switchToCommunityTabAndRefresh() {
getIntent().putExtra(SWITCH_TO_COMMUNITY, false);
Log.e("Switch", "true");
EventBus.getDefault().post(new EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_ASK));
EventBus.getDefault().post(new EBSkip(MainActivity.EB_SKIP_MAIN, MainWrapperFragment.INDEX_BBS));
EventBus.getDefault().post(new EBReuse(CommunityFragment.EB_RETRY_PAGE));
}
@ -882,7 +891,7 @@ public class MainActivity extends BaseActivity {
if (info != null) {
if (EntranceUtils.HOST_COMMUNITY.equals(info.getType())) {
UserManager.getInstance().setCommunityData(new CommunityEntity(info.getLink(), info.getText()));
runOnUiThread(() -> mMainWrapperFragment.setCurrentItem(MainWrapperFragment.INDEX_ASK));
runOnUiThread(() -> mMainWrapperFragment.setCurrentItem(MainWrapperFragment.INDEX_BBS));
} else {
DirectUtils.directToSpecificPage(this,
info.getType(),

View File

@ -39,6 +39,7 @@ import com.gh.common.util.DisplayUtils;
import com.gh.common.util.EntranceUtils;
import com.gh.common.util.ExtensionsKt;
import com.gh.common.util.MtaHelper;
import com.gh.common.util.NewLogUtils;
import com.gh.common.util.ShareUtils;
import com.gh.common.view.FixLinearLayoutManager;
import com.gh.common.view.VerticalItemDecoration;
@ -667,6 +668,15 @@ public class NewsDetailActivity extends ToolBarActivity implements OnClickListen
});
}
@Override
protected void onStop() {
super.onStop();
if (mEntrance.contains("板块成员") || mEntrance.contains("论坛详情")) {
long stayTime = (System.currentTimeMillis() - startPageTime) / 1000;
NewLogUtils.INSTANCE.logActivityPause("版规说明", "jump_layout_description", stayTime, "", "", "", "");
}
}
// 关注事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(EBConcernChanged changed) {

View File

@ -0,0 +1,27 @@
package com.gh.gamecenter
import android.content.Intent
import com.gh.common.util.EntranceUtils
import com.halo.assistant.fragment.WebFragment
class SingletonWebActivity : WebActivity() {
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent?.extras?.getString(EntranceUtils.KEY_URL)
== targetFragment.arguments?.getString(EntranceUtils.KEY_URL)) {
// 同样的地址,不理会
} else {
if (targetFragment is WebFragment) {
targetFragment.arguments?.putString(EntranceUtils.KEY_URL, intent?.extras?.getString(EntranceUtils.KEY_URL))
(targetFragment as WebFragment).reload()
}
}
}
override fun provideNormalIntent(): Intent? {
return getTargetIntent(this, SingletonWebActivity::class.java, WebFragment::class.java)
}
}

View File

@ -222,7 +222,7 @@ public class SkipActivity extends BaseActivity {
} else {
location = path;
}
DirectUtils.directToVideoDetail(this, path, location,
DirectUtils.directToLegacyVideoDetail(this, path, location,
false, TextUtils.isEmpty(gameId) ? "" : gameId, ENTRANCE_BROWSER, "浏览器", TextUtils.isEmpty(referer) ? "" : referer,
TextUtils.isEmpty(type) ? "" : type, TextUtils.isEmpty(act) ? "" : act, TextUtils.isEmpty(paginationType) ? "page" : paginationType, TextUtils.isEmpty(fieldId) ? "" : fieldId,
TextUtils.isEmpty(sectionName) ? "" : sectionName);
@ -273,12 +273,17 @@ public class SkipActivity extends BaseActivity {
bundle.putString(KEY_TYPE, type);
EntranceUtils.jumpActivity(this, bundle);
break;
case EntranceUtils.HOST_VIDEO_DETAIL:
DirectUtils.directToVideoDetail(this, path, ENTRANCE_BROWSER, "");
break;
case HOST_LIBAO:
DirectUtils.directToGiftDetail(this, path, ENTRANCE_BROWSER);
break;
case HOST_USERHOME:
String position = uri.getQueryParameter("position");
DirectUtils.directToHomeActivity(this, path, TextUtils.isEmpty(position) ? -1 : Integer.parseInt(position), ENTRANCE_BROWSER, "浏览器");
String subTypeString = uri.getQueryParameter("sub_type");
DirectUtils.directToHomeActivity(this, path, subTypeString, TextUtils.isEmpty(position) ? -1 : Integer.parseInt(position), ENTRANCE_BROWSER, "浏览器");
break;
case HOST_COMMUNITY_COLUMN:
CommunityEntity community = new CommunityEntity();
@ -376,7 +381,8 @@ public class SkipActivity extends BaseActivity {
DirectUtils.directToGameRatingDetail(this, uri.getQueryParameter(EntranceUtils.KEY_GAME_ID), uri.getQueryParameter(EntranceUtils.KEY_COMMENT_ID), ENTRANCE_BROWSER);
break;
case EntranceUtils.HOST_FORUM:
DirectUtils.directToForum(this);
position = uri.getQueryParameter("position");
DirectUtils.directToForum(this, TextUtils.isEmpty(position) ? 0 : Integer.parseInt(position));
break;
case EntranceUtils.HOST_HELP_AND_FEEDBACK:
position = uri.getQueryParameter("position");

View File

@ -5,10 +5,8 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
@ -27,14 +25,12 @@ import com.gh.common.AppExecutor;
import com.gh.common.constant.Config;
import com.gh.common.constant.Constants;
import com.gh.common.dialog.PrivacyDialogFragment;
import com.gh.common.tracker.Tracker;
import com.gh.common.tracker.TrackerLogger;
import com.gh.common.util.AdHelper;
import com.gh.common.util.DataUtils;
import com.gh.common.util.DeviceTokenUtils;
import com.gh.common.util.DeviceUtils;
import com.gh.common.util.DialogUtils;
import com.gh.common.util.EmptyCallback;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.GameSubstituteRepositoryHelper;
import com.gh.common.util.GsonUtils;
import com.gh.common.util.MtaHelper;
@ -103,10 +99,13 @@ public class SplashScreenActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) {
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
mIsNewForThisVersion = mSharedPreferences.getBoolean("isNewFirstLaunchV" + PackageUtils.getVersionName(), true);
HaloApp.getInstance().isNewForThisVersion = mIsNewForThisVersion;
super.onCreate(savedInstanceState);
TrackerLogger.logAppLaunch(Tracker.INSTANCE.getLaunchId(), Tracker.INSTANCE.getSessionId());
DisplayUtils.transparentStatusBar(this);
TrackerLogger.logAppLaunch(this);
// 处理助手已经在后台运行导致的再次启动助手
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
@ -122,7 +121,9 @@ public class SplashScreenActivity extends BaseActivity {
// 判断是不是光环的新用户
if (SPUtils.getBoolean(Constants.SP_BRAND_NEW_USER, true)) {
mStartMainActivityDirectly = true;
// 引导页需用户点击 “立即体验” 按钮才进入首页,所以这里不能置为true
// https://git.ghzs.com/pm/halo-app-issues/-/issues/1422第3点
// mStartMainActivityDirectly = true;
SPUtils.setLong(Constants.SP_INITIAL_USAGE_TIME, System.currentTimeMillis());
HaloApp.getInstance().isBrandNewInstall = true;
showPrivacyDialog(guideLayout);
@ -237,6 +238,30 @@ public class SplashScreenActivity extends BaseActivity {
});
}
@SuppressLint("CheckResult")
private void getMark() {
// 安装相应包名以后不需请求接口获取返回数据
if (PackageUtils.isInstalled(HaloApp.getInstance().getApplication(), "com.enotary.cloud")) {
HaloApp.getInstance().setServerUserMark("new");
return;
}
RetrofitManager.getInstance(HaloApp.getInstance().getApplication())
.getApi()
.getMark()
.subscribe(new BiResponse<ResponseBody>() {
@Override
public void onSuccess(ResponseBody data) {
try {
JSONObject object = new JSONObject(data.string());
HaloApp.getInstance().setServerUserMark(object.getString("mark"));
} catch (Throwable e) {
e.printStackTrace();
}
}
});
}
@SuppressLint("CheckResult")
private void showPrivacyPolicy(SimpleCallback<Boolean> callback) {
RetrofitManager.getInstance(this).getApi()
@ -291,7 +316,7 @@ public class SplashScreenActivity extends BaseActivity {
private void launchMainActivity() {
HaloApp.getInstance().postInit(true);
TrackerLogger.logAppLaunchSuccessful(Tracker.INSTANCE.getLaunchId(), Tracker.INSTANCE.getSessionId());
TrackerLogger.logAppLaunchSuccessful();
getAd();
prefetchData();
@ -355,6 +380,7 @@ public class SplashScreenActivity extends BaseActivity {
deviceDialogSetting();
getFilterDetailTags();
getAuthDialog();
getMark();
getRegulationTestStatus();
UsageStatsHelper.checkAndPostUsageStats();
GameSubstituteRepositoryHelper.updateGameSubstituteRepository();

View File

@ -550,6 +550,7 @@ public class SuggestionActivity extends ToolBarActivity implements OnRequestCall
@Override
public <T> void onListClick(View view, int position, T data) {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, MEDIA_STORE_CREDENTIALS_REQUEST);
}
}, null);
@ -567,6 +568,7 @@ public class SuggestionActivity extends ToolBarActivity implements OnRequestCall
@Override
public <T> void onListClick(View view, int position, T data) {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, MEDIA_STORE_SCREENSHOT_REQUEST);
}
}, null);
@ -1052,6 +1054,11 @@ public class SuggestionActivity extends ToolBarActivity implements OnRequestCall
}
}
@Override
public void onSingleSuccess(@NotNull Map<String, String> imageUrl) {
}
@Override
public void onSuccess(@NotNull LinkedHashMap<String, String> imageUrl, @NotNull Map<String, ? extends Exception> errorMap) {
Utils.log("意见反馈:图片上传完成");
@ -1381,6 +1388,7 @@ public class SuggestionActivity extends ToolBarActivity implements OnRequestCall
List<String> picList = (List<String>) data;
if (position == mAdapter.getItemCount() - 1 && picList.size() < 5) {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, MEDIA_STORE_REQUEST);
}
}, null);
@ -1389,6 +1397,7 @@ public class SuggestionActivity extends ToolBarActivity implements OnRequestCall
List<String> picList = (List<String>) data;
if (position == mAdapter.getItemCount() - 1 && picList.size() < 5) {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, MEDIA_STORE_REQUEST);
}
}

View File

@ -1,482 +0,0 @@
//package com.gh.gamecenter;
//
//import android.annotation.SuppressLint;
//import android.app.Activity;
//import android.app.Dialog;
//import android.content.Context;
//import android.content.Intent;
//import android.content.res.Configuration;
//import android.graphics.BitmapFactory;
//import android.graphics.Color;
//import android.net.Uri;
//import android.os.Bundle;
//import android.os.Environment;
//import android.text.TextUtils;
//import android.util.Base64;
//import android.util.DisplayMetrics;
//import android.view.View;
//import android.view.ViewGroup;
//import android.view.Window;
//import android.widget.LinearLayout;
//import android.widget.RelativeLayout;
//import android.widget.TextView;
//
//import androidx.annotation.NonNull;
//import androidx.annotation.Nullable;
//import androidx.core.content.ContextCompat;
//import androidx.viewpager.widget.PagerAdapter;
//import androidx.viewpager.widget.ViewPager.OnPageChangeListener;
//
//import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
//import com.facebook.drawee.backends.pipeline.Fresco;
//import com.facebook.imagepipeline.core.ImagePipeline;
//import com.facebook.imagepipeline.request.ImageRequest;
//import com.gh.base.BaseActivity;
//import com.gh.common.AppExecutor;
//import com.gh.common.Base64ImageHolder;
//import com.gh.common.util.DisplayUtils;
//import com.gh.common.util.EntranceUtils;
//import com.gh.common.util.ImageUtils;
//import com.gh.common.util.MD5Utils;
//import com.gh.common.util.MessageShareUtils;
//import com.gh.common.util.NetworkUtils;
//import com.gh.common.util.PermissionHelper;
//import com.gh.common.util.SimpleImageLoader;
//import com.gh.common.view.Gh_RelativeLayout;
//import com.gh.common.view.Gh_ViewPager;
//import com.gh.gamecenter.entity.ImageInfoEntity;
//import com.gh.gamecenter.retrofit.Response;
//import com.gh.gamecenter.retrofit.RetrofitManager;
//import com.github.piasy.biv.view.BigImageView;
//import com.github.piasy.biv.view.FrescoImageViewFactory;
//import com.lightgame.utils.Utils;
//
//import java.io.BufferedOutputStream;
//import java.io.File;
//import java.io.FileInputStream;
//import java.io.FileOutputStream;
//import java.io.InputStream;
//import java.io.OutputStream;
//import java.util.ArrayList;
//import java.util.HashMap;
//import java.util.HashSet;
//import java.util.Locale;
//import java.util.Map;
//
//import butterknife.BindView;
//import io.reactivex.android.schedulers.AndroidSchedulers;
//import io.reactivex.schedulers.Schedulers;
//
///**
// * 查看游戏截图页面
// *
// * @author 黄壮华
// *
// * todo BigImageView静态webp/动态webp(ImageInfoExtractor.getImageType(image))判断有问题,导致部分静态webp无法使用缩放功能
// */
//public class ViewImageActivity extends BaseActivity implements OnPageChangeListener {
//
// public static final int REQUEST_FOR_VIEWED_IMAGE = 921;
// public static final String VIEWED_IMAGE = "viewed_image";
//
// @BindView(R.id.image_detail_page)
// Gh_ViewPager mViewPager;
// @BindView(R.id.image_detail_progress)
// TextView mProgressHint;
// @BindView(R.id.image_mask)
// View mIndicatorMask;
// @BindView(R.id.image_indicator_tv)
// TextView mIndicatorTv;
//
// private ViewImageAdapter adapter;
//
// private ImagePipeline mImagePipeline;
//
// private boolean mShowBase64Image = false;
//
// private static final String KEY_BASE64 = "base64";
//
// private static final String KEY_URLS = "urls";
// private static final String KEY_CURRENT = "current";
//
// private ArrayList<String> urls;
// private HashSet<Integer> mViewedSet; // 让调用者知道该图片是否被看过了
// private Map<String, ImageInfoEntity> mImageInfoMap;
//
// private int mLimitWidth;
//
// private boolean isOrientation;
//
// public static Intent getBase64ViewImageIntent(Context context, boolean showSingleBase64Image) {
// Intent checkIntent = new Intent(context, ViewImageActivity.class);
// checkIntent.putExtra(KEY_BASE64, showSingleBase64Image);
// return checkIntent;
// }
//
// public static Intent getViewImageIntent(Context context, ArrayList<String> list, int position, String entrance) {
// Intent checkIntent = new Intent(context, ViewImageActivity.class);
// checkIntent.putExtra(KEY_URLS, list);
// checkIntent.putExtra(KEY_CURRENT, position);
// checkIntent.putExtra(EntranceUtils.KEY_ENTRANCE, entrance);
// return checkIntent;
// }
//
// @Override
// protected int getLayoutId() {
// return R.layout.activity_viewimage;
// }
//
// @Override
// protected void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// mViewedSet = new HashSet<>();
// mImageInfoMap = new HashMap<>();
// // init data
// int current = 0;
// Bundle extras = getIntent().getExtras();
// if (extras != null) {
// if (extras.getBoolean(KEY_BASE64)) {
// mShowBase64Image = true;
// urls = new ArrayList<>();
// urls.add(Base64ImageHolder.INSTANCE.getImage());
// } else {
// urls = extras.getStringArrayList(KEY_URLS);
// current = extras.getInt(KEY_CURRENT, 0);
// }
// }
//
// if (savedInstanceState != null) {
// current = savedInstanceState.getInt(EntranceUtils.KEY_CURRENTITEM, 0);
// isOrientation = savedInstanceState.getBoolean("isOrientation");
// }
//
// if (urls.size() > 1) mIndicatorMask.setVisibility(View.VISIBLE);
// mIndicatorTv.setText(String.format("%d/%d", current + 1, urls.size()));
//
// mImagePipeline = Fresco.getImagePipeline();
//
// // init slide
// DisplayMetrics outMetrics = new DisplayMetrics();
// getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
// int widthPixels = outMetrics.widthPixels;
// if (NetworkUtils.isWifiOr4GConnected(this)) {
// mLimitWidth = widthPixels * 2;
// } else {
// mLimitWidth = widthPixels;
// }
// // init viewPage
// adapter = new ViewImageAdapter();
// mViewPager.setAdapter(adapter);
// mViewPager.setCurrentItem(current);
// mViewPager.addOnPageChangeListener(this);
//
// if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
// isOrientation = true; // 横屏
// } else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
// isOrientation = false;// 竖屏
// }
//
// mProgressHint.setOnClickListener(v -> {
// int position = mViewPager.getCurrentItem();
// Object object = mViewPager.findViewWithTag(position);
// if (object != null) {
// mProgressHint.setWidth(mProgressHint.getWidth());
// RelativeLayout view = (RelativeLayout) object;
// final BigImageView imageView = view.findViewById(R.id.viewimage_iv_show);
// String url = urls.get(position);
// imageView.showImage(Uri.parse(url));
// imageView.setImageLoaderCallback(new SimpleImageLoader() {
// @Override
// public void onProgress(int progress) {
// if (position == mViewPager.getCurrentItem()) { // 防止下载过程中切换图片
// if (progress < 100) {
// mProgressHint.setText((progress + "%"));
// } else {
// mProgressHint.setText("已完成");
// mBaseHandler.postDelayed(() -> {
// if (position == mViewPager.getCurrentItem()) { // 防止等待过程中切换图片
// mProgressHint.setVisibility(View.GONE);
// }
// }, 500);
// }
// }
// }
// });
// }
// });
//
// DisplayUtils.transparentStatusAndNavigation(this);
// }
//
// @Override
// protected void onSaveInstanceState(Bundle outState) {
// super.onSaveInstanceState(outState);
// outState.putInt(EntranceUtils.KEY_CURRENTITEM, mViewPager.getCurrentItem());
// outState.putBoolean("isOrientation", isOrientation);
// }
//
// @Override
// protected void onDestroy() {
// super.onDestroy();
// if (mShowBase64Image) {
// urls.clear();
// Base64ImageHolder.INSTANCE.setImage("");
// }
// mViewPager.onDestroy(); // 注销EventBus
// }
//
// @Override
// public void onPageScrolled(int position, float positionOffset,
// int positionOffsetPixels) {
// if (positionOffset != 0) {
// mProgressHint.setVisibility(View.GONE);
// } else {
// String url = urls.get(position);
// ImageInfoEntity imageInfoEntity = mImageInfoMap.get(url);
// if (imageInfoEntity != null && imageInfoEntity.getFileSize() != null &&
// !mImagePipeline.isInBitmapMemoryCache(ImageRequest.fromUri(url)) &&
// !mImagePipeline.isInDiskCacheSync(ImageRequest.fromUri(url))) {
// String size = String.format(Locale.CHINA, "%.1fM",
// Integer.valueOf(imageInfoEntity.getFileSize().getValue()) / 1024F / 1024F);
// mProgressHint.setVisibility(View.VISIBLE);
// mProgressHint.setText(("查看原图(" + size + ")"));
// ViewGroup.LayoutParams layoutParams = mProgressHint.getLayoutParams();
// layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
// mProgressHint.setLayoutParams(layoutParams);
// }
// }
//
// mViewedSet.add(position);
// setResult(Activity.RESULT_OK, new Intent().putExtra(VIEWED_IMAGE, mViewedSet));
// }
//
// @Override
// public void onPageSelected(int position) {
// Gh_RelativeLayout ghRelativeLayout;
// for (int i = 0; i < mViewPager.getChildCount(); i++) {
// if (mViewPager.getChildAt(i).getTag() != null) {
// ghRelativeLayout = (Gh_RelativeLayout) mViewPager.getChildAt(i);
// if (ghRelativeLayout == null) {
// return;
// }
//
// BigImageView imageView = ghRelativeLayout.findViewById(R.id.viewimage_iv_show);
// SubsamplingScaleImageView ssiv = imageView.getSSIV();
// if (ssiv != null) ssiv.resetScaleAndCenter();
//
// }
// }
// mIndicatorTv.setText(String.format("%d/%d", position + 1, urls.size()));
// }
//
// @Override
// public void onPageScrollStateChanged(int newState) {
//
// }
//
// private void loadImageInfo(int position, int width) {
// String url = urls.get(position);
// RetrofitManager.getInstance(this)
// .getApi().getImageInfo(url + "?x-oss-process=image/info")
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(new Response<ImageInfoEntity>() {
// @Override
// public void onResponse(@Nullable ImageInfoEntity response) {
// if (response != null && response.getImageWidth() != null &&
// Integer.valueOf(response.getImageWidth().getValue()) > width) {
// mImageInfoMap.put(url, response);
// if (position == mViewPager.getCurrentItem()) {
// onPageScrolled(position, 0, 0); // 刷新下载原图提示按钮
// }
// }
// }
// });
// }
//
// private void loadImage(String url, final BigImageView imageView) {
// if (TextUtils.isEmpty(url)) return;
//
// if (url.startsWith("data:image/png;base64")) {
// AppExecutor.getIoExecutor().execute(() -> {
// String base64String = url.replace("data:image/png;base64", "");
// try {
// File imageFile = new File(getCacheDir().getAbsolutePath() + File.separator + System.currentTimeMillis() + ".png");
//
// byte[] decodedString = Base64.decode(base64String, Base64.DEFAULT);
//
// BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(imageFile));
// bos.write(decodedString);
// bos.flush();
// bos.close();
//
// AppExecutor.getUiExecutor().execute(() -> {
// imageView.setImageViewFactory(new FrescoImageViewFactory());
// imageView.showImage(Uri.fromFile(imageFile));
// });
// } catch (Exception e) {
// e.printStackTrace();
// }
// });
// } else {
// // 添加GIF支持
// imageView.setImageViewFactory(new FrescoImageViewFactory());
// imageView.showImage(Uri.parse(url));
// }
// }
//
// private class ViewImageAdapter extends PagerAdapter {
//
// @Override
// public int getCount() {
// if (urls == null) {
// return 0;
// }
// return urls.size();
// }
//
// @SuppressLint("MissingPermission")
// @NonNull
// @Override
// public Object instantiateItem(@NonNull ViewGroup container, int position) {
// String url = urls.get(position);
// ImageRequest imageRequest = ImageRequest.fromUri(url);
// boolean isInMemoryCache = mImagePipeline.isInBitmapMemoryCache(imageRequest);
// boolean isInDiskCache = imageRequest != null && mImagePipeline.isInDiskCacheSync(imageRequest);
//
// Gh_RelativeLayout view = (Gh_RelativeLayout) View.inflate(container.getContext(), R.layout.viewimage_normal_item, null);
// BigImageView imageView = view.findViewById(R.id.viewimage_iv_show);
//
// if (!isInMemoryCache
// && !isInDiskCache
// && !NetworkUtils.isWifiOr4GConnected(ViewImageActivity.this)
// && !url.contains(".gif")) {
// url = ImageUtils.getTransformLimitUrl(url, mLimitWidth, getApplicationContext());
// }
//
// String finalUrl = url;
// imageView.setImageLoaderCallback(new SimpleImageLoader() {
// @Override
// public void onSuccess(File image) {
// if (!finalUrl.equals(urls.get(position))) {
// BitmapFactory.Options options = new BitmapFactory.Options();
// options.inJustDecodeBounds = true;
// BitmapFactory.decodeFile(new File(image.getPath()).getAbsolutePath(), options);
// loadImageInfo(position, options.outWidth); // 加载图片参数,目的是用户显示原文按钮
// }
// SubsamplingScaleImageView ssiv = imageView.getSSIV();
// if (ssiv != null) {
// ssiv.setMaxScale(10f); // 这个缩放倍数最好很具宽高自动调节
// ssiv.setOnImageEventListener(new SubsamplingScaleImageView.DefaultOnImageEventListener() {
// @Override
// public void onReady() {
// ssiv.resetScaleAndCenter();
// }
// });
// ssiv.setOnClickListener(v -> finish());
//
// }
// }
// });
// loadImage(url, imageView);
//
// //长按
// imageView.setOnLongClickListener(v -> {
// final Dialog dialog = new Dialog(ViewImageActivity.this);
//
// LinearLayout container1 = new LinearLayout(ViewImageActivity.this);
// container1.setOrientation(LinearLayout.VERTICAL);
// container1.setBackgroundColor(Color.WHITE);
//
// final TextView reportTv = new TextView(ViewImageActivity.this);
// reportTv.setPadding(
// DisplayUtils.dip2px(ViewImageActivity.this, 20),
// DisplayUtils.dip2px(ViewImageActivity.this, 12),
// 0,
// DisplayUtils.dip2px(ViewImageActivity.this, 12));
// reportTv.setText(R.string.save_pic);
// reportTv.setTextSize(17);
// reportTv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.title));
// reportTv.setBackgroundResource(R.drawable.textview_white_style);
// int widthPixels = getResources().getDisplayMetrics().widthPixels;
// reportTv.setLayoutParams(new LinearLayout.LayoutParams((widthPixels * 9) / 10,
// LinearLayout.LayoutParams.WRAP_CONTENT));
// container1.addView(reportTv);
//
// dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
// dialog.setContentView(container1);
//
// if (!isFinishing()) {
// dialog.show();
// }
//
// reportTv.setOnClickListener(v1 -> {
// PermissionHelper.checkStoragePermissionBeforeAction(ViewImageActivity.this, () -> {
// saveImageToFile(imageView.getCurrentImageFile(), finalUrl);
// dialog.cancel();
// });
// dialog.cancel();
// });
//
// return false;
// });
//
// view.setTag(position);
// container.addView(view);
// return view;
// }
//
// private void saveImageToFile(File src, String curUrl) {
// InputStream in = null;
// OutputStream out = null;
// try {
// String fileName;
// if (mShowBase64Image) {
// fileName = MD5Utils.getUrlMD5(curUrl.substring(0, 50)) + ".png";
// } else {
// fileName = curUrl.substring(curUrl.lastIndexOf("/"));
// }
// String savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pictures/ghzhushou/";
// File file = new File(savePath);
// if (!file.exists()) {
// file.mkdirs();
// }
//
// File dst = new File(savePath, fileName);
// if (dst.exists()) {
// dst.delete();
// }
//
// in = new FileInputStream(src);
// out = new FileOutputStream(dst);
// byte[] buf = new byte[1024];
// int len;
// while ((len = in.read(buf)) > 0) {
// out.write(buf, 0, len);
// }
//
// Utils.toast(ViewImageActivity.this, "图片已保存到/Pictures/ghzhushou/");
// MessageShareUtils.refreshImage(ViewImageActivity.this, dst);
// } catch (Exception e) {
// Utils.log("图片保存失败:" + e.toString());
// } finally {
// try {
// if (out != null) out.close();
// if (in != null) in.close();
// } catch (Exception ignore) {
// }
// }
// }
//
//
// @Override
// public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
// container.removeView((View) object);
// }
//
// @Override
// public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
// return view == object;
// }
// }
//}

View File

@ -2,7 +2,6 @@ package com.gh.gamecenter;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.KeyEvent;
@ -12,16 +11,11 @@ import androidx.annotation.NonNull;
import com.gh.common.constant.Constants;
import com.gh.common.util.EntranceUtils;
import com.gh.common.util.LogUtils;
import com.gh.common.util.IntentUtils;
import com.gh.gamecenter.entity.ConcernEntity;
import com.gh.gamecenter.entity.NewsEntity;
import com.gh.gamecenter.entity.ToolBoxEntity;
import com.halo.assistant.fragment.WebFragment;
import com.lightgame.utils.Utils;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Locale;
import static com.halo.assistant.fragment.WebFragment.KEY_GAME_NAME;
import static com.halo.assistant.fragment.WebFragment.KEY_REQUIRE_BACK_CONFIRMATION;
@ -51,7 +45,6 @@ public class WebActivity extends NormalActivity {
} else {
super.onCreate(savedInstanceState);
}
}
@Override
@ -65,7 +58,7 @@ public class WebActivity extends NormalActivity {
bundle.putBoolean(EntranceUtils.KEY_WEB_SHARE, showWebShare);
bundle.putString(EntranceUtils.KEY_URL, url);
bundle.putBoolean(WebFragment.KEY_LEAVE_WEB_PAGE_TO_HANDLE_TITLE, true);
return getTargetIntent(context, WebActivity.class, WebFragment.class, bundle);
return IntentUtils.getWebTargetIntent(context, bundle, url);
}
@NonNull
@ -77,7 +70,7 @@ public class WebActivity extends NormalActivity {
}
@NonNull
public static Intent getWebIntent(Context context) {
public static Intent getUserRegulationWebIntent(Context context) {
Bundle bundle = new Bundle();
bundle.putString(EntranceUtils.KEY_GAMENAME, context.getString(R.string.disclaimer_title));
bundle.putString(EntranceUtils.KEY_URL, context.getString(R.string.disclaimer_url));
@ -169,23 +162,6 @@ public class WebActivity extends NormalActivity {
return getTargetIntent(context, WebActivity.class, WebFragment.class, bundle);
}
@SuppressWarnings("ConstantConditions")
public static Intent getBadgeCenterIntent(Context context, String userId, String name, String icon) {
String url;
if (("internal").equals(BuildConfig.FLAVOR)) {
url = Constants.BADGE_ADDRESS_DEV;
} else {
url = Constants.BADGE_ADDRESS;
}
url = String.format(Locale.CHINA, "%s?user_id=%s&name=%s&icon=%s&timestamp=%d", url, userId, name, URLEncoder.encode(icon), Math.round((new Date().getTime() / 1000) / 1000));
Bundle bundle = new Bundle();
bundle.putString(EntranceUtils.KEY_URL, url);
bundle.putBoolean(WebFragment.KEY_LEAVE_WEB_PAGE_TO_HANDLE_TITLE, true);
return getTargetIntent(context, WebActivity.class, WebFragment.class, bundle);
}
@NonNull
public static Intent getIntentByNews(Context context, ConcernEntity concernEntity, String entrance) {
Bundle bundle = new Bundle();
@ -206,14 +182,13 @@ public class WebActivity extends NormalActivity {
return getTargetIntent(context, WebActivity.class, WebFragment.class, bundle);
}
@NonNull
public static Intent getIntent(Context context, String url, boolean autoCompletionTitle) {
Bundle bundle = new Bundle();
bundle.putString(EntranceUtils.KEY_URL, url);
bundle.putBoolean(WebFragment.KEY_COMPLETION_TITLE, autoCompletionTitle);
bundle.putBoolean(WebFragment.KEY_LEAVE_WEB_PAGE_TO_HANDLE_TITLE, false);
return getTargetIntent(context, WebActivity.class, WebFragment.class, bundle);
return IntentUtils.getWebTargetIntent(context, bundle, url);
}
@NonNull
@ -224,7 +199,7 @@ public class WebActivity extends NormalActivity {
bundle.putBoolean(WebFragment.KEY_COMPLETION_TITLE, autoCompletionTitle);
bundle.putBoolean(WebFragment.KEY_LEAVE_WEB_PAGE_TO_HANDLE_TITLE, false);
bundle.putBoolean(WebFragment.KEY_OPEN_NATIVE_PAGE, isOpenNativePage);
return getTargetIntent(context, WebActivity.class, WebFragment.class, bundle);
return IntentUtils.getWebTargetIntent(context, bundle, url);
}
@NonNull
@ -237,20 +212,7 @@ public class WebActivity extends NormalActivity {
bundle.putBoolean(WebFragment.KEY_OPEN_NATIVE_PAGE, false);
bundle.putBoolean(WebFragment.KEY_LEAVE_WEB_PAGE_TO_HANDLE_BACK_PRESSED, isWebPageHandleBackPressed);
bundle.putInt(WebFragment.KEY_QA_TYPE, qaType);
return getTargetIntent(context, WebActivity.class, WebFragment.class, bundle);
}
@NonNull
public static Intent getIntentByUrl(Context context, String url) {
return getIntentByUrl(context, url, false);
}
@NonNull
public static Intent getIntentByUrl(Context context, String url, boolean leaveWebPageHandleTitle) {
Bundle bundle = new Bundle();
bundle.putString(EntranceUtils.KEY_URL, url);
bundle.putBoolean(WebFragment.KEY_LEAVE_WEB_PAGE_TO_HANDLE_TITLE, leaveWebPageHandleTitle);
return getTargetIntent(context, WebActivity.class, WebFragment.class, bundle);
return IntentUtils.getWebTargetIntent(context, bundle, url);
}
public static Intent getIntentForWebGame(Context context, String url, String gameName, boolean interveneBackpress, String closeButton) {
@ -263,7 +225,7 @@ public class WebActivity extends NormalActivity {
bundle.putBoolean(KEY_REQUIRE_BACK_CONFIRMATION, true);
bundle.putString(WebFragment.KEY_BACK_CONFIRMATION_CONTENT, "退出后将不保存当前游戏进度,确定退出吗?");
}
return getTargetIntent(context, WebActivity.class, WebFragment.class, bundle);
return IntentUtils.getWebTargetIntent(context, bundle, url);
}
@Override

View File

@ -18,6 +18,7 @@ import com.gh.common.util.EnergyTaskHelper;
import com.gh.common.util.ImageUtils;
import com.gh.common.util.IntegralLogHelper;
import com.gh.common.util.LogUtils;
import com.gh.common.util.NewLogUtils;
import com.gh.common.util.ShareUtils;
import com.gh.gamecenter.eventbus.EBShare;
import com.lightgame.utils.Utils;
@ -233,6 +234,11 @@ public class WeiBoShareActivity extends Activity implements WbShareCallback {
if (ShareUtils.shareEntrance == ShareUtils.ShareEntrance.inviteFriends) {
IntegralLogHelper.INSTANCE.logInviteResult("成功", "微博");
}
if (ShareUtils.shareEntrance == ShareUtils.ShareEntrance.askNormal ||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.communityArticle ||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.video) {
NewLogUtils.logShareResult(true);
}
} else {
IntegralLogHelper.INSTANCE.logInviteResult("成功", "微博");
}
@ -248,6 +254,11 @@ public class WeiBoShareActivity extends Activity implements WbShareCallback {
if (ShareUtils.shareEntrance == ShareUtils.ShareEntrance.inviteFriends) {
IntegralLogHelper.INSTANCE.logInviteResult("取消", "微博");
}
if (ShareUtils.shareEntrance == ShareUtils.ShareEntrance.askNormal ||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.communityArticle ||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.video) {
NewLogUtils.logShareResult(false);
}
} else {
IntegralLogHelper.INSTANCE.logInviteResult("取消", "微博");
}
@ -263,6 +274,11 @@ public class WeiBoShareActivity extends Activity implements WbShareCallback {
if (ShareUtils.shareEntrance == ShareUtils.ShareEntrance.inviteFriends) {
IntegralLogHelper.INSTANCE.logInviteResult("失败", "微博");
}
if (ShareUtils.shareEntrance == ShareUtils.ShareEntrance.askNormal ||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.communityArticle ||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.video) {
NewLogUtils.logShareResult(false);
}
} else {
IntegralLogHelper.INSTANCE.logInviteResult("失败", "微博");
}

View File

@ -326,7 +326,8 @@ public class DetailViewHolder {
}
if (mViewHolder.mDownloadPb.getDownloadType() != DownloadProgressBar.DownloadType.INSTALL_NORMAL
&& mViewHolder.mDownloadPb.getDownloadType() != DownloadProgressBar.DownloadType.INSTALL_PLUGIN) {
&& mViewHolder.mDownloadPb.getDownloadType() != DownloadProgressBar.DownloadType.INSTALL_PLUGIN
&& mViewHolder.mDownloadPb.getDownloadType() != DownloadProgressBar.DownloadType.LAUNCH_OR_OPEN) {
EventBus.getDefault().post(new EBScroll(Constants.EB_GAME_DETAIL, mGameEntity.getId()));
}
}

View File

@ -7,7 +7,8 @@ import com.gh.common.util.dip2px
import com.gh.gamecenter.databinding.GameHeadItemBinding
import com.gh.gamecenter.entity.SubjectEntity
class GameHeadViewHolder(var binding: GameHeadItemBinding) : BaseRecyclerViewHolder<Any>(binding.root) {
class GameHeadViewHolder(var binding: GameHeadItemBinding) :
BaseRecyclerViewHolder<Any>(binding.root) {
fun bindHead(subject: SubjectEntity) {
val headContainerLp = binding.headContainer.layoutParams
@ -24,10 +25,19 @@ class GameHeadViewHolder(var binding: GameHeadItemBinding) : BaseRecyclerViewHol
binding.subject = subject
binding.headPb.visibility = View.GONE
val text = if ("change" == subject.home) "换一批" else "全部 >"
val text = if ("change" == subject.home) {
"换一批"
} else {
when (subject.home) {
"more" -> "更多 >"
"hide" -> ""
else -> "全部 >"
}
}
binding.headMore.text = text
if (subject.indexRightTop != null && subject.indexRightTop != "none") {
// 开测表用到的
binding.headMore.visibility = View.VISIBLE
if (subject.indexRightTop == "all") {
binding.headMore.text = "全部 >"
@ -40,6 +50,8 @@ class GameHeadViewHolder(var binding: GameHeadItemBinding) : BaseRecyclerViewHol
&& subject.type != "column_collection"
&& subject.type != "gallery_slide") {
binding.headMore.visibility = View.GONE
} else if (subject.home == "hide"){
binding.headMore.visibility = View.GONE
} else {
binding.headMore.visibility = View.VISIBLE
}

View File

@ -1,6 +1,7 @@
package com.gh.gamecenter.adapter.viewholder;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@ -24,6 +25,10 @@ public class GameViewHolder extends BaseRecyclerViewHolder {
public LinearLayout gameLabelList;
public LinearLayout gameInfo;
public ProgressBar gameProgressbar;
@Nullable
public View recommendContainer;
public TextView recommendTv;
public ImageView recommendIv;
public TextView gameDownloadSpeed;
public TextView gameDownloadPercentage;
public TextView gameServerType;
@ -44,6 +49,9 @@ public class GameViewHolder extends BaseRecyclerViewHolder {
gameDes = binding.gameDes;
gameDownloadSpeed = binding.downloadSpeed;
gameRating = binding.gameRating;
recommendContainer = binding.recommendContainer;
recommendTv = binding.recommendTv;
recommendIv = binding.recommendIv;
}
public void initServerType(GameEntity gameEntity) {

View File

@ -214,9 +214,13 @@ class AmwayFragment : LazyListFragment<AmwayListItemData, AmwayViewModel>() {
MtaHelper.onEventWithTime("安利墙", mElapsedHelper.elapsedTime, "浏览")
}
override fun onFragmentResume() {
override fun onResume() {
if (isEverPause) mAdapter?.notifyDataSetChanged()
super.onResume()
}
override fun onFragmentResume() {
super.onFragmentResume()
DownloadManager.getInstance(context).addObserver(dataWatcher)
mElapsedHelper.resetCounting()

View File

@ -30,7 +30,15 @@ class AmwaySuccessFragment : NormalFragment() {
setNavigationTitle("安利墙")
checkCommentBtn.setOnClickListener {
GameDetailActivity.startGameDetailCommentActivity(requireContext(), mGameEntity, "安利墙")
if (mGameEntity != null) {
GameDetailActivity.startGameDetailCommentActivity(
requireContext(),
mGameEntity,
"安利墙"
)
} else {
requireActivity().finish()
}
}
checkAmwayBtn.setOnClickListener {

View File

@ -226,7 +226,9 @@ public abstract class LazyListFragment<T, VM extends BaseListViewModel /* 该泛
mListLoading.setVisibility(mListRefresh == null || !mListRefresh.isRefreshing() ? View.VISIBLE : View.GONE);
if (mReuseNoData != null) mReuseNoData.setVisibility(View.GONE);
mListRv.setVisibility(View.GONE);
if (mListRv != null) {
mListRv.setVisibility(View.GONE);
}
mBaseHandler.postDelayed(() -> {
mListViewModel.load(LoadType.REFRESH);
}, 500);

View File

@ -145,6 +145,7 @@ public abstract class ListFragment<T, VM extends BaseListViewModel /* 该泛型
mListRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (!shouldLoadMore()) return;
RecyclerView.LayoutManager layoutManager = mListRv.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
if (((LinearLayoutManager) layoutManager).findLastVisibleItemPosition() == provideListAdapter().getItemCount() - 1
@ -282,4 +283,8 @@ public abstract class ListFragment<T, VM extends BaseListViewModel /* 该泛型
protected void hideRefreshingLayout() {
if (mListRefresh != null) mListRefresh.setRefreshing(false);
}
protected boolean shouldLoadMore() {
return true;
}
}

View File

@ -137,6 +137,10 @@ public abstract class ListViewModel<LD /*ListData*/, ID /*ItemData*/> extends Ba
loadStatusControl(response.size());
}
public MutableLiveData<List<LD>> getListLiveData(){
return mListLiveData;
}
private void handleFailure(Exception exception) {
if (exception instanceof HttpException && ((HttpException) exception).code() == 404) {
loadStatusControl(0);

View File

@ -33,6 +33,9 @@ class CatalogFragment : LazyFragment() {
mViewModel = viewModelProviderFromParent(CatalogViewModel.Factory(mCatalogId, mCatalogTitle), mCatalogId)
mViewModel?.validEntranceName = if (mEntrance.contains("首页")) "首页" else "板块"
if (arguments?.getBoolean(EntranceUtils.KEY_IS_HOME) == true) {
mViewModel?.validEntranceName = "首页Tab栏"
}
mViewModel?.logAppearance()
super.onFragmentFirstVisible()

View File

@ -8,10 +8,7 @@ import butterknife.BindView
import com.ethanhua.skeleton.Skeleton
import com.gh.common.constant.Constants
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.DialogUtils
import com.gh.common.util.EntranceUtils
import com.gh.common.util.observeNonNull
import com.gh.common.util.viewModelProvider
import com.gh.common.util.*
import com.gh.common.view.ConfigFilterView
import com.gh.common.xapk.XapkInstaller
import com.gh.common.xapk.XapkUnzipStatus
@ -139,7 +136,7 @@ class NewCategoryListFragment : ListFragment<GameEntity, NewCategoryListViewMode
for (index in 0 until categoryList.size) {
if (categoryList[index].name == mViewModel.selectedCategory.name) {
tagsRecyclerView.postDelayed({
tagsRecyclerView.smoothScrollToPosition(index)
tryCatchInRelease { tagsRecyclerView.smoothScrollToPosition(index) }
}, 200)
break
}

View File

@ -3,6 +3,7 @@ package com.gh.gamecenter.category2
import android.graphics.Color
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
@ -39,14 +40,15 @@ class CategoryV2Fragment : LazyFragment() {
mCategoryTitle = arguments?.getString(EntranceUtils.KEY_CATEGORY_TITLE) ?: ""
mViewModel = viewModelProviderFromParent(CategoryV2ViewModel.Factory(mCategoryId, mCategoryTitle), mCategoryId)
// 除了这里以外,下面还有一个判断是否为首页 tab 栏的赋值
mViewModel?.entrance = if (mEntrance.contains("首页")) "首页" else "板块"
if (arguments?.getBoolean(EntranceUtils.KEY_IS_HOME) == true) {
mHomeViewModel = viewModelProviderFromParent()
mViewModel?.entrance = "首页Tab栏"
}
mViewModel?.logAppearance()
super.onFragmentFirstVisible()
if (arguments?.getBoolean(EntranceUtils.KEY_IS_HOME) == true) {
mHomeViewModel = viewModelProviderFromParent()
}
}
override fun onRealLayoutInflated(inflatedView: View) {
@ -63,6 +65,21 @@ class CategoryV2Fragment : LazyFragment() {
drawerLayout.setScrimColor(R.color.black_alpha_30.toColor())
// 关闭手势滑动
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
override fun onDrawerStateChanged(newState: Int) {
}
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
}
override fun onDrawerClosed(drawerView: View) {
showGuide()
}
override fun onDrawerOpened(drawerView: View) {
}
})
directoryContainer.layoutParams.width = width
directoryRv.layoutParams.width = width
@ -131,6 +148,33 @@ class CategoryV2Fragment : LazyFragment() {
})
}
fun removeGuide() {
mBinding?.run {
guideContainer.visibility = View.GONE
SPUtils.setBoolean(Constants.SP_SHOW_CATEGORY_GUIDE, true)
}
}
private fun showGuide() {
mBinding?.run {
val isShow = SPUtils.getBoolean(Constants.SP_SHOW_CATEGORY_GUIDE)
if (isShow) return
guideContainer.layoutParams = (guideContainer.layoutParams as ViewGroup.MarginLayoutParams).apply {
val screenWidth = resources.displayMetrics.widthPixels
leftMargin = screenWidth * 66F.dip2px() / 360F.dip2px()
}
guideContainer.visibility = View.VISIBLE
postDelayedRunnable({
tryCatchInRelease {
guideContainer.visibility = View.GONE
SPUtils.setBoolean(Constants.SP_SHOW_CATEGORY_GUIDE, true)
}
}, 3000)
}
}
override fun onMenuItemClick(menuItem: MenuItem?) {
menuItem?.run {
if (itemId == R.id.menu_search) {
@ -222,7 +266,7 @@ class CategoryV2Fragment : LazyFragment() {
if (SPUtils.getBoolean(Constants.SP_FIRST_ENTER_CATEGORY_V2, true)) {
SPUtils.setBoolean(Constants.SP_FIRST_ENTER_CATEGORY_V2, false)
mBinding?.drawerLayout?.postDelayed({
openDrawer()
tryCatchInRelease { openDrawer() }
}, 500L)
}
}
@ -253,6 +297,8 @@ class CategoryV2Fragment : LazyFragment() {
.commitAllowingStateLoss()
} else {
if (position == 0) {
removeGuide()
mSpecialCatalogFragment = childFragmentManager
.findFragmentByTag(SpecialCatalogFragment::class.java.simpleName)
as? SpecialCatalogFragment ?: SpecialCatalogFragment()
@ -307,6 +353,8 @@ class CategoryV2Fragment : LazyFragment() {
}
}
}
} else if (sidebar.name == "全部" && selectedCategoryList.isNotEmpty()) {
i = selectedCategoryList[0].primaryIndex
}
}
}

View File

@ -103,7 +103,6 @@ class CategoryV2ListAdapter(context: Context,
gameName.run {
textSize = 13F
typeface = Typeface.DEFAULT
}
gameDescContainer.layoutParams = (gameDescContainer.layoutParams as ViewGroup.MarginLayoutParams).apply {

View File

@ -104,14 +104,22 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
logClickReset("游戏列表")
resetDirectoryList()
}
resetSortSize()
changeCategoryTab()
openDirectoryLayout()
// openDirectoryLayout()
}
}
private fun resetSortSize() {
mBinding?.filterContainer?.resetSortSize()
mListViewModel?.sortSize = SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小")
}
private fun initFilterView() {
mBinding?.filterContainer?.run {
visibility = View.VISIBLE
setOnConfigSetupListener(object : CategoryFilterView.OnCategoryFilterSetupListener {
override fun onSetupSortSize(sortSize: SubjectSettingEntity.Size) {
mListViewModel?.updateSortConfig(sortSize = sortSize)
@ -125,6 +133,21 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
openDirectoryLayout()
}
})
setOnFilterClickListener(object : CategoryFilterView.OnFilterClickListener {
override fun onCategoryClick() {
removeGuide()
}
override fun onTypeClick() {
removeGuide()
}
override fun onSizeClick() {
removeGuide()
}
})
}
}
@ -222,6 +245,10 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
}
}
private fun removeGuide() {
(parentFragment as? CategoryV2Fragment)?.removeGuide()
}
override fun onResume() {
if (isEverPause && mAdapter != null) mAdapter?.notifyDataSetChanged()
super.onResume()

View File

@ -16,15 +16,15 @@ import com.gh.gamecenter.baselist.ListAdapter;
import com.gh.gamecenter.baselist.LoadType;
import com.gh.gamecenter.databinding.CommunityAnswerItemBinding;
import com.gh.gamecenter.qa.answer.CommunityAnswerItemViewHolder;
import com.gh.gamecenter.qa.answer.detail.AnswerDetailActivity;
import com.gh.gamecenter.qa.entity.AnswerEntity;
import com.gh.gamecenter.qa.entity.Questions;
import com.gh.gamecenter.qa.questions.detail.QuestionsDetailActivity;
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity;
import com.lightgame.utils.Utils;
import org.jetbrains.annotations.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import kotlin.Pair;
/**
@ -99,7 +99,8 @@ public class AnswerAdapter extends ListAdapter<AnswerEntity> implements ISyncAda
holder.itemView.setOnClickListener(v -> {
if (entity.getActive()) {
mContext.startActivity(AnswerDetailActivity.getIntent(mContext, entity.getId(), mEntrance, path));
// mContext.startActivity(AnswerDetailActivity.getIntent(mContext, entity.getId(), mEntrance, path));
mContext.startActivity(NewQuestionDetailActivity.getCommentIntent(mContext, entity.getQuestions().getId(), entity.getId(), mEntrance, path));
} else {
showDeleteDialog(entity.getId());
}
@ -112,7 +113,8 @@ public class AnswerAdapter extends ListAdapter<AnswerEntity> implements ISyncAda
});
viewHolder.getBinding().title.setOnClickListener(v -> {
Questions questions = entity.getQuestions();
mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, questions.getId(), mEntrance, path));
// mContext.startActivity(QuestionsDetailActivity.getIntent(mContext, questions.getId(), mEntrance, path));
mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, questions.getId(), mEntrance, path));
});
break;
case ItemViewType.ITEM_FOOTER:

View File

@ -13,9 +13,11 @@ import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
import com.gh.gamecenter.baselist.ListAdapter
import com.gh.gamecenter.databinding.CommunityAnswerItemBinding
import com.gh.gamecenter.entity.CommunityEntity
import com.gh.gamecenter.qa.answer.CommunityAnswerItemViewHolder
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.qa.entity.ArticleEntity
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
class CommunityArticleAdapter(context: Context,
private val mViewModel: CommunityArticleViewModel,
@ -68,14 +70,21 @@ class CommunityArticleAdapter(context: Context,
}
}
if (entity.bbs == CommunityEntity()) {
entity.bbs = entity.community
}
holder.bindArticleItem(entity, mEntrance, path)
holder.itemView.setOnClickListener {
if (entity.active) {
mContext.startActivity(ArticleDetailActivity.getIntent(mContext, entity.community, entity.id, mEntrance, path))
if (entity.type == "question") {
mContext.startActivity(NewQuestionDetailActivity.getIntent(mContext, entity.id, mEntrance, path))
} else {
DialogUtils.showCancelAlertDialog(mContext, "提示", "内容已被删除,是否取消收藏?", "取消收藏", "暂不", {
mViewModel.deleteCollection(entity.community.id, entity.id)
}, null)
if (entity.active) {
mContext.startActivity(ArticleDetailActivity.getIntent(mContext, entity.community, entity.id, mEntrance, path))
} else {
DialogUtils.showCancelAlertDialog(mContext, "提示", "内容已被删除,是否取消收藏?", "取消收藏", "暂不", {
mViewModel.deleteCollection(entity.community.id, entity.id)
}, null)
}
}
if (!entity.read) {

View File

@ -9,6 +9,7 @@ import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.ListViewModel
import com.gh.gamecenter.baselist.LoadType
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.qa.entity.AnswerEntity
import com.gh.gamecenter.qa.entity.ArticleEntity
import com.gh.gamecenter.retrofit.BiResponse
import com.gh.gamecenter.retrofit.Response
@ -17,11 +18,11 @@ import com.lightgame.utils.Utils
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Function
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import retrofit2.HttpException
class CommunityArticleViewModel(application: Application) : ListViewModel<ArticleEntity, ArticleEntity>(application) {
var type: String = CommunityArticleFragment.Type.COLLECTION.value
@ -32,7 +33,17 @@ class CommunityArticleViewModel(application: Application) : ListViewModel<Articl
override fun provideDataSingle(page: Int): Single<List<ArticleEntity>> {
return if (type == CommunityArticleFragment.Type.COLLECTION.value) {
Single.fromObservable(RetrofitManager.getInstance(getApplication()).api.getCollectionCommunityArticle(UserManager.getInstance().userId, page))
Single.fromObservable(
RetrofitManager.getInstance(getApplication()).api
.getCollectionArticleAndQuestion(UserManager.getInstance().userId, page)
.flatMap(Function<List<AnswerEntity>, Observable<List<ArticleEntity>>> { list ->
Observable.create { emitter->
val articleList = list.map { it.transformArticleEntity() }.toList()
emitter.onNext(articleList)
emitter.onComplete()
}
})
)
} else {
if (page > 5) {
Single.create { it.onSuccess(arrayListOf()) }

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